30namespace plask {
namespace electrical {
namespace drift_diffusion {
38 double m = pow(M.
c00 * M.
c00 * M.
c11, 0.3333333333333333);
39 return 2
e-6 * pow(
fact * m * T, 1.5);
46static inline double Ni(
double Nc,
double Nv,
double Eg,
double T) {
50template <
typename Geometry2DType>
57 mXx(sqrt((phys::epsilon0*phys::kB_J*mTx*mEpsRx)/(phys::qe*phys::qe*mNx))*1
e3),
60 mRx(((phys::kB_J*mTx*mMix*mNx)/(phys::qe*mXx*mXx))*1
e8),
61 mJx(((phys::kB_J*mNx)*mTx*mMix/mXx)*10.),
65 mCx(mRx/(mNx*mNx*mNx)),
67 mPx((mXx*phys::qe*mNx)*1
e-4),
111template <
typename Geometry2DType>
118 if (
param ==
"voltage")
119 this->readBoundaryConditions(manager, source, voltage_boundary);
120 else if (
param ==
"loop") {
124 .value(
"Maxwell-Boltzmann",
STAT_MB)
128 .value(
"ohmic",
OHMIC)
131 mSchottkyP = source.
getAttribute<
double>(
"SchottkyP", mSchottkyP);
132 mSchottkyN = source.
getAttribute<
double>(
"SchottkyN", mSchottkyN);
137 mFullIon = source.
getAttribute<
bool>(
"FullIon", mFullIon);
138 maxerrPsiI = source.
getAttribute<
double>(
"maxerrVi", maxerrPsiI);
139 maxerrPsi0 = source.
getAttribute<
double>(
"maxerrV0", maxerrPsi0);
140 maxerrPsi = source.
getAttribute<
double>(
"maxerrV", maxerrPsi);
141 maxerrFn = source.
getAttribute<
double>(
"maxerrFn", maxerrFn);
142 maxerrFp = source.
getAttribute<
double>(
"maxerrFp", maxerrFp);
143 loopsPsiI = source.
getAttribute<
size_t>(
"loopsVi", loopsPsiI);
144 loopsPsi0 = source.
getAttribute<
size_t>(
"loopsV0", loopsPsi0);
145 loopsPsi = source.
getAttribute<
size_t>(
"loopsV", loopsPsi);
146 loopsFn = source.
getAttribute<
size_t>(
"loopsFn", loopsFn);
147 loopsFp = source.
getAttribute<
size_t>(
"loopsFp", loopsFp);
149 }
else if (
param ==
"config") {
151 strained = source.
getAttribute<
bool>(
"strained", strained);
154 }
else if (!this->parseFemConfiguration(source, manager)) {
155 this->parseStandardConfiguration(source, manager);
161template <
typename Geometry2DType>
166template<
typename Geometry2DType>
175 size_t ileft = 0,
iright = points->axis[0]->size();
177 for (
size_t r = 0; r < points->axis[1]->size(); ++r) {
179 for (
size_t c = 0; c < points->axis[0]->size(); ++c) {
180 auto point = points->at(c,r);
181 bool active = isActive(point);
194 else throw Exception(
"{}: Right edge of the active region not aligned.", this->getId());
208 acthi = points->axis[1]->size();
211 throw BadInput(this->getId(),
"wrong active region number {}",
actnum);
216template <
typename Geometry2DType>
221 detectActiveRegions();
223 size = this->mesh->size();
226 dvnFnEta.reset(size, 1.);
227 dvnFpKsi.reset(size, 1.);
229 dvePsi.reset(this->mesh->getElementsCount());
230 dveFnEta.reset(this->mesh->getElementsCount(), 1.);
231 dveFpKsi.reset(this->mesh->getElementsCount(), 1.);
232 dveN.reset(this->mesh->getElementsCount());
233 dveP.reset(this->mesh->getElementsCount());
235 currentsN.reset(this->mesh->getElementsCount());
236 currentsP.reset(this->mesh->getElementsCount());
242template <
typename Geometry2DType>
257 substrateMaterial.reset();
263 double&,
double&,
double&,
double&,
double&,
double&,
269inline void DriftDiffusionModel2DSolver<Geometry2DCylindrical>::addCurvature(
double&
k44,
double&
k33,
double&
k22,
double&
k11,
286template <
typename Geometry2DType>
287template <CalcType calctype>
304 for (
auto e:
this->mesh->elements()) {
306 size_t i =
e.getIndex();
315 double hx = (
e.getUpper0() -
e.getLower0()) / mXx;
316 double hy = (
e.getUpper1() -
e.getLower1()) / mXx;
319 auto material = this->geometry->getMaterial(
midpoint);
333 double normNc = Neff(material->Me(T, 0.,
'*'), T) / mNx;
334 double normEc0 = material->CB(T, 0.,
'*') / mEx;
335 double normNv = Neff(material->Mh(T, 0.), T) / mNx;
336 double normEv0 = material->VB(T, 0.,
'*') / mEx;
337 double normT = T / mTx;
354 double kk, kx, ky,
gg, ff;
365 normEc0 = material->CB(T, 0.,
'*') / mEx;
366 normNc = Neff(material->Me(T, 0.,
'*'), T) / mNx;
367 normNv = Neff(material->Mh(T, 0.), T) / mNx;
370 normMobN = 0.5*(material->mobe(T).c00+material->mobe(T).c11) / mMix;
379 kk = 1. / (3.*(
hx*0.5)*(
hy*0.5));
387 double normte = material->taue(T) * mAx * 1
e-9;
388 double normth = material->tauh(T) * mAx * 1
e-9;
395 double normB = material->B(T) / mBx;
401 double normCe = material->Ce(T) / mCx;
402 double normCh = material->Ch(T) / mCx;
417 normEv0 = material->VB(T, 0.,
'*') / mEx;
418 normNc = Neff(material->Me(T, 0.,
'*'), T) / mNx;
419 normNv = Neff(material->Mh(T, 0.), T) / mNx;
422 normMobP = 0.5*(material->mobh(T).c00+material->mobh(T).c11) / mMix;
431 kk = 1. / (3.*(
hx*0.5)*(
hy*0.5));
439 double normte = material->taue(T) * mAx * 1
e-9;
440 double normth = material->tauh(T) * mAx * 1
e-9;
447 double normB = material->B(T) / mBx;
453 double normCe = material->Ce(T) / mCx;
454 double normCh = material->Ch(T) / mCx;
461 double normEps = material->eps(T) / mEpsRx;
463 kk = 1. / (3.*(
hx*0.5)*(
hy*0.5));
472 gg = (1./9.) * (p +
n) * (
hx*0.5) * (
hy*0.5);
474 double normNc = Neff(material->Me(T, 0.,
'*'), T) / mNx;
475 double normNv = Neff(material->Mh(T, 0.), T) / mNx;
477 double normNd = material->Nd() / mNx;
478 double normNa = material->Na() / mNx;
483 double gD(2.),
gA(4.);
484 double normEd = material->EactD(T) / mEx;
485 double normEa = material->EactA(T) / mEx;
493 double eII = (3.188 - material->lattC(T,
'a')) / material->lattC(T,
'a');
494 double eL = -2. *
eII * material->c13(T) / material->c33(T);
495 double Ppz = material->e33(T) *
eL + 2. * material->e13(T) *
eII;
496 double Ptot = material->Psp(T) +
Ppz;
520 addCurvature(
k44,
k33,
k22,
k11,
k43,
k21,
k42,
k31,
k32,
k41, ky,
hx,
midpoint);
570 double*
aend =
A.data +
A.size;
573 throw ComputationError(this->getId(),
"error in stiffness matrix at position {0} ({1})",
pa-
A.data,
isnan(*
pa)?
"nan":
"inf");
580template <
typename Geometry2DType>
581void DriftDiffusionModel2DSolver<Geometry2DType>::savePsi()
583 for (
auto el:
this->mesh->elements()) {
584 size_t i = el.getIndex();
585 size_t loleftno = el.getLoLoIndex();
586 size_t lorghtno = el.getUpLoIndex();
587 size_t upleftno = el.getLoUpIndex();
588 size_t uprghtno = el.getUpUpIndex();
595template <
typename Geometry2DType>
596void DriftDiffusionModel2DSolver<Geometry2DType>::saveFnEta()
598 for (
auto el:
this->mesh->elements()) {
599 size_t i = el.getIndex();
600 size_t loleftno = el.getLoLoIndex();
601 size_t lorghtno = el.getUpLoIndex();
602 size_t upleftno = el.getLoUpIndex();
603 size_t uprghtno = el.getUpUpIndex();
610template <
typename Geometry2DType>
611void DriftDiffusionModel2DSolver<Geometry2DType>::saveFpKsi()
613 for (
auto el:
this->mesh->elements()) {
614 size_t i = el.getIndex();
615 size_t loleftno = el.getLoLoIndex();
616 size_t lorghtno = el.getUpLoIndex();
617 size_t upleftno = el.getLoUpIndex();
618 size_t uprghtno = el.getUpUpIndex();
625template <
typename Geometry2DType>
626void DriftDiffusionModel2DSolver<Geometry2DType>::saveN()
632 auto iMeshE = (this->mesh)->getElementMesh();
635 for (
auto e:
this->mesh->elements())
637 size_t i =
e.getIndex();
655template <
typename Geometry2DType>
656void DriftDiffusionModel2DSolver<Geometry2DType>::saveP()
662 auto iMeshE = (this->mesh)->getElementMesh();
665 for (
auto e:
this->mesh->elements())
667 size_t i =
e.getIndex();
686template <
typename Geometry2DType>
687template <CalcType calctype>
688double DriftDiffusionModel2DSolver<Geometry2DType>::addCorr(DataVector<double>&
corr,
const BoundaryConditionsWithMesh <RectangularMesh<2>::Boundary,
double>&
vconst)
697 for (
auto i: cond.place)
702 double normDel = maxDelPsi0 / mEx;
703 for (std::size_t i = 0;
i < this->mesh->size(); ++
i) {
705 err = std::max(err, std::abs(
corr[i]));
708 this->
writelog(
LOG_DETAIL,
"Maximum update for the built-in potential: {:g} V", err*mEx);
712 double normDel = maxDelPsi / mEx;
713 for (std::size_t i = 0;
i < this->mesh->size(); ++
i) {
715 err = std::max(err, std::abs(
corr[i]));
723 for (std::size_t i = 0;
i < this->mesh->size(); ++
i) {
725 err = std::max(err, std::abs(
corr[i]/dvnFnEta[i]));
727 this->
writelog(
LOG_DETAIL,
"Maximum relative update for the quasi-Fermi energy level for electrons: {0}.", err);
732 for (std::size_t i = 0;
i < this->mesh->size(); ++
i) {
734 err = std::max(err, std::abs(
corr[i]/dvnFpKsi[i]));
736 this->
writelog(
LOG_DETAIL,
"Maximum relative update for the quasi-Fermi energy level for holes: {0}.", err);
763template <
typename Geometry2DType>
768 typedef std::pair<const Material*, unsigned>
KeyT;
769 std::map<KeyT, double> cache;
771 dvnPsi0.reset(size, 0.);
775 auto iMeshE = (this->mesh)->getElementMesh();
778 for (
auto el: this->mesh->elements()) {
779 size_t i = el.getIndex();
782 auto material = this->geometry->getMaterial(
midpoint);
789 KeyT key = std::make_pair(material.get(),
unsigned(0.5+T*100.));
790 auto found = cache.find(key);
793 if (
found != cache.end()) {
798 cache[key] =
epsi = 0.;
802 double normEc0 = material->CB(T, 0.,
'*') / mEx;
803 double normEv0 = material->VB(T, 0.,
'*',
'h') / mEx;
804 double normNc = Neff(material->Me(T, 0.,
'*'), T) / mNx;
805 double normNv = Neff(material->Mh(T, 0), T) / mNx;
806 double normNd = material->Nd() / mNx;
807 double normNa = material->Na() / mNx;
808 double normEd = material->EactD(T) / mEx;
809 double normEa = material->EactA(T) / mEx;
810 double normT = T / mTx;
811 std::size_t
loop = 0;
812 cache[key] =
epsi = findPsiI(
normEc0,
normEv0,
normNc,
normNv,
normNd,
normNa,
normEd,
normEa, 1., 1.,
normT,
loop);
815 size_t loleftno = el.getLoLoIndex();
816 size_t lorghtno = el.getUpLoIndex();
817 size_t upleftno = el.getLoUpIndex();
818 size_t uprghtno = el.getUpUpIndex();
824 divideByElements(dvnPsi0);
828 auto vconst = voltage_boundary(this->mesh, this->geometry);
830 for (
auto i: cond.place) {
832 dvnPsi0[i] += mSchottkyN/mEx;
833 else if (cond.value != 0)
834 dvnPsi0[i] += mSchottkyP/mEx;
840template <
typename Geometry2DType>
841double DriftDiffusionModel2DSolver<Geometry2DType>::findPsiI(
double iEc0,
double iEv0,
double iNc,
double iNv,
double iNd,
double iNa,
double iEd,
double iEa,
double iFnEta,
double iFpKsi,
double iT, std::size_t &
loop)
const
855 for (
int i = 0; i <
tPsi0n; ++i)
858 for (
int i = 0; i <
tPsi0n; ++i) {
868 double gD(2.),
gA(4.);
883 else if (
tNtot > 0.) {
899 while ((std::abs(
tPsiUpd) > (maxerrPsiI)/mEx) && (
tL < loopsPsiI)) {
910 double gD(2.),
gA(4.);
923 else if (
tNtot > 0.) {
936 this->
writelog(
LOG_DEBUG,
"Initial potential correction: {0} eV", (tPsiUpd)*mEx);
949template <
typename Geometry2DType>
956 double kx = 0., ky = 0.;
966 double dzdz1 = 1. / (dz*dz*1
e6);
967 double dz1 = 1. / (dz*1
e3);
976 Eigen::MatrixXcd
Hc(
N,
N);
978 std::complex<double>
Hc_zero(0., 0.);
979 for (
size_t i=1; i<=
N; ++i)
980 for (
size_t j=1; j <=
N; ++j)
983 for (
size_t i=1; i<=nn; ++i)
986 point_LE[0] = meshActMid->axis[0]->at(0);
987 point_LE[1] = meshActMid->axis[1]->at(i-1);
990 point_RI[0] = meshActMid->axis[0]->at(0);
991 point_RI[1] = meshActMid->axis[1]->at(i);
1042 this->
writelog(
LOG_INFO,
"Finding energy levels and wave functions for electrons..");
1043 Eigen::ComplexEigenSolver<Eigen::MatrixXcd>
ces;
1055 for (
size_t i = 1; i <
nEigVal; ++i)
1057 if ((
ces.eigenvalues()[i].real() - Eshift > CBelMin) && (
ces.eigenvalues()[i].real() - Eshift < CBelMax))
1059 std::vector<double>
sum(
K, 0.);
1060 for (
size_t j = 1; j < nn*1; j += 1)
1062 sum[0] += (pow(
ces.eigenvectors().col(i)[j].real(), 2.) + pow(
ces.eigenvectors().col(i)[j].imag(), 2.));
1068 lev_el.push_back(
ces.eigenvalues()[i].real() - Eshift);
1076 std::sort(lev_el.begin(), lev_el.end());
1077 for (
size_t i = 0; i < n_lev_el; ++i)
1079 this->
writelog(
LOG_INFO,
"energy level for electron {0}: {1} eV", i, lev_el[i]);
1094template <
typename Geometry2DType>
1098 std::vector<double>
CBel;
1101 for (
size_t i = 0; i < ne+2; ++i)
1124 for (
size_t i = 0; i < nn+2; ++i)
1125 CBel.push_back(5.0);
1126 for (
size_t i = 60; i < 140; ++i)
1131 for (
size_t i = 0; i < nn+2; ++i)
1133 if (
CBel[i] < CBelMin)
1135 if (
CBel[i] > CBelMax)
1140 CBel[nn+1] = CBelMax;
1150template <
typename Geometry2DType>
1158 auto iMesh = (this->mesh)->getElementMesh();
1159 auto temperatures = inTemperature(
iMesh);
1162 auto vconst = voltage_boundary(this->mesh, this->geometry);
1164 this->
writelog(
LOG_INFO,
"Running drift-diffusion calculations for a single voltage");
1166 std::unique_ptr<FemMatrix>
pA(this->getMatrix());
1177 while (
errorPsi0 > maxerrPsi0 && iter < loopsPsi0) {
1181 this->
writelog(
LOG_DEBUG,
"Initial potential maximum update: {0}", errorPsi0*mEx);
1185 dvnPsi = dvnPsi0.copy();
1197 for (
auto cond:
vconst) {
1198 for (
auto i: cond.place) {
1199 double dU = cond.value / mEx;
1201 dvnPsi[i] = dvnPsi0[i] + dU;
1202 dvnFnEta[i] =
exp(-dU);
1203 dvnFpKsi[i] =
exp(+dU);
1208 std::copy(dvnPsi0.begin(), dvnPsi0.end(), dvnPsi.begin());
1221 if (
loops == 0)
loops = std::numeric_limits<unsigned>::max();
1222 unsigned loopno = 0;
1230 while(err > maxerrPsi &&
itersPsi < loopsPsi) {
1245 while(err > maxerrFn &&
itersFn < loopsFn) {
1250 this->
writelog(
LOG_DETAIL,
"Maximum electrons quasi-Fermi level update: {0}", err*mEx);
1259 while(err > maxerrFp &&
itersFp < loopsFp) {
1272 for (
auto el: this->mesh->elements()) {
1273 size_t i = el.getIndex();
1274 size_t loleftno = el.getLoLoIndex();
1275 size_t lorghtno = el.getUpLoIndex();
1276 size_t upleftno = el.getLoUpIndex();
1277 size_t uprghtno = el.getUpUpIndex();
1279 / ((el.getUpper0() - el.getLower0())/mXx);
1281 / ((el.getUpper1() - el.getLower1())/mXx);
1283 / ((el.getUpper0() - el.getLower0())/mXx);
1285 / ((el.getUpper1() - el.getLower1())/mXx);
1306 auto material = this->geometry->getMaterial(
midpoint);
1308 double normMobN = 0.5*(material->mobe(T).c00+material->mobe(T).c11) / mMix;
1311 double normMobP = 0.5*(material->mobh(T).c00+material->mobh(T).c11) / mMix;
1314 currentsN[i] =
curN;
1315 currentsP[i] =
curP;
1318 outPotential.fireChanged();
1319 outFermiLevels.fireChanged();
1320 outBandEdges.fireChanged();
1321 outCurrentDensityForElectrons.fireChanged();
1322 outCurrentDensityForHoles.fireChanged();
1323 outCarriersConcentration.fireChanged();
1324 outHeat.fireChanged();
1329template <
typename Geometry2DType>
1334 heats.reset(this->mesh->getElementsCount());
1336 auto iMesh = (this->mesh)->getElementMesh();
1337 auto temperatures = inTemperature(
iMesh);
1340 for (
auto e:
this->mesh->elements()) {
1341 size_t i =
e.getIndex();
1347 auto material = this->geometry->getMaterial(
midpoint);
1353 double normMobN = 0.5*(material->mobe(T).c00+material->mobe(T).c11) / mMix;
1354 double normMobP = 0.5*(material->mobh(T).c00+material->mobh(T).c11) / mMix;
1356 heats[i] = ((currentsN[i].c0*currentsN[i].c0+currentsN[i].c1*currentsN[i].c1) / (
normMobN*dveN[i]) + (currentsP[i].c0*currentsP[i].c0+currentsP[i].c1*currentsP[i].c1) / (
normMobP*dveP[i])) * (1
e12 /
phys::qe);
1371 if (!dvnPsi)
throw NoValue(
"current densities");
1374 for (
size_t i = 0; i < mesh->axis[0]->size()-1; ++i) {
1375 auto element = mesh->element(i,
vindex);
1376 if (!
onlyactive || isActive(element.getMidpoint()))
1377 result += currentsN[element.getIndex()].c1 * element.getSize0() + currentsP[element.getIndex()].c1 * element.getSize0();
1380 return result * geometry->getExtrusion()->getLength() * 0.01;
1386 if (!dvnPsi)
throw NoValue(
"current densities");
1389 for (
size_t i = 0; i < mesh->axis[0]->size()-1; ++i) {
1390 auto element = mesh->element(i,
vindex);
1391 if (!
onlyactive || isActive(element.getMidpoint())) {
1392 double rin = element.getLower0(),
rout = element.getUpper0();
1400template <
typename Geometry2DType>
1403 size_t level = getActiveRegionMeshIndex(
nact);
1404 return integrateCurrent(level,
true);
1408template <
typename Geometry2DType>
1411 if (!dvnPsi)
throw NoValue(
"potential");
1414 return interpolate(this->mesh, dvnPsi*mEx, dst_mesh, method, this->geometry);
1418template <
typename Geometry2DType>
1423 if (!dvnFnEta)
throw NoValue(
"quasi-Fermi electron level");
1427 for (
size_t i = 0; i != dvnFnEta.size(); ++i) {
1428 if (dvnFnEta[i] > 0.)
dvnFn[i] =
log(dvnFnEta[i]) * mEx;
1435 if (!dvnFpKsi)
throw NoValue(
"quasi-Fermi hole level");
1439 for (
size_t i = 0; i != dvnFpKsi.size(); ++i) {
1440 if (dvnFpKsi[i] > 0.)
dvnFp[i] = -
log(dvnFpKsi[i]) * mEx;
1452template <
typename Geometry2DType>
1457 if (!dvnPsi)
throw NoValue(
"conduction band edge");
1462 auto iMeshE = (this->mesh)->getElementMesh();
1468 for (
auto e: this->mesh->elements()) {
1469 size_t i =
e.getIndex();
1476 auto material = this->geometry->getMaterial(
midpoint);
1485 divideByElements(
dvnEc);
1490 if (!dvnPsi)
throw NoValue(
"valence band edge");
1495 auto iMeshE = (this->mesh)->getElementMesh();
1501 for (
auto e: this->mesh->elements()) {
1502 size_t i =
e.getIndex();
1509 auto material = this->geometry->getMaterial(
midpoint);
1518 divideByElements(
dvnEv);
1528template <
typename Geometry2DType>
1531 if (!dvnFnEta)
throw NoValue(
"current density");
1535 auto result =
interpolate(this->mesh->getElementMesh(), currentsN, dest_mesh, method, flags);
1537 [
this, dest_mesh,
result, flags](
size_t i) {
1538 return this->geometry->getChildBoundingBox().contains(flags.
wrap(dest_mesh->at(i)))?
result[i] :
Vec<2>(0.,0.);
1544template <
typename Geometry2DType>
1547 if (!dvnFpKsi)
throw NoValue(
"current density");
1551 auto result =
interpolate(this->mesh->getElementMesh(), currentsP, dest_mesh, method, flags);
1553 [
this, dest_mesh,
result, flags](
size_t i) {
1554 return this->geometry->getChildBoundingBox().contains(flags.
wrap(dest_mesh->at(i)))?
result[i] :
Vec<2>(0.,0.);
1560template <
typename Geometry2DType>
1570 if (!dveN)
throw NoValue(
"electron concentration");
1573 for (
auto e: this->mesh->elements()) {
1574 size_t i =
e.getIndex();
1588 divideByElements(
dvn);
1591 return interpolate(this->mesh,
dvn, dst_mesh, method, this->geometry);
1594 if (!dveP)
throw NoValue(
"hole concentration");
1597 for (
auto e: this->mesh->elements()) {
1598 size_t i =
e.getIndex();
1612 divideByElements(
dvn);
1615 return interpolate(this->mesh,
dvn, dst_mesh, method, this->geometry);
1618 throw NotImplemented(
"{}: Carriers concentration of this type", this->getId());
1623template <
typename Geometry2DType>
1626 if ((!dvnFnEta)||(!dvnFpKsi))
throw NoValue(
"heat density");
1628 if (!heats) saveHeatDensities();
1631 auto result =
interpolate(this->mesh->getElementMesh(), heats, dest_mesh, method, flags);
1633 [
this, dest_mesh,
result, flags](
size_t i) {
1634 return this->geometry->getChildBoundingBox().contains(flags.wrap(dest_mesh->at(i)))? result[i] : 0.;
1640template <
typename Geometry2DType>
1648 size_t ileft = 0,
iright = points->axis[0]->size();
1654 for (
size_t r = 0; r < points->axis[1]->size(); ++r) {
1659 for (
size_t c = 0; c < points->axis[0]->size(); ++c)
1661 auto point = points->at(c, r);
1662 auto tags = this->geometry->getRolesAt(point);
1663 bool active =
false;
for (
const auto&
tag :
tags)
if (
tag.
substr(0, 6) ==
"active") { active =
true;
break; }
1668 if (!substrateMaterial)
1669 substrateMaterial = this->geometry->getMaterial(point);
1670 else if (*substrateMaterial != *this->geometry->getMaterial(point))
1671 throw Exception(
"{0}: Non-uniform substrate layer.", this->getId());
1675 throw Exception(
"{0}: All marked quantum wells must belong to marked active region.", this->getId());
1679 throw Exception(
"{0}: Left edge of the active region not aligned.", this->getId());
1683 throw Exception(
"{0}: Right edge of the active region not aligned.", this->getId());
1691 regions.emplace_back(mesh->at(c, r));
1699 throw Exception(
"{0}: Non-uniform active region layer.", this->getId());
1701 throw Exception(
"{0}: Quantum-well role of the active region layer not consistent.", this->getId());
1721 throw Exception(
"{0}: Right edge of the active region not aligned.", this->getId());
1729 ActiveRegionInfo* region = regions.empty() ?
nullptr : ®ions.back();
1733 throw Exception(
"{0}: Active region cannot start from the edge of the structure.", this->getId());
1738 throw Exception(
"{0}: Material below active region not uniform.", this->getId());
1739 auto& region = regions.back();
1740 double w = mesh->axis[0]->at(
iright) - mesh->axis[0]->at(
ileft);
1741 double h = mesh->axis[1]->at(r) - mesh->axis[1]->at(r - 1);
1742 region.origin += Vec<2>(0., -h);
1749 double h = mesh->axis[1]->at(r + 1) - mesh->axis[1]->at(r);
1750 double w = mesh->axis[0]->at(
iright) - mesh->axis[0]->at(
ileft);
1752 size_t n = region->layers->getChildrenCount();
1755 assert(!last || last->size.c0 == w);
1756 if (last &&
layer_material == last->getRepresentativeMaterial() &&
layer_QW == region->isQW(region->size() - 1)) {
1758 last->setSize(w, last->size.c1 + h);
1762 if (
layer_QW) layer->addRole(
"QW");
1763 region->layers->push_back(layer);
1773 throw Exception(
"{0}: Material above quantum well not uniform.", this->getId());
1778 iright = points->axis[0]->size();
1785 if (!regions.empty() && regions.back().isQW(regions.back().size() - 1))
1786 throw Exception(
"{0}: Quantum-well cannot be located at the edge of the structure.", this->getId());
1788 if (strained && !substrateMaterial)
1789 throw BadInput(this->getId(),
"strained quantum wells requested but no layer with substrate role set");
1791 this->
writelog(
LOG_DETAIL,
"Found {0} active region{1}", regions.size(), (regions.size() == 1) ?
"" :
"s");
1792 for (
auto& region : regions) region.summarize(
this);
2025template <
typename Geometry2DType>
2031 solver->writelog(
LOG_DETAIL,
"coordinates | bbox.upper: {0} um, bbox.lower: {1} um, bottom: {2} um, top: {3} um, total: {4} um",
bbox.upper[1],
bbox.lower[1], bottom, top, total);
2032 materials.clear(); materials.reserve(
layers->children.size());
2033 thicknesses.clear(); thicknesses.reserve(
layers->children.size());
2034 for (
const auto& layer :
layers->children) {
2036 auto material =
block->singleMaterial();
2037 if (!material)
throw plask::Exception(
"{}: Active region can consist only of solid layers",
solver->getId());
2040 solver->writelog(
LOG_DETAIL,
"layer | material: {0}, thickness: {1} um", material->name(),
thck);
2041 materials.push_back(material);
2042 thicknesses.push_back(
thck);