PLaSK library
Loading...
Searching...
No Matches
fourier2d-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 "fourier2d-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<FourierSolver2D>() {
23 return "Fourier2D.compute_reflectivity";
24}
25
27 return "Fourier2D.compute_transmittivity";
28}
29
30
31template <> py::object Eigenmodes<FourierSolver2D>::array(const dcomplex* data, size_t N) const {
32 int dim = 2, strid = 2;
33 if (solver.separated()) strid = dim = 1;
34 npy_intp dims[] = {npy_intp(N / strid), npy_intp(strid)};
35 npy_intp strides[] = {npy_intp(strid * sizeof(dcomplex)), npy_intp(sizeof(dcomplex))};
36 PyObject* arr = PyArray_New(&PyArray_Type, dim, dims, NPY_CDOUBLE, strides, (void*)data, 0, 0, NULL);
37 if (arr == nullptr) throw plask::CriticalException("cannot create array");
38 return py::object(py::handle<>(arr));
39}
40
41inline static std::string polarization_str(Expansion::Component val) {
42 AxisNames* axes = getCurrentAxes();
43 switch (val) {
44 case Expansion::E_TRAN: return "E" + axes->getNameForTran();
45 case Expansion::E_LONG: return "E" + axes->getNameForLong();
46 default: return "none";
47 }
48}
49
50static py::object FourierSolver2D_getMirrors(const FourierSolver2D& self) {
51 if (!self.mirrors) return py::object();
52 return py::make_tuple(self.mirrors->first, self.mirrors->second);
53}
54
55static void FourierSolver2D_setMirrors(FourierSolver2D& self, py::object value) {
56 if (value.is_none())
57 self.mirrors.reset();
58 else {
59 try {
60 double v = py::extract<double>(value);
61 self.mirrors.reset(std::make_pair(v, v));
62 } catch (py::error_already_set&) {
64 try {
65 if (py::len(value) != 2) throw py::error_already_set();
66 self.mirrors.reset(
67 std::make_pair<double, double>(double(py::extract<double>(value[0])), double(py::extract<double>(value[1]))));
68 } catch (py::error_already_set&) {
69 throw ValueError("none, float, or tuple of two floats required");
70 }
71 }
72 }
73}
74
75static py::object FourierSolver2D_getDeterminant(py::tuple args, py::dict kwargs) {
76 if (py::len(args) != 1)
77 throw TypeError(u8"get_determinant() takes exactly one non-keyword argument ({0} given)", py::len(args));
78 FourierSolver2D* self = py::extract<FourierSolver2D*>(args[0]);
79 auto* expansion = &self->expansion;
80
81 enum What { WHAT_NOTHING = 0, WHAT_WAVELENGTH, WHAT_K0, WHAT_NEFF, WHAT_KTRAN, WHAT_BETA };
82 What what = WHAT_NOTHING;
83 py::object array;
84
85 plask::optional<dcomplex> k0, neff, ktran, beta;
86 std::string _ktran, _beta;
87
89 py::stl_input_iterator<std::string> begin(kwargs), end;
90 for (auto i = begin; i != end; ++i) {
91 if (*i == "lam") {
92 if (what == WHAT_K0 || k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
93 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
94 if (what) throw TypeError(u8"only one key may be an array");
95 what = WHAT_WAVELENGTH;
96 array = kwargs[*i];
97 } else
98 k0.reset(2e3 * PI / py::extract<dcomplex>(kwargs[*i])());
99 } else if (*i == "k0") {
100 if (what == WHAT_WAVELENGTH || k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
101 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
102 if (what) throw TypeError(u8"only one key may be an array");
103 what = WHAT_K0;
104 array = kwargs[*i];
105 } else
106 k0.reset(py::extract<dcomplex>(kwargs[*i]));
107 } else if (*i == "neff") {
108 if (what == WHAT_BETA || beta) throw BadInput(self->getId(), u8"'neff' and '{}' are mutually exclusive", _beta);
109 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
110 if (expansion->separated())
111 throw Exception("{0}: Cannot get determinant for effective index array with polarization separation",
112 self->getId());
113 if (what) throw TypeError(u8"only one key may be an array");
114 what = WHAT_NEFF;
115 array = kwargs[*i];
116 } else {
117 neff.reset(py::extract<dcomplex>(kwargs[*i]));
118 if (expansion->separated() && *neff != 0.)
119 throw Exception("{0}: Effective index must be 0 with polarization separation",
120 self->getId());
121 }
122 } else if (*i == "ktran" || *i == "kt" || *i == "k" + axes->getNameForTran()) {
123 if (what == WHAT_KTRAN || ktran) throw BadInput(self->getId(), u8"'{}' was already specified as '{}'", *i, _ktran);
124 _ktran = *i;
125 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
126 if (expansion->symmetric())
127 throw Exception("{0}: Cannot get determinant for transverse wavevector array with symmetry", self->getId());
128 if (what) throw TypeError(u8"only one key may be an array");
129 what = WHAT_KTRAN;
130 array = kwargs[*i];
131 } else {
132 ktran.reset(py::extract<dcomplex>(kwargs[*i]));
133 if (expansion->symmetric() && *ktran != 0.)
134 throw Exception("{0}: Transverse wavevector must be 0 with symmetry",
135 self->getId());
136 }
137 } else if (*i == "beta" || *i == "klong" || *i == "kl" || *i == "k"+axes->getNameForLong()) {
138 if (what == WHAT_BETA || beta) throw BadInput(self->getId(), u8"'{}' was already specified as '{}'", *i, _beta);
139 _beta = *i;
140 if (what == WHAT_NEFF || neff) throw BadInput(self->getId(), u8"'{}' and 'neff' are mutually exclusive", *i);
141 if (PyArray_Check(py::object(kwargs[*i]).ptr())) {
142 if (expansion->separated())
143 throw Exception("{0}: Cannot get determinant for longitudinal wavevector array with polarization separation",
144 self->getId());
145 if (what) throw TypeError(u8"only one key may be an array");
146 what = WHAT_BETA;
147 array = kwargs[*i];
148 } else {
149 beta.reset(py::extract<dcomplex>(kwargs[*i]));
150 if (expansion->separated() && *beta != 0.)
151 throw Exception("{0}: Longitudinal wavevector must be 0 with polarization separation",
152 self->getId());
153 }
154 } else
155 throw TypeError(u8"get_determinant() got unexpected keyword argument '{0}'", *i);
156 }
157
158 self->Solver::initCalculation();
159
160 if (k0) expansion->setK0(*k0);
161 else expansion->setK0(self->getK0());
162 if (neff) { if (what != WHAT_WAVELENGTH && what != WHAT_K0) expansion->setBeta(*neff * expansion->k0); }
163 else if (beta) expansion->setBeta(*beta);
164 else expansion->setBeta(self->getBeta());
165 if (ktran) expansion->setKtran(*ktran);
166 else expansion->setKtran(self->getKtran());
167 expansion->setLam0(self->getLam0());
168 expansion->setSymmetry(self->getSymmetry());
169 expansion->setPolarization(self->getPolarization());
170
171 switch (what) {
172 case WHAT_NOTHING: return py::object(self->getDeterminant());
173 case WHAT_WAVELENGTH:
174 return UFUNC<dcomplex>(
175 [self, neff](dcomplex x) -> dcomplex {
176 self->expansion.setK0(2e3 * PI / x);
177 if (neff) self->expansion.setBeta(*neff * self->expansion.k0);
178 return self->getDeterminant();
179 },
180 array,
181 "Fourier2D.get_determinant",
182 "lam");
183 case WHAT_K0:
184 return UFUNC<dcomplex>(
185 [self, neff](dcomplex x) -> dcomplex {
186 self->expansion.setK0(x);
187 if (neff) self->expansion.setBeta(*neff * x);
188 return self->getDeterminant();
189 },
190 array,
191 "Fourier2D.get_determinant",
192 "k0");
193 case WHAT_NEFF:
194 return UFUNC<dcomplex>(
195 [self](dcomplex x) -> dcomplex {
196 self->expansion.setBeta(x * self->getK0());
197 return self->getDeterminant();
198 },
199 array,
200 "Fourier2D.get_determinant",
201 "neff");
202 case WHAT_KTRAN:
203 return UFUNC<dcomplex>(
204 [self](dcomplex x) -> dcomplex {
205 self->expansion.setKtran(x);
206 return self->getDeterminant();
207 },
208 array,
209 "Fourier2D.get_determinant",
210 "ktran");
211 case WHAT_BETA:
212 return UFUNC<dcomplex>(
213 [self](dcomplex x) -> dcomplex {
214 self->expansion.setBeta(x);
215 return self->getDeterminant();
216 },
217 array,
218 "Fourier2D.get_determinant",
219 "beta");
220 }
221 return py::object();
222}
223
224static size_t FourierSolver2D_setMode(py::tuple args, py::dict kwargs) {
225 if (py::len(args) != 1) throw TypeError(u8"set_mode() takes exactly one non-keyword argument ({0} given)", py::len(args));
226 FourierSolver2D* self = py::extract<FourierSolver2D*>(args[0]);
227 auto* expansion = &self->expansion;
228
230 plask::optional<dcomplex> k0, neff, ktran;
231 py::stl_input_iterator<std::string> begin(kwargs), end;
232 for (auto i = begin; i != end; ++i) {
233 if (*i == "lam" || *i == "wavelength") {
234 if (k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
235 k0.reset(2e3 * PI / py::extract<dcomplex>(kwargs[*i])());
236 } else if (*i == "k0") {
237 if (k0) throw BadInput(self->getId(), u8"'lam' and 'k0' are mutually exclusive");
238 k0.reset(py::extract<dcomplex>(kwargs[*i]));
239 } else if (*i == "neff") {
240 neff.reset(py::extract<dcomplex>(kwargs[*i]));
241 } else if (*i == "ktran" || *i == "kt" || *i == "k" + axes->getNameForTran()) {
242 ktran.reset(py::extract<dcomplex>(kwargs[*i]));
243 } else
244 throw TypeError(u8"set_mode() got unexpected keyword argument '{0}'", *i);
245 }
246
247 self->Solver::initCalculation();
248
249 if (k0)
250 expansion->setK0(*k0);
251 else
252 expansion->setK0(self->getK0());
253 if (neff)
254 expansion->setBeta(*neff * expansion->k0);
255 else
256 expansion->setBeta(self->getBeta());
257 if (ktran)
258 expansion->setKtran(*ktran);
259 else
260 expansion->setKtran(self->getKtran());
261 expansion->setLam0(self->getLam0());
262 expansion->setSymmetry(self->getSymmetry());
263 expansion->setPolarization(self->getPolarization());
264
265 return self->setMode();
266}
267
268static size_t FourierSolver2D_findMode(py::tuple args, py::dict kwargs) {
269 if (py::len(args) != 1) throw TypeError(u8"find_mode() takes exactly one non-keyword argument ({0} given)", py::len(args));
270 FourierSolver2D* self = py::extract<FourierSolver2D*>(args[0]);
271
272 if (py::len(kwargs) != 1) throw TypeError(u8"find_mode() takes exactly one keyword argument ({0} given)", py::len(kwargs));
273 std::string key = py::extract<std::string>(kwargs.keys()[0]);
274 dcomplex value = py::extract<dcomplex>(kwargs[key]);
277
278 if (key == "lam" || key == "wavelength")
280 else if (key == "k0")
282 else if (key == "neff")
284 else if (key == "ktran" || key == "kt" || key == "k" + axes->getNameForTran())
286 else if (key == "beta" || key == "klong" || key == "kl" || key == "k" + axes->getNameForTran())
288 else
289 throw TypeError(u8"find_mode() got unexpected keyword argument '{0}'", key);
290
291 return self->findMode(what, value);
292}
293
294static dcomplex FourierSolver2D_Mode_Neff(const FourierSolver2D::Mode& mode) { return mode.beta / mode.k0; }
295
296static py::object FourierSolver2D_Mode__getattr__(const FourierSolver2D::Mode& mode, const std::string name) {
297 auto axes = getCurrentAxes();
298 if (name == "k" + axes->getNameForLong()) return py::object(mode.beta);
299 if (name == "k" + axes->getNameForTran()) return py::object(mode.ktran);
300 throw AttributeError(u8"'Mode' object has no attribute '{0}'", name);
301 return py::object();
302}
303
304static std::string FourierSolver2D_Mode_str(const FourierSolver2D::Mode& self) {
306 std::string pol;
307 switch (self.polarization) {
308 case Expansion::E_TRAN: pol = "E" + axes->getNameForTran(); break;
309 case Expansion::E_LONG: pol = "E" + axes->getNameForLong(); break;
310 default: pol = "none";
311 }
312 std::string sym;
313 switch (self.symmetry) {
314 case Expansion::E_TRAN: sym = "E" + axes->getNameForTran(); break;
315 case Expansion::E_LONG: sym = "E" + axes->getNameForLong(); break;
316 default: sym = "none";
317 }
318 return format(u8"<lam: {:.2f}nm, neff: {}, ktran: {}/um, polarization: {}, symmetry: {}, power: {:.2g} mW>",
319 real(2e3 * PI / self.k0), str(self.beta / self.k0, "{:.3f}{:+.3g}j"),
320 str(self.ktran, "({:.3g}{:+.3g}j)", "{:.3g}"), pol, sym, self.power);
321}
322static std::string FourierSolver2D_Mode_repr(const FourierSolver2D::Mode& self) {
324 std::string pol;
325 switch (self.polarization) {
326 case Expansion::E_TRAN: pol = "'E" + axes->getNameForTran() + "'"; break;
327 case Expansion::E_LONG: pol = "'E" + axes->getNameForLong() + "'"; break;
328 default: pol = "None";
329 }
330 std::string sym;
331 switch (self.symmetry) {
332 case Expansion::E_TRAN: sym = "'E" + axes->getNameForTran() + "'"; break;
333 case Expansion::E_LONG: sym = "'E" + axes->getNameForLong() + "'"; break;
334 default: sym = "None";
335 }
336 return format(u8"Fourier2D.Mode(lam={0}, neff={1}, ktran={2}, polarization={3}, symmetry={4}, power={5})",
337 str(2e3 * PI / self.k0), str(self.beta / self.k0), str(self.ktran), pol, sym, self.power);
338}
339
340static py::object FourierSolver2D_getFieldVectorE(FourierSolver2D& self, int num, double z) {
341 if (num < 0) num += int(self.modes.size());
342 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
343 return arrayFromVec2D<NPY_CDOUBLE>(self.getFieldVectorE(num, z), self.separated(), 2);
344}
345
346static py::object FourierSolver2D_getFieldVectorH(FourierSolver2D& self, int num, double z) {
347 if (num < 0) num += int(self.modes.size());
348 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
349 return arrayFromVec2D<NPY_CDOUBLE>(self.getFieldVectorH(num, z), self.separated(), 2);
350}
351
352template <>
354 return arrayFromVec2D<NPY_CDOUBLE>(solver->getScatteredFieldVectorE(incident, side, z), solver->separated(), 2);
355}
356
357template <>
359 return arrayFromVec2D<NPY_CDOUBLE>(solver->getScatteredFieldVectorH(incident, side, z), solver->separated(), 2);
360}
361
362
363static py::object FourierSolver2D_incidentGaussian(FourierSolver2D& self,
365 Expansion::Component polarization,
366 double sigma,
367 double center) {
368 return arrayFromVec<NPY_CDOUBLE>(self.incidentGaussian(side, polarization, sigma, center));
369}
370
371static shared_ptr<Scattering<FourierSolver2D>> FourierSolver2D_scatteringGaussian(FourierSolver2D& self,
373 Expansion::Component polarization,
374 double sigma,
375 double center) {
377 new Scattering<FourierSolver2D>(&self, side, self.incidentGaussian(side, polarization, sigma, center)));
378}
379
380namespace detail {
381 static Expansion::Component getPolarization2D(PyObject* obj) {
383 if (obj == Py_None) {
385 } else {
386 AxisNames* axes = getCurrentAxes();
387 try {
388 std::string repr = py::extract<std::string>(obj);
389 if (repr == "none" || repr == "NONE" || repr == "None")
391 else if (repr == "Etran" || repr == "Et" || repr == "E"+axes->getNameForTran() ||
392 repr == "Hlong" || repr == "Hl" || repr == "H"+axes->getNameForLong() || repr == "TM")
393 val = Expansion::E_TRAN;
394 else if (repr == "Elong" || repr == "El" || repr == "E"+axes->getNameForLong() ||
395 repr == "Htran" || repr == "Ht" || repr == "H"+axes->getNameForTran() || repr == "TE")
396 val = Expansion::E_LONG;
397 else
398 throw py::error_already_set();
399 } catch (py::error_already_set&) {
400 throw ValueError("wrong component specification.");
401 }
402 }
403 return val;
404 }
405}
406
408 self->setPolarization(detail::getPolarization2D(polarization));
409}
410
412 py::object wavelength,
414 PyObject* polarization
415 ) {
416 return Solver_computeReflectivity_polarization(self, wavelength, side, detail::getPolarization2D(polarization));
417}
418
420 py::object wavelength,
422 PyObject* polarization
423 ) {
424 return Solver_computeTransmittivity_polarization(self, wavelength, side, detail::getPolarization2D(polarization));
425}
426
429 PyObject* polarization
430 ) {
431 return Scattering<FourierSolver2D>::from_polarization(parent, side, detail::getPolarization2D(polarization));
432}
433
434
437 .value("DISCRETE", FourierSolver2D::FOURIER_DISCRETE)
438 .value("ANALYTIC", FourierSolver2D::FOURIER_ANALYTIC);
439
440 CLASS(FourierSolver2D, "Fourier2D",
441 u8"Optical Solver using Fourier expansion in 2D.\n\n"
442 u8"It calculates optical modes and optical field distribution using Fourier modal method\n"
443 u8"and reflection transfer in two-dimensional Cartesian space.")
444 export_base(solver);
445 PROVIDER(outNeff, "Effective index of the last computed mode.");
446 RW_PROPERTY(size, getSize, setSize, "Orthogonal expansion size.");
447 RW_PROPERTY(symmetry, getSymmetry, setSymmetry, "Mode symmetry.");
448 solver.add_property("polarization", &__Class__::getPolarization, &FourierSolver2D_setPolarization, "Mode polarization.");
449 solver.add_property("lam", &__Class__::getLam, &Solver_setLam<__Class__>,
450 u8"Wavelength of the light (nm).\n\n"
451 u8"Use this property only if you are looking for anything else than\n"
452 u8"the wavelength, e.g. the effective index of lateral wavevector.\n");
453 solver.add_property("wavelength", &__Class__::getLam, &Solver_setLam<__Class__>, u8"Alias for :attr:`lam`");
454 solver.add_property("k0", &__Class__::getK0, &Solver_setK0<__Class__>,
455 u8"Normalized frequency of the light (1/µm).\n\n"
456 u8"Use this property only if you are looking for anything else than\n"
457 u8"the wavelength,e.g. the effective index of lateral wavevector.\n");
458 RW_PROPERTY(klong, getBeta, setBeta,
459 u8"Longitudinal propagation constant of the light (1/µm).\n\n"
460 u8"Use this property only if you are looking for anything else than\n"
461 u8"the longitudinal component of the propagation vector and the effective index.\n");
462 RW_PROPERTY(beta, getBeta, setBeta,
463 u8"Alias for :attr:`klong`\n");
464 RW_PROPERTY(ktran, getKtran, setKtran,
465 u8"Transverse propagation constant of the light (1/µm).\n\n"
466 u8"Use this property only if you are looking for anything else than\n"
467 u8"the transverse component of the propagation vector.\n");
468 RW_FIELD(refine, "Number of refinement points for refractive index averaging.");
469 solver.add_property("ft", &__Class__::getFourierType, &__Class__::setFourierType,
470 u8"Type of the Fourier transform. Analytic transform is faster and more precise,\n"
471 u8"however it ignores temperature and gain distributions.\n");
472 solver.add_property("dct", &__Class__::getDCT, &__Class__::setDCT,
473 "Type of discrete cosine transform for symmetric expansion.");
474 RW_FIELD(emission,
475 "Direction of the useful light emission.\n\n"
476 u8"Necessary for the over-threshold model to correctly compute the output power.\n"
477 u8"Currently the fields are normalized only if this parameter is set to\n"
478 u8"``top`` or ``bottom``. Otherwise, it is ``undefined`` (default) and the fields\n"
479 u8"are not normalized.");
480 solver.def("get_determinant", py::raw_function(FourierSolver2D_getDeterminant),
481 u8"Compute discontinuity matrix determinant.\n\n"
482 u8"Arguments can be given through keywords only.\n\n"
483 u8"Args:\n"
484 u8" lam (complex): Wavelength (nm).\n"
485 u8" k0 (complex): Normalized frequency (1/µm).\n"
486 u8" neff (complex): Longitudinal effective index.\n"
487 u8" ktran (complex): Transverse wavevector (1/µm).\n");
488 solver.def("find_mode", py::raw_function(FourierSolver2D_findMode),
489 u8"Compute the mode near the specified effective index.\n\n"
490 u8"Only one of the following arguments can be given through a keyword.\n"
491 u8"It is the starting point for search of the specified parameter.\n\n"
492 u8"Args:\n"
493 u8" lam (complex): Wavelength (nm).\n"
494 u8" k0 (complex): Normalized frequency (1/µm).\n"
495 u8" neff (complex): Longitudinal effective index.\n"
496 u8" ktran (complex): Transverse wavevector (1/µm).\n");
497 solver.def("set_mode", py::raw_function(FourierSolver2D_setMode),
498 u8"Set the mode for specified parameters.\n\n"
499 u8"This method should be used if you have found a mode manually and want to insert\n"
500 u8"it into the solver in order to determine the fields. Calling this will raise an\n"
501 u8"exception if the determinant for the specified parameters is too large.\n\n"
502 u8"Arguments can be given through keywords only.\n\n"
503 u8"Args:\n"
504 u8" lam (complex): Wavelength (nm).\n"
505 u8" k0 (complex): Normalized frequency (1/µm).\n"
506 u8" neff (complex): Longitudinal effective index.\n"
507 u8" ktran (complex): Transverse wavevector (1/µm).\n");
508 solver.def("compute_reflectivity", &FourierSolver2D_computeReflectivity_polarization,
509 (py::arg("lam"), "side", "polarization"));
510 solver.def("compute_reflectivity", &Solver_computeReflectivity_index<FourierSolver2D>, (py::arg("lam"), "side", "index"));
511 solver.def("compute_reflectivity", &Solver_computeReflectivity_array<FourierSolver2D>, (py::arg("lam"), "side", "coffs"),
512 u8"Compute reflection coefficient on planar incidence [%].\n\n"
513 u8"Args:\n"
514 u8" lam (float or array of floats): Incident light wavelength (nm).\n"
515 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
516 u8" present.\n"
517 u8" polarization: Specification of the incident light polarization.\n"
518 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis\n"
519 u8" name of the non-vanishing electric field component.\n"
520 u8" idx: Eigenmode number.\n"
521 u8" coeffs: expansion coefficients of the incident vector.\n");
522 solver.def("compute_transmittivity", &FourierSolver2D_computeTransmittivity_polarization,
523 (py::arg("lam"), "side", "polarization"));
524 solver.def("compute_transmittivity", &Solver_computeTransmittivity_index<FourierSolver2D>, (py::arg("lam"), "side", "index"));
525 solver.def("compute_transmittivity", &Solver_computeTransmittivity_array<FourierSolver2D>, (py::arg("lam"), "side", "coffs"),
526 u8"Compute transmission coefficient on planar incidence [%].\n\n"
527 u8"Args:\n"
528 u8" lam (float or array of floats): Incident light wavelength (nm).\n"
529 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
530 u8" present.\n"
531 u8" polarization: Specification of the incident light polarization.\n"
532 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
533 u8" of the non-vanishing electric field component.\n"
534 u8" idx: Eigenmode number.\n"
535 u8" coeffs: expansion coefficients of the incident vector.\n");
536 solver.add_property("mirrors", FourierSolver2D_getMirrors, FourierSolver2D_setMirrors,
537 u8"Mirror reflectivities. If None then they are automatically estimated from the\n"
538 u8"Fresnel equations.");
539 solver.add_property("pml", py::make_function(&Solver_getPML<FourierSolver2D>, py::with_custodian_and_ward_postcall<0, 1>()),
540 &Solver_setPML<FourierSolver2D>, u8"Side Perfectly Matched Layers boundary conditions.\n\n" PML_ATTRS_DOC);
541 RO_FIELD(modes, "Computed modes.");
542 solver.def("scattering", FourierSolver2D_Scattering_from_polarization, py::with_custodian_and_ward_postcall<0, 1>(),
543 (py::arg("side"), "polarization"));
544 solver.def("scattering", Scattering<FourierSolver2D>::from_index, py::with_custodian_and_ward_postcall<0, 1>(),
545 (py::arg("side"), "idx"));
546 solver.def("scattering", Scattering<FourierSolver2D>::from_array, py::with_custodian_and_ward_postcall<0, 1>(),
547 (py::arg("side"), "coeffs"),
548 u8"Access to the reflected field.\n\n"
549 u8"Args:\n"
550 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
551 u8" present.\n"
552 u8" polarization: Specification of the incident light polarization.\n"
553 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
554 u8" of the non-vanishing electric field component.\n"
555 u8" idx: Eigenmode number.\n"
556 u8" coeffs: expansion coefficients of the incident vector.\n\n"
557 u8":rtype: Fourier2D.Scattering\n");
558 solver.def("get_raw_E", FourierSolver2D_getFieldVectorE, (py::arg("num"), "level"),
559 u8"Get Fourier expansion coefficients for the electric field.\n\n"
560 u8"This is a low-level function returning $E_l$ and/or $E_t$ Fourier\n"
561 u8"expansion coefficients. Please refer to the detailed solver description for their\n"
562 u8"interpretation.\n\n"
563 u8"Args:\n"
564 u8" num (int): Computed mode number.\n"
565 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
566 u8":rtype: numpy.ndarray\n");
567 solver.def("get_raw_H", FourierSolver2D_getFieldVectorH, (py::arg("num"), "level"),
568 u8"Get Fourier expansion coefficients for the magnetic field.\n\n"
569 u8"This is a low-level function returning $H_l$ and/or $H_t$ Fourier\n"
570 u8"expansion coefficients. Please refer to the detailed solver description for their\n"
571 u8"interpretation.\n\n"
572 u8"Args:\n"
573 u8" num (int): Computed mode number.\n"
574 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
575 u8":rtype: numpy.ndarray\n");
576 solver.def("layer_eigenmodes", &Eigenmodes<FourierSolver2D>::fromZ, py::arg("level"),
577 u8"Get eignemodes for a layer at specified level.\n\n"
578 u8"This is a low-level function to access diagonalized eigenmodes for a specific\n"
579 u8"layer. Please refer to the detailed solver description for the interpretation\n"
580 u8"of the returned values.\n\n"
581 u8"Args:\n"
582 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
583 u8":rtype: :class:`~optical.modal.Fourier2D.Eigenmodes`\n",
584 py::with_custodian_and_ward_postcall<0, 1>());
585 solver.def("gaussian", &FourierSolver2D_incidentGaussian, (py::arg("side"), "polarization", "sigma", py::arg("center") = 0.),
586 u8"Create coefficients vector with Gaussian profile.\n\n"
587 u8"This method is intended to use for :py:meth:`scattering` method.\n\n"
588 u8"Args:\n"
589 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
590 u8" present.\n"
591 u8" polarization: Specification of the incident light polarization.\n"
592 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
593 u8" of the non-vanishing electric field component.\n"
594 u8" sigma (float): Gaussian standard deviation (µm).\n"
595 u8" center (float): Position of the beam center (µm).\n\n"
596 u8"Example:\n"
597 u8" >>> scattered = fourier.scattering('top', \n"
598 u8" ... fourier.gaussian('top', 'Ex', 0.2))\n");
599 solver.def("scattering_gaussian", &FourierSolver2D_scatteringGaussian,
600 (py::arg("side"), "polarization", "sigma", py::arg("center") = 0.),
601 u8"Helper function to Access reflected fields for access incidence.\n\n"
602 u8"This method is equivalent to calling:\n\n"
603 u8" >>> fourier.scattering(side,\n"
604 u8" ... fourier.gaussian(side, polarization, sigma, center))\n\n"
605 u8"Args:\n"
606 u8" side (`top` or `bottom`): Side of the structure where the incident light is\n"
607 u8" present.\n"
608 u8" polarization: Specification of the incident light polarization.\n"
609 u8" It should be a string of the form 'E\\ *#*\\ ', where *#* is the axis name\n"
610 u8" of the non-vanishing electric field component.\n"
611 u8" sigma (float): Gaussian standard deviation (µm).\n"
612 u8" center (float): Position of the beam center (µm).\n\n");
613
614 py::scope scope = solver;
615 (void)scope; // don't warn about unused variable scope
616
618 py::class_<FourierSolver2D::Mode>("Mode", u8"Detailed information about the mode.", py::no_init)
619 .def_readonly("symmetry", &FourierSolver2D::Mode::symmetry, u8"Mode horizontal symmetry.")
620 .def_readonly("polarization", &FourierSolver2D::Mode::polarization, u8"Mode polarization.")
621 .add_property("lam", &getModeWavelength<FourierSolver2D::Mode>, u8"Mode wavelength (nm).")
622 .add_property("wavelength", &getModeWavelength<FourierSolver2D::Mode>, u8"Mode wavelength (nm).")
623 .def_readonly("k0", &FourierSolver2D::Mode::k0, u8"Mode normalized frequency (1/µm).")
624 .def_readonly("beta", &FourierSolver2D::Mode::beta, u8"Mode longitudinal wavevector (1/µm).")
625 .add_property("neff", &FourierSolver2D_Mode_Neff, u8"Mode longitudinal effective index (-).")
626 .def_readonly("ktran", &FourierSolver2D::Mode::ktran, u8"Mode transverse wavevector (1/µm).")
627 .def_readwrite("power", &FourierSolver2D::Mode::power, u8"Total power emitted into the mode (mW).")
628 .def("__str__", &FourierSolver2D_Mode_str)
629 .def("__repr__", &FourierSolver2D_Mode_repr)
630 .def("__getattr__", &FourierSolver2D_Mode__getattr__);
631
634}
635
636}}}} // namespace plask::optical::modal::python