PLaSK library
Loading...
Searching...
No Matches
modal-python.hpp
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#ifndef PLASK__SOLVER_OPTICAL_MODAL_PYTHON_H
15#define PLASK__SOLVER_OPTICAL_MODAL_PYTHON_H
16
17#include <cmath>
18#include <cstring>
19#include <plask/python.hpp>
20#include "plask/python_numpy.hpp"
21#include "plask/python_util/ufunc.hpp"
22#include <boost/python/raw_function.hpp>
23using namespace plask;
24using namespace plask::python;
25
26#include "../matrices.hpp"
27#include "../expansion.hpp"
28#include "../diagonalizer.hpp"
29
30#include <plask/config.hpp>
31
32#if defined(_WIN32) && defined(interface)
33# undef interface
34#endif
35
36namespace plask { namespace optical { namespace modal { namespace python {
37
38#define ROOTDIGGER_ATTRS_DOC \
39 ".. rubric:: Attributes:\n\n" \
40 ".. autosummary::\n\n" \
41 " ~optical.modal.RootParams.alpha\n" \
42 " ~optical.modal.RootParams.lambd\n" \
43 " ~optical.modal.RootParams.initial_range\n" \
44 " ~optical.modal.RootParams.maxiter\n" \
45 " ~optical.modal.RootParams.maxstep\n" \
46 " ~optical.modal.RootParams.method\n" \
47 " ~optical.modal.RootParams.tolf_max\n" \
48 " ~optical.modal.RootParams.tolf_min\n" \
49 " ~optical.modal.RootParams.tolx\n\n" \
50 ":rtype: RootParams\n"
51
52#define PML_ATTRS_DOC \
53 ".. rubric:: Attributes:\n\n" \
54 ".. autosummary::\n\n" \
55 " ~optical.modal.PML.factor\n" \
56 " ~optical.modal.PML.shape\n" \
57 " ~optical.modal.PML.dist\n" \
58 " ~optical.modal.PML.size\n\n" \
59 ":rtype: PML"
60
61
62template <NPY_TYPES type, typename T>
63inline static py::object arrayFromVec(const DataVector<T>& data) {
64 npy_intp dims[] = { npy_intp(data.size()) };
65 npy_intp strides[] = { npy_intp(sizeof(T)) };
66 PyObject* arr = PyArray_New(&PyArray_Type, 1, dims, type, strides, (void*)data.data(), 0, 0, NULL);
67 if (arr == nullptr) throw plask::CriticalException(u8"cannot create array from field coefficients");
69 py::object odata(wrap); py::incref(odata.ptr());
70 PyArray_SetBaseObject((PyArrayObject*)arr, odata.ptr()); // Make sure the data vector stays alive as long as the array
71 return py::object(py::handle<>(arr));
72}
73
74
75template <NPY_TYPES type>
76static inline py::object arrayFromVec2D(cvector data, bool sep, int dim=1) {
77 int strid = 2;
78 if (sep) strid = dim = 1;
79 npy_intp dims[] = { npy_intp(data.size() / strid), npy_intp(strid) };
80 npy_intp strides[] = { npy_intp(strid * sizeof(dcomplex)), npy_intp(sizeof(dcomplex)) };
81 PyObject* arr = PyArray_New(&PyArray_Type, dim, dims, type, strides, (void*)data.data(), 0, 0, NULL);
82 if (arr == nullptr) throw plask::CriticalException("cannot create array from field coefficients");
84 py::object odata(wrap); py::incref(odata.ptr());
85 PyArray_SetBaseObject((PyArrayObject*)arr, odata.ptr()); // Make sure the data vector stays alive as long as the array
86 return py::object(py::handle<>(arr));
87}
88
89
90template <typename SolverT>
91static py::object Solver_getInterface(SolverT& self) {
92 size_t interface = self.getInterface();
93 if (interface == size_t(-1)) return py::object();
94 else return py::object(interface);
95}
96
97template <typename SolverT>
98static void Solver_setInterface(SolverT& PLASK_UNUSED(self), const py::object& PLASK_UNUSED(value)) {
99 throw AttributeError("setting interface by layer index is not supported anymore (set it by object or position)");
100}
101
102
103template <typename SolverT>
104static py::object Solver_getLam0(const SolverT& self) {
105 if (!isnan(self.lam0)) return py::object(self.lam0);
106 else return py::object();
107}
108
109template <typename SolverT>
110static void Solver_setLam0(SolverT& self, py::object value) {
111 if (value.is_none()) self.clearLam0();
112 else self.setLam0(py::extract<double>(value));
113}
114
115template <typename SolverT>
116static py::tuple ModalSolver_getStack(SolverT& self) {
117 self.Solver::initCalculation();
118 py::list result;
119 for (auto i: self.getStack()) {
120 result.append(i);
121 }
122 return py::tuple(result);
123}
124
125template <typename SolverT>
126static shared_ptr<OrderedAxis> ModalSolver_getLayerEdges(SolverT& self) {
127 self.Solver::initCalculation();
128 return make_shared<OrderedAxis>(*self.vbounds);
129}
130
131template <typename SolverT>
132static shared_ptr<OrderedAxis> ModalSolver_getLayerCenters(SolverT& self) {
133 self.Solver::initCalculation();
134 return make_shared<OrderedAxis>(*self.verts);
135}
136
137// template <typename SolverT>
138// static py::tuple ModalSolver_getLayerSets(const SolverT& self) {
139// py::list result;
140// for (auto i: self.getLayersPoints()) {
141// result.append(i);
142// }
143// return py::tuple(result);
144// }
145
147
148 // Determine if obj can be converted into component
149 static void* convertible(PyObject* obj) {
150 if (obj == Py_None) return obj;
151 if (!PyUnicode_Check(obj)) return nullptr;
152 const char* repr = PyUnicode_AsUTF8(obj);
153 if (!strcmp(repr, "none") || !strcmp(repr, "NONE") || !strcmp(repr, "None") ||
154 ((repr[0] == 'E' || repr[0] == 'H') &&
155 (repr[1] == 'l' || repr[1] == 't' || repr[1] == 'x' || repr[1] == 'y' || repr[1] == 'z' || repr[1] == 'r' || repr[1] == 'p')))
156 return obj;
157 return nullptr;
158 }
159
160 static void construct(PyObject* obj, boost::python::converter::rvalue_from_python_stage1_data* data) {
161 void* storage = ((boost::python::converter::rvalue_from_python_storage<Expansion::Component>*)data)->storage.bytes;
162 AxisNames* axes = getCurrentAxes();
164 if (obj == Py_None) {
166 } else {
167 try {
168 std::string repr = py::extract<std::string>(obj);
169 if (repr == "none" || repr == "NONE" || repr == "None")
171 else if (repr == "Etran" || repr == "Et" || repr == "E"+axes->getNameForTran() ||
172 repr == "Hlong" || repr == "Hl" || repr == "H"+axes->getNameForLong())
173 val = Expansion::E_TRAN;
174 else if (repr == "Elong" || repr == "El" || repr == "E"+axes->getNameForLong() ||
175 repr == "Htran" || repr == "Ht" || repr == "H"+axes->getNameForTran())
176 val = Expansion::E_LONG;
177 else
178 throw py::error_already_set();
179 } catch (py::error_already_set&) {
180 throw ValueError("wrong component specification.");
181 }
182 }
184 data->convertible = storage;
185 }
186
188 AxisNames* axes = getCurrentAxes();
189 switch (val) {
190 case Expansion::E_TRAN: return py::incref(py::object("E"+axes->getNameForTran()).ptr());
191 case Expansion::E_LONG: return py::incref(py::object("E"+axes->getNameForLong()).ptr());
192 default: return py::incref(Py_None);
193 }
194 }
195};
196
198
201
203
205 if (solver) pml = orig.pml;
206 else pml = new PML(*pml);
207 }
208
209 ~PmlWrapper() { if (!solver) delete pml; }
210
211 static shared_ptr<PmlWrapper> __init__(dcomplex factor, double size, double dist, double order) {
212 return plask::make_shared<PmlWrapper>(nullptr, new PML(factor, size, dist, order));
213 }
214
215 operator PML() const { return *pml; }
216
217 dcomplex get_factor() const { return pml->factor; }
218 void set_factor(dcomplex val) {
219 pml->factor = val;
220 if (solver) solver->invalidate();
221 }
222
223 double get_size() const { return pml->size; }
224 void set_size(double val) {
225 pml->size = val;
226 if (solver) solver->invalidate();
227 }
228
229 double get_dist() const { return pml->dist; }
230 void set_dist(double val) {
231 pml->dist = val;
232 if (solver) solver->invalidate();
233 }
234
235 double get_order() const { return pml->order; }
236 void set_order(double val) {
237 pml->order = val;
238 if (solver) solver->invalidate();
239 }
240
241 std::string __str__() const {
242 return format("<factor: {0}, size: {1}, dist: {2}, shape: {3}>", str(pml->factor), pml->size, pml->dist, pml->order);
243 }
244
245 std::string __repr__() const {
246 return format("PML(factor={0}, size={1}, dist={2}, shape={3})", str(pml->factor), pml->size, pml->dist, pml->order);
247 }
248};
249
250template <typename T>
252 typedef T Wrapper;
253 typedef T Extracted;
254 typedef py::default_call_policies CallPolicy;
255 template <typename S> static Wrapper make(S* /*solver*/, T* item) { return *item; }
256};
257
258template <>
262 typedef py::with_custodian_and_ward_postcall<0,1> CallPolicy;
263 template <typename S> static Wrapper make(S* solver, PML* item) { return PmlWrapper(solver, item); }
264};
265
266
267template <typename SolverT>
268static void Solver_setLam(SolverT& self, dcomplex lam) { self.setLam(lam); }
269
270template <typename SolverT>
271static void Solver_setK0(SolverT& self, dcomplex k0) { self.setK0(k0); }
272
273template <typename SolverT>
274static PmlWrapper Solver_vPML(SolverT* self) {
275 return PmlWrapper(self, &self->vpml);
276}
277
278template <typename SolverT>
279static void Solver_setvPML(SolverT* self, const PmlWrapper& value) {
280 self->vpml = *value.pml;
281 self->invalidate();
282}
283
284template <typename Solver>
285static PmlWrapper Solver_getPML(Solver* self) {
286 return PmlWrapper(self, &self->pml);
287}
288
289template <typename Solver>
290static void Solver_setPML(Solver* self, const PmlWrapper& value) {
291 self->pml = *value.pml;
292 self->invalidate();
293}
294
295template <typename Mode>
296static dcomplex getModeWavelength(const Mode& mode) {
297 return 2e3 * PI / mode.k0;
298}
299
300template <typename Mode>
301static double getModeLoss(const Mode& mode) {
302 return 2e4 * mode.k0.imag();
303}
304
305#ifndef NDEBUG
306template <typename Solver>
307py::tuple Solver_getMatrices(Solver& self, size_t layer) {
308 cmatrix RE, RH;
309 self.getMatrices(layer, RE, RH);
310 return py::make_tuple(py::object(RE), py::object(RH));
311}
312
313template <typename Solver>
314py::tuple Solver_getDiagonalized(Solver& self, size_t layer) {
315 self.Solver::initCalculation();
316 if (!self.transfer) {
317 self.initTransfer(self.getExpansion(), false);
318 self.transfer->initDiagonalization();
319 self.transfer->diagonalizer->diagonalizeLayer(layer);
320 } else if (!self.transfer->diagonalizer->isDiagonalized(layer)) {
321 self.transfer->diagonalizer->diagonalizeLayer(layer);
322 }
323 cdiagonal gamma = self.transfer->diagonalizer->Gamma(layer);
324 cmatrix TE = self.transfer->diagonalizer->TE(layer),
325 TH = self.transfer->diagonalizer->TH(layer);
326 return py::make_tuple(py::object(gamma), py::object(TE), py::object(TH));
327}
328#endif
329
330
331
335template <typename SolverT>
339
341 size_t layer;
342
346
347
352 bool changed = solver.Solver::initCalculation() || solver.setExpansionDefaults(true);
353 if (!solver.transfer) {
354 solver.initTransfer(solver.getExpansion(), false);
355 changed = true;
356 }
357 if (changed) {
358 solver.transfer->initDiagonalization();
359 solver.transfer->diagonalizer->diagonalizeLayer(layer);
360 } else if (!solver.transfer->diagonalizer->isDiagonalized(layer))
361 solver.transfer->diagonalizer->diagonalizeLayer(layer);
362 gamma = solver.transfer->diagonalizer->Gamma(layer);
363 TE = solver.transfer->diagonalizer->TE(layer),
364 TH = solver.transfer->diagonalizer->TH(layer);
365 }
366
368 solver.Solver::initCalculation();
369 size_t layer = solver.stack[solver.getLayerFor(z)];
371 }
372
373 size_t size() const {
374 return gamma.size();
375 }
376
377 py::object array(const dcomplex* data, size_t N) const;
378
380 if (n >= gamma.size())
381 throw IndexError("bad eigenmode number");
382 cvector E(TE.data() + TE.rows()*n, TE.rows());
383 cvector H(TH.data() + TH.rows()*n, TH.rows());
384 solver.transfer->diagonalizer->source()->initField(FIELD_E, method);
385 DataVector<double> destination(dst_mesh->size());
386 auto levels = makeLevelsAdapter(dst_mesh);
387 while (auto level = levels->yield()) {
388 //TODO warn if z is outside of the layer
389 double z = level->vpos();
390 dcomplex phas = exp(- I * gamma[n] * z);
391 //size_t n = solver->getLayerFor(z);
392 auto dest = solver.transfer->diagonalizer->source()->getField(layer, level, E, H);
393 for (size_t i = 0; i != level->size(); ++i) destination[level->index(i)] = abs2(phas * dest[i]);
394 }
395 solver.transfer->diagonalizer->source()->cleanupField();
396 return destination;
397 }
398
400 if (n >= gamma.size())
401 throw IndexError("bad eigenmode number");
402 cvector E(TE.data() + TE.rows()*n, TE.rows());
403 cvector H(TH.data() + TH.rows()*n, TH.rows());
404 solver.transfer->diagonalizer->source()->initField(FIELD_E, method);
405 DataVector<Vec<3,dcomplex>> destination(dst_mesh->size());
406 auto levels = makeLevelsAdapter(dst_mesh);
407 while (auto level = levels->yield()) {
408 //TODO warn if z is outside of the layer
409 double z = level->vpos();
410 dcomplex phas = exp(- I * gamma[n] * z);
411 //size_t n = solver->getLayerFor(z);
412 auto dest = solver.transfer->diagonalizer->source()->getField(layer, level, E, H);
413 for (size_t i = 0; i != level->size(); ++i) destination[level->index(i)] = phas * dest[i];
414 }
415 solver.transfer->diagonalizer->source()->cleanupField();
416 return destination;
417 }
418
420 if (n >= gamma.size())
421 throw IndexError("bad eigenmode number");
422 cvector E(TE.data() + TE.rows()*n, TE.rows());
423 cvector H(TH.data() + TH.rows()*n, TH.rows());
424 solver.transfer->diagonalizer->source()->initField(FIELD_H, method);
425 DataVector<Vec<3,dcomplex>> destination(dst_mesh->size());
426 auto levels = makeLevelsAdapter(dst_mesh);
427 while (auto level = levels->yield()) {
428 //TODO warn if z is outside of the layer
429 double z = level->vpos();
430 dcomplex phas = exp(- I * gamma[n] * z);
431 //size_t n = solver->getLayerFor(z);
432 auto dest = solver.transfer->diagonalizer->source()->getField(layer, level, E, H);
433 for (size_t i = 0; i != level->size(); ++i) destination[level->index(i)] = phas * dest[i];
434 }
435 solver.transfer->diagonalizer->source()->cleanupField();
436 return destination;
437 }
438
439 struct Eigenmode {
441 size_t n;
442
443 Eigenmode(Eigenmodes* eigenmodes, int n): ems(eigenmodes), n(n) {}
444
445 dcomplex getGamma() const {
446 return ems->gamma[n];
447 }
448
449 py::object getCoefficientsE() const {
450 return ems->array(ems->TE.data() + ems->TE.rows()*n, ems->TE.rows());
451 }
452
453 py::object getCoefficientsH() const {
454 return ems->array(ems->TH.data() + ems->TH.rows()*n, ems->TH.rows());
455 }
456
457 double getFlux() const {
458 return abs(ems->solver.getExpansion().getModeFlux(n, ems->TE, ems->TH));
459 }
460 };
461
463 if (n < 0) n = size() + n;
464 if (n < 0 || n >= size())
465 throw IndexError("bad eigenmode number");
466 return Eigenmode(this, n);
467 }
468
469 static void registerClass(const char* solver, const char* suffix) {
470 py::class_<Eigenmodes, shared_ptr<Eigenmodes>, boost::noncopyable> ems("Eigenmodes",
471 u8"Layer eignemodes\n\n"
472 u8"This is an advanced class allowing to extract eignemodes in each layer.\n", py::no_init); ems
473 .def("__len__", &Eigenmodes::size)
474 .def("__getitem__", &Eigenmodes::__getitem__, py::with_custodian_and_ward_postcall<0,1>())
475 .def_readonly("outLightMagnitude",
477 format(docstring_attr_provider<ModeLightMagnitude>(), "LightMagnitude", suffix, u8"light intensity", u8"W/m²", "", "", "", "outLightMagnitude", "n=0", ":param int n: Number of the mode found with :meth:`find_mode`.").c_str()
478 )
479 .def_readonly("outLightE",
481 format(docstring_attr_provider<ModeLightE>(), "LightE", suffix, u8"electric field", u8"V/m", "", "", "", "outLightE", "n=0", ":param int n: Number of the mode found with :meth:`find_mode`.").c_str()
482 )
483 .def_readonly("outLightH",
485 format(docstring_attr_provider<ModeLightE>(), "LightH", suffix, u8"electric field", u8"A/m", "", "", "", "outLightH", "n=0", ":param int n: Number of the mode found with :meth:`find_mode`.").c_str()
486 )
487 ;
488 py::scope scope(ems);
489 py::class_<Eigenmode>("Eigenmode",
490 format("Single eignemode. You can access object of this class by indexing\n"
491 ":class:`~optical.modal.{}.Eigenmodes` object.", solver).c_str(),
492 py::no_init)
493 .add_property("kvert", &Eigenmode::getGamma,
494 u8"Vertical propagation constant for the eigenmode.")
495 .add_property("raw_E", &Eigenmode::getCoefficientsE,
496 u8"Electric field coefficients for the eigenmode.")
497 .add_property("raw_H", &Eigenmode::getCoefficientsH,
498 u8"Magnetic field coefficients for the eigenmode.")
499 .add_property("flux", &Eigenmode::getFlux,
500 u8"Vertical flux for the eigenmode.\n\n"
501 u8"This is equal to the vertical component of the Pointing vector integrated over\n"
502 u8"the numerical domain.\n"
503 )
504 ;
505 }
506};
507
508
510
512
516
517 CoeffsArray(const CoeffsArray& src): array(src.array) {
519 }
520
522 src.array = nullptr;
523 }
524
528
529 static void* convertible(PyObject* obj);
530 static void construct(PyObject* obj, boost::python::converter::rvalue_from_python_stage1_data* data);
531};
532
533
537template <typename SolverT>
539
541
544
547
550
553
554
555 double reflectivity() {
556 if (!solver->initCalculation()) solver->setExpansionDefaults();
557 return solver->getReflection(incident, side);
558 }
559
560 double transmittivity() {
561 if (!solver->initCalculation()) solver->setExpansionDefaults();
562 return solver->getTransmission(incident, side);
563 }
564
566 if (!solver->initCalculation()) solver->setExpansionDefaults();
567 return 100. * solver->getReflection(incident, side);
568 }
569
571 if (!solver->initCalculation()) solver->setExpansionDefaults();
572 return 100. * solver->getTransmission(incident, side);
573 }
574
575 struct Reflected {
583 py::object get_coefficients() {
584 if (!parent->solver->initCalculation()) parent->solver->setExpansionDefaults();
585 return arrayFromVec<NPY_CDOUBLE>(parent->solver->getReflectedCoefficients(parent->incident, parent->side));
586 }
587 py::object get_fluxes() {
588 if (!parent->solver->initCalculation()) parent->solver->setExpansionDefaults();
589 return arrayFromVec<NPY_DOUBLE>(parent->solver->getReflectedFluxes(parent->incident, parent->side));
590 }
592 parent->solver->Solver::initCalculation();
593 size_t layer = (parent->side == Transfer::INCIDENCE_BOTTOM)? 0 : parent->solver->stack.back();
595 }
597 return parent->solver->getScatteredFieldE(parent->incident, parent->side, dst_mesh, method,
599 }
601 return parent->solver->getScatteredFieldH(parent->incident, parent->side, dst_mesh, method,
603 }
604 inline static void registerClass(const char* suffix, const char* name) {
605 py::class_<Scattering<SolverT>::Reflected, boost::noncopyable>("Reflected", "Reflected field details", py::no_init)
606 .add_property("coeffs", &Scattering<SolverT>::Reflected::get_coefficients, "Raw reflection ceofficients for modes.")
607 .add_property("fluxes", &Scattering<SolverT>::Reflected::get_fluxes, "Perpendicular fluxes for reflected modes.")
608 .add_property("eigenmodes", py::make_function(&Scattering<SolverT>::Reflected::eigenmodes, py::with_custodian_and_ward_postcall<0,1>()),
609 format("Reflected eigenmodes.\n\n"
610 ":rtype: :class:`~optical.modal.{}{}.Eigenmodes`", name, suffix).c_str()
611 )
614 format(docstring_attr_provider<LightE>(), "LightE", suffix, u8"electric field", u8"V/m", "", "", "", "outLightE").c_str() )
617 format(docstring_attr_provider<LightH>(), "LightH", suffix, u8"magnetic field", u8"A/m", "", "", "", "outLightH").c_str() )
618 ;
619 }
620 };
621 Reflected* get_reflected() { return new Reflected(this); }
622
623 struct Incident {
631 py::object get_coefficients() {
633 }
634 py::object get_fluxes() {
635 if (!parent->solver->initCalculation()) parent->solver->setExpansionDefaults();
636 return arrayFromVec<NPY_DOUBLE>(parent->solver->getIncidentFluxes(parent->incident, parent->side));
637 }
639 parent->solver->Solver::initCalculation();
640 size_t layer = (parent->side == Transfer::INCIDENCE_BOTTOM)? 0 : parent->solver->stack.back();
642 }
644 return parent->solver->getScatteredFieldE(parent->incident, parent->side, dst_mesh, method,
646 }
648 return parent->solver->getScatteredFieldH(parent->incident, parent->side, dst_mesh, method,
650 }
651 inline static void registerClass(const char* suffix, const char* name) {
652 py::class_<Scattering<SolverT>::Incident, boost::noncopyable>("Incident", "Incident field details", py::no_init)
653 .add_property("coeffs", &Scattering<SolverT>::Incident::get_coefficients, "Raw incident ceofficients for modes.")
654 .add_property("fluxes", &Scattering<SolverT>::Incident::get_fluxes, "Perpendicular fluxes for incident modes.")
655 .add_property("eigenmodes", py::make_function(&Scattering<SolverT>::Incident::eigenmodes, py::with_custodian_and_ward_postcall<0,1>()),
656 format("Incident eigenmodes.\n\n"
657 ":rtype: :class:`~optical.modal.{}{}.Eigenmodes`", name, suffix).c_str()
658 )
661 format(docstring_attr_provider<LightE>(), "LightE", suffix, u8"electric field", u8"V/m", "", "", "", "outLightE").c_str() )
664 format(docstring_attr_provider<LightH>(), "LightH", suffix, u8"magnetic field", u8"A/m", "", "", "", "outLightH").c_str() )
665 ;
666 }
667 };
668 Incident* get_incident() { return new Incident(this); }
669
670 struct Transmitted {
678 py::object get_coefficients() {
679 if (!parent->solver->initCalculation()) parent->solver->setExpansionDefaults();
680 return arrayFromVec<NPY_CDOUBLE>(parent->solver->getTransmittedCoefficients(parent->incident, parent->side));
681 }
682 py::object get_fluxes() {
683 if (!parent->solver->initCalculation()) parent->solver->setExpansionDefaults();
684 return arrayFromVec<NPY_DOUBLE>(parent->solver->getTransmittedFluxes(parent->incident, parent->side));
685 }
687 parent->solver->Solver::initCalculation();
688 size_t layer = (parent->side == Transfer::INCIDENCE_TOP)? 0 : parent->solver->stack.back();
690 }
692 return parent->solver->getScatteredFieldE(parent->incident, parent->side, dst_mesh, method,
694 }
696 return parent->solver->getScatteredFieldH(parent->incident, parent->side, dst_mesh, method,
698 }
699 inline static void registerClass(const char* suffix, const char* name) {
700 py::class_<Scattering<SolverT>::Transmitted, boost::noncopyable>("Transmitted", "Transmitted field details", py::no_init)
701 .add_property("coeffs", &Scattering<SolverT>::Transmitted::get_coefficients, "Raw transmission ceofficients for modes.")
702 .add_property("fluxes", &Scattering<SolverT>::Transmitted::get_fluxes, "Perpendicular fluxes for transmitted modes.")
703 .add_property("eigenmodes", py::make_function(&Scattering<SolverT>::Transmitted::eigenmodes, py::with_custodian_and_ward_postcall<0,1>()),
704 format("Transmitted eigenmodes.\n\n"
705 ":rtype: :class:`~optical.modal.{}{}.Eigenmodes`", name, suffix).c_str()
706 )
709 format(docstring_attr_provider<LightE>(), "LightE", suffix, u8"electric field", u8"V/m", "", "", "", "outLightE").c_str() )
712 format(docstring_attr_provider<LightH>(), "LightH", suffix, u8"magnetic field", u8"A/m", "", "", "", "outLightH").c_str() )
713 ;
714 }
715 };
716 Transmitted* get_transmitted() { return new Transmitted(this); }
717
718
720 return solver->getScatteredFieldE(incident, side, dst_mesh, method);
721 }
722
724 return solver->getScatteredFieldH(incident, side, dst_mesh, method);
725 }
726
728 return solver->getScatteredFieldMagnitude(incident, side, dst_mesh, method);
729 }
730
731 py::object getFieldVectorE(double z);
732
733 py::object getFieldVectorH(double z);
734
735 double getIntegralEE(double z1, double z2) {
736 return solver->getScatteredIntegralEE(incident, side, z1, z2);
737 }
738
739 double getIntegralHH(double z1, double z2) {
740 return solver->getScatteredIntegralHH(incident, side, z1, z2);
741 }
742
752 solver(solver), incident(solver->incidentVector(side, incident, NAN)), side(side),
756 solver->initCalculation();
757 if (!solver->transfer) solver->initTransfer(solver->getExpansion(), true);
758 if (incident.size() != solver->transfer->diagonalizer->matrixSize())
759 throw BadInput(solver->getId(), "wrong incident vector size ({}, should be {}", incident.size(), solver->transfer->diagonalizer->matrixSize());
760 }
761
774
787
793
796 size_t idx) {
797 return make_shared<Scattering<SolverT>>(parent, side, idx);
798 }
799
802 CoeffsArray coeffs) {
803 PyArrayObject* arr = coeffs.array;
804 cvector incident((dcomplex*)PyArray_DATA(arr), size_t(PyArray_DIMS(arr)[0]), plask::python::detail::NumpyDataDeleter(arr));
806 }
807
808 static py::class_<Scattering<SolverT>, shared_ptr<Scattering<SolverT>>, boost::noncopyable> registerClass(const char* suffix, const char* name) {
809 py::class_<Scattering<SolverT>, shared_ptr<Scattering<SolverT>>, boost::noncopyable> cls("Scattering",
810 u8"Reflected mode proxy.\n\n"
811 u8"This class contains providers for the scattered field.\n"
812 , py::no_init); cls
813 .def_readonly("outLightE", reinterpret_cast<ProviderFor<LightE, typename SolverT::SpaceType> Scattering<SolverT>::*>
815 format(docstring_attr_provider<LightE>(), "LightE", suffix, u8"electric field", u8"V/m", "", "", "", "outLightE").c_str() )
816 .def_readonly("outLightH", reinterpret_cast<ProviderFor<LightH, typename SolverT::SpaceType> Scattering<SolverT>::*>
818 format(docstring_attr_provider<LightH>(), "LightH", suffix, u8"magnetic field", u8"A/m", "", "", "", "outLightH").c_str() )
819 .def_readonly("outLightMagnitude", reinterpret_cast<ProviderFor<LightMagnitude, typename SolverT::SpaceType> Scattering<SolverT>::*>
821 format(docstring_attr_provider<LightMagnitude>(), "LightMagnitude", suffix, u8"light intensity", u8"W/m²", "", "", "", "outLightMagnitude").c_str() )
822 .def("get_raw_E", &Scattering<SolverT>::getFieldVectorE, (py::arg("level")),
823 u8"Get Fourier expansion coefficients for the electric field.\n\n"
824 u8"This is a low-level function returning expansion coefficients for electric\n"
825 u8"field. Please refer to the detailed solver description for their\n"
826 u8"interpretation.\n\n"
827 u8"Args:\n"
828 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
829 u8":rtype: numpy.ndarray\n"
830 )
831 .def("get_raw_H", &Scattering<SolverT>::getFieldVectorH, (py::arg("level")),
832 u8"Get Fourier expansion coefficients for the magnetic field.\n\n"
833 u8"This is a low-level function returning expansion coefficients for magnetic\n"
834 u8"field. Please refer to the detailed solver description for their\n"
835 u8"interpretation.\n\n"
836 u8"Args:\n"
837 u8" level (float): Vertical level at which the coefficients are computed.\n\n"
838 u8":rtype: numpy.ndarray\n"
839 );
840
841 if (suffix[0] == '2') {
842 cls
843 .def("integrateEE", &Scattering<SolverT>::getIntegralEE, (py::arg("z1"), "z2"),
844 u8"Get average integral of the squared electric field:\n\n"
845 u8"\\\\[\\\\frac 1 2 \\\\int\\\\int_{z_1}^{z_2} \\|E\\|^2.\\\\]\n\n"
846 u8"In the lateral direction integration is performed over the whole domain.\n\n"
847 u8"Args:\n"
848 u8" z1 (float): Lower vertical bound of the integral.\n"
849 u8" z2 (float): Upper vertical bound of the integral.\n\n"
850 u8"Returns:\n"
851 u8" float: Computed integral [V\\ :sup:`2`].\n\n"
852 // u8"Warning:\n"
853 // u8" This method may return incorrect results for layers with gain,\n"
854 // u8" due to the strong non-Hemiticity!\n"
855 )
856 .def("integrateHH", &Scattering<SolverT>::getIntegralHH, (py::arg("z1"), "z2"),
857 u8"Get average integral of the squared magnetic field:\n\n"
858 u8"\\\\[\\\\frac 1 2 \\\\int\\\\int_{z_1}^{z_2} \\|H\\|^2.\\\\]\n\n"
859 u8"In the lateral direction integration is performed over the whole domain.\n\n"
860 u8"Args:\n"
861 u8" z1 (float): Lower vertical bound of the integral.\n"
862 u8" z2 (float): Upper vertical bound of the integral.\n"
863 u8"Returns:\n"
864 u8" float: Computed integral [A\\ :sup:`2`].\n"
865 // u8"Warning:\n"
866 // u8" This method may return incorrect results for layers with gain,\n"
867 // u8" due to the strong non-Hemiticity!\n"
868 );
869 } else {
870 cls
871 .def("integrateEE", &Scattering<SolverT>::getIntegralEE, (py::arg("z1"), "z2"),
872 u8"Get average integral of the squared electric field:\n\n"
873 u8"\\\\[\\\\frac 1 2 \\\\int\\\\int_{z_1}^{z_2} \\|E\\|^2.\\\\]\n\n"
874 u8"In the lateral direction integration is performed over the whole domain.\n\n"
875 u8"Args:\n"
876 u8" z1 (float): Lower vertical bound of the integral.\n"
877 u8" z2 (float): Upper vertical bound of the integral.\n\n"
878 u8"Returns:\n"
879 u8" float: Computed integral [V\\ :sup:`2` m].\n\n"
880 // u8"Warning:\n"
881 // u8" This method may return incorrect results for layers with gain,\n"
882 // u8" due to the strong non-Hemiticity!\n"
883 )
884 .def("integrateHH", &Scattering<SolverT>::getIntegralHH, (py::arg("z1"), "z2"),
885 u8"Get average integral of the squared magnetic field:\n\n"
886 u8"\\\\[\\\\frac 1 2 \\\\int\\\\int_{z_1}^{z_2} \\|H\\|^2.\\\\]\n\n"
887 u8"In the lateral direction integration is performed over the whole domain.\n\n"
888 u8"Args:\n"
889 u8" z1 (float): Lower vertical bound of the integral.\n"
890 u8" z2 (float): Upper vertical bound of the integral.\n"
891 u8"Returns:\n"
892 u8" float: Computed integral [A\\ :sup:`2` m].\n"
893 // u8"Warning:\n"
894 // u8" This method may return incorrect results for layers with gain,\n"
895 // u8" due to the strong non-Hemiticity!\n"
896 );
897 }
898
899 cls
900 .add_property("R", &Scattering<SolverT>::reflectivity, u8"Total reflection coefficient (-).")
901 .add_property("T", &Scattering<SolverT>::transmittivity, u8"Total transmission coefficient (-).")
902 .add_property("reflectivity", &Scattering<SolverT>::reflectivity100, u8"Total reflection coefficient [%].\n\nThis differs from :attr:`Scattering.R` by unit.\n")
903 .add_property("transmittivity", &Scattering<SolverT>::transmittivity100, u8"Total transmission coefficient [%].\n\nThis differs from :attr:`Scattering.T` by unit.\n")
904
905 .add_property("reflected", py::make_function(&Scattering<SolverT>::get_reflected,
906 py::return_value_policy<py::manage_new_object, py::with_custodian_and_ward_postcall<0,1>>()),
907 format(u8"Reflected field details.\n\n"
908 u8":rtype: optical.modal.{}{}.Reflected", name, suffix).c_str())
909 .add_property("transmitted", py::make_function(&Scattering<SolverT>::get_transmitted,
910 py::return_value_policy<py::manage_new_object, py::with_custodian_and_ward_postcall<0,1>>()),
911 format(u8"Transmitted field details.\n\n"
912 u8":rtype: optical.modal.{}{}.Transmitted", name, suffix).c_str())
913 .add_property("incident", py::make_function(&Scattering<SolverT>::get_incident,
914 py::return_value_policy<py::manage_new_object, py::with_custodian_and_ward_postcall<0,1>>()),
915 format(u8"Incident field details.\n\n"
916 u8":rtype: optical.modal.{}{}.Incident", name, suffix).c_str())
917 ;
918
919 py::scope scope(cls);
920
922
924
926
927// .def("get_electric_coefficients", FourierSolver2D_getReflectedFieldVectorE, py::arg("level"),
928// u8"Get Fourier expansion coefficients for the electric field.\n\n"
929// u8"This is a low-level function returning $E_l$ and/or $E_t$ Fourier\n"
930// u8"expansion coefficients. Please refer to the detailed solver description for their\n"
931// u8"interpretation.\n\n"
932// u8"Args:\n"
933// u8" level (float): Vertical level at which the coefficients are computed.\n\n"
934// u8":rtype: numproxypy.ndarray\n"
935// )
936// .def("get_magnetic_coefficients", FourierSolver2D_getReflectedFieldVectorH, py::arg("level"),
937// u8"Get Fourier expansion coefficients for the magnegtic field.\n\n"
938// u8"This is a low-level function returning $H_l$ and/or $H_t$ Fourier\n"
939// u8"expansion coefficients. Please refer to the detailed solver description for their\n"
940// u8"interpretation.\n\n"
941// u8"Args:\n"
942// u8" level (float): Vertical level at which the coefficients are computed.\n\n"
943// u8":rtype: numpy.ndarray\n"
944// )
945// ;
946 return cls;
947 }
948};
949
950template <typename SolverT> inline const char* solver_compute_reflectivity_name() { return "compute_reflectivity"; }
951template <typename SolverT> inline const char* solver_compute_transmittivity_name() { return "compute_transmittivity"; }
952
953
954template <typename SolverT>
956 py::object wavelength,
958 Expansion::Component polarization
959 )
960{
961 if (!self->Solver::initCalculation())
962 self->setExpansionDefaults(false);
963 return UFUNC<double>([=](double lam)->double {
964 double k0 = 2e3*PI/lam;
965 cvector incident = self->incidentVector(side, polarization, lam);
966 self->getExpansion().setK0(k0);
967 return 100. * self->getReflection(incident, side);
968 }, wavelength, solver_compute_reflectivity_name<SolverT>(), "lam");
969}
970
971template <typename SolverT>
973 py::object wavelength,
975 Expansion::Component polarization
976 )
977{
978 if (!self->Solver::initCalculation())
979 self->setExpansionDefaults(false);
980 return UFUNC<double>([=](double lam)->double {
981 double k0 = 2e3*PI/lam;
982 cvector incident = self->incidentVector(side, polarization, lam);
983 self->getExpansion().setK0(k0);
984 return 100. * self->getTransmission(incident, side);
985 }, wavelength, solver_compute_transmittivity_name<SolverT>(), "lam");
986}
987
988template <typename SolverT>
990 py::object wavelength,
992 size_t index
993 )
994{
995 if (!self->Solver::initCalculation())
996 self->setExpansionDefaults(false);
997 return UFUNC<double>([=](double lam)->double {
998 double k0 = 2e3*PI/lam;
999 cvector incident = self->incidentVector(side, index, lam);
1000 self->getExpansion().setK0(k0);
1001 return 100. * self->getReflection(incident, side);
1002 }, wavelength, solver_compute_reflectivity_name<SolverT>(), "lam");
1003}
1004
1005template <typename SolverT>
1007 py::object wavelength,
1009 size_t index
1010 )
1011{
1012 if (!self->Solver::initCalculation())
1013 self->setExpansionDefaults(false);
1014 return UFUNC<double>([=](double lam)->double {
1015 double k0 = 2e3*PI/lam;
1016 cvector incident = self->incidentVector(side, index, lam);
1017 self->getExpansion().setK0(k0);
1018 return 100. * self->getTransmission(incident, side);
1019 }, wavelength, solver_compute_transmittivity_name<SolverT>(), "lam");
1020}
1021
1022template <typename SolverT>
1024 py::object wavelength,
1026 CoeffsArray coeffs
1027 )
1028{
1029 if (!self->Solver::initCalculation()) self->setExpansionDefaults(false);
1030 if (!self->transfer) self->initTransfer(self->getExpansion(), true);
1031
1032 PyArrayObject* arr = coeffs.array;
1033 size_t size(PyArray_DIMS(arr)[0]);
1034 if (size != self->transfer->diagonalizer->matrixSize())
1035 throw BadInput(self->getId(), "wrong incident vector size ({}, should be {}", size, self->transfer->diagonalizer->matrixSize());
1036
1037 cvector incident((dcomplex*)PyArray_DATA(arr), size_t(PyArray_DIMS(arr)[0]), plask::python::detail::NumpyDataDeleter(arr));
1038
1039 return UFUNC<double>([self, incident, side](double lam)->double {
1040 double k0 = 2e3*PI/lam;
1041 self->getExpansion().setK0(k0);
1042 return 100. * self->getReflection(self->incidentVector(side, incident, lam), side);
1043 }, wavelength, solver_compute_reflectivity_name<SolverT>(), "lam");
1044}
1045
1046template <typename SolverT>
1048 py::object wavelength,
1050 CoeffsArray coeffs
1051 )
1052{
1053 if (!self->Solver::initCalculation()) self->setExpansionDefaults(false);
1054 if (!self->transfer) self->initTransfer(self->getExpansion(), true);
1055
1056 PyArrayObject* arr = coeffs.array;
1057 size_t size(PyArray_DIMS(arr)[0]);
1058 if (size != self->transfer->diagonalizer->matrixSize())
1059 throw BadInput(self->getId(), "wrong incident vector size ({}, should be {}", size, self->transfer->diagonalizer->matrixSize());
1060
1061 cvector incident((dcomplex*)PyArray_DATA(arr), size_t(PyArray_DIMS(arr)[0]), plask::python::detail::NumpyDataDeleter(arr));
1062
1063 return UFUNC<double>([self, incident, side](double lam)->double {
1064 double k0 = 2e3*PI/lam;
1065 self->getExpansion().setK0(k0);
1066 return 100. * self->getTransmission(self->incidentVector(side, incident, lam), side);
1067 }, wavelength, solver_compute_transmittivity_name<SolverT>(), "lam");
1068}
1069
1070
1071
1072template <typename SolverT>
1073py::object get_max_temp_diff(SolverT* self) {
1074 double value = self->getMaxTempDiff();
1075 if (isnan(value) || isinf(value)) return py::object();
1076 return py::object(value);
1077}
1078
1079template <typename SolverT>
1080void set_max_temp_diff(SolverT* self, py::object value) {
1081 if (value.is_none()) self->setMaxTempDiff(NAN);
1082 else self->setMaxTempDiff(py::extract<double>(value));
1083}
1084
1085
1086template <typename SolverT>
1087static double getIntegralEE_0(SolverT& self, double z1, double z2) {
1088 if (self.modes.size() == 0) throw IndexError(u8"no mode computed");
1089 return self.getIntegralEE(0, z1, z2);
1090}
1091
1092template <typename SolverT>
1093static double getIntegralHH_0(SolverT& self, double z1, double z2) {
1094 if (self.modes.size() == 0) throw IndexError(u8"no mode computed");
1095 return self.getIntegralHH(0, z1, z2);
1096}
1097
1098template <typename SolverT>
1099static double getIntegralEE(SolverT& self, int num, double z1, double z2) {
1100 if (num < 0) num += int(self.modes.size());
1101 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
1102 return self.getIntegralEE(num, z1, z2);
1103}
1104
1105template <typename SolverT>
1106static double getIntegralHH(SolverT& self, int num, double z1, double z2) {
1107 if (num < 0) num += int(self.modes.size());
1108 if (std::size_t(num) >= self.modes.size()) throw IndexError(u8"bad mode number {:d}", num);
1109 return self.getIntegralHH(num, z1, z2);
1110}
1111
1112
1113
1114template <typename Class>
1115inline void export_base(Class solver) {
1116 typedef typename Class::wrapped_type Solver;
1117 solver.add_property("interface", &Solver_getInterface<Solver>, &Solver_setInterface<Solver>, "Matching interface position.");
1118 solver.def("set_interface", (void(Solver::*)(const shared_ptr<const GeometryObject>&, const PathHints&))&Solver::setInterfaceOn,
1119 "Set interface at the bottom of the specified object.\n\n"
1120 "Args:\n"
1121 " object (geometry object): object to set the interface at.\n"
1122 " path (path): Optional path specifying an instance of the object.",
1123 (py::arg("object"), py::arg("path")=py::object()));
1124 solver.def("set_interface", &Solver::setInterfaceAt,
1125 "Set interface as close as possible to the specified position.\n\n"
1126 "Args:\n"
1127 " pos (float): Position, near which the interface will be located.", py::arg("pos"));
1128 solver.add_property("smooth", &Solver::getSmooth, &Solver::setSmooth, "Smoothing parameter for material boundaries (increases convergence).");
1129 solver.add_property("stack", &ModalSolver_getStack<Solver>, "Stack of distinct layers.");
1130 solver.add_property("layer_edges", &ModalSolver_getLayerEdges<Solver>, "Vertical posiotions of egges of each layer.");
1131 solver.add_property("layer_centers", &ModalSolver_getLayerCenters<Solver>,
1132 "Vertical posiotions of centers of each layer.\n\n"
1133 "At these positions materials and temperatures are probed.\n");
1134 // solver.add_property("layer_sets", &ModalSolver_getLayerSets<Solver>, "Vertical positions of layers in each layer set.");
1135 solver.add_property("group_layers", &Solver::getGroupLayers, &Solver::setGroupLayers,
1136 "Layer grouping switch.\n\n"
1137 "If this property is ``True``, similar layers are grouped for efficiency.");
1138 solver.add_property("temp_diff", &get_max_temp_diff<Solver>, &set_max_temp_diff<Solver>,
1139 "Maximum temperature difference between the layers in one group.\n\n"
1140 "If a temperature in a single layer varies vertically more than this value,\n"
1141 "the layer is split into two and put into separate groups. If this is empty,\n"
1142 "temperature gradient is ignored in layers grouping.\n\n");
1143 solver.add_property("temp_dist", &Solver::getTempDist, &Solver::setTempDist,
1144 "Temperature probing step.\n\n"
1145 "If :attr:`temp_diff` is not ``None``, the temperature is laterally probed\n"
1146 "in points approximately separated by this distance.\n");
1147 solver.add_property("temp_layer", &Solver::getTempLayer, &Solver::setTempLayer,
1148 "Temperature probing step.\n\n"
1149 "If :attr:`temp_diff` is not ``None``, this is the minimum thickness of sublayers\n"
1150 "resulting from temperature-gradient division.\n");
1151 solver.template add_receiver<ReceiverFor<Temperature, typename Solver::SpaceType>, Solver>("inTemperature", &Solver::inTemperature, "");
1152 solver.template add_receiver<ReceiverFor<Epsilon, typename Solver::SpaceType>, Solver>("inEpsilon", &Solver::inEpsilon, "");
1153 solver.template add_receiver<ReceiverFor<Gain, typename Solver::SpaceType>, Solver>("inGain", &Solver::inGain, "");
1154 solver.template add_receiver<ReceiverFor<CarriersConcentration, typename Solver::SpaceType>, Solver>("inCarriersConcentration", &Solver::inCarriersConcentration, "");
1155 solver.add_provider("outEpsilon", &Solver::outEpsilon, "");
1156 solver.add_provider("outRefractiveIndex", &Solver::outRefractiveIndex, "");
1157 solver.add_provider("outWavelength", &Solver::outWavelength, "");
1158 solver.add_provider("outLightMagnitude", &Solver::outLightMagnitude, "");
1159 solver.add_provider("outLightE", &Solver::outLightE, "");
1160 solver.add_provider("outLightH", &Solver::outLightH, "");
1161 solver.add_provider("outUpwardsLightE", &Solver::outUpwardsLightE, "");
1162 solver.add_provider("outUpwardsLightH", &Solver::outUpwardsLightH, "");
1163 solver.add_provider("outDownwardsLightE", &Solver::outDownwardsLightE, "");
1164 solver.add_provider("outDownwardsLightH", &Solver::outDownwardsLightH, "");
1165 solver.def_readwrite("root", &Solver::root,
1166 "Configuration of the root searching algorithm.\n\n"
1168 );
1169 solver.add_property("vpml", py::make_function(&Solver_vPML<Solver>, py::with_custodian_and_ward_postcall<0,1>()),
1171 "Vertical Perfectly Matched Layers boundary conditions.\n\n"
1172 ".. rubric:: Attributes\n\n"
1173 ".. autosummary::\n\n"
1174 " ~optical.modal.PML.factor\n"
1175 " ~optical.modal.PML.dist\n"
1176 " ~optical.modal.PML.size\n\n"
1177 "Attribute ``shape`` is ignored for vertical PML (it is always 0).\n"
1178 );
1179 solver.add_property("transfer", &Solver::getTransferMethod, &Solver::setTransferMethod,
1180 "Preferred transfer method.\n\n"
1181 "Can take on of the following values:\n\n"
1182 "============ ====================================\n"
1183 "*auto* Automatically choose the best method\n"
1184 "*reflection* Reflection Transfer Method\n"
1185 "*admittance* Admittance Transfer Method\n"
1186 "*impedance* Impedance Transfer Method\n"
1187 "============ ====================================\n\n"
1188 "Reflection transfer can have optional suffix ``-admittance`` (default)\n"
1189 "or ``-impedance``, in which case the admittance/impedance matching is done\n"
1190 "at interface (for eigenmode search). You should prefer admittance if electric\n"
1191 "field is expected to have significant horizontal components (particularly\n"
1192 "at the interface) i.e. for TE-like modes and impedance for TM-like modes."
1193 );
1194 solver.def_readwrite("determinant_type", &ModalBase::determinant_type,
1195 "Type of determinant that is computed in root finding.\n\n"
1196 "This attribute specifies what is returned by the :meth:`get_determinant`\n"
1197 "method. Regardless of the determinant type, its value must be zero for any mode.\n\n"
1198 "Can take on of the following values that specified what quantity is computed\n"
1199 "for the characteristic matrix:\n\n"
1200 "============ ======================================\n"
1201 "*eigenvalue* Eigenvalue with the smallest magnitude\n"
1202 "*full* Determinant of the matrix\n"
1203 "============ ======================================\n"
1204 );
1205 solver.add_property("lam0", Solver_getLam0<Solver>, Solver_setLam0<Solver>,
1206 "Reference wavelength.\n\n"
1207 "This is a wavelength at which refractive index is retrieved from the structure.\n"
1208 "If this parameter is None, material parameters are computed each time,\n"
1209 "the wavelength changes even slightly (this is most accurate, but can be very\n"
1210 "inefficient.\n"
1211 );
1212 solver.def_readwrite("update_gain", &Solver::always_recompute_gain,
1213 "Recompute dynamic parameters.\n\n"
1214 "If this attribute is set to True, material parameters are always recomputed for\n"
1215 "layers with gain or permittivity provided by py::attr:`inEpsilon`. This allows\n"
1216 "to set 'lam0' for better efficiency and still consider slight changes of wavelength,\n"
1217 "where it matters the most.\n"
1218 );
1219 solver.def("integrateEE", &getIntegralEE_0<Solver>, (py::arg("z1"), "z2"));
1220 solver.def("integrateEE", &getIntegralEE<Solver>, (py::arg("num"), "z1", "z2"),
1221 u8"Get average integral of the squared electric field:\n\n"
1222 u8"\\\\[\\\\frac 1 2 \\\\int_{z_1}^{z_2} \\|E\\|^2.\\\\]\n\n"
1223 u8"In the lateral direction integration is performed over the whole domain.\n\n"
1224 u8"Args:\n"
1225 u8" num (int): Computed mode number.\n"
1226 u8" z1 (float): Lower vertical bound of the integral.\n"
1227 u8" z2 (float): Upper vertical bound of the integral.\n\n"
1228 u8"Returns:\n"
1229 u8" float: Computed integral [V\\ :sup:`2` / m\\ :sup:`2`].\n"
1230 u8"Warning:\n"
1231 u8" This method may return incorrect results for layers with gain,\n"
1232 u8" due to the strong non-Hemiticity!\n"
1233 );
1234 solver.def("integrateHH", &getIntegralHH_0<Solver>, (py::arg("z1"), "z2"));
1235 solver.def("integrateHH", &getIntegralHH<Solver>, (py::arg("num"), "z1", "z2"),
1236 u8"Get average integral of the squared magnetic field:\n\n"
1237 u8"\\\\[\\\\frac 1 2 \\\\int_{z_1}^{z_2} \\|H\\|^2.\\\\]\n\n"
1238 u8"In the lateral direction integration is performed over the whole domain.\n\n"
1239 u8"Args:\n"
1240 u8" num (int): Computed mode number.\n"
1241 u8" z1 (float): Lower vertical bound of the integral.\n"
1242 u8" z2 (float): Upper vertical bound of the integral.\n"
1243 u8"Returns:\n"
1244 u8" float: Computed integral [A\\ :sup:`2` / m\\ :sup:`2`].\n"
1245 u8"Warning:\n"
1246 u8" This method may return incorrect results for layers with gain,\n"
1247 u8" due to the strong non-Hemiticity!\n"
1248 );
1249
1250#ifndef NDEBUG
1251 solver.def("get_matrices", Solver_getMatrices<Solver>);
1252 solver.def("get_diagonalized", Solver_getDiagonalized<Solver>);
1253#endif
1254}
1255
1256
1257}}}} // # namespace plask::optical::modal::python
1258
1259#endif // PLASK__SOLVER_OPTICAL_MODAL_PYTHON_H