15#include <plask/common/fem/python.hpp>
16#include <plask/python.hpp>
21#include "../electr2d.hpp"
22#include "../electr3d.hpp"
25static py::object outPotential(
const py::object& self) {
26 throw TypeError(
u8"{}: 'outPotential' is reserved for drift-diffusion model; use 'outVoltage' instead",
27 std::string(py::extract<std::string>(self.attr(
"id"))));
44 if (attr.substr(0, 4) ==
"beta")
return py::object(
getBeta(boost::lexical_cast<size_t>(attr.substr(4))));
45 if (attr.substr(0, 2) ==
"js")
return py::object(
getJs(boost::lexical_cast<size_t>(attr.substr(2))));
46 }
catch (boost::bad_lexical_cast&) {
51 static void __setattr__(
const py::object&
oself,
const std::string& attr,
const py::object& value) {
54 if (attr.substr(0, 4) ==
"beta") {
55 self.
setBeta(boost::lexical_cast<size_t>(attr.substr(4)), value);
58 if (attr.substr(0, 2) ==
"js") {
59 self.
setJs(boost::lexical_cast<size_t>(attr.substr(2)), value);
62 }
catch (boost::bad_lexical_cast&) {
64 oself.attr(
"__class__").attr(
"__base__").attr(
"__setattr__")(
oself, attr, value);
72 void setBeta(
size_t n,
const py::object& value) {
73 py::extract<double> val(value);
77 if (this->beta_function.size() <=
n) this->beta_function.resize(
n + 1);
81 throw TypeError(
"{}: beta{} must be a float or a callable", this->getId(),
n);
90 void setJs(
size_t n,
const py::object& value) {
91 py::extract<double> val(value);
95 if (this->js_function.size() <=
n) this->js_function.resize(
n + 1);
99 throw TypeError(
"{}: js{} must be a float or a callable", this->getId(),
n);
119 if (
size_t actn = this->isActive(dest_mesh->at(i))) {
132template <
typename GeometryT>
133struct PythonCondSolver :
public std::conditional<std::is_same<GeometryT, Geometry3D>::value,
134 ElectricalFem3DSolver,
135 ElectricalFem2DSolver<GeometryT>>::type {
136 typedef typename std::conditional<std::is_same<GeometryT, Geometry3D>::value,
154 if (this->cond_function.size() <=
n) this->cond_function.resize(
n + 1);
158 throw TypeError(
"{}: cond{} must be a a callable", this->getId(),
n);
164 if (attr.substr(0, 4) ==
"cond")
return py::object(
getCond(boost::lexical_cast<size_t>(attr.substr(4))));
165 }
catch (boost::bad_lexical_cast&) {
170 static void __setattr__(
const py::object&
oself,
const std::string& attr,
const py::object& value) {
174 if (attr.substr(0, 4) ==
"cond") {
175 self.
setCond(boost::lexical_cast<size_t>(attr.substr(4)), value);
178 }
catch (boost::bad_lexical_cast&) {
181 oself.attr(
"__class__").attr(
"__base__").attr(
"__setattr__")(
oself, attr, value);
192 throw IndexError(
"no conductivity for active region {}",
n);
196 return py::extract<Tensor2<double>>(cond);
214template <
typename __Class__>
217 format(
u8"{0}(name=\"\")\n\n"
218 u8"Finite element electric solver for {1} geometry.",
221 py::init<std::string>(py::arg(
"name") =
""));
222 METHOD(compute, compute,
u8"Run electrical calculations", py::arg(
"loops") = 0);
223 METHOD(get_total_current, getTotalCurrent,
u8"Get total current flowing through active region (mA)", py::arg(
"nact") = 0);
230 BOUNDARY_CONDITIONS(voltage_boundary,
u8"Boundary conditions of the first kind (constant potential)");
231 RW_FIELD(maxerr,
u8"Limit for the potential updates");
232 RW_FIELD(convergence,
u8"Convergence method.\n\nIf stable, n is slowed down to ensure stability.");
233 RW_PROPERTY(pcond, getCondPcontact, setCondPcontact,
u8"Conductivity of the p-contact");
234 RW_PROPERTY(ncond, getCondNcontact, setCondNcontact,
u8"Conductivity of the n-contact");
236 u8"Default effective conductivity of the active region.\n\n"
237 u8"Effective junction conductivity will be computed starting from this value.\n"
238 u8"Note that the actual junction conductivity after convergence can be obtained\n"
239 u8"with :attr:`outConductivity`.");
240 solver.attr(
"pnjcond") = solver.attr(
"start_cond");
241 solver.add_property(
"outPotential", outPotential,
u8"Not available in this solver. Use :attr:`outVoltage` instead.");
243 u8"Get the energy stored in the electrostatic field in the analyzed structure.\n\n"
245 u8" Total electrostatic energy [J].\n");
247 u8"Get the structure capacitance.\n\n"
249 u8" Total capacitance [pF].\n\n"
251 u8" This method can only be used it there are exactly two boundary conditions\n"
252 u8" specifying the voltage. Otherwise use :meth:`get_electrostatic_energy` to\n"
253 u8" obtain the stored energy $W$ and compute the capacitance as:\n"
254 u8" $C = 2 \\, W / U^2$, where $U$ is the applied voltage.\n");
256 u8"Get the total heat produced by the current flowing in the structure.\n\n"
258 u8" Total produced heat (mW).\n");
264template <
typename GeoT>
inline static void register_shockley_solver(
const char* name,
const char*
geoname) {
267 solver.add_property(
"beta", &__Class__::getBeta0, &__Class__::setBeta0,
268 u8"Junction coefficient (1/V).\n\n"
269 u8"In case, there is more than one junction you may set $\\\\beta$ parameter for any\n"
270 u8"of them by using ``beta#`` property, where # is the junction number (specified\n"
271 u8"by a role ``junction#`` or ``active#``).\n\n"
272 u8"``beta`` is an alias for ``beta0``.\n");
273 solver.add_property(
"js", &__Class__::getJs0, &__Class__::setJs0,
274 u8"Reverse bias current density (A/m\\ :sup:`2`\\ ).\n\n"
275 u8"In case, there is more than one junction you may set $j_s$ parameter for any\n"
276 u8"of them by using ``js#`` property, where # is the junction number (specified\n"
277 u8"by a role ``junction#`` or ``active#``).\n\n"
278 u8"``js`` is an alias for ``js0``.\n");
279 solver.def(
"__getattr__", &__Class__::__getattr__);
280 solver.def(
"__setattr__", &__Class__::__setattr__);
281 PROVIDER(outDifferentialConductivity,
u8"This provider gives the differential conductivity of the active regions.");
284template <
typename GeoT>
inline static void register_cond_solver(
const char* name,
const char*
geoname) {
287 solver.add_property(
"cond", &__Class__::getCond0, &__Class__::setCond0,
288 u8"Junction conductivity function [S/m].\n\n"
289 u8"This function should take junction voltage (V), current density (kA/cm²)\n"
290 u8"and temperature (K) as arguments and return a conductivity [S/m]. In case,\n"
291 u8"there is more than one junction you may set such function for any of them\n"
292 u8"by using ``cond#`` property, where # is the junction number (specified by\n"
293 u8"a role ``junction#`` or ``active#``).\n\n"
295 u8" >>> def cond(U, j, T):\n"
296 u8" ... return 1e-3 * j**2 + 1e-4 * T**2\n"
297 u8" >>> solver.cond = cond\n\n"
298 u8"``cond`` is an alias for ``cond0``.\n");
299 solver.def(
"__getattr__", &__Class__::__getattr__);
300 solver.def(
"__setattr__", &__Class__::__setattr__);