PLaSK library
Loading...
Searching...
No Matches
wasiak-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 */
17#include "plask/python_util/ufunc.hpp"
18#include <boost/python/raw_function.hpp>
19#include <cmath>
20#include <plask/python.hpp>
21using namespace plask;
22using namespace plask::python;
23
24#include "../ferminew.hpp"
25using namespace plask::solvers;
26
27template <typename GeometryT>
29 double c0,
30 double c1) {
31 return solver->getGainSpectrum(Vec<2>(c0, c1));
32}
33
34template <typename GeometryT>
35static py::object FermiNewGainSpectrum__call__(FermiNew::GainSpectrum<GeometryT>& self, py::object wavelengths) {
36 return UFUNC<Tensor2<double>, double>([&](double x) { return self.getGain(x); }, wavelengths, "Spectrum", "lam");
37}
38
39template <typename GeometryT>
41FermiNewGetLuminescenceSpectrum2(FermiNew::FermiNewGainSolver<GeometryT>* solver, double c0, double c1) {
42 return solver->getLuminescenceSpectrum(Vec<2>(c0, c1));
43}
44
45template <typename GeometryT>
46static py::object FermiNewLuminescenceSpectrum__call__(FermiNew::LuminescenceSpectrum<GeometryT>& self,
47 py::object wavelengths) {
48 return UFUNC<Tensor2<double>, double>([&](double x) { return self.getLuminescence(x); }, wavelengths, "Spectrum", "lam");
49}
50
51/*template <typename GeometryT>
52static py::object FermiNewGain_setLevels(py::tuple args, py::dict kwargs)
53{
54 if (py::len(args) != 1) {
55 throw TypeError("set_levels() takes exactly 1 non-keyword argument1 ({0} given)", py::len(args));
56 }
57
58 double* el = nullptr;
59 double* hh = nullptr;
60 double* lh = nullptr;
61 try {
62 py::stl_input_iterator<std::string> begin(kwargs), end;
63 for (auto key = begin; key != end; ++key) {
64 if (*key == "el") {
65 size_t n = py::len(kwargs["el"]);
66 el = new double[n+1]; el[n] = 1.;
67 for (size_t i = 0; i != n; ++i) el[i] = - py::extract<double>(kwargs["el"][i]);
68 } else if (*key == "hh") {
69 size_t n = py::len(kwargs["hh"]);
70 hh = new double[n+1]; hh[n] = 1.;
71 for (size_t i = 0; i != n; ++i) hh[i] = - py::extract<double>(kwargs["hh"][i]);
72 } else if (*key == "lh") {
73 size_t n = py::len(kwargs["lh"]);
74 lh = new double[n+1]; lh[n] = 1.;
75 for (size_t i = 0; i != n; ++i) lh[i] = - py::extract<double>(kwargs["lh"][i]);
76 } else if (*key != "Fc" && *key != "Fv")
77 throw TypeError("set_levels() got an unexpected keyword argument '{}'", *key);
78 }
79 if (!el || !hh || !lh) {
80 throw ValueError("all 'el', 'hh', and 'lh' levels must be set");
81 }
82 } catch(...) {
83 delete[] el; delete[] hh; delete[] lh;
84 throw;
85 }
86
87 return py::object();
88}*/
89
90template <typename GeometryT>
91static py::object FermiNew_getLevels(FermiNew::FermiNewGainSolver<GeometryT>& self, py::object To) {
92 self.initCalculation();
93 py::list result;
94 double T = To.is_none() ? self.Tref : py::extract<double>(To);
95 for (size_t reg = 0; reg < self.region_levels.size(); ++reg) {
96 py::dict info;
97 py::list el, hh, lh;
98 FermiNew::Levels* levels;
99 std::unique_ptr<FermiNew::Levels> levels_guard;
100 double deltaEg2 = 0.;
101 if (self.build_struct_once) {
102 if (!self.region_levels[reg])
103 self.findEnergyLevels(self.region_levels[reg], self.regions[reg], self.Tref);
104 double Eg = self.regions[reg].getLayerMaterial(0)->CB(T, 0.) - self.regions[reg].getLayerMaterial(0)->VB(T, 0.);
105 // TODO Add strain
106 deltaEg2 = 0.5 * (Eg - self.region_levels[reg].Eg);
107 levels = &self.region_levels[reg];
108 } else {
110 levels = levels_guard.get();
111 self.findEnergyLevels(*levels, self.regions[reg], T);
112 }
113 if (levels->bandsEc)
114 for (auto stan : levels->bandsEc->rozwiazania) el.append(stan.poziom + deltaEg2);
115 if (levels->bandsEvhh)
116 for (auto stan : levels->bandsEvhh->rozwiazania) hh.append(-stan.poziom - deltaEg2);
117 if (levels->bandsEvlh)
118 for (auto stan : levels->bandsEvlh->rozwiazania) lh.append(-stan.poziom - deltaEg2);
119 info["el"] = el;
120 info["hh"] = hh;
121 info["lh"] = lh;
122 result.append(info);
123 }
124 return result;
125}
126
127template <typename GeometryT>
128static py::object FermiNew_getFermiLevels(FermiNew::FermiNewGainSolver<GeometryT>& self,
129 double n,
130 py::object To,
131 int reg) {
132 self.initCalculation();
133 double T = To.is_none() ? self.Tref : py::extract<double>(To);
134 if (reg < 0) reg += int(self.regions.size());
135 if (reg < 0 || std::size_t(reg) >= self.regions.size())
136 throw IndexError(u8"{}: Bad active region index", self.getId());
138 FermiNew::Levels* levels;
139 std::unique_ptr<FermiNew::Levels> levels_guard;
140 if (self.build_struct_once) {
141 if (!self.region_levels[reg])
142 self.findEnergyLevels(self.region_levels[reg], region, self.Tref);
143 levels = &self.region_levels[reg];
144 } else {
146 levels = levels_guard.get();
147 self.findEnergyLevels(*levels, region, T);
148 }
149 kubly::wzmocnienie gMod{self.getGainModule(1000., T, n, region, *levels)};
150
151 double straine =
152 self.strains ? self.substrateMaterial->lattC(T, 'a') / region.getLayerMaterial(0)->lattC(T, 'a') - 1. : 0.;
153 double DEc = region.getLayerMaterial(0)->CB(T, straine);
154 double DEv = region.getLayerMaterial(0)->VB(T, straine);
155
156 return py::make_tuple(gMod.qFlc + DEc, gMod.qFlv + DEv);
157}
158
164
165 {
167 "Gain solver based on Fermi Golden Rule for Cartesian 2D geometry.")
168 solver.add_property("strained", &__Class__::getStrains, &__Class__::setStrains,
169 "Consider strain in QW and barriers? (True or False).");
170 RW_PROPERTY(substrate, getSubstrate, setSubstrate,
171 u8"Substrate material.\n\n"
172 u8"Material of the substrate. This is used to compute strain in the active region.\n"
173 u8"If not set, the solver looks for geometry object with the __substrate__ role.\n");
174 solver.add_property("adjust_layers", &__Class__::getAdjustWidths, &__Class__::setAdjustWidths,
175 "Adjust thicknesses of quantum wells?\n\n"
176 "Setting this to True, allows to adjust the widths of the gain region layers\n"
177 "by few angstroms to improve numerical stability.");
178 solver.add_property("fast_levels", &__Class__::getBuildStructOnce, &__Class__::setBuildStructOnce,
179 "Compute levels only once and simply shift for different temperatures?\n\n"
180 "Setting this to True stongly increases computation speed, but makes the results\n"
181 "less accurate for high gains.");
182 RECEIVER(inTemperature, "");
183 RECEIVER(inCarriersConcentration, "");
184 PROVIDER(outGain, "");
185 PROVIDER(outLuminescence, "");
186 RW_PROPERTY(geometry_mod, getModGeometry, setModGeometry, "Modified geometry for broadening calculations.");
187 RW_PROPERTY(roughness, getRoughness, setRoughness,
188 "If there is no modified geometry: roughness of the thicknesses of the quantum wells.\n"
189 "With modified geometry present: broadening factor. (-).\n");
190 solver.attr("broadening") = solver.attr("roughness");
191 RW_PROPERTY(lifetime, getLifeTime, setLifeTime, "Carriers lifetime (ps)");
192 RW_PROPERTY(matrix_elem, getMatrixElem, setMatrixElem, "Optical matrix element (m₀ eV)");
193 RW_PROPERTY(cond_shift, getCondQWShift, setCondQWShift, "Additional conduction band shift for QW (eV)");
194 RW_PROPERTY(vale_shift, getValeQWShift, setValeQWShift, "Additional valence band shift for QW (eV)");
195 RW_PROPERTY(Tref, getTref, setTref,
196 "Reference temperature. If *fast_levels* is True, this is the temperature used\n"
197 "for initial computation of the energy levels (K).");
198 solver.def("get_levels", &FermiNew_getLevels<Geometry2DCartesian>, py::arg("T") = py::object());
199 solver.def("get_fermi_levels", &FermiNew_getFermiLevels<Geometry2DCartesian>,
200 (py::arg("n"), py::arg("T") = py::object(), py::arg("reg") = 0));
201 solver.def("spectrum", &__Class__::getGainSpectrum, "Get gain spectrum at given point", py::arg("point"),
202 py::with_custodian_and_ward_postcall<0, 1>());
203 solver.def("spectrum", FermiNewGetGainSpectrum2<Geometry2DCartesian>, "Get gain spectrum at given point",
204 (py::arg("c0"), "c1"), py::with_custodian_and_ward_postcall<0, 1>());
205 solver.def("luminescence_spectrum", &__Class__::getLuminescenceSpectrum,
206 "Get luminescence spectrum at given point", py::arg("point"),
207 py::with_custodian_and_ward_postcall<0, 1>());
208 solver.def("luminescence_spectrum", FermiNewGetLuminescenceSpectrum2<Geometry2DCartesian>,
209 "Get luminescence spectrum at given point", (py::arg("c0"), "c1"),
210 py::with_custodian_and_ward_postcall<0, 1>());
211
212 py::scope scope = solver;
213 py::class_<FermiNew::GainSpectrum<Geometry2DCartesian>,
215 "Spectrum", "Gain spectrum class. You can call it like a function to get gains for different vavelengths.",
216 py::no_init)
217 .def("__call__", &FermiNewGainSpectrum__call__<Geometry2DCartesian>, py::arg("lam"));
218 py::class_<FermiNew::LuminescenceSpectrum<Geometry2DCartesian>,
220 "LuminescenceSpectrum",
221 "Luminescence spectrum class. You can call it like a function to get luminescences for different "
222 "vavelengths.",
223 py::no_init)
225 }
226 {
228 "Gain solver based on Fermi Golden Rule for Cylindrical 2D geometry.")
229 solver.add_property("strained", &__Class__::getStrains, &__Class__::setStrains,
230 "Consider strain in QW and barriers? (True or False).");
231 RW_PROPERTY(substrate, getSubstrate, setSubstrate,
232 u8"Substrate material.\n\n"
233 u8"Material of the substrate. This is used to compute strain in the active region.\n"
234 u8"If not set, the solver looks for geometry object with the __substrate__ role.\n");
235 solver.add_property("adjust_layers", &__Class__::getAdjustWidths, &__Class__::setAdjustWidths,
236 "Adjust thicknesses of quantum wells?\n\n"
237 "Setting this to True, allows to adjust the widths of the gain region layers\n"
238 "by few angstroms to improve numerical stability.");
239 solver.add_property("fast_levels", &__Class__::getBuildStructOnce, &__Class__::setBuildStructOnce,
240 "Compute levels only once and simply shift for different temperatures?\n\n"
241 "Setting this to True strongly increases computation speed, but makes the results\n"
242 "less accurate for high gains.");
243 RECEIVER(inTemperature, "");
244 RECEIVER(inCarriersConcentration, "");
245 PROVIDER(outGain, "");
246 PROVIDER(outLuminescence, "");
247 RW_PROPERTY(geometry_mod, getModGeometry, setModGeometry, "Modified geomery for broadening calculations.");
248 RW_PROPERTY(roughness, getRoughness, setRoughness,
249 "If there is no modified geometry: roughness of the thicknesses of the quantum wells.\n"
250 "With modified geometry present: broadening factor. (-).\n");
251 solver.attr("broadening") = solver.attr("roughness");
252 RW_PROPERTY(lifetime, getLifeTime, setLifeTime, "Carriers lifetime (ps)");
253 RW_PROPERTY(matrix_elem, getMatrixElem, setMatrixElem, "optical matrix element (m₀ eV)");
254 RW_PROPERTY(cond_shift, getCondQWShift, setCondQWShift, "Additional conduction band shift for QW (eV)");
255 RW_PROPERTY(vale_shift, getValeQWShift, setValeQWShift, "Additional valence band shift for QW (eV)");
256 RW_PROPERTY(Tref, getTref, setTref,
257 "Reference temperature. If *fast_levels* is True, this is the temperature used\n"
258 "for initial computation of the energy levels (K).");
259 solver.def("get_levels", &FermiNew_getLevels<Geometry2DCylindrical>, py::arg("T") = py::object());
260 solver.def("get_fermi_levels", &FermiNew_getFermiLevels<Geometry2DCylindrical>,
261 (py::arg("n"), py::arg("T") = py::object(), py::arg("reg") = 0));
262 solver.def("spectrum", &__Class__::getGainSpectrum, "Get gain spectrum at given point", py::arg("point"),
263 py::with_custodian_and_ward_postcall<0, 1>());
264 solver.def("spectrum", FermiNewGetGainSpectrum2<Geometry2DCylindrical>, "Get gain spectrum at given point",
265 (py::arg("c0"), "c1"), py::with_custodian_and_ward_postcall<0, 1>());
266 solver.def("luminescence_spectrum", &__Class__::getLuminescenceSpectrum,
267 "Get luminescence spectrum at given point", py::arg("point"),
268 py::with_custodian_and_ward_postcall<0, 1>());
269 solver.def("luminescence_spectrum", FermiNewGetLuminescenceSpectrum2<Geometry2DCylindrical>,
270 "Get luminescence spectrum at given point", (py::arg("c0"), "c1"),
271 py::with_custodian_and_ward_postcall<0, 1>());
272
273 py::scope scope = solver;
274 py::class_<FermiNew::GainSpectrum<Geometry2DCylindrical>,
276 "Spectrum", "Gain spectrum class. You can call it like a function to get gains for different wavelengths.",
277 py::no_init)
278 .def("__call__", &FermiNewGainSpectrum__call__<Geometry2DCylindrical>, py::arg("lam"));
279 py::class_<FermiNew::LuminescenceSpectrum<Geometry2DCylindrical>,
281 "LuminescenceSpectrum",
282 "Luminescence spectrum class. You can call it like a function to get luminescences for different "
283 "wavelengths.",
284 py::no_init)
286 }
287}