PLaSK library
Loading...
Searching...
No Matches
fourier3d-python.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#define PY_ARRAY_UNIQUE_SYMBOL PLASK_OPTICAL_SLAB_ARRAY_API
15#define NO_IMPORT_ARRAY
16
17#include "fourier3d-python.hpp"
18#include "modal-python.hpp"
19
20namespace plask { namespace optical { namespace modal { namespace python {
21
22template <> inline const char* solver_compute_reflectivity_name<FourierSolver3D>() {
23 return "Fourier3D.compute_reflectivity";
24}
25
27 return "Fourier3D.compute_transmittivity";
28}
29
30
31template <NPY_TYPES type>
32static inline py::object arrayFromVec3D(cvector data, size_t minor, int dim) {
33 npy_intp dims[] = { npy_intp(data.size()/(2*minor)), npy_intp(minor), 2 };
34 npy_intp strides[] = { npy_intp(2*minor*sizeof(dcomplex)), npy_intp(2*sizeof(dcomplex)), npy_intp(sizeof(dcomplex)) };
35 PyObject* arr = PyArray_New(&PyArray_Type, dim, dims, type, strides, (void*)data.data(), 0, 0, NULL);
36 if (arr == nullptr) throw plask::CriticalException(u8"cannot create array from field coefficients");
38 py::object odata(wrap); py::incref(odata.ptr());
39 PyArray_SetBaseObject((PyArrayObject*)arr, odata.ptr()); // Make sure the data vector stays alive as long as the array
40 return py::object(py::handle<>(arr));
41}
42
43template <>
44py::object Eigenmodes<FourierSolver3D>::array(const dcomplex* data, size_t N) const {
45 npy_intp dims[] = { npy_intp(N/(2*solver.minor())), npy_intp(solver.minor()), 2 };
46 npy_intp strides[] = { npy_intp(2*solver.minor()*sizeof(dcomplex)), npy_intp(2*sizeof(dcomplex)), npy_intp(sizeof(dcomplex)) };
47 PyObject* arr = PyArray_New(&PyArray_Type, 3, dims, NPY_CDOUBLE, strides, (void*)data, 0, 0, NULL);
48 if (arr == nullptr) throw plask::CriticalException("cannot create array");
49 return py::object(py::handle<>(arr));
50}
51
52
53py::object FourierSolver3D_Mode__getattr__(const FourierSolver3D::Mode& mode, const std::string name) {
54 auto axes = getCurrentAxes();
55 if (name == "k"+axes->getNameForLong()) return py::object(mode.klong);
56 if (name == "k"+axes->getNameForTran()) return py::object(mode.ktran);
57 throw AttributeError(u8"'Mode' object has no attribute '{0}'", name);
58 return py::object();
59}
60
61
63 AxisNames* axes = getCurrentAxes();
64 std::string syml, symt;
65 switch (self.symmetry_long) {
66 case Expansion::E_TRAN: syml = "E" + axes->getNameForTran(); break;
67 case Expansion::E_LONG: syml = "E" + axes->getNameForLong(); break;
68 default: syml = "none";
69 }
70 switch (self.symmetry_tran) {
71 case Expansion::E_TRAN: symt = "E" + axes->getNameForTran(); break;
72 case Expansion::E_LONG: symt = "E" + axes->getNameForLong(); break;
73 default: symt = "none";
74 }
75 return syml + "," + symt;
76}
77
79 return format(u8"<lam: {}nm, klong: {}/um, ktran: {}/um, symmetry: ({}), power: {:.2g}mW>",
80 str(2e3*PI/self.k0, u8"({:.3f}{:+.3g}j)", "{:.3f}"),
81 str(self.klong, u8"{:.3f}{:+.3g}j", "{:.3f}"),
82 str(self.ktran, u8"{:.3f}{:+.3g}j", "{:.3f}"),
84 self.power
85 );
86}
88 return format(u8"Fourier3D.Mode(lam={0}, klong={1}, ktran={2}, symmetry=({3}), power={4})",
89 str(2e3*PI/self.k0),
90 str(self.klong),
91 str(self.ktran),
93 self.power
94 );
95}
96
97
98template <typename T>
103
106
108 if (i < 0) i = 2 - i;
109 switch (i) {
110 case 0: return WrappedType<T>::make(self, ptr_long);
111 case 1: return WrappedType<T>::make(self, ptr_tran);
112 default: throw IndexError(u8"index out of range");
113 }
114 }
115
116 void __setitem__(int i, const typename WrappedType<T>::Wrapper& value) {
117 if (i < 0) i = 2 - i;
118 switch (i) {
119 case 0: *ptr_long = value; self->invalidate(); return;
120 case 1: *ptr_tran = value; self->invalidate(); return;
121 default: throw IndexError(u8"index out of range");
122 }
123 }
124
125 typename WrappedType<T>::Wrapper __getattr__(const std::string& name) {
126 AxisNames* axes = getCurrentAxes();
127 if (name == "long" || name == "l" || name == axes->getNameForLong()) return WrappedType<T>::make(self, ptr_long);
128 if (name == "tran" || name == "t" || name == axes->getNameForTran()) return WrappedType<T>::make(self, ptr_tran);
129 throw AttributeError(u8"object has no attribute '{0}'", name);
130 }
131
132 void __setattr__(const std::string& name, const typename WrappedType<T>::Wrapper& value) {
133 AxisNames* axes = getCurrentAxes();
134 if (name == "long" || name == "l" || name == axes->getNameForLong()) { *ptr_long = value; self->invalidate(); }
135 else if (name == "tran" || name == "t" || name == axes->getNameForLong()) { *ptr_tran = value; self->invalidate(); }
136 else throw AttributeError(u8"object has no attribute '{0}'", name);
137 }
138
139 std::string __str__() {
140 return "(" + std::string(py::extract<std::string>(py::str(py::object(WrappedType<T>::make(self, ptr_long))))) + ", "
141 + std::string(py::extract<std::string>(py::str(py::object(WrappedType<T>::make(self, ptr_tran))))) + ")";
142 }
143
144 static void register_(const std::string& name) {
145 py::class_<FourierSolver3D_LongTranWrapper<T>>(name.c_str(), u8"Access wrapper for parameter along long/tran axis", py::no_init)
151 ;
152 }
153};
154
155template <typename T>
159
162
163 void operator()(FourierSolver3D& self, const py::object object) {
164 try {
165 typename WrappedType<T>::Extracted value = py::extract<typename WrappedType<T>::Extracted>(object);
166 self.*field_long = value;
167 self.*field_tran = value;
168 self.invalidate();
169 } catch (py::error_already_set&) {
170 PyErr_Clear();
171 try {
172 FourierSolver3D_LongTranWrapper<T>* value = py::extract<FourierSolver3D_LongTranWrapper<T>*>(object);
173 self.*field_long = *value->ptr_long;
174 self.*field_tran = *value->ptr_tran;
175 self.invalidate();
176 } catch (py::error_already_set&) {
177 PyErr_Clear();
178 try {
179 if (py::len(object) != 2) throw py::error_already_set();
180 T value_long = py::extract<T>(object[0]),
181 value_tran = py::extract<T>(object[1]);
182 self.*field_long = value_long;
183 self.*field_tran = value_tran;
184 self.invalidate();
185 } catch (py::error_already_set&) {
186 throw TypeError(u8"you may only assign a value or a sequence of two values");
187 }
188 }
189 }
190 }
191};
192
196
200
204
207
209
211 if (i < 0) i = 2 - i;
212 switch (i) {
213 case 0: return self->getSymmetryLong();
214 case 1: return self->getSymmetryTran();
215 default: throw IndexError(u8"index out of range");
216 }
217 }
218
220 if (i < 0) i = 2 - i;
221 switch (i) {
222 case 0: self->setSymmetryLong(value); return;
223 case 1: self->setSymmetryTran(value); return;
224 default: throw IndexError(u8"index out of range");
225 }
226 }
227
228 Expansion::Component __getattr__(const std::string& name) {
229 AxisNames* axes = getCurrentAxes();
230 if (name == "long" || name == "l" || name == axes->getNameForLong()) return self->getSymmetryLong();
231 if (name == "tran" || name == "t" || name == axes->getNameForTran()) return self->getSymmetryTran();
232 throw AttributeError(u8"object has no attribute '{0}'", name);
233 }
234
235 void __setattr__(const std::string& name, Expansion::Component value) {
236 AxisNames* axes = getCurrentAxes();
237 if (name == "long" || name == "l" || name == axes->getNameForLong()) self->setSymmetryLong(value);
238 else if (name == "tran" || name == "t" || name == axes->getNameForLong()) self->setSymmetryTran(value);
239 else throw AttributeError(u8"object has no attribute '{0}'", name);
240 }
241
242 std::string __str__() {
243 return "(" + std::string(py::extract<std::string>(py::str(py::object(self->getSymmetryLong())))) + ", "
244 + std::string(py::extract<std::string>(py::str(py::object(self->getSymmetryTran())))) + ")";
245 }
246
247 static void register_() {
248 py::class_<FourierSolver3D_SymmetryLongTranWrapper>("Symmetries", u8"Access wrapper for parameter along long/tran axis", py::no_init)
254 ;
255 }
256
260
261 static void setter(FourierSolver3D& self, py::object values) {
262 try { if (py::len(values) != 2 || py::extract<std::string>(values).check()) throw py::error_already_set(); }
263 catch (py::error_already_set&) { throw TypeError(u8"you may only assign a sequence of two values"); }
264 self.setSymmetryLong(py::extract<Expansion::Component>(values[0]));
265 self.setSymmetryTran(py::extract<Expansion::Component>(values[1]));
266 }
267};
268
269
270
271py::object FourierSolver3D_getDeterminant(py::tuple args, py::dict kwargs) {
272 if (py::len(args) != 1)
273 throw TypeError(u8"get_determinant() takes exactly one non-keyword argument ({0} given)", py::len(args));
274 FourierSolver3D* self = py::extract<FourierSolver3D*>(args[0]);
275 auto* expansion = &self->expansion;
276
277 enum What {
278 WHAT_NOTHING = 0,
279 WHAT_WAVELENGTH,
280 WHAT_K0,
281 WHAT_KLONG,
282 WHAT_KTRAN
283 };
284 What what = WHAT_NOTHING;
285 py::object array;
286
287 dcomplex klong = self->getKlong(), ktran = self->getKtran();
288 plask::optional<dcomplex> wavelength, k0;
289 std::string _klong, _ktran;
290
291 AxisNames* axes = getCurrentAxes();
292 py::stl_input_iterator<std::string> begin(kwargs), end;
293 for (auto i = begin; i != end; ++i) {
294 if (*i == "lam") {
295 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
296 if (what) throw TypeError(u8"only one key may be an array");
297 what = WHAT_WAVELENGTH; array = kwargs[*i];
298 } else
299 wavelength.reset(py::extract<dcomplex>(kwargs[*i]));
300 } else if (*i == "k0") {
301 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
302 if (what) throw TypeError(u8"only one key may be an array");
303 what = WHAT_K0; array = kwargs[*i];
304 } else
305 k0.reset(dcomplex(py::extract<dcomplex>(kwargs[*i])));
306 } else if (*i == "klong" || *i == "kl" || *i == "k"+axes->getNameForLong()) {
307 if (_klong != "") throw BadInput(self->getId(), u8"'{}' was already specified as '{}'", *i, _klong);
308 _klong = *i;
309 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
310 if (expansion->symmetric_long())
311 throw Exception("{0}: Cannot get determinant for longitudinal wavevector array with longitudinal symmetry",
312 self->getId());
313 if (what) throw TypeError(u8"only one key may be an array");
314 what = WHAT_KLONG; array = kwargs[*i];
315 } else {
316 klong = py::extract<dcomplex>(kwargs[*i]);
317 if (expansion->symmetric_long() && klong != 0.)
318 throw Exception("{0}: Longitudinal wavevector must be 0 with longitudinal symmetry", self->getId());
319 }
320 } else if (*i == "ktran" || *i == "kt" || *i == "k"+axes->getNameForTran()) {
321 if (_ktran != "") throw BadInput(self->getId(), u8"'{}' was already specified as '{}'", *i, _ktran);
322 _ktran = *i;
323 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
324 if (expansion->symmetric_tran())
325 throw Exception("{0}: Cannot get determinant for transverse wavevector array with transverse symmetry",
326 self->getId());
327 if (what) throw TypeError(u8"only one key may be an array");
328 what = WHAT_KTRAN; array = kwargs[*i];
329 } else {
330 ktran = py::extract<dcomplex>(kwargs[*i]);
331 if (expansion->symmetric_tran() && ktran != 0.)
332 throw Exception("{0}: Transverse wavevector must be 0 with transverse symmetry", self->getId());
333 }
334 } else
335 throw TypeError(u8"get_determinant() got unexpected keyword argument '{0}'", *i);
336 }
337
338 self->Solver::initCalculation();
339
340 if (wavelength) {
341 if (k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
342 expansion->setK0(2e3*PI / (*wavelength));
343 } else if (k0)
344 expansion->setK0(*k0);
345 else
346 expansion->setK0(self->getK0());
347
348 expansion->setKlong(klong);
349 expansion->setKtran(ktran);
350 expansion->setLam0(self->getLam0());
351 expansion->setSymmetryLong(self->getSymmetryLong());
352 expansion->setSymmetryTran(self->getSymmetryTran());
353
354 switch (what) {
355 case WHAT_NOTHING:
356 return py::object(self->getDeterminant());
357 case WHAT_WAVELENGTH:
358 return UFUNC<dcomplex>(
359 [self](dcomplex x) -> dcomplex { self->expansion.setK0(2e3*PI/x); return self->getDeterminant(); },
360 array, "Fourier3D.get_determinant", "lam"
361 );
362 case WHAT_K0:
363 return UFUNC<dcomplex>(
364 [self](dcomplex x) -> dcomplex { self->expansion.setK0(x); return self->getDeterminant(); },
365 array, "Fourier3D.get_determinant", "k0"
366 );
367 case WHAT_KLONG:
368 return UFUNC<dcomplex>(
369 [self](dcomplex x) -> dcomplex { self->expansion.setKlong(x); return self->getDeterminant(); },
370 array, "Fourier3D.get_determinant", "klong"
371 );
372 case WHAT_KTRAN:
373 return UFUNC<dcomplex>(
374 [self](dcomplex x) -> dcomplex { self->expansion.setKtran(x); return self->getDeterminant(); },
375 array, "Fourier3D.get_determinant", "ktran"
376 );
377 }
378 return py::object();
379}
380
381static size_t FourierSolver3D_setMode(py::tuple args, py::dict kwargs) {
382 if (py::len(args) != 1)
383 throw TypeError(u8"set_mode() takes exactly one non-keyword argument ({0} given)", py::len(args));
384 FourierSolver3D* self = py::extract<FourierSolver3D*>(args[0]);
385
386 plask::optional<dcomplex> wavelength, k0;
387 dcomplex klong = self->getKlong(), ktran = self->getKtran();
388
389 AxisNames* axes = getCurrentAxes();
390 py::stl_input_iterator<std::string> begin(kwargs), end;
391 for (auto i = begin; i != end; ++i) {
392 if (*i == "lam") {
393 wavelength.reset(py::extract<dcomplex>(kwargs[*i]));
394 } else if (*i == "k0")
395 k0.reset(dcomplex(py::extract<dcomplex>(kwargs[*i])));
396 else if (*i == "klong" || *i == "kl" || *i == "k"+axes->getNameForLong())
397 klong = py::extract<dcomplex>(kwargs[*i]);
398 else if (*i == "ktran" || *i == "kt" || *i == "k"+axes->getNameForTran())
399 ktran = py::extract<dcomplex>(kwargs[*i]);
400 else
401 throw TypeError(u8"set_mode() got unexpected keyword argument '{0}'", *i);
402 }
403
404 self->Solver::initCalculation();
405
406 if (wavelength) {
407 if (k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
408 self->expansion.setK0(2e3*PI / (*wavelength));
409 } else if (k0)
410 self->expansion.setK0(*k0);
411 else
412 self->expansion.setK0(self->getK0());
413
414 self->expansion.setKlong(klong);
415 self->expansion.setKtran(ktran);
416 self->expansion.setLam0(self->getLam0());
419
420 return self->setMode();
421}
422
423size_t FourierSolver3D_findMode(py::tuple args, py::dict kwargs) {
424 if (py::len(args) != 1)
425 throw TypeError(u8"find_mode() takes exactly one non-keyword argument ({0} given)", py::len(args));
426 FourierSolver3D* self = py::extract<FourierSolver3D*>(args[0]);
427
428 if (py::len(kwargs) != 1)
429 throw TypeError(u8"find_mode() takes exactly one keyword argument ({0} given)", py::len(kwargs));
430 std::string key = py::extract<std::string>(kwargs.keys()[0]);
431 dcomplex value = py::extract<dcomplex>(kwargs[key]);
432 AxisNames* axes = getCurrentAxes();
434
435 if (key == "lam")
437 else if (key == "k0")
439 else if (key == "klong" || key == "kl" || key == "k"+axes->getNameForLong())
441 else if (key == "ktran" || key == "kt" || key == "k"+axes->getNameForTran())
443 else
444 throw TypeError(u8"find_mode() got unexpected keyword argument '{0}'", key);
445
446 return self->findMode(what, value);
447}
448
449
450static py::object FourierSolver3D_getFieldVectorE(FourierSolver3D& self, int num, double z) {
451 if (num < 0) num += int(self.modes.size());
452 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
453 return arrayFromVec3D<NPY_CDOUBLE>(self.getFieldVectorE(std::size_t(num), z), self.minor(), 3);
454}
455
456static py::object FourierSolver3D_getFieldVectorH(FourierSolver3D& self, int num, double z) {
457 if (num < 0) num += int(self.modes.size());
458 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
459 return arrayFromVec3D<NPY_CDOUBLE>(self.getFieldVectorH(std::size_t(num), z), self.minor(), 3);
460}
461
462
463template <>
465 return arrayFromVec3D<NPY_CDOUBLE>(solver->getScatteredFieldVectorE(incident, side, z), solver->minor(), 3);
466}
467
468template <>
470 return arrayFromVec3D<NPY_CDOUBLE>(solver->getScatteredFieldVectorH(incident, side, z), solver->minor(), 3);
471}
472
473
474static cvector FourierSolver3D_gaussian(FourierSolver3D& self, Transfer::IncidentDirection side, Expansion::Component polarization, py::object sigma, py::object center) {
475 if (py::len(center) != 2)
476 throw ValueError("fourier3D.gaussian: 'center' must be a sequence of two floats");
477 double cx = py::extract<double>(center[0]);
478 double cy = py::extract<double>(center[1]);
479 double sx, sy;
480 try {
481 sx = sy = py::extract<double>(sigma);
482 } catch (py::error_already_set) {
483 PyErr_Clear();
484 if (!PySequence_Check(sigma.ptr()) || py::len(sigma) != 2)
485 throw ValueError("fourier3D.gaussian: 'sigma' must be a float or a sequence of two floats");
486 sx = py::extract<double>(sigma[0]);
487 sy = py::extract<double>(sigma[1]);
488 }
489 return self.incidentGaussian(side, polarization, sx, sy, cx, cy);
490}
491
492static py::object FourierSolver3D_incidentGaussian(FourierSolver3D& self, Transfer::IncidentDirection side, Expansion::Component polarization, py::object sigma, py::object center) {
493 return arrayFromVec<NPY_CDOUBLE>(FourierSolver3D_gaussian(self, side, polarization, sigma, center));
494}
495
496static shared_ptr<Scattering<FourierSolver3D>> FourierSolver3D_scatteringGaussian(FourierSolver3D& self, Transfer::IncidentDirection side, Expansion::Component polarization, py::object sigma, py::object center) {
497 return shared_ptr<Scattering<FourierSolver3D>>(new Scattering<FourierSolver3D>(&self, side, FourierSolver3D_gaussian(self, side, polarization, sigma, center)));
498}
499
500
502{
504 .value("DIRECT", FourierSolver3D::RULE_DIRECT)
505 .value("COMBINED", FourierSolver3D::RULE_COMBINED)
506 .value("INVERSE", FourierSolver3D::RULE_INVERSE)
507 .value("OLD", FourierSolver3D::RULE_OLD)
508 ;
509
510 py::object flow_module = py::object(py::handle<>(py::borrowed(PyImport_AddModule("plask.flow"))));
513 .value("COS2", GradientFunctions::COS2)
514 .value("COSSIN", GradientFunctions::COSSIN)
515 .value("C2", GradientFunctions::COS2)
516 .value("CS", GradientFunctions::COSSIN)
517 ;
518
519
520 CLASS(FourierSolver3D, "Fourier3D",
521 u8"Optical Solver using Fourier expansion in 3D.\n\n"
522 u8"It calculates optical modes and optical field distribution using Fourier modal method\n"
523 u8"and reflection transfer in three-dimensional Cartesian space.")
524 export_base(solver);
525 solver.add_property("size",
526 py::make_function(FourierSolver3D_getSize, py::with_custodian_and_ward_postcall<0,1>()),
528 py::default_call_policies(),
529 boost::mpl::vector3<void, FourierSolver3D&, py::object>()),
530 u8"Orthogonal expansion sizes in longitudinal and transverse directions.");
531 solver.add_property("refine",
532 py::make_function(FourierSolver3D_getRefine, py::with_custodian_and_ward_postcall<0,1>()),
534 py::default_call_policies(),
535 boost::mpl::vector3<void, FourierSolver3D&, py::object>()),
536 u8"Number of refinement points for refractive index averaging in longitudinal and transverse directions.");
537 solver.add_property("pmls",
538 py::make_function(FourierSolver3D_getPml, py::with_custodian_and_ward_postcall<0,1>()),
540 py::default_call_policies(),
541 boost::mpl::vector3<void, FourierSolver3D&, py::object>()),
542 u8"Longitudinal and transverse edge Perfectly Matched Layers boundary conditions.\n\n"
544 solver.add_property("symmetry",
545 py::make_function(&FourierSolver3D_SymmetryLongTranWrapper::getter, py::with_custodian_and_ward_postcall<0,1>()),
547 u8"Longitudinal and transverse mode symmetries.\n");
548 solver.add_property("dct", &__Class__::getDCT, &__Class__::setDCT, "Type of discrete cosine transform for symmetric expansion.");
549 solver.add_property("rule", &__Class__::getRule, &__Class__::setRule,
550 "Permittivity inversion rule.\n\n"
551 "Can be 'direct', 'inverse', 'combined', or 'old'. The new 'combined' rule is\n"
552 "supposed to provide the best convergence. 'old' is available for consistency\n"
553 "with old results.\n");
554 solver.add_property("grad_smooth", &__Class__::getGradSmooth, &__Class__::setGradSmooth,
555 "Smoothing parameter for material boundaries gradients (needed for the new expansion rule).");
556 solver.add_provider("outGradients", &__Class__::outGradients, "Gradients are important if the new factorization rule is used.");
557 solver.add_property("lam", &__Class__::getLam, &Solver_setLam<__Class__>,
558 u8"Wavelength of the light (nm).\n\n"
559 u8"Use this property only if you are looking for anything else than\n"
560 u8"the wavelength, e.g. the effective index of lateral wavevector.\n");
561 solver.add_property("wavelength", &__Class__::getLam, &Solver_setLam<__Class__>,
562 u8"Alias for :attr:`lam`");
563 solver.add_property("k0", &__Class__::getK0, &Solver_setK0<__Class__>,
564 u8"Normalized frequency of the light (1/µm).\n\n"
565 u8"Use this property only if you are looking for anything else than\n"
566 u8"the wavelength,e.g. the effective index of lateral wavevector.\n");
567 RW_PROPERTY(klong, getKlong, setKlong,
568 u8"Longitudinal propagation constant of the light (1/µm).\n\n"
569 u8"Use this property only if you are looking for anything else than\n"
570 u8"the longitudinal component of the propagation vector and the effective index.\n");
571 RW_PROPERTY(ktran, getKtran, setKtran,
572 u8"Transverse propagation constant of the light (1/µm).\n\n"
573 u8"Use this property only if you are looking for anything else than\n"
574 u8"the transverse component of the propagation vector.\n");
575 RW_FIELD(emission, u8"Direction of the useful light emission.\n\n"
576 u8"Necessary for the over-threshold model to correctly compute the output power.\n"
577 u8"Currently the fields are normalized only if this parameter is set to\n"
578 u8"``top`` or ``bottom``. Otherwise, it is ``undefined`` (default) and the fields\n"
579 u8"are not normalized.");
580 solver.def("get_determinant", py::raw_function(FourierSolver3D_getDeterminant),
581 u8"Compute discontinuity matrix determinant.\n\n"
582 u8"Arguments can be given through keywords only.\n\n"
583 u8"Args:\n"
584 u8" lam (complex): Wavelength (nm).\n"
585 u8" k0 (complex): Normalized frequency (1/µm).\n"
586 u8" klong (complex): Longitudinal wavevector (1/µm).\n"
587 u8" ktran (complex): Transverse wavevector (1/µm).\n");
588 solver.def("find_mode", py::raw_function(FourierSolver3D_findMode),
589 u8"Compute the mode near the specified effective index.\n\n"
590 u8"Only one of the following arguments can be given through a keyword.\n"
591 u8"It is the starting point for search of the specified parameter.\n\n"
592 u8"Args:\n"
593 u8" lam (complex): Wavelength (nm).\n"
594 u8" k0 (complex): Normalized frequency (1/µm).\n"
595 u8" klong (complex): Longitudinal wavevector (1/µm).\n"
596 u8" ktran (complex): Transverse wavevector (1/µm).\n");
597 solver.def("set_mode", py::raw_function(FourierSolver3D_setMode),
598 u8"Set the mode for specified parameters.\n\n"
599 u8"This method should be used if you have found a mode manually and want to insert\n"
600 u8"it into the solver in order to determine the fields. Calling this will raise an\n"
601 u8"exception if the determinant for the specified parameters is too large.\n\n"
602 u8"Arguments can be given through keywords only.\n\n"
603 u8"Args:\n"
604 u8" lam (complex): Wavelength (nm).\n"
605 u8" k0 (complex): Normalized frequency (1/µm).\n"
606 u8" klong (complex): Longitudinal wavevector (1/µm).\n"
607 u8" ktran (complex): Transverse wavevector (1/µm).\n");
608 solver.def("compute_reflectivity", &Solver_computeReflectivity_polarization<FourierSolver3D>,
609 (py::arg("lam"), "side", "polarization"));
610 solver.def("compute_reflectivity", &Solver_computeReflectivity_index<FourierSolver3D>,
611 (py::arg("lam"), "side", "index"));
612 solver.def("compute_reflectivity", &Solver_computeReflectivity_array<FourierSolver3D>,
613 (py::arg("lam"), "side", "coffs"),
614 u8"Compute reflection coefficient on planar incidence [%].\n\n"
615 u8"Args:\n"
616 u8" lam (float or array of floats): Incident light wavelength (nm).\n"
617 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
618 u8" present.\n"
619 u8" polarization: Specification of the incident light polarization.\n"
620 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis\n"
621 u8" name of the non-vanishing electric field component.\n"
622 u8" idx: Eigenmode number.\n"
623 u8" coeffs: expansion coefficients of the incident vector.\n");
624 solver.def("compute_transmittivity", &Solver_computeTransmittivity_polarization<FourierSolver3D>,
625 (py::arg("lam"), "side", "polarization"));
626 solver.def("compute_transmittivity", &Solver_computeTransmittivity_index<FourierSolver3D>,
627 (py::arg("lam"), "side", "index"));
628 solver.def("compute_transmittivity", &Solver_computeTransmittivity_array<FourierSolver3D>,
629 (py::arg("lam"), "side", "coffs"),
630 u8"Compute transmission coefficient on planar incidence [%].\n\n"
631 u8"Args:\n"
632 u8" lam (float or array of floats): Incident light wavelength (nm).\n"
633 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
634 u8" present.\n"
635 u8" polarization: Specification of the incident light polarization.\n"
636 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
637 u8" of the non-vanishing electric field component.\n"
638 u8" idx: Eigenmode number.\n"
639 u8" coeffs: expansion coefficients of the incident vector.\n");
640 solver.def("scattering", Scattering<FourierSolver3D>::from_polarization, py::with_custodian_and_ward_postcall<0,1>(), (py::arg("side"), "polarization"));
641 solver.def("scattering", Scattering<FourierSolver3D>::from_index, py::with_custodian_and_ward_postcall<0,1>(), (py::arg("side"), "idx"));
642 solver.def("scattering", Scattering<FourierSolver3D>::from_array, py::with_custodian_and_ward_postcall<0,1>(), (py::arg("side"), "coeffs"),
643 u8"Access to the reflected field.\n\n"
644 u8"Args:\n"
645 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
646 u8" present.\n"
647 u8" polarization: Specification of the incident light polarization.\n"
648 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
649 u8" of the non-vanishing electric field component.\n"
650 u8" idx: Eigenmode number.\n"
651 u8" coeffs: expansion coefficients of the incident vector.\n\n"
652 u8":rtype: Fourier3D.Scattering\n"
653 );
654 solver.def("get_raw_E", FourierSolver3D_getFieldVectorE, (py::arg("num"), "level"),
655 u8"Get Fourier expansion coefficients for the electric field.\n\n"
656 u8"This is a low-level function returning $E_l$ and/or $E_t$ Fourier\n"
657 u8"expansion coefficients. Please refer to the detailed solver description for their\n"
658 u8"interpretation.\n\n"
659 u8"Args:\n"
660 u8" num (int): Computed mode number.\n"
661 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
662 u8":rtype: numpy.ndarray\n"
663 );
664 solver.def("get_raw_H", FourierSolver3D_getFieldVectorH, (py::arg("num"), "level"),
665 u8"Get Fourier expansion coefficients for the magnetic field.\n\n"
666 u8"This is a low-level function returning $H_l$ and/or $H_t$ Fourier\n"
667 u8"expansion coefficients. Please refer to the detailed solver description for their\n"
668 u8"interpretation.\n\n"
669 u8"Args:\n"
670 u8" num (int): Computed mode number.\n"
671 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
672 u8":rtype: numpy.ndarray\n"
673 );
674 solver.def("layer_eigenmodes", &Eigenmodes<FourierSolver3D>::fromZ, py::arg("level"),
675 u8"Get eignemodes for a layer at specified level.\n\n"
676 u8"This is a low-level function to access diagonalized eigenmodes for a specific\n"
677 u8"layer. Please refer to the detailed solver description for the interpretation\n"
678 u8"of the returned values.\n\n"
679 u8"Args:\n"
680 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
681 u8":rtype: :class:`~optical.modal.Fourier3D.Eigenmodes`\n",
682 py::with_custodian_and_ward_postcall<0,1>()
683 );
684 RO_FIELD(modes, "Computed modes.");
685 solver.def("gaussian", &FourierSolver3D_incidentGaussian, (py::arg("side"), "polarization", "sigma", py::arg("center")=py::make_tuple(0., 0.)),
686 u8"Create coefficients vector with Gaussian profile.\n\n"
687 u8"This method is intended to use for :py:meth:`scattering` method.\n\n"
688 u8"Args:\n"
689 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
690 u8" present.\n"
691 u8" polarization: Specification of the incident light polarization.\n"
692 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
693 u8" of the non-vanishing electric field component.\n"
694 u8" sigma (float or tuple): Gaussian standard deviation in longitudinal and\n"
695 u8" transverse directions [µm, µm].\n"
696 u8" center (tuple): Position of the beam center [µm, µm].\n\n"
697 u8"Example:\n"
698 u8" >>> scattered = fourier.scattering('top', \n"
699 u8" ... fourier.gaussian('top', 'Ex', 0.2))\n"
700 );
701 solver.def("scattering_gaussian", &FourierSolver3D_scatteringGaussian, (py::arg("side"), "polarization", "sigma", py::arg("center")=py::make_tuple(0., 0.)),
702 u8"Helper function to Access reflected fields for access incidence.\n\n"
703 u8"This method is equivalent to calling:\n\n"
704 u8" >>> fourier.scattering(side,\n"
705 u8" ... fourier.gaussian(side, polarization, sigma, center))\n\n"
706 u8"Args:\n"
707 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
708 u8" present.\n"
709 u8" polarization: Specification of the incident light polarization.\n"
710 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
711 u8" of the non-vanishing electric field component.\n"
712 u8" sigma (float): Gaussian standard deviation (µm).\n"
713 u8" center (float): Position of the beam center (µm).\n\n"
714 );
715
716 py::scope scope = solver;
717 (void) scope; // don't warn about unused variable scope
718
720 py::class_<FourierSolver3D::Mode>("Mode", u8"Detailed information about the mode.", py::no_init)
721 .add_property("symmetry", &FourierSolver3D_Mode_symmetry, u8"Mode horizontal symmetry.")
722 .add_property("lam", &getModeWavelength<FourierSolver3D::Mode>, u8"Mode wavelength (nm).")
723 .add_property("wavelength", &getModeWavelength<FourierSolver3D::Mode>, u8"Mode wavelength (nm).")
724 .def_readonly("k0", &FourierSolver3D::Mode::k0, u8"Mode normalized frequency (1/µm).")
725 .def_readonly("klong", &FourierSolver3D::Mode::klong, u8"Mode longitudinal wavevector (1/µm).")
726 .def_readonly("ktran", &FourierSolver3D::Mode::ktran, u8"Mode transverse wavevector (1/µm).")
727 .def_readwrite("power", &FourierSolver3D::Mode::power, u8"Total power emitted into the mode (mW).")
728 .def("__str__", &FourierSolver3D_Mode_str)
729 .def("__repr__", &FourierSolver3D_Mode_repr)
730 .def("__getattr__", &FourierSolver3D_Mode__getattr__)
731 ;
732
735
740}
741
742}}}} // namespace plask::optical::modal::python