PLaSK library
Loading...
Searching...
No Matches
effective.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 */
17#include <cmath>
18#include <plask/python.hpp>
19#include "plask/python_util/ufunc.hpp"
20using namespace plask;
21using namespace plask::python;
22
23#include "../efm.hpp"
24#include "../eim.hpp"
25using namespace plask::optical::effective;
26
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"
41
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"
48
49static py::object EffectiveIndex2D_getSymmetry(const EffectiveIndex2D::Mode& self) {
50 switch (self.symmetry) {
51 case EffectiveIndex2D::SYMMETRY_POSITIVE: return py::object("positive");
52 case EffectiveIndex2D::SYMMETRY_NEGATIVE: return py::object("negative");
53 default: return py::object();
54 }
55 return py::object();
56}
57
58static EffectiveIndex2D::Symmetry parseSymmetry(py::object symmetry) {
59 if (symmetry.is_none()) {
61 }
62 try {
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 == "-" ||
69 sym == "-1") {
71 }
72 throw py::error_already_set();
73 } catch (py::error_already_set&) {
75 try {
76 int sym = py::extract<int>(symmetry);
77 if (sym == 0) {
79 } else if (sym == +1) {
81 } else if (sym == -1) {
83 }
84 throw py::error_already_set();
85 } catch (py::error_already_set&) {
86 throw ValueError(u8"wrong symmetry specification.");
87 }
88 }
89}
90
91static std::string EffectiveIndex2D_getPolarization(const EffectiveIndex2D& self) {
92 return self.getPolarization() == EffectiveIndex2D::TE ? "TE" : "TM";
93}
94
95static void EffectiveIndex2D_setPolarization(EffectiveIndex2D& self, std::string polarization) {
96 if (polarization == "TE" || polarization == "s") {
98 return;
99 }
100 if (polarization == "TM" || polarization == "p") {
102 return;
103 }
104}
105
106static py::object EffectiveIndex2D_getDeterminant(EffectiveIndex2D& self, py::object val) {
107 return UFUNC<dcomplex>([&](dcomplex x) { return self.getDeterminant(x); }, val, "EffectiveIndex2D.get_determinant", "neff");
108}
109py::object EffectiveIndex2D_getVertDeterminant(EffectiveIndex2D& self, py::object val) {
110 return UFUNC<dcomplex>([&](dcomplex x) { return self.getVertDeterminant(x); }, val, "EffectiveIndex2D.get_vert_determinant",
111 "neff");
112}
113
114static py::object EffectiveFrequencyCyl_getDeterminant(EffectiveFrequencyCyl& self, py::object val, int m) {
115 return UFUNC<dcomplex>([&](dcomplex x) { return self.getDeterminant(x, m); }, val,
116 "EffectiveFrequencyCyl.get_determinant", "lam");
117}
118static py::object EffectiveFrequencyCyl_getVertDeterminant(EffectiveFrequencyCyl& self, py::object val) {
119 return UFUNC<dcomplex>([&](dcomplex x) { return self.getVertDeterminant(x); }, val,
120 "EffectiveFrequencyCyl.get_vert_determinant", "lam");
121}
122
123dcomplex EffectiveFrequencyCyl_getLambda0(const EffectiveFrequencyCyl& self) { return 2e3 * PI / self.k0; }
124
126
128 if (!self.mirrors) return py::object();
129 return py::make_tuple(self.mirrors->first, self.mirrors->second);
130}
131
132void EffectiveIndex2D_setMirrors(EffectiveIndex2D& self, py::object value) {
133 if (value.is_none())
134 self.mirrors.reset();
135 else {
136 try {
137 double v = py::extract<double>(value);
138 self.mirrors.reset(std::make_pair(v, v));
139 } catch (py::error_already_set&) {
140 PyErr_Clear();
141 try {
142 if (py::len(value) != 2) throw py::error_already_set();
143 self.mirrors.reset(
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");
147 }
148 }
149 }
150}
151
152static size_t EffectiveIndex2D_findMode(EffectiveIndex2D& self, py::object neff, py::object symmetry) {
153 return self.findMode(py::extract<dcomplex>(neff), parseSymmetry(symmetry));
154}
155
157 dcomplex neff1,
158 dcomplex neff2,
159 py::object symmetry,
160 size_t resteps,
161 size_t imsteps,
162 dcomplex eps) {
163 return self.findModes(neff1, neff2, parseSymmetry(symmetry), resteps, imsteps, eps);
164}
165
166static size_t EffectiveIndex2D_setMode(EffectiveIndex2D& self, py::object neff, py::object symmetry) {
167 return self.setMode(py::extract<dcomplex>(neff), parseSymmetry(symmetry));
168}
169
171 std::string sym;
172 switch (self.symmetry) {
173 case EffectiveIndex2D::SYMMETRY_POSITIVE: sym = "positive"; break;
174 case EffectiveIndex2D::SYMMETRY_NEGATIVE: sym = "negative"; break;
175 default: sym = "none";
176 }
177 return format(u8"<neff: {:.3f}{:+.3g}j, symmetry: {}, power: {:.2g}mW>", real(self.neff), imag(self.neff), sym, self.power);
178}
180 std::string sym;
181 switch (self.symmetry) {
182 case EffectiveIndex2D::SYMMETRY_POSITIVE: sym = "'positive'"; break;
183 case EffectiveIndex2D::SYMMETRY_NEGATIVE: sym = "'negative'"; break;
184 default: sym = "None";
185 }
186 return format(u8"EffectiveIndex2D.Mode(neff={0}, symmetry={1}, power={2})", str(self.neff), sym, self.power);
187}
188
189static size_t EffectiveFrequencyCyl_findMode(EffectiveFrequencyCyl& self, py::object lam, int m) {
190 return self.findMode(py::extract<dcomplex>(lam), m);
191}
192
194
195template <typename SolverT> static void Optical_setMesh(SolverT& self, py::object omesh) {
196 try {
197 shared_ptr<OrderedMesh1D> mesh = py::extract<shared_ptr<OrderedMesh1D>>(omesh);
198 self.setHorizontalMesh(mesh);
199 } catch (py::error_already_set&) {
200 PyErr_Clear();
201 try {
202 shared_ptr<MeshGeneratorD<1>> meshg = py::extract<shared_ptr<MeshGeneratorD<1>>>(omesh);
204 } catch (py::error_already_set&) {
205 PyErr_Clear();
206 plask::python::detail::ExportedSolverDefaultDefs<SolverT>::Solver_setMesh(self, omesh);
207 }
208 }
209}
210
212 double r = self.getStripeR();
213 if (std::isnan(r)) return py::object();
214 return py::object(r);
215}
216
218 if (r.is_none())
219 self.useAllStripes();
220 else
221 self.setStripeR(py::extract<double>(r));
222}
223
225 return format(u8"<m: {:d}, lam: ({:.3f}{:+.3g}j)nm, power: {:.2g}mW>", self.m, real(self.lam), imag(self.lam), self.power);
226}
228 return format(u8"EffectiveFrequencyCyl.Mode(m={0}, lam={1}, power={2})", self.m, str(self.lam), self.power);
229}
230
231template <typename Solver> static double Mode_total_absorption(typename Solver::Mode& self) {
232 return self.solver->getTotalAbsorption(self);
233}
234
235static double Mode_gain_integral(EffectiveFrequencyCyl::Mode& self) { return self.solver->getGainIntegral(self); }
236
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");
239}
240
241static py::object EffectiveFrequencyCyl_getNNg(EffectiveFrequencyCyl& self, py::object pos) {
242 return UFUNC<dcomplex, double>([&](double p) { return self.getNNg(p); }, pos, "EffectiveFrequencyCyl.get_nng", "pos");
243}
244
252 if (!plask_import_array()) throw(py::error_already_set());
253
254 {
255 CLASS(EffectiveIndex2D, "EffectiveIndex2D",
256 u8"Calculate optical modes and optical field distribution using the effective index\n"
257 u8"method in two-dimensional Cartesian space.\n")
258 solver.add_property("mesh", &EffectiveIndex2D::getMesh, &Optical_setMesh<EffectiveIndex2D>,
259 u8"Mesh provided to the solver.");
260 solver.add_property("polarization", &EffectiveIndex2D_getPolarization, &EffectiveIndex2D_setPolarization,
261 u8"Polarization of the searched modes.");
262 RO_FIELD(root,
263 u8"Configuration of the root searching algorithm for horizontal component of the\n"
264 u8"mode.\n\n" ROOTDIGGER_ATTRS_DOC);
265 RO_FIELD(stripe_root,
266 u8"Configuration of the root searching algorithm for vertical component of the mode\n"
267 u8"in a single stripe.\n\n" ROOTDIGGER_ATTRS_DOC);
268 RW_FIELD(emission, u8"Emission direction.");
269 METHOD(set_simple_mesh, setSimpleMesh, u8"Set simple mesh based on the geometry objects bounding boxes.");
270 // METHOD(set_horizontal_mesh, setHorizontalMesh, "Set custom mesh in horizontal direction, vertical one is based on the
271 // geometry objects bounding boxes", "points");
272 METHOD(search_vneff, searchVNeffs,
273 u8"Find the effective indices in the vertical direction within the specified range\n"
274 u8"using global method.\n\n"
275 u8"Args:\n" SEARCH_ARGS_DOC
276 "\n"
277 u8"Returns:\n"
278 u8" list of floats: List of the found effective indices in the vertical\n"
279 u8" direction.\n",
280 arg("start") = 0., arg("end") = 0., arg("resteps") = 256, arg("imsteps") = 64, arg("eps") = dcomplex(1e-6, 1e-9));
281 solver.def("find_mode", &EffectiveIndex2D_findMode,
282 u8"Compute the mode near the specified effective index.\n\n"
283 u8"Args:\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"
289 u8"Returns:\n"
290 u8" integer: Index in the :attr:`modes` list of the found mode.\n",
291 (arg("neff"), arg("symmetry") = py::object()));
292 solver.def("find_modes", &EffectiveIndex2D_findModes,
293 u8"Find the modes within the specified range using global method.\n\n"
294 u8"Args:\n" SEARCH_ARGS_DOC
295 "\n"
296 u8"Returns:\n"
297 u8" list of integers: List of the indices in the :attr:`modes` list of the found\n"
298 u8" modes.\n",
299 (arg("start") = 0., arg("end") = 0., arg("symmetry") = py::object(), arg("resteps") = 256, arg("imsteps") = 64,
300 arg("eps") = dcomplex(1e-6, 1e-9)));
301 solver.def("set_mode", EffectiveIndex2D_setMode,
302 u8"Set the current mode the specified effective index.\n\n"
303 u8"Args:\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());
307 METHOD(clear_modes, clearModes, "Clear all computed modes.\n");
308 solver.def("get_total_absorption", (double (EffectiveIndex2D::*)(size_t)) & EffectiveIndex2D::getTotalAbsorption,
309 u8"Get total energy absorbed by from a mode in unit time.\n\n"
310 u8"Args:\n"
311 u8" num (int): number of the mode.\n\n"
312 u8"Returns:\n"
313 u8" Total absorbed energy (mW).\n",
314 py::arg("num") = 0);
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.");
317 solver.def("get_delta_neff", &getDeltaNeff<EffectiveIndex2D>, py::arg("pos"),
318 u8"Return effective index part for lateral propagation at specified horizontal\n"
319 u8"position.\n\n"
320 u8"Args:\n"
321 u8" pos (float or array of floats): Horizontal position to get the effective\n"
322 u8" index.\n");
323 solver.add_property("mirrors", EffectiveIndex2D_getMirrors, EffectiveIndex2D_setMirrors,
324 u8"Mirror reflectivities. If None then they are automatically estimated from the"
325 u8"Fresnel equations.\n");
326 solver.def(u8"get_vert_determinant", EffectiveIndex2D_getVertDeterminant,
327 u8"Get vertical modal determinant for debugging purposes.\n\n"
328 u8"Args:\n"
329 u8" neff (complex of numeric array of complex): Vertical effective index value\n"
330 u8" to compute the determinant at.\n\n"
331 u8"Returns:\n"
332 u8" complex or list of complex: Determinant at the vertical effective index\n"
333 u8" *neff* or an array matching its size.",
334 py::arg("neff"));
335 solver.def("get_determinant", &EffectiveIndex2D_getDeterminant,
336 u8"Get modal determinant.\n\n"
337 u8"Args:\n"
338 u8" neff (complex or array of complex): effective index value\n"
339 u8" to compute the determinant at.\n\n"
340 u8"Returns:\n"
341 u8" complex or list of complex: Determinant at the effective index *neff* or\n"
342 u8" an array matching its size.",
343 py::arg("neff"));
344 RW_PROPERTY(wavelength, getWavelength, setWavelength, "Current wavelength.");
345 RECEIVER(inTemperature, "");
346 RECEIVER(inGain, "");
347 RECEIVER(inCarriersConcentration, "");
348 PROVIDER(outNeff, "");
349 PROVIDER(outLightMagnitude, "");
350 PROVIDER(outLightE, "");
351 solver.def_readonly(
352 "outElectricField",
354 "Alias for :attr:`outLightE`.");
355 PROVIDER(outRefractiveIndex, "");
356 PROVIDER(outHeat, "");
357 RO_FIELD(modes,
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");
366
367 py::scope scope = solver;
368 (void)scope; // don't warn about unused variable scope
369
371
372 py::class_<EffectiveIndex2D::Mode>("Mode", u8"Detailed information about the mode.", py::no_init)
373 .def_readonly("neff", &EffectiveIndex2D::Mode::neff, u8"Mode effective index.")
374 .add_property("symmetry", &EffectiveIndex2D_getSymmetry, u8"Mode symmetry ('positive', 'negative', or None).")
375 .def_readwrite("power", &EffectiveIndex2D::Mode::power, u8"Total power emitted into the mode (mW).")
376 .add_property("loss", &EffectiveIndex2D::Mode::loss, u8"Mode losses (1/cm).")
377 .add_property("total_absorption", &Mode_total_absorption<EffectiveFrequencyCyl>,
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"
380 u8"structure.")
381 .def("__str__", &EffectiveIndex2D_Mode_str)
382 .def("__repr__", &EffectiveIndex2D_Mode_repr);
383
385 }
386
387 {
388 CLASS(EffectiveFrequencyCyl, "EffectiveFrequencyCyl",
389 u8"Calculate optical modes and optical field distribution using the effective\n"
390 u8"frequency method in two-dimensional cylindrical space.\n")
391 solver.add_property("mesh", &EffectiveFrequencyCyl::getMesh, &Optical_setMesh<EffectiveFrequencyCyl>,
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.");
397 RO_FIELD(root,
398 u8"Configuration of the root searching algorithm for horizontal component of the\n"
399 u8"mode.\n\n" ROOTDIGGER_ATTRS_DOC);
400 RO_FIELD(stripe_root,
401 u8"Configuration of the root searching algorithm for vertical component of the mode\n"
402 u8"in a single stripe.\n\n" ROOTDIGGER_ATTRS_DOC);
403 // RW_PROPERTY(asymptotic, getAsymptotic, setAsymptotic,
404 // "Flag indicating whether the solver uses asymptotic exponential field\n"
405 // "in the outermost layer.")
406 RW_PROPERTY(emission, getEmission, setEmission, u8"Emission direction.");
407 solver.def_readwrite("determinant_mode", &EffectiveFrequencyCyl::determinant,
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.");
413 METHOD(set_simple_mesh, setSimpleMesh, u8"Set simple mesh based on the geometry objects bounding boxes.");
414 // METHOD(set_horizontal_mesh, setHorizontalMesh, u8"Set custom mesh in horizontal direction, vertical one is based on the
415 // geometry objects bounding boxes", "points");
416 solver.def("find_mode", &EffectiveFrequencyCyl_findMode,
417 u8"Compute the mode near the specified wavelength.\n\n"
418 u8"Args:\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"
421 u8"Returns:\n"
422 u8" integer: Index in the :attr:`modes` list of the found mode.\n",
423 (arg("lam"), arg("m") = 0));
424 METHOD(find_modes, findModes,
425 u8"Find the modes within the specified range using global method.\n\n"
426 u8"Args:\n"
427 u8" m (int): Angular mode number (O for LP0x, 1 for LP1x, etc.).\n\n" SEARCH_ARGS_DOC
428 "\n"
429 u8"Returns:\n"
430 u8" list of integers: List of the indices in the :attr:`modes` list of the found\n"
431 u8" modes.\n",
432 arg("start") = 0., arg("end") = 0., arg("m") = 0, arg("resteps") = 256, arg("imsteps") = 64,
433 arg("eps") = dcomplex(1e-6, 1e-9));
434 solver.def("get_vert_determinant", &EffectiveFrequencyCyl_getVertDeterminant,
435 u8"Get vertical modal determinant for debugging purposes.\n\n"
436 u8"Args:\n"
437 u8" vlam (complex of numeric array of complex): Vertical wavelength value\n"
438 u8" to compute the determinant at.\n\n"
439 u8"Returns:\n"
440 u8" complex or list of complex: Determinant at the vertical wavelength *vlam* or\n"
441 u8" an array matching its size.\n",
442 py::arg("vlam"));
443 solver.def("get_determinant", &EffectiveFrequencyCyl_getDeterminant,
444 u8"Get modal determinant.\n\n"
445 u8"Args:\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",
449 u8"Returns:\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));
453 solver.def("set_mode", (size_t(EffectiveFrequencyCyl::*)(dcomplex, int)) & EffectiveFrequencyCyl::setMode,
454 (py::arg("lam"), py::arg("m") = 0));
455 solver.def("set_mode", (size_t(EffectiveFrequencyCyl::*)(double, double, int)) & EffectiveFrequencyCyl::setMode,
456 u8"Set the current mode the specified wavelength.\n\n"
457 u8"Args:\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));
462 METHOD(clear_modes, clearModes, u8"Clear all computed modes.\n");
463 solver.def("get_total_absorption", (double (EffectiveFrequencyCyl::*)(size_t)) & EffectiveFrequencyCyl::getTotalAbsorption,
464 u8"Get total energy absorbed from a mode in unit time.\n\n"
465 u8"Args:\n"
466 u8" num (int): number of the mode.\n\n"
467 u8"Returns:\n"
468 u8" Total absorbed energy (mW).\n",
469 py::arg("num") = 0);
470 solver.def("get_gain_integral", (double (EffectiveFrequencyCyl::*)(size_t)) & EffectiveFrequencyCyl::getGainIntegral,
471 u8"Get total energy generated in the gain region to a mode in unit time.\n\n"
472 u8"Args:\n"
473 u8" num (int): number of the mode.\n\n"
474 u8"Returns:\n"
475 u8" Total generated energy (mW).\n",
476 py::arg("num") = 0);
477 RECEIVER(inTemperature, "");
478 RECEIVER(inGain, "");
479 RECEIVER(inCarriersConcentration, "");
480 PROVIDER(outWavelength, "");
481 PROVIDER(outLoss, "");
482 PROVIDER(outLightMagnitude, "");
483 PROVIDER(outLightE, "");
484 solver.def_readonly("outElectricField",
487 u8"Alias for :attr:`outLightE`.");
488 PROVIDER(outRefractiveIndex, "");
489 PROVIDER(outHeat, "");
490 RO_FIELD(modes,
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"
504 u8"the stripes.\n");
505 solver.def("get_delta_neff", &getDeltaNeff<EffectiveFrequencyCyl>, py::arg("pos"),
506 u8"Return effective index part for lateral propagation at specified radial\n"
507 u8"position.\n\n"
508 u8"Args:\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"
512 u8"Args:\n"
513 u8" pos (float or array of floats): Radial position to get the effective index.\n");
514
515 py::scope scope = solver;
516 (void)scope; // don't warn about unused variable scope
517
519
520 py::class_<EffectiveFrequencyCyl::Mode>("Mode", u8"Detailed information about the mode.", py::no_init)
521 .def_readonly("m", &EffectiveFrequencyCyl::Mode::m, u8"LP_mn mode parameter describing angular dependence.")
522 .def_readonly("lam", &EffectiveFrequencyCyl::Mode::lam,
523 u8"Alias for :attr:`~optical.effective.EffectiveFrequencyCyl.Mode.wavelength`.")
524 .def_readonly("wavelength", &EffectiveFrequencyCyl::Mode::lam, u8"Mode wavelength (nm).")
525 .def_readwrite("power", &EffectiveFrequencyCyl::Mode::power, u8"Total power emitted into the mode.")
526 .add_property("loss", &EffectiveFrequencyCyl::Mode::loss, u8"Mode losses (1/cm).")
527 .add_property("total_absorption", &Mode_total_absorption<EffectiveFrequencyCyl>,
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"
530 u8"structure.")
531 .add_property("gain_integral", &Mode_gain_integral, u8"Total gain for the mode (mW).")
532 .def("__str__", &EffectiveFrequencyCyl_Mode_str)
533 .def("__repr__", &EffectiveFrequencyCyl_Mode_repr);
534
541 .value("TOP", EffectiveFrequencyCyl::TOP)
542 .value("BOTTOM", EffectiveFrequencyCyl::BOTTOM);
543 }
544
545 py::class_<RootDigger::Params, boost::noncopyable>("RootParams", u8"Configuration of the root finding algorithm.", py::no_init)
546 .def_readwrite("method", &RootDigger::Params::method, u8"Root finding method ('muller', 'broyden', or 'brent')")
547 .def_readwrite("tolx", &RootDigger::Params::tolx, u8"Absolute tolerance on the argument.")
548 .def_readwrite("tolf_min", &RootDigger::Params::tolf_min, u8"Sufficient tolerance on the function value.")
549 .def_readwrite("tolf_max", &RootDigger::Params::tolf_max, u8"Required tolerance on the function value.")
550 .def_readwrite("maxiter", &RootDigger::Params::maxiter, u8"Maximum number of iterations.")
551 .def_readwrite("maxstep", &RootDigger::Params::maxstep, u8"Maximum step in one iteration (Broyden method only).")
552 .def_readwrite("alpha", &RootDigger::Params::maxstep,
553 u8"Parameter ensuring sufficient decrease of determinant in each step\n(Broyden method only).")
554 .def_readwrite("lambd", &RootDigger::Params::maxstep, u8"Minimum decrease ratio of one step (Broyden method only).")
555 .def_readwrite("initial_range", &RootDigger::Params::initial_dist, u8"Initial range size (Muller and Brent methods only).")
556 .def_readwrite("stairs", &RootDigger::Params::stairs, u8"Number of staircase iterations (Brent method only).");
557
559 .value("MULLER", RootDigger::ROOT_MULLER)
560 .value("BROYDEN", RootDigger::ROOT_BROYDEN)
561 .value("BRENT", RootDigger::ROOT_BRENT);
562}