290 constexpr double fact = 2
e-12 * phys::me / (phys::hb_eV * phys::hb_J);
292 double m1 = params.M[which][start].c11;
293 double k1_2 = fact * m1 * (
E - params.U[which][start]);
294 if (which != EL) k1_2 = -k1_2;
295 double k1 = sqrt(abs(k1_2));
298 A(0, 0) = A(nA - 1, nA - 1) = 1.;
299 A(0, 1) = A(nA - 1, nA - 2) = 0.;
301 for (
size_t i = start, o = 1; i < stop; i++, o += 2) {
302 double k0_2 = k1_2, k0 = k1, m0 = m1;
303 double d = (o == 1) ? 0. : params.region.thicknesses[i];
305 double coskd = cos(k0 * d), sinkd = sin(k0 * d);
307 A(o + 1, o - 1) = -sinkd;
311 double phi = exp(-k0 * d);
313 A(o + 1, o - 1) = -phi;
315 A(o + 1, o) = 1. / phi;
319 A(o - 1, o + 1) = 0.;
321 m1 = params.M[which][i + 1].c11;
322 k1_2 = fact * m1 * (
E - params.U[which][i + 1]);
323 if (which != EL) k1_2 = -k1_2;
327 A(o + 1, o + 1) = 0.;
329 A(o + 1, o + 2) = -(k1 * m0) / (k0 * m1);
332 double f = (k1 * m0) / (k0 * m1);
336 A(o + 1, o + 2) = -f;
340 return A.determinant();
343template <
typename BaseT>
345 if (params.
U[which].size() < 3)
return;
348 double umin = std::numeric_limits<double>::max(), umax = std::numeric_limits<double>::lowest();
350 double ustart, ustop;
352 for (
size_t i = start; i <= stop; ++i) {
353 double ub = params.
U[which][i];
354 if (i == start) ustart = ub;
355 if (i == stop) ustop = ub;
356 auto m = params.
M[which][i];
368 if (i != start && i != stop) {
369 double no = 1
e-6 /
PI * params.
region.
thicknesses[i] * sqrt(2. * phys::me / (phys::hb_eV * phys::hb_J) * m.c11);
374 umax =
min(ustart, ustop);
376 umin =
max(ustart, ustop);
378 throw Exception(
"{}: Outer layers of active region have wrong band offset", this->getId());
379 num = 2. * ceil(
sqrt(umax - umin) * num);
380 umin += 0.5 * levelsep;
381 umax -= 0.5 * levelsep;
382 double step = (umax - umin) / num;
383 size_t n = size_t(num);
385 double fa, fb =
level(which,
b, params, qw);
387 params.
levels[which].emplace_back(fb, M, which, params);
389 fb =
level(which,
b, params, qw);
391 for (
size_t i = 0;
i <
n; ++
i) {
395 fb =
level(which,
b, params, qw);
397 params.
levels[which].emplace_back(fb, M, which, params);
400 if ((fa < 0.) != (fb < 0.)) {
401 boost::uintmax_t iters = 1000;
403 std::tie(xa, xb) = toms748_solve([&](
double x) {
return level(which, x, params, qw); }, a,
b, fa, fb,
404 [
this](
double l,
double r) {
return r - l < levelsep; }, iters);
405 if (xb - xa > levelsep)
throw ComputationError(this->getId(),
"could not find level estimate in quantum well");
406 params.
levels[which].emplace_back(0.5 * (xa + xb), M, which, params);
411template <
typename BaseT>
413 if (params.
U[which].size() < 5)
return;
416 size_t N = params.
U[EL].size() - 1;
417 double umin = std::numeric_limits<double>::max(), umax = std::numeric_limits<double>::lowest();
419 umax =
min(params.
U[EL][0], params.
U[EL][
N]);
421 umin =
max(params.
U[which][0], params.
U[which][params.
U[which].size() - 1]);
424 if (i == 0 || i ==
N)
continue;
425 double ub = params.
U[which][i];
429 M = params.
M[which][i];
434 M = params.
M[which][i];
439 if (umax <= umin)
return;
442 2. * ceil(1
e-6 /
PI * params.
region.
total * sqrt(2. * (umax - umin) * phys::me / (phys::hb_eV * phys::hb_J) * M.
c11));
443 umin += 0.5 * levelsep;
444 umax -= 0.5 * levelsep;
445 double step = (umax - umin) / num;
446 size_t n = size_t(num);
448 double fa, fb = level(which,
b, params);
450 params.
levels[which].emplace_back(fb, M, which, params);
452 fb = level(which,
b, params);
454 for (
size_t i = 0; i <
n; ++i) {
458 fb = level(which,
b, params);
460 params.
levels[which].emplace_back(fb, M, which, params);
463 if ((fa < 0.) != (fb < 0.)) {
464 boost::uintmax_t iters = 1000;
466 std::tie(xa, xb) = toms748_solve([&](
double x) {
return level(which, x, params); }, a,
b, fa, fb,
467 [
this](
double l,
double r) {
return r - l < levelsep; }, iters);
468 if (xb - xa > levelsep)
throw ComputationError(this->getId(),
"could not find level estimate above quantum wells");
469 params.
levels[which].emplace_back(0.5 * (xa + xb), M, which, params);
476 params0.reserve(regions.size());
480 params0.emplace_back(
this, region);
482 for (
size_t qw = 0; qw < region.
wells.size() - 1; ++qw) {
483 estimateWellLevels(EL, params, qw);
484 if (region.
holes & ActiveRegionInfo::HEAVY_HOLES)
485 estimateWellLevels(HH, params, qw);
487 params.
levels[HH].clear();
488 if (region.
holes & ActiveRegionInfo::LIGHT_HOLES)
489 estimateWellLevels(LH, params, qw);
491 params.
levels[LH].clear();
493 std::sort(params.
levels[EL].begin(), params.
levels[EL].end(), [](
const Level& a,
const Level&
b) { return a.E < b.E; });
494 std::sort(params.
levels[HH].begin(), params.
levels[HH].end(), [](
const Level& a,
const Level&
b) { return a.E > b.E; });
495 std::sort(params.
levels[LH].begin(), params.
levels[LH].end(), [](
const Level& a,
const Level&
b) { return a.E > b.E; });
496 params.
nhh = std::min(params.
levels[EL].size(), params.
levels[HH].size());
497 params.
nlh = std::min(params.
levels[EL].size(), params.
levels[LH].size());
498 estimateAboveLevels(EL, params);
499 estimateAboveLevels(HH, params);
500 estimateAboveLevels(LH, params);
504 std::stringstream
str;
505 std::string sep =
"";
506 for (
auto l : params.
levels[EL]) {
507 str << sep << format(
"{:.4f}", l.E);
510 this->
writelog(
LOG_DETAIL,
"Estimated electron levels for active region {:d} (eV): {}", reg++,
str.str());
513 std::stringstream
str;
514 std::string sep =
"";
515 for (
auto l : params.
levels[HH]) {
516 str << sep << format(
"{:.4f}", l.E);
519 this->
writelog(
LOG_DETAIL,
"Estimated heavy hole levels for active region {:d} (eV): {}", reg - 1,
str.str());
522 std::stringstream
str;
523 std::string sep =
"";
524 for (
auto l : params.
levels[LH]) {
525 str << sep << format(
"{:.4f}", l.E);
528 this->
writelog(
LOG_DETAIL,
"Estimated light hole levels for active region {:d} (eV): {}", reg - 1,
str.str());
532 if (params.
levels[EL].empty())
throw Exception(
"{}: No electron levels found", this->getId());
533 if (params.
levels[HH].empty() && params.
levels[LH].empty())
throw Exception(
"{}: No hole levels found", this->getId());
538 size_t n = params.
levels[EL].size();
539 const double kT = phys::kB_eV * T;
540 constexpr double fact = phys::me * phys::kB_eV / (2. *
PI * phys::hb_eV * phys::hb_J);
544 for (
size_t i = 0; i <
n; ++i) {
545 double M = params.
levels[EL][i].M.c00;
546 N += 2. * fact * T * M / params.
levels[EL][i].thickness * log(1 + exp((
F - params.
levels[EL][i].E) / kT));
553 size_t nh = params.
levels[HH].size(), nl = params.
levels[LH].size();
554 const double kT = phys::kB_eV * T;
555 constexpr double fact = phys::me * phys::kB_eV / (2. *
PI * phys::hb_eV * phys::hb_J);
561 for (
size_t i = 0; i < nh; ++i) {
562 double M = params.
levels[HH][i].M.c00;
563 N += 2. * fact * T * M / params.
levels[HH][i].thickness * log(1 + exp((params.
levels[HH][i].E -
F) / kT));
566 for (
size_t i = 0; i < nl; ++i) {
567 double M = params.
levels[LH][i].M.c00;
568 N += 2. * fact * T * M / params.
levels[LH][i].thickness * log(1 + exp((params.
levels[LH][i].E -
F) / kT));
574template <
typename BaseT>
577 double Ue = params.
sideU(EL), Uh = params.
sideU(HH);
578 double fs = 0.05 * abs(Ue - Uh);
579 if (fs <= levelsep) fs = 2. * levelsep;
580 if (
isnan(Fc)) Fc = Ue;
581 if (
isnan(Fv)) Fv = Uh;
582 boost::uintmax_t iters;
586 std::tie(xa, xb) = fermi_bracket_and_solve([
this, T,
n, ¶ms](
double x) {
return getN(x, T, params) -
n; }, Fc, fs, iters);
587 if (xb - xa > levelsep)
throw ComputationError(this->getId(),
"could not find quasi-Fermi level for electrons");
588 Fc = 0.5 * (xa + xb);
591 std::tie(xa, xb) = fermi_bracket_and_solve([
this, T,
n, ¶ms](
double x) {
return getP(x, T, params) -
n; }, Fv, fs, iters);
592 if (xb - xa > levelsep)
throw ComputationError(this->getId(),
"could not find quasi-Fermi level for holes");
593 Fv = 0.5 * (xa + xb);
596template <
typename BaseT>
603 constexpr double fac = 1e4 * phys::qe * phys::qe / (2. * phys::c * phys::epsilon0 * phys::hb_J);
604 const double ikT = (1. / phys::kB_eV) / T;
605 const double Dhw = hw - params.
Eg;
609 for (
size_t i = 0; i < params.
nhh; ++i) {
610 const double Ec = params.
levels[EL][i].E, Ev = params.
levels[HH][i].E;
611 const double Ep = hw - (Ec - Ev);
612 if (Ep < 0.)
continue;
613 const double sin2 = (Dhw > 0.) ? Ep / Dhw : 0.;
615 const double mu = 1. / (1. / params.
levels[EL][i].M.c00 + 1. / params.
levels[HH][i].M.c00);
616 const double Ecp = Ec + Ep *
mu / params.
levels[EL][i].M.c00, Evp = Ev - Ep *
mu / params.
levels[HH][i].M.c00;
617 g +=
mu * (1. / (exp(ikT * (Ecp - Fc)) + 1) - 1. / (exp(ikT * (Evp - Fv)) + 1)) * pp;
620 for (
size_t i = 0; i < params.
nlh; ++i) {
621 const double Ec = params.
levels[EL][i].E, Ev = params.
levels[LH][i].E;
622 const double Ep = hw - (Ec - Ev);
623 if (Ep < 0.)
continue;
624 const double sin2 = (Dhw > 0.) ? Ep / Dhw : 0.;
625 const Tensor2<double> pp(0.3333333333333333333333 + 0.5 * sin2, 1.3333333333333333333333 - sin2);
626 const double mu = 1. / (1. / params.
levels[EL][i].M.c00 + 1. / params.
levels[LH][i].M.c00);
627 const double Ecp = Ec + Ep *
mu / params.
levels[EL][i].M.c00, Evp = Ev - Ep *
mu / params.
levels[LH][i].M.c00;
628 g +=
mu * (1. / (exp(ikT * (Ecp - Fc)) + 1) - 1. / (exp(ikT * (Evp - Fv)) + 1)) * pp;
633template <
typename BaseT>
640 if (lifetime == 0)
return getGain0(hw, Fc, Fv, T, nr, params);
642 const double E0 = params.
levels[EL][0].E - ((params.
region.
holes == ActiveRegionInfo::BOTH_HOLES)
643 ? std::max(params.
levels[HH][0].E, params.
levels[LH][0].E)
644 : (params.
region.
holes == ActiveRegionInfo::HEAVY_HOLES) ? params.
levels[HH][0].E
645 : params.
levels[LH][0].E);
647 const double b = 1e12 * phys::hb_eV / lifetime;
648 const double tmax = 32. *
b;
649 const double tmin = std::max(-tmax, E0 - hw);
650 double dt = (tmax - tmin) / 1024.;
653 for (
double t = tmin; t <= tmax; t += dt) {
655 g += getGain0(hw + t, Fc, Fv, T, nr, params) / (t * t +
b *
b);