18#include <plask/python.hpp>
19#include "plask/python_util/ufunc.hpp"
27#define ROOTDIGGER_ATTRS_DOC \
28 u8".. rubric:: Attributes:\n\n" \
29 u8".. autosummary::\n\n" \
30 u8" ~optical.effective.RootParams.alpha\n" \
31 u8" ~optical.effective.RootParams.lambd\n" \
32 u8" ~optical.effective.RootParams.initial_range\n" \
33 u8" ~optical.effective.RootParams.maxiter\n" \
34 u8" ~optical.effective.RootParams.maxstep\n" \
35 u8" ~optical.effective.RootParams.method\n" \
36 u8" ~optical.effective.RootParams.tolf_max\n" \
37 u8" ~optical.effective.RootParams.tolf_min\n" \
38 u8" ~optical.effective.RootParams.tolx\n\n" \
39 u8" ~optical.effective.RootParams.stairs\n\n" \
40 u8":rtype: RootParams\n"
42#define SEARCH_ARGS_DOC \
43 u8" start (complex): Start of the search range (0 means automatic).\n" \
44 u8" end (complex): End of the search range (0 means automatic).\n" \
45 u8" resteps (integer): Number of steps on the real axis during the search.\n" \
46 u8" imsteps (integer): Number of steps on the imaginary axis during the search.\n" \
47 u8" eps (complex): required precision of the search.\n"
53 default:
return py::object();
63 std::string
sym = py::extract<std::string>(symmetry);
64 if (sym ==
"0" || sym ==
"none") {
66 }
else if (sym ==
"positive" || sym ==
"pos" || sym ==
"symmeric" || sym ==
"+" || sym ==
"+1") {
68 }
else if (sym ==
"negative" || sym ==
"neg" || sym ==
"anti-symmeric" || sym ==
"antisymmeric" || sym ==
"-" ||
72 throw py::error_already_set();
73 }
catch (py::error_already_set&) {
76 int sym = py::extract<int>(symmetry);
79 }
else if (sym == +1) {
81 }
else if (sym == -1) {
84 throw py::error_already_set();
85 }
catch (py::error_already_set&) {
91static std::string EffectiveIndex2D_getPolarization(
const EffectiveIndex2D& self) {
95static void EffectiveIndex2D_setPolarization(
EffectiveIndex2D& self, std::string polarization) {
96 if (polarization ==
"TE" || polarization ==
"s") {
100 if (polarization ==
"TM" || polarization ==
"p") {
106static py::object EffectiveIndex2D_getDeterminant(
EffectiveIndex2D& self, py::object val) {
114static py::object EffectiveFrequencyCyl_getDeterminant(
EffectiveFrequencyCyl& self, py::object val,
int m) {
116 "EffectiveFrequencyCyl.get_determinant",
"lam");
118static py::object EffectiveFrequencyCyl_getVertDeterminant(
EffectiveFrequencyCyl& self, py::object val) {
120 "EffectiveFrequencyCyl.get_vert_determinant",
"lam");
128 if (!self.
mirrors)
return py::object();
137 double v = py::extract<double>(value);
138 self.
mirrors.reset(std::make_pair(
v,
v));
139 }
catch (py::error_already_set&) {
142 if (py::len(value) != 2)
throw py::error_already_set();
144 std::make_pair<double, double>(
double(py::extract<double>(value[0])),
double(py::extract<double>(value[1]))));
145 }
catch (py::error_already_set&) {
146 throw ValueError(
u8"none, float, or tuple of two floats required");
152static size_t EffectiveIndex2D_findMode(
EffectiveIndex2D& self, py::object neff, py::object symmetry) {
153 return self.
findMode(py::extract<dcomplex>(neff), parseSymmetry(symmetry));
166static size_t EffectiveIndex2D_setMode(
EffectiveIndex2D& self, py::object neff, py::object symmetry) {
167 return self.
setMode(py::extract<dcomplex>(neff), parseSymmetry(symmetry));
175 default: sym =
"none";
177 return format(
u8"<neff: {:.3f}{:+.3g}j, symmetry: {}, power: {:.2g}mW>",
real(self.
neff),
imag(self.
neff), sym, self.
power);
184 default: sym =
"None";
186 return format(
u8"EffectiveIndex2D.Mode(neff={0}, symmetry={1}, power={2})",
str(self.
neff), sym, self.
power);
190 return self.
findMode(py::extract<dcomplex>(lam), m);
195template <
typename SolverT>
static void Optical_setMesh(
SolverT& self, py::object
omesh) {
198 self.setHorizontalMesh(mesh);
199 }
catch (py::error_already_set&) {
204 }
catch (py::error_already_set&) {
206 plask::python::detail::ExportedSolverDefaultDefs<SolverT>::Solver_setMesh(self,
omesh);
214 return py::object(r);
225 return format(
u8"<m: {:d}, lam: ({:.3f}{:+.3g}j)nm, power: {:.2g}mW>", self.
m,
real(self.
lam),
imag(self.
lam), self.
power);
228 return format(
u8"EffectiveFrequencyCyl.Mode(m={0}, lam={1}, power={2})", self.
m,
str(self.
lam), self.
power);
231template <
typename Solver>
static double Mode_total_absorption(
typename Solver::Mode& self) {
232 return self.solver->getTotalAbsorption(self);
237template <
typename Solver>
static py::object getDeltaNeff(
Solver& self, py::object pos) {
238 return UFUNC<dcomplex, double>([&](
double p) {
return self.getDeltaNeff(p); },
pos,
"EffectiveIndex2D.get_delta_neff",
"pos");
256 u8"Calculate optical modes and optical field distribution using the effective index\n"
257 u8"method in two-dimensional Cartesian space.\n")
259 u8"Mesh provided to the solver.");
260 solver.add_property(
"polarization", &EffectiveIndex2D_getPolarization, &EffectiveIndex2D_setPolarization,
261 u8"Polarization of the searched modes.");
263 u8"Configuration of the root searching algorithm for horizontal component of the\n"
266 u8"Configuration of the root searching algorithm for vertical component of the mode\n"
273 u8"Find the effective indices in the vertical direction within the specified range\n"
274 u8"using global method.\n\n"
278 u8" list of floats: List of the found effective indices in the vertical\n"
280 arg(
"start") = 0., arg(
"end") = 0., arg(
"resteps") = 256, arg(
"imsteps") = 64, arg(
"eps") = dcomplex(1
e-6, 1
e-9));
281 solver.def(
"find_mode", &EffectiveIndex2D_findMode,
282 u8"Compute the mode near the specified effective index.\n\n"
284 u8" neff (complex): Starting point of the root search.\n"
285 u8" symmetry ('+' or '-'): Symmetry of the mode to search. If this parameter\n"
286 u8" is not specified, the default symmetry is used:\n"
287 u8" positive mode symmetry fir symmetrical geometries\n"
288 u8" and no symmetry for asymmetrical geometries.\n\n"
290 u8" integer: Index in the :attr:`modes` list of the found mode.\n",
291 (arg(
"neff"), arg(
"symmetry") = py::object()));
293 u8"Find the modes within the specified range using global method.\n\n"
297 u8" list of integers: List of the indices in the :attr:`modes` list of the found\n"
299 (arg(
"start") = 0., arg(
"end") = 0., arg(
"symmetry") = py::object(), arg(
"resteps") = 256, arg(
"imsteps") = 64,
300 arg(
"eps") = dcomplex(1
e-6, 1
e-9)));
301 solver.def(
"set_mode", EffectiveIndex2D_setMode,
302 u8"Set the current mode the specified effective index.\n\n"
304 u8" neff (complex): Mode effective index.\n"
305 u8" symmetry ('+' or '-'): Symmetry of the mode to search.\n",
306 "neff", arg(
"symmetry") = py::object());
309 u8"Get total energy absorbed by from a mode in unit time.\n\n"
311 u8" num (int): number of the mode.\n\n"
313 u8" Total absorbed energy (mW).\n",
315 RW_PROPERTY(
vat, getStripeX, setStripeX,
u8"Horizontal position of the main stripe (with dominant mode).");
316 RW_FIELD(vneff,
u8"Effective index in the vertical direction.");
318 u8"Return effective index part for lateral propagation at specified horizontal\n"
321 u8" pos (float or array of floats): Horizontal position to get the effective\n"
324 u8"Mirror reflectivities. If None then they are automatically estimated from the"
325 u8"Fresnel equations.\n");
327 u8"Get vertical modal determinant for debugging purposes.\n\n"
329 u8" neff (complex of numeric array of complex): Vertical effective index value\n"
330 u8" to compute the determinant at.\n\n"
332 u8" complex or list of complex: Determinant at the vertical effective index\n"
333 u8" *neff* or an array matching its size.",
335 solver.def(
"get_determinant", &EffectiveIndex2D_getDeterminant,
336 u8"Get modal determinant.\n\n"
338 u8" neff (complex or array of complex): effective index value\n"
339 u8" to compute the determinant at.\n\n"
341 u8" complex or list of complex: Determinant at the effective index *neff* or\n"
342 u8" an array matching its size.",
344 RW_PROPERTY(wavelength, getWavelength, setWavelength,
"Current wavelength.");
347 RECEIVER(inCarriersConcentration,
"");
354 "Alias for :attr:`outLightE`.");
358 u8"List of the computed modes.\n\n"
359 u8".. rubric:: Item Attributes\n\n"
360 u8".. autosummary::\n\n"
361 u8" ~optical.effective.EffectiveIndex2D.Mode.neff\n"
362 u8" ~optical.effective.EffectiveIndex2D.Mode.symmetry\n"
363 u8" ~optical.effective.EffectiveIndex2D.Mode.power\n"
364 u8" ~optical.effective.EffectiveIndex2D.Mode.total_absorption\n\n"
365 u8":rtype: optical.effecticve.EffectiveIndex2D.Mode\n");
367 py::scope
scope = solver;
372 py::class_<EffectiveIndex2D::Mode>(
"Mode",
u8"Detailed information about the mode.", py::no_init)
374 .add_property(
"symmetry", &EffectiveIndex2D_getSymmetry,
u8"Mode symmetry ('positive', 'negative', or None).")
378 u8"Cumulated absorption for the mode (mW).\n\n"
379 u8"This property combines gain in active region and absorption in the whole\n"
389 u8"Calculate optical modes and optical field distribution using the effective\n"
390 u8"frequency method in two-dimensional cylindrical space.\n")
392 "Mesh provided to the solver.");
393 RW_FIELD(k0,
u8"Reference normalized frequency.");
394 RW_FIELD(vlam,
u8"'Vertical wavelength' used as a helper for searching vertical modes.");
396 u8"Reference wavelength.");
398 u8"Configuration of the root searching algorithm for horizontal component of the\n"
401 u8"Configuration of the root searching algorithm for vertical component of the mode\n"
406 RW_PROPERTY(emission, getEmission, setEmission,
u8"Emission direction.");
408 u8"Radial determinant mode.\n\n"
409 u8"This parameter determines the method used to compute radial determinant.\n"
410 u8"If it is set to 'transfer', 2x2 transfer matrix is used to ensure field\n"
411 u8"continuity at the interfaces. For the 'full' value, one single matrix is\n"
412 u8"constructed for all the interfaces and its determinant is returned.");
416 solver.def(
"find_mode", &EffectiveFrequencyCyl_findMode,
417 u8"Compute the mode near the specified wavelength.\n\n"
419 u8" lam (complex): Initial wavelength to for root finging algorithm.\n"
420 u8" m (int): Angular mode number (O for LP0x, 1 for LP1x, etc.).\n\n"
422 u8" integer: Index in the :attr:`modes` list of the found mode.\n",
423 (arg(
"lam"), arg(
"m") = 0));
425 u8"Find the modes within the specified range using global method.\n\n"
427 u8" m (int): Angular mode number (O for LP0x, 1 for LP1x, etc.).\n\n" SEARCH_ARGS_DOC
430 u8" list of integers: List of the indices in the :attr:`modes` list of the found\n"
432 arg(
"start") = 0., arg(
"end") = 0., arg(
"m") = 0, arg(
"resteps") = 256, arg(
"imsteps") = 64,
433 arg(
"eps") = dcomplex(1
e-6, 1
e-9));
434 solver.def(
"get_vert_determinant", &EffectiveFrequencyCyl_getVertDeterminant,
435 u8"Get vertical modal determinant for debugging purposes.\n\n"
437 u8" vlam (complex of numeric array of complex): Vertical wavelength value\n"
438 u8" to compute the determinant at.\n\n"
440 u8" complex or list of complex: Determinant at the vertical wavelength *vlam* or\n"
441 u8" an array matching its size.\n",
443 solver.def(
"get_determinant", &EffectiveFrequencyCyl_getDeterminant,
444 u8"Get modal determinant.\n\n"
446 u8" lam (complex of numeric array of complex): wavelength to compute the\n"
447 u8" determinant at.\n"
448 u8" m (int): Angular mode number (O for LP0x, 1 for LP1x, etc.).\n\n",
450 u8" complex or list of complex: Determinant at the effective index *neff* or\n"
451 u8" an array matching its size.\n",
452 (py::arg(
"lam"), py::arg(
"m") = 0));
454 (py::arg(
"lam"), py::arg(
"m") = 0));
456 u8"Set the current mode the specified wavelength.\n\n"
458 u8" lam (float of complex): Mode wavelength.\n"
459 u8" loss (float): Mode losses. Allowed only if *lam* is a float.\n"
460 u8" m (int): Angular mode number (O for LP0x, 1 for LP1x, etc.).\n",
461 (py::arg(
"lam"),
"loss", py::arg(
"m") = 0));
464 u8"Get total energy absorbed from a mode in unit time.\n\n"
466 u8" num (int): number of the mode.\n\n"
468 u8" Total absorbed energy (mW).\n",
471 u8"Get total energy generated in the gain region to a mode in unit time.\n\n"
473 u8" num (int): number of the mode.\n\n"
475 u8" Total generated energy (mW).\n",
479 RECEIVER(inCarriersConcentration,
"");
484 solver.def_readonly(
"outElectricField",
487 u8"Alias for :attr:`outLightE`.");
491 u8"List of the computed modes.\n\n"
492 u8".. rubric:: Item Attributes\n\n"
493 u8".. autosummary::\n\n"
494 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.m\n"
495 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.lam\n"
496 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.wavelength\n"
497 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.power\n"
498 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.total_absorption\n"
499 u8" ~optical.effective.EffectiveFrequencyCyl.Mode.gain_integral\n\n"
500 u8":rtype: optical.effective.EffectiveFrquencyCyl.Mode\n");
502 u8"Radial position of at which the vertical part of the field is calculated.\n\n"
503 u8"Should be a float number or ``None`` to compute effective frequencies for all\n"
506 u8"Return effective index part for lateral propagation at specified radial\n"
509 u8" pos (float or array of floats): Radial position to get the effective index.\n");
510 solver.def(
"get_nng", &EffectiveFrequencyCyl_getNNg, py::arg(
"pos"),
511 u8"Return average index at specified radial position.\n\n"
513 u8" pos (float or array of floats): Radial position to get the effective index.\n");
515 py::scope
scope = solver;
520 py::class_<EffectiveFrequencyCyl::Mode>(
"Mode",
u8"Detailed information about the mode.", py::no_init)
523 u8"Alias for :attr:`~optical.effective.EffectiveFrequencyCyl.Mode.wavelength`.")
528 u8"Cumulated absorption for the mode (mW).\n\n"
529 u8"This property combines gain in active region and absorption in the whole\n"
531 .add_property(
"gain_integral", &Mode_gain_integral,
u8"Total gain for the mode (mW).")
545 py::class_<RootDigger::Params, boost::noncopyable>(
"RootParams",
u8"Configuration of the root finding algorithm.", py::no_init)
553 u8"Parameter ensuring sufficient decrease of determinant in each step\n(Broyden method only).")