Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
MediumSilicon.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <cmath>
3#include <fstream>
4#include <iostream>
5#include <sstream>
6
10#include "Garfield/Random.hh"
11#include "Garfield/Utilities.hh"
12
13namespace Garfield {
14
16 : Medium(),
17 m_eStepXL(m_eFinalXL / nEnergyStepsXL),
18 m_eStepG(m_eFinalG / nEnergyStepsG),
19 m_eStepV(m_eFinalV / nEnergyStepsV) {
20 m_className = "MediumSilicon";
21 m_name = "Si";
22
23 SetTemperature(300.);
28
29 m_driftable = true;
30 m_ionisable = true;
31 m_microscopic = true;
32
33 m_w = 3.6;
34 m_fano = 0.11;
35
36 m_ieMinL = int(m_eMinL / m_eStepXL) + 1;
37 m_ieMinG = int(m_eMinG / m_eStepG) + 1;
38
39 // Load the density of states table.
40 InitialiseDensityOfStates();
41}
42
43void MediumSilicon::SetDoping(const char type, const double c) {
44 if (toupper(type) == 'N') {
45 m_dopingType = 'n';
46 if (c > Small) {
47 m_dopingConcentration = c;
48 } else {
49 std::cerr << m_className << "::SetDoping:\n"
50 << " Doping concentration must be greater than zero.\n"
51 << " Using default value for n-type silicon "
52 << "(10^12 cm-3) instead.\n";
53 m_dopingConcentration = 1.e12;
54 }
55 } else if (toupper(type) == 'P') {
56 m_dopingType = 'p';
57 if (c > Small) {
58 m_dopingConcentration = c;
59 } else {
60 std::cerr << m_className << "::SetDoping:\n"
61 << " Doping concentration must be greater than zero.\n"
62 << " Using default value for p-type silicon "
63 << "(10^18 cm-3) instead.\n";
64 m_dopingConcentration = 1.e18;
65 }
66 } else if (toupper(type) == 'I') {
67 m_dopingType = 'i';
68 m_dopingConcentration = 0.;
69 } else {
70 std::cerr << m_className << "::SetDoping:\n"
71 << " Unknown dopant type (" << type << ").\n"
72 << " Available types are n, p and i (intrinsic).\n";
73 return;
74 }
75
76 m_isChanged = true;
77}
78
79void MediumSilicon::GetDoping(char& type, double& c) const {
80 type = m_dopingType;
81 c = m_dopingConcentration;
82}
83
84void MediumSilicon::SetTrapCrossSection(const double ecs, const double hcs) {
85 if (ecs < 0.) {
86 std::cerr << m_className << "::SetTrapCrossSection:\n"
87 << " Capture cross-section [cm2] must non-negative.\n";
88 } else {
89 m_eTrapCs = ecs;
90 }
91
92 if (hcs < 0.) {
93 std::cerr << m_className << "::SetTrapCrossSection:\n"
94 << " Capture cross-section [cm2] must be non-negative.n";
95 } else {
96 m_hTrapCs = hcs;
97 }
98
99 m_trappingModel = 0;
100 m_isChanged = true;
101}
102
103void MediumSilicon::SetTrapDensity(const double n) {
104 if (n < 0.) {
105 std::cerr << m_className << "::SetTrapDensity:\n"
106 << " Trap density [cm-3] must be non-negative.\n";
107 } else {
108 m_eTrapDensity = n;
109 m_hTrapDensity = n;
110 }
111
112 m_trappingModel = 0;
113 m_isChanged = true;
114}
115
116void MediumSilicon::SetTrappingTime(const double etau, const double htau) {
117 if (etau <= 0.) {
118 std::cerr << m_className << "::SetTrappingTime:\n"
119 << " Trapping time [ns-1] must be positive.\n";
120 } else {
121 m_eTrapTime = etau;
122 }
123
124 if (htau <= 0.) {
125 std::cerr << m_className << "::SetTrappingTime:\n"
126 << " Trapping time [ns-1] must be positive.\n";
127 } else {
128 m_hTrapTime = htau;
129 }
130
131 m_trappingModel = 1;
132 m_isChanged = true;
133}
134
135bool MediumSilicon::ElectronVelocity(const double ex, const double ey,
136 const double ez, const double bx,
137 const double by, const double bz,
138 double& vx, double& vy, double& vz) {
139 vx = vy = vz = 0.;
140 if (m_isChanged) {
141 if (!UpdateTransportParameters()) {
142 std::cerr << m_className << "::ElectronVelocity:\n"
143 << " Error calculating the transport parameters.\n";
144 return false;
145 }
146 m_isChanged = false;
147 }
148
149 if (!m_eVelE.empty()) {
150 // Interpolation in user table.
151 return Medium::ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
152 }
153
154 // Calculate the mobility.
155 const double emag = sqrt(ex * ex + ey * ey + ez * ez);
156 const double mu = -ElectronMobility(emag);
157
158 if (fabs(bx) < Small && fabs(by) < Small && fabs(bz) < Small) {
159 vx = mu * ex;
160 vy = mu * ey;
161 vz = mu * ez;
162 } else {
163 Langevin(ex, ey, ez, bx, by, bz, mu, m_eHallFactor * mu, vx, vy, vz);
164 }
165 return true;
166}
167
168bool MediumSilicon::ElectronTownsend(const double ex, const double ey,
169 const double ez, const double bx,
170 const double by, const double bz,
171 double& alpha) {
172 alpha = 0.;
173 if (m_isChanged) {
174 if (!UpdateTransportParameters()) {
175 std::cerr << m_className << "::ElectronTownsend:\n"
176 << " Error calculating the transport parameters.\n";
177 return false;
178 }
179 m_isChanged = false;
180 }
181
182 if (!m_eAlp.empty()) {
183 // Interpolation in user table.
184 return Medium::ElectronTownsend(ex, ey, ez, bx, by, bz, alpha);
185 }
186
187 const double emag = sqrt(ex * ex + ey * ey + ez * ez);
188 alpha = ElectronAlpha(emag);
189 return true;
190}
191
192bool MediumSilicon::ElectronAttachment(const double ex, const double ey,
193 const double ez, const double bx,
194 const double by, const double bz,
195 double& eta) {
196 eta = 0.;
197 if (m_isChanged) {
198 if (!UpdateTransportParameters()) {
199 std::cerr << m_className << "::ElectronAttachment:\n"
200 << " Error calculating the transport parameters.\n";
201 return false;
202 }
203 m_isChanged = false;
204 }
205
206 if (!m_eAtt.empty()) {
207 // Interpolation in user table.
208 return Medium::ElectronAttachment(ex, ey, ez, bx, by, bz, eta);
209 }
210
211 switch (m_trappingModel) {
212 case 0:
213 eta = m_eTrapCs * m_eTrapDensity;
214 break;
215 case 1:
216 double vx, vy, vz;
217 ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
218 eta = m_eTrapTime * sqrt(vx * vx + vy * vy + vz * vz);
219 if (eta > 0.) eta = -1. / eta;
220 break;
221 default:
222 std::cerr << m_className << "::ElectronAttachment: Unknown model. Bug!\n";
223 return false;
224 }
225
226 return true;
227}
228
229bool MediumSilicon::HoleVelocity(const double ex, const double ey,
230 const double ez, const double bx,
231 const double by, const double bz, double& vx,
232 double& vy, double& vz) {
233 vx = vy = vz = 0.;
234 if (m_isChanged) {
235 if (!UpdateTransportParameters()) {
236 std::cerr << m_className << "::HoleVelocity:\n"
237 << " Error calculating the transport parameters.\n";
238 return false;
239 }
240 m_isChanged = false;
241 }
242
243 if (!m_hVelE.empty()) {
244 // Interpolation in user table.
245 return Medium::HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
246 }
247
248 // Calculate the mobility.
249 const double emag = sqrt(ex * ex + ey * ey + ez * ez);
250 const double mu = HoleMobility(emag);
251
252 if (fabs(bx) < Small && fabs(by) < Small && fabs(bz) < Small) {
253 vx = mu * ex;
254 vy = mu * ey;
255 vz = mu * ez;
256 } else {
257 Langevin(ex, ey, ez, bx, by, bz, mu, m_hHallFactor * mu, vx, vy, vz);
258 }
259 return true;
260}
261
262bool MediumSilicon::HoleTownsend(const double ex, const double ey,
263 const double ez, const double bx,
264 const double by, const double bz,
265 double& alpha) {
266 alpha = 0.;
267 if (m_isChanged) {
268 if (!UpdateTransportParameters()) {
269 std::cerr << m_className << "::HoleTownsend:\n"
270 << " Error calculating the transport parameters.\n";
271 return false;
272 }
273 m_isChanged = false;
274 }
275
276 if (!m_hAlp.empty()) {
277 // Interpolation in user table.
278 return Medium::HoleTownsend(ex, ey, ez, bx, by, bz, alpha);
279 }
280
281 const double emag = sqrt(ex * ex + ey * ey + ez * ez);
282 alpha = HoleAlpha(emag);
283 return true;
284}
285
286bool MediumSilicon::HoleAttachment(const double ex, const double ey,
287 const double ez, const double bx,
288 const double by, const double bz,
289 double& eta) {
290 eta = 0.;
291 if (m_isChanged) {
292 if (!UpdateTransportParameters()) {
293 std::cerr << m_className << "::HoleAttachment:\n"
294 << " Error calculating the transport parameters.\n";
295 return false;
296 }
297 m_isChanged = false;
298 }
299
300 if (!m_hAtt.empty()) {
301 // Interpolation in user table.
302 return Medium::HoleAttachment(ex, ey, ez, bx, by, bz, eta);
303 }
304
305 switch (m_trappingModel) {
306 case 0:
307 eta = m_hTrapCs * m_hTrapDensity;
308 break;
309 case 1:
310 double vx, vy, vz;
311 HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
312 eta = m_hTrapTime * sqrt(vx * vx + vy * vy + vz * vz);
313 if (eta > 0.) eta = -1. / eta;
314 break;
315 default:
316 std::cerr << m_className << "::HoleAttachment: Unknown model. Bug!\n";
317 return false;
318 }
319
320 return true;
321}
322
323void MediumSilicon::SetLowFieldMobility(const double mue, const double muh) {
324 if (mue <= 0. || muh <= 0.) {
325 std::cerr << m_className << "::SetLowFieldMobility:\n"
326 << " Mobility must be greater than zero.\n";
327 return;
328 }
329
330 m_eMobility = mue;
331 m_hMobility = muh;
332 m_hasUserMobility = true;
333 m_isChanged = true;
334}
335
337 m_latticeMobilityModel = LatticeMobility::Minimos;
338 m_hasUserMobility = false;
339 m_isChanged = true;
340}
341
343 m_latticeMobilityModel = LatticeMobility::Sentaurus;
344 m_hasUserMobility = false;
345 m_isChanged = true;
346}
347
349 m_latticeMobilityModel = LatticeMobility::Reggiani;
350 m_hasUserMobility = false;
351 m_isChanged = true;
352}
353
355 m_dopingMobilityModel = DopingMobility::Minimos;
356 m_hasUserMobility = false;
357 m_isChanged = true;
358}
359
361 m_dopingMobilityModel = DopingMobility::Masetti;
362 m_hasUserMobility = false;
363 m_isChanged = true;
364}
365
367 const double vsath) {
368 if (vsate <= 0. || vsath <= 0.) {
369 std::cout << m_className << "::SetSaturationVelocity:\n"
370 << " Restoring default values.\n";
371 m_hasUserSaturationVelocity = false;
372 } else {
373 m_eSatVel = vsate;
374 m_hSatVel = vsath;
375 m_hasUserSaturationVelocity = true;
376 }
377
378 m_isChanged = true;
379}
380
382 m_saturationVelocityModel = SaturationVelocity::Minimos;
383 m_hasUserSaturationVelocity = false;
384 m_isChanged = true;
385}
386
388 m_saturationVelocityModel = SaturationVelocity::Canali;
389 m_hasUserSaturationVelocity = false;
390 m_isChanged = true;
391}
392
394 m_saturationVelocityModel = SaturationVelocity::Reggiani;
395 m_hasUserSaturationVelocity = false;
396 m_isChanged = true;
397}
398
400 m_highFieldMobilityModel = HighFieldMobility::Minimos;
401 m_isChanged = true;
402}
403
405 m_highFieldMobilityModel = HighFieldMobility::Canali;
406 m_isChanged = true;
407}
408
410 m_highFieldMobilityModel = HighFieldMobility::Reggiani;
411 m_isChanged = true;
412}
413
415 m_highFieldMobilityModel = HighFieldMobility::Constant;
416}
417
419 m_impactIonisationModel = ImpactIonisation::VanOverstraeten;
420 m_isChanged = true;
421}
422
424 m_impactIonisationModel = ImpactIonisation::Grant;
425 m_isChanged = true;
426}
427
429 m_impactIonisationModel = ImpactIonisation::Massey;
430 m_isChanged = true;
431}
432
434 m_impactIonisationModel = ImpactIonisation::Okuto;
435 m_isChanged = true;
436}
437
439 if (e <= m_eMinG + Small) {
440 std::cerr << m_className << "::SetMaxElectronEnergy:\n"
441 << " Requested upper electron energy limit (" << e
442 << " eV) is too small.\n";
443 return false;
444 }
445
446 m_eFinalG = e;
447 // Determine the energy interval size.
448 m_eStepG = m_eFinalG / nEnergyStepsG;
449
450 m_isChanged = true;
451
452 return true;
453}
454
455double MediumSilicon::GetElectronEnergy(const double px, const double py,
456 const double pz, double& vx, double& vy,
457 double& vz, const int band) {
458 // Effective masses
459 double mx = ElectronMass, my = ElectronMass, mz = ElectronMass;
460 // Energy offset
461 double e0 = 0.;
462 if (band >= 0 && band < m_nValleysX) {
463 // X valley
464 if (m_anisotropic) {
465 switch (band) {
466 case 0:
467 case 1:
468 // X 100, -100
469 mx *= m_mLongX;
470 my *= m_mTransX;
471 mz *= m_mTransX;
472 break;
473 case 2:
474 case 3:
475 // X 010, 0-10
476 mx *= m_mTransX;
477 my *= m_mLongX;
478 mz *= m_mTransX;
479 break;
480 case 4:
481 case 5:
482 // X 001, 00-1
483 mx *= m_mTransX;
484 my *= m_mTransX;
485 mz *= m_mLongX;
486 break;
487 default:
488 std::cerr << m_className << "::GetElectronEnergy:\n"
489 << " Unexpected band index " << band << "!\n";
490 break;
491 }
492 } else {
493 // Conduction effective mass
494 const double mc = 3. / (1. / m_mLongX + 2. / m_mTransX);
495 mx *= mc;
496 my *= mc;
497 mz *= mc;
498 }
499 } else if (band < m_nValleysX + m_nValleysL) {
500 // L valley, isotropic approximation
501 e0 = m_eMinL;
502 // Effective mass
503 const double mc = 3. / (1. / m_mLongL + 2. / m_mTransL);
504 mx *= mc;
505 my *= mc;
506 mz *= mc;
507 } else if (band == m_nValleysX + m_nValleysL) {
508 // Higher band(s)
509 }
510
511 if (m_nonParabolic) {
512 // Non-parabolicity parameter
513 double alpha = 0.;
514 if (band < m_nValleysX) {
515 // X valley
516 alpha = m_alphaX;
517 } else if (band < m_nValleysX + m_nValleysL) {
518 // L valley
519 alpha = m_alphaL;
520 }
521
522 const double p2 = 0.5 * (px * px / mx + py * py / my + pz * pz / mz);
523 if (alpha > 0.) {
524 const double e = 0.5 * (sqrt(1. + 4 * alpha * p2) - 1.) / alpha;
525 const double a = SpeedOfLight / (1. + 2 * alpha * e);
526 vx = a * px / mx;
527 vy = a * py / my;
528 vz = a * pz / mz;
529 return e0 + e;
530 }
531 }
532
533 const double e = 0.5 * (px * px / mx + py * py / my + pz * pz / mz);
534 vx = SpeedOfLight * px / mx;
535 vy = SpeedOfLight * py / my;
536 vz = SpeedOfLight * pz / mz;
537 return e0 + e;
538}
539
540void MediumSilicon::GetElectronMomentum(const double e, double& px, double& py,
541 double& pz, int& band) {
542 // If the band index is out of range, choose one at random.
543 if (band < 0 || band > m_nValleysX + m_nValleysL ||
544 (e < m_eMinL || band >= m_nValleysX) ||
545 (e < m_eMinG || band == m_nValleysX + m_nValleysL)) {
546 if (e < m_eMinL) {
547 band = int(m_nValleysX * RndmUniform());
548 if (band >= m_nValleysX) band = m_nValleysX - 1;
549 } else {
550 const double dosX = GetConductionBandDensityOfStates(e, 0);
551 const double dosL = GetConductionBandDensityOfStates(e, m_nValleysX);
552 const double dosG =
553 GetConductionBandDensityOfStates(e, m_nValleysX + m_nValleysL);
554 const double dosSum = m_nValleysX * dosX + m_nValleysL * dosL + dosG;
555 if (dosSum < Small) {
556 band = m_nValleysX + m_nValleysL;
557 } else {
558 const double r = RndmUniform() * dosSum;
559 if (r < dosX) {
560 band = int(m_nValleysX * RndmUniform());
561 if (band >= m_nValleysX) band = m_nValleysX - 1;
562 } else if (r < dosX + dosL) {
563 band = m_nValleysX + int(m_nValleysL * RndmUniform());
564 if (band >= m_nValleysX + m_nValleysL) band = m_nValleysL - 1;
565 } else {
566 band = m_nValleysX + m_nValleysL;
567 }
568 }
569 }
570 if (m_debug) {
571 std::cout << m_className << "::GetElectronMomentum:\n"
572 << " Randomised band index: " << band << "\n";
573 }
574 }
575 if (band < m_nValleysX) {
576 // X valleys
577 double pstar = sqrt(2. * ElectronMass * e);
578 if (m_nonParabolic) {
579 const double alpha = m_alphaX;
580 pstar *= sqrt(1. + alpha * e);
581 }
582
583 const double ctheta = 1. - 2. * RndmUniform();
584 const double stheta = sqrt(1. - ctheta * ctheta);
585 const double phi = TwoPi * RndmUniform();
586
587 if (m_anisotropic) {
588 const double pl = pstar * sqrt(m_mLongX);
589 const double pt = pstar * sqrt(m_mTransX);
590 switch (band) {
591 case 0:
592 case 1:
593 // 100
594 px = pl * ctheta;
595 py = pt * cos(phi) * stheta;
596 pz = pt * sin(phi) * stheta;
597 break;
598 case 2:
599 case 3:
600 // 010
601 px = pt * sin(phi) * stheta;
602 py = pl * ctheta;
603 pz = pt * cos(phi) * stheta;
604 break;
605 case 4:
606 case 5:
607 // 001
608 px = pt * cos(phi) * stheta;
609 py = pt * sin(phi) * stheta;
610 pz = pl * ctheta;
611 break;
612 default:
613 // Other band; should not occur.
614 std::cerr << m_className << "::GetElectronMomentum:\n"
615 << " Unexpected band index (" << band << ").\n";
616 px = pstar * stheta * cos(phi);
617 py = pstar * stheta * sin(phi);
618 pz = pstar * ctheta;
619 break;
620 }
621 } else {
622 pstar *= sqrt(3. / (1. / m_mLongX + 2. / m_mTransX));
623 px = pstar * cos(phi) * stheta;
624 py = pstar * sin(phi) * stheta;
625 pz = pstar * ctheta;
626 }
627 } else if (band < m_nValleysX + m_nValleysL) {
628 // L valleys
629 double pstar = sqrt(2. * ElectronMass * (e - m_eMinL));
630 if (m_nonParabolic) {
631 const double alpha = m_alphaL;
632 pstar *= sqrt(1. + alpha * (e - m_eMinL));
633 }
634 pstar *= sqrt(3. / (1. / m_mLongL + 2. / m_mTransL));
635 RndmDirection(px, py, pz, pstar);
636 } else if (band == m_nValleysX + m_nValleysL) {
637 // Higher band
638 const double pstar = sqrt(2. * ElectronMass * e);
639 RndmDirection(px, py, pz, pstar);
640 }
641}
642
644 if (m_isChanged) {
645 if (!UpdateTransportParameters()) {
646 std::cerr << m_className << "::GetElectronNullCollisionRate:\n"
647 << " Error calculating the collision rates table.\n";
648 return 0.;
649 }
650 m_isChanged = false;
651 }
652
653 if (band >= 0 && band < m_nValleysX) {
654 return m_cfNullElectronsX;
655 } else if (band >= m_nValleysX && band < m_nValleysX + m_nValleysL) {
656 return m_cfNullElectronsL;
657 } else if (band == m_nValleysX + m_nValleysL) {
658 return m_cfNullElectronsG;
659 }
660 std::cerr << m_className << "::GetElectronNullCollisionRate:\n"
661 << " Band index (" << band << ") out of range.\n";
662 return 0.;
663}
664
665double MediumSilicon::GetElectronCollisionRate(const double e, const int band) {
666 if (e <= 0.) {
667 std::cerr << m_className << "::GetElectronCollisionRate:\n"
668 << " Electron energy must be positive.\n";
669 return 0.;
670 }
671
672 if (e > m_eFinalG) {
673 std::cerr << m_className << "::GetElectronCollisionRate:\n"
674 << " Collision rate at " << e << " eV (band " << band
675 << ") is not included in the current table.\n"
676 << " Increasing energy range to " << 1.05 * e << " eV.\n";
677 SetMaxElectronEnergy(1.05 * e);
678 }
679
680 if (m_isChanged) {
681 if (!UpdateTransportParameters()) {
682 std::cerr << m_className << "::GetElectronCollisionRate:\n"
683 << " Error calculating the collision rates table.\n";
684 return 0.;
685 }
686 m_isChanged = false;
687 }
688
689 if (band >= 0 && band < m_nValleysX) {
690 int iE = int(e / m_eStepXL);
691 if (iE >= nEnergyStepsXL)
692 iE = nEnergyStepsXL - 1;
693 else if (iE < 0)
694 iE = 0;
695 return m_cfTotElectronsX[iE];
696 } else if (band >= m_nValleysX && band < m_nValleysX + m_nValleysL) {
697 int iE = int(e / m_eStepXL);
698 if (iE >= nEnergyStepsXL)
699 iE = nEnergyStepsXL - 1;
700 else if (iE < m_ieMinL)
701 iE = m_ieMinL;
702 return m_cfTotElectronsL[iE];
703 } else if (band == m_nValleysX + m_nValleysL) {
704 int iE = int(e / m_eStepG);
705 if (iE >= nEnergyStepsG)
706 iE = nEnergyStepsG - 1;
707 else if (iE < m_ieMinG)
708 iE = m_ieMinG;
709 return m_cfTotElectronsG[iE];
710 }
711
712 std::cerr << m_className << "::GetElectronCollisionRate:\n"
713 << " Band index (" << band << ") out of range.\n";
714 return 0.;
715}
716
717bool MediumSilicon::ElectronCollision(const double e, int& type,
718 int& level, double& e1, double& px, double& py, double& pz,
719 std::vector<std::pair<Particle, double> >& secondaries, int& ndxc,
720 int& band) {
721 if (e > m_eFinalG) {
722 std::cerr << m_className << "::ElectronCollision:\n"
723 << " Requested electron energy (" << e << " eV) exceeds the "
724 << "current energy range (" << m_eFinalG << " eV).\n"
725 << " Increasing energy range to " << 1.05 * e << " eV.\n";
726 SetMaxElectronEnergy(1.05 * e);
727 } else if (e <= 0.) {
728 std::cerr << m_className << "::ElectronCollision:\n"
729 << " Electron energy must be greater than zero.\n";
730 return false;
731 }
732
733 if (m_isChanged) {
734 if (!UpdateTransportParameters()) {
735 std::cerr << m_className << "::ElectronCollision:\n"
736 << " Error calculating the collision rates table.\n";
737 return false;
738 }
739 m_isChanged = false;
740 }
741
742 // Energy loss
743 double loss = 0.;
744 // Sample the scattering process.
745 if (band >= 0 && band < m_nValleysX) {
746 // X valley
747 // Get the energy interval.
748 int iE = int(e / m_eStepXL);
749 if (iE >= nEnergyStepsXL) iE = nEnergyStepsXL - 1;
750 if (iE < 0) iE = 0;
751 // Select the scattering process.
752 const double r = RndmUniform();
753 if (r <= m_cfElectronsX[iE][0]) {
754 level = 0;
755 } else if (r >= m_cfElectronsX[iE][m_nLevelsX - 1]) {
756 level = m_nLevelsX - 1;
757 } else {
758 const auto begin = m_cfElectronsX[iE].cbegin();
759 level = std::lower_bound(begin, begin + m_nLevelsX, r) - begin;
760 }
761
762 // Get the collision type.
763 type = m_scatTypeElectronsX[level];
764 // Fill the collision counters.
765 ++m_nCollElectronDetailed[level];
766 ++m_nCollElectronBand[band];
767 if (type == ElectronCollisionTypeAcousticPhonon) {
768 ++m_nCollElectronAcoustic;
769 } else if (type == ElectronCollisionTypeIntervalleyG) {
770 // Intervalley scattering (g type)
771 ++m_nCollElectronIntervalley;
772 // Final valley is in opposite direction.
773 switch (band) {
774 case 0:
775 band = 1;
776 break;
777 case 1:
778 band = 0;
779 break;
780 case 2:
781 band = 3;
782 break;
783 case 3:
784 band = 2;
785 break;
786 case 4:
787 band = 5;
788 break;
789 case 5:
790 band = 4;
791 break;
792 default:
793 break;
794 }
795 } else if (type == ElectronCollisionTypeIntervalleyF) {
796 // Intervalley scattering (f type)
797 ++m_nCollElectronIntervalley;
798 // Final valley is perpendicular.
799 switch (band) {
800 case 0:
801 case 1:
802 band = int(RndmUniform() * 4) + 2;
803 break;
804 case 2:
805 case 3:
806 band = int(RndmUniform() * 4);
807 if (band > 1) band += 2;
808 break;
809 case 4:
810 case 5:
811 band = int(RndmUniform() * 4);
812 break;
813 }
814 } else if (type == ElectronCollisionTypeInterbandXL) {
815 // XL scattering
816 ++m_nCollElectronIntervalley;
817 // Final valley is in L band.
818 band = m_nValleysX + int(RndmUniform() * m_nValleysL);
819 if (band >= m_nValleysX + m_nValleysL)
820 band = m_nValleysX + m_nValleysL - 1;
821 } else if (type == ElectronCollisionTypeInterbandXG) {
822 ++m_nCollElectronIntervalley;
823 band = m_nValleysX + m_nValleysL;
824 } else if (type == ElectronCollisionTypeImpurity) {
825 ++m_nCollElectronImpurity;
826 } else if (type == ElectronCollisionTypeIonisation) {
827 ++m_nCollElectronIonisation;
828 } else {
829 std::cerr << m_className << "::ElectronCollision:\n";
830 std::cerr << " Unexpected collision type (" << type << ").\n";
831 }
832
833 // Get the energy loss.
834 loss = m_energyLossElectronsX[level];
835
836 } else if (band >= m_nValleysX && band < m_nValleysX + m_nValleysL) {
837 // L valley
838 // Get the energy interval.
839 int iE = int(e / m_eStepXL);
840 if (iE >= nEnergyStepsXL) iE = nEnergyStepsXL - 1;
841 if (iE < m_ieMinL) iE = m_ieMinL;
842 // Select the scattering process.
843 const double r = RndmUniform();
844 if (r <= m_cfElectronsL[iE][0]) {
845 level = 0;
846 } else if (r >= m_cfElectronsL[iE][m_nLevelsL - 1]) {
847 level = m_nLevelsL - 1;
848 } else {
849 const auto begin = m_cfElectronsL[iE].cbegin();
850 level = std::lower_bound(begin, begin + m_nLevelsL, r) - begin;
851 }
852
853 // Get the collision type.
854 type = m_scatTypeElectronsL[level];
855 // Fill the collision counters.
856 ++m_nCollElectronDetailed[m_nLevelsX + level];
857 ++m_nCollElectronBand[band];
858 if (type == ElectronCollisionTypeAcousticPhonon) {
859 ++m_nCollElectronAcoustic;
860 } else if (type == ElectronCollisionTypeOpticalPhonon) {
861 ++m_nCollElectronOptical;
862 } else if (type == ElectronCollisionTypeIntervalleyG ||
863 type == ElectronCollisionTypeIntervalleyF) {
864 // Equivalent intervalley scattering
865 ++m_nCollElectronIntervalley;
866 // Randomise the final valley.
867 band = m_nValleysX + int(RndmUniform() * m_nValleysL);
868 if (band >= m_nValleysX + m_nValleysL) band = m_nValleysX + m_nValleysL;
869 } else if (type == ElectronCollisionTypeInterbandXL) {
870 // LX scattering
871 ++m_nCollElectronIntervalley;
872 // Randomise the final valley.
873 band = int(RndmUniform() * m_nValleysX);
874 if (band >= m_nValleysX) band = m_nValleysX - 1;
875 } else if (type == ElectronCollisionTypeInterbandLG) {
876 // LG scattering
877 ++m_nCollElectronIntervalley;
878 band = m_nValleysX + m_nValleysL;
879 } else if (type == ElectronCollisionTypeImpurity) {
880 ++m_nCollElectronImpurity;
881 } else if (type == ElectronCollisionTypeIonisation) {
882 ++m_nCollElectronIonisation;
883 } else {
884 std::cerr << m_className << "::ElectronCollision:\n"
885 << " Unexpected collision type (" << type << ").\n";
886 }
887
888 // Get the energy loss.
889 loss = m_energyLossElectronsL[level];
890 } else if (band == m_nValleysX + m_nValleysL) {
891 // Higher bands
892 // Get the energy interval.
893 int iE = int(e / m_eStepG);
894 if (iE >= nEnergyStepsG) iE = nEnergyStepsG - 1;
895 if (iE < m_ieMinG) iE = m_ieMinG;
896 // Select the scattering process.
897 const double r = RndmUniform();
898 if (r <= m_cfElectronsG[iE][0]) {
899 level = 0;
900 } else if (r >= m_cfElectronsG[iE][m_nLevelsG - 1]) {
901 level = m_nLevelsG - 1;
902 } else {
903 const auto begin = m_cfElectronsG[iE].cbegin();
904 level = std::lower_bound(begin, begin + m_nLevelsG, r) - begin;
905 }
906
907 // Get the collision type.
908 type = m_scatTypeElectronsG[level];
909 // Fill the collision counters.
910 ++m_nCollElectronDetailed[m_nLevelsX + m_nLevelsL + level];
911 ++m_nCollElectronBand[band];
912 if (type == ElectronCollisionTypeAcousticPhonon) {
913 ++m_nCollElectronAcoustic;
914 } else if (type == ElectronCollisionTypeOpticalPhonon) {
915 ++m_nCollElectronOptical;
916 } else if (type == ElectronCollisionTypeIntervalleyG ||
917 type == ElectronCollisionTypeIntervalleyF) {
918 // Equivalent intervalley scattering
919 ++m_nCollElectronIntervalley;
920 } else if (type == ElectronCollisionTypeInterbandXG) {
921 // GX scattering
922 ++m_nCollElectronIntervalley;
923 // Randomise the final valley.
924 band = int(RndmUniform() * m_nValleysX);
925 if (band >= m_nValleysX) band = m_nValleysX - 1;
926 } else if (type == ElectronCollisionTypeInterbandLG) {
927 // GL scattering
928 ++m_nCollElectronIntervalley;
929 // Randomise the final valley.
930 band = m_nValleysX + int(RndmUniform() * m_nValleysL);
931 if (band >= m_nValleysX + m_nValleysL)
932 band = m_nValleysX + m_nValleysL - 1;
933 } else if (type == ElectronCollisionTypeIonisation) {
934 ++m_nCollElectronIonisation;
935 } else {
936 std::cerr << m_className << "::ElectronCollision:\n"
937 << " Unexpected collision type (" << type << ").\n";
938 }
939
940 // Get the energy loss.
941 loss = m_energyLossElectronsG[level];
942 } else {
943 std::cerr << m_className << "::ElectronCollision:\n"
944 << " Band index (" << band << ") out of range.\n";
945 return false;
946 }
947
948 // Secondaries
949 ndxc = 0;
950 // Ionising collision
951 if (type == ElectronCollisionTypeIonisation) {
952 double ee = 0., eh = 0.;
953 ComputeSecondaries(e, ee, eh);
954 loss = ee + eh + m_bandGap;
955 // Add the secondary electron.
956 secondaries.emplace_back(std::make_pair(Particle::Electron, ee));
957 // Add the hole.
958 secondaries.emplace_back(std::make_pair(Particle::Hole, eh));
959 }
960
961 if (e < loss) loss = e - 0.00001;
962 // Update the energy.
963 e1 = e - loss;
964 if (e1 < Small) e1 = Small;
965
966 // Update the momentum.
967 if (band >= 0 && band < m_nValleysX) {
968 // X valleys
969 double pstar = sqrt(2. * ElectronMass * e1);
970 if (m_nonParabolic) {
971 const double alpha = m_alphaX;
972 pstar *= sqrt(1. + alpha * e1);
973 }
974
975 const double ctheta = 1. - 2. * RndmUniform();
976 const double stheta = sqrt(1. - ctheta * ctheta);
977 const double phi = TwoPi * RndmUniform();
978
979 if (m_anisotropic) {
980 const double pl = pstar * sqrt(m_mLongX);
981 const double pt = pstar * sqrt(m_mTransX);
982 switch (band) {
983 case 0:
984 case 1:
985 // 100
986 px = pl * ctheta;
987 py = pt * cos(phi) * stheta;
988 pz = pt * sin(phi) * stheta;
989 break;
990 case 2:
991 case 3:
992 // 010
993 px = pt * sin(phi) * stheta;
994 py = pl * ctheta;
995 pz = pt * cos(phi) * stheta;
996 break;
997 case 4:
998 case 5:
999 // 001
1000 px = pt * cos(phi) * stheta;
1001 py = pt * sin(phi) * stheta;
1002 pz = pl * ctheta;
1003 break;
1004 default:
1005 return false;
1006 }
1007 } else {
1008 pstar *= sqrt(3. / (1. / m_mLongX + 2. / m_mTransX));
1009 px = pstar * cos(phi) * stheta;
1010 py = pstar * sin(phi) * stheta;
1011 pz = pstar * ctheta;
1012 }
1013 return true;
1014
1015 } else if (band >= m_nValleysX && band < m_nValleysX + m_nValleysL) {
1016 // L valleys
1017 double pstar = sqrt(2. * ElectronMass * (e1 - m_eMinL));
1018 if (m_nonParabolic) {
1019 const double alpha = m_alphaL;
1020 pstar *= sqrt(1. + alpha * (e1 - m_eMinL));
1021 }
1022 pstar *= sqrt(3. / (1. / m_mLongL + 2. / m_mTransL));
1023 RndmDirection(px, py, pz, pstar);
1024 return true;
1025 }
1026 const double pstar = sqrt(2. * ElectronMass * e1);
1027 RndmDirection(px, py, pz, pstar);
1028 return true;
1029}
1030
1032 m_nCollElectronAcoustic = m_nCollElectronOptical = 0;
1033 m_nCollElectronIntervalley = 0;
1034 m_nCollElectronImpurity = 0;
1035 m_nCollElectronIonisation = 0;
1036 const auto nLevels = m_nLevelsX + m_nLevelsL + m_nLevelsG;
1037 m_nCollElectronDetailed.assign(nLevels, 0);
1038 const auto nBands = m_nValleysX + m_nValleysL + 1;
1039 m_nCollElectronBand.assign(nBands, 0);
1040}
1041
1043 return m_nCollElectronAcoustic + m_nCollElectronOptical +
1044 m_nCollElectronIntervalley + m_nCollElectronImpurity +
1045 m_nCollElectronIonisation;
1046}
1047
1049 return m_nLevelsX + m_nLevelsL + m_nLevelsG;
1050}
1051
1053 const unsigned int level) const {
1054 const unsigned int nLevels = m_nLevelsX + m_nLevelsL + m_nLevelsG;
1055 if (level >= nLevels) {
1056 std::cerr << m_className << "::GetNumberOfElectronCollisions:\n"
1057 << " Scattering rate term (" << level << ") does not exist.\n";
1058 return 0;
1059 }
1060 return m_nCollElectronDetailed[level];
1061}
1062
1064 return m_nValleysX + m_nValleysL + 1;
1065}
1066
1068 const int nBands = m_nValleysX + m_nValleysL + 1;
1069 if (band < 0 || band >= nBands) {
1070 std::cerr << m_className << "::GetElectronBandPopulation:\n";
1071 std::cerr << " Band index (" << band << ") out of range.\n";
1072 return 0;
1073 }
1074 return m_nCollElectronBand[band];
1075}
1076
1077bool MediumSilicon::GetOpticalDataRange(double& emin, double& emax,
1078 const unsigned int i) {
1079 if (i != 0) {
1080 std::cerr << m_className << "::GetOpticalDataRange: Index out of range.\n";
1081 }
1082
1083 // Make sure the optical data table has been loaded.
1084 if (m_opticalDataEnergies.empty()) {
1085 if (!LoadOpticalData(m_opticalDataFile)) {
1086 std::cerr << m_className << "::GetOpticalDataRange:\n"
1087 << " Optical data table could not be loaded.\n";
1088 return false;
1089 }
1090 }
1091
1092 emin = m_opticalDataEnergies.front();
1093 emax = m_opticalDataEnergies.back();
1094 if (m_debug) {
1095 std::cout << m_className << "::GetOpticalDataRange:\n"
1096 << " " << emin << " < E [eV] < " << emax << "\n";
1097 }
1098 return true;
1099}
1100
1101bool MediumSilicon::GetDielectricFunction(const double e, double& eps1,
1102 double& eps2, const unsigned int i) {
1103 if (i != 0) {
1104 std::cerr << m_className + "::GetDielectricFunction: Index out of range.\n";
1105 return false;
1106 }
1107
1108 // Make sure the optical data table has been loaded.
1109 if (m_opticalDataEnergies.empty()) {
1110 if (!LoadOpticalData(m_opticalDataFile)) {
1111 std::cerr << m_className << "::GetDielectricFunction:\n";
1112 std::cerr << " Optical data table could not be loaded.\n";
1113 return false;
1114 }
1115 }
1116
1117 // Make sure the requested energy is within the range of the table.
1118 const double emin = m_opticalDataEnergies.front();
1119 const double emax = m_opticalDataEnergies.back();
1120 if (e < emin || e > emax) {
1121 std::cerr << m_className << "::GetDielectricFunction:\n"
1122 << " Requested energy (" << e << " eV) "
1123 << " is outside the range of the optical data table.\n"
1124 << " " << emin << " < E [eV] < " << emax << "\n";
1125 eps1 = eps2 = 0.;
1126 return false;
1127 }
1128
1129 // Locate the requested energy in the table.
1130 const auto begin = m_opticalDataEnergies.cbegin();
1131 const auto it1 = std::upper_bound(begin, m_opticalDataEnergies.cend(), e);
1132 if (it1 == begin) {
1133 eps1 = m_opticalDataEpsilon.front().first;
1134 eps2 = m_opticalDataEpsilon.front().second;
1135 return true;
1136 }
1137 const auto it0 = std::prev(it1);
1138
1139 // Interpolate the real part of dielectric function.
1140 const double x0 = *it0;
1141 const double x1 = *it1;
1142 const double lnx0 = log(*it0);
1143 const double lnx1 = log(*it1);
1144 const double lnx = log(e);
1145 const double y0 = m_opticalDataEpsilon[it0 - begin].first;
1146 const double y1 = m_opticalDataEpsilon[it1 - begin].first;
1147 if (y0 <= 0. || y1 <= 0.) {
1148 // Use linear interpolation if one of the values is negative.
1149 eps1 = y0 + (e - x0) * (y1 - y0) / (x1 - x0);
1150 } else {
1151 // Otherwise use log-log interpolation.
1152 const double lny0 = log(y0);
1153 const double lny1 = log(y1);
1154 eps1 = lny0 + (lnx - lnx0) * (lny1 - lny0) / (lnx1 - lnx0);
1155 eps1 = exp(eps1);
1156 }
1157
1158 // Interpolate the imaginary part of dielectric function,
1159 // using log-log interpolation.
1160 const double lnz0 = log(m_opticalDataEpsilon[it0 - begin].second);
1161 const double lnz1 = log(m_opticalDataEpsilon[it1 - begin].second);
1162 eps2 = lnz0 + (lnx - lnx0) * (lnz1 - lnz0) / (lnx1 - lnx0);
1163 eps2 = exp(eps2);
1164 return true;
1165}
1166
1168 if (!m_isChanged) {
1169 if (m_debug) {
1170 std::cerr << m_className << "::Initialise: Nothing changed.\n";
1171 }
1172 return true;
1173 }
1174 if (!UpdateTransportParameters()) {
1175 std::cerr << m_className << "::Initialise:\n Error preparing "
1176 << "transport parameters/calculating collision rates.\n";
1177 return false;
1178 }
1179 return true;
1180}
1181
1182bool MediumSilicon::UpdateTransportParameters() {
1183 std::lock_guard<std::mutex> guard(m_mutex);
1184
1185 // Calculate impact ionisation coefficients.
1186 UpdateImpactIonisation();
1187
1188 if (!m_hasUserMobility) {
1189 // Calculate lattice mobility.
1190 UpdateLatticeMobility();
1191
1192 // Calculate doping mobility
1193 switch (m_dopingMobilityModel) {
1194 case DopingMobility::Minimos:
1195 UpdateDopingMobilityMinimos();
1196 break;
1197 case DopingMobility::Masetti:
1198 UpdateDopingMobilityMasetti();
1199 break;
1200 default:
1201 std::cerr << m_className << "::UpdateTransportParameters:\n "
1202 << "Unknown doping mobility model. Program bug!\n";
1203 break;
1204 }
1205 }
1206
1207 // Calculate saturation velocity
1208 if (!m_hasUserSaturationVelocity) {
1209 UpdateSaturationVelocity();
1210 }
1211
1212 // Calculate high field saturation parameters
1213 if (m_highFieldMobilityModel == HighFieldMobility::Canali) {
1214 UpdateHighFieldMobilityCanali();
1215 }
1216
1217 if (m_debug) {
1218 std::cout << m_className << "::UpdateTransportParameters:\n"
1219 << " Low-field mobility [cm2 V-1 ns-1]\n"
1220 << " Electrons: " << m_eMobility << "\n"
1221 << " Holes: " << m_hMobility << "\n";
1222 if (m_highFieldMobilityModel == HighFieldMobility::Constant) {
1223 std::cout << " Mobility is not field-dependent.\n";
1224 } else {
1225 std::cout << " Saturation velocity [cm / ns]\n"
1226 << " Electrons: " << m_eSatVel << "\n"
1227 << " Holes: " << m_hSatVel << "\n";
1228 }
1229 }
1230
1231 if (!ElectronScatteringRates()) return false;
1232 if (!HoleScatteringRates()) return false;
1233
1234 // Reset the collision counters.
1236
1237 return true;
1238}
1239
1240void MediumSilicon::UpdateLatticeMobility() {
1241
1242 // Temperature normalized to 300 K
1243 const double t = m_temperature / 300.;
1244
1245 switch (m_latticeMobilityModel) {
1246 case LatticeMobility::Minimos:
1247 // - S. Selberherr, W. Haensch, M. Seavey, J. Slotboom,
1248 // Solid State Electronics 33 (1990), 1425
1249 // - Minimos 6.1 User's Guide (1999)
1250 m_eLatticeMobility = 1.43e-6 * pow(t, -2.);
1251 m_hLatticeMobility = 0.46e-6 * pow(t, -2.18);
1252 break;
1253 case LatticeMobility::Sentaurus:
1254 // - C. Lombardi et al., IEEE Trans. CAD 7 (1988), 1164
1255 // - Sentaurus Device User Guide (2007)
1256 m_eLatticeMobility = 1.417e-6 * pow(t, -2.5);
1257 m_hLatticeMobility = 0.4705e-6 * pow(t, -2.2);
1258 break;
1259 case LatticeMobility::Reggiani:
1260 // M. A. Omar, L. Reggiani, Solid State Electronics 30 (1987), 693
1261 m_eLatticeMobility = 1.320e-6 * pow(t, -2.);
1262 m_hLatticeMobility = 0.460e-6 * pow(t, -2.2);
1263 break;
1264 default:
1265 std::cerr << m_className << "::UpdateLatticeMobility:\n"
1266 << " Unknown lattice mobility model. Program bug!\n";
1267 break;
1268 }
1269}
1270
1271void MediumSilicon::UpdateDopingMobilityMinimos() {
1272 // References:
1273 // - S. Selberherr, W. Haensch, M. Seavey, J. Slotboom,
1274 // Solid State Electronics 33 (1990), 1425-1436
1275 // - Minimos 6.1 User's Guide (1999)
1276
1277 // Mobility reduction due to ionised impurity scattering
1278 // Surface term not taken into account
1279 double eMuMin = 0.080e-6;
1280 double hMuMin = 0.045e-6;
1281 if (m_temperature > 200.) {
1282 const double c0 = pow(m_temperature / 300., -0.45);
1283 eMuMin *= c0;
1284 hMuMin *= c0;
1285 } else {
1286 const double c0 = pow(2. / 3., -0.45) * pow(m_temperature / 200., -0.15);
1287 eMuMin *= c0;
1288 hMuMin *= c0;
1289 }
1290 const double c1 = pow(m_temperature / 300., 3.2);
1291 const double eRefC = 1.12e17 * c1;
1292 const double hRefC = 2.23e17 * c1;
1293 const double alpha = 0.72 * pow(m_temperature / 300., 0.065);
1294 // Assume impurity concentration equal to doping concentration
1295 m_eMobility = eMuMin +
1296 (m_eLatticeMobility - eMuMin) /
1297 (1. + pow(m_dopingConcentration / eRefC, alpha));
1298 m_hMobility = hMuMin +
1299 (m_hLatticeMobility - hMuMin) /
1300 (1. + pow(m_dopingConcentration / hRefC, alpha));
1301}
1302
1303void MediumSilicon::UpdateDopingMobilityMasetti() {
1304 // Reference:
1305 // - G. Masetti, M. Severi, S. Solmi,
1306 // IEEE Trans. Electron Devices 30 (1983), 764-769
1307 // - Sentaurus Device User Guide (2007)
1308 // - Minimos NT User Guide (2004)
1309
1310 if (m_dopingConcentration < 1.e13) {
1311 m_eMobility = m_eLatticeMobility;
1312 m_hMobility = m_hLatticeMobility;
1313 return;
1314 }
1315
1316 // Parameters adopted from Minimos NT User Guide
1317 constexpr double eMuMin1 = 0.0522e-6;
1318 constexpr double eMuMin2 = 0.0522e-6;
1319 constexpr double eMu1 = 0.0434e-6;
1320 constexpr double hMuMin1 = 0.0449e-6;
1321 constexpr double hMuMin2 = 0.;
1322 constexpr double hMu1 = 0.029e-6;
1323 constexpr double eCr = 9.68e16;
1324 constexpr double eCs = 3.42e20;
1325 constexpr double hCr = 2.23e17;
1326 constexpr double hCs = 6.10e20;
1327 constexpr double hPc = 9.23e16;
1328 constexpr double eAlpha = 0.68;
1329 constexpr double eBeta = 2.;
1330 constexpr double hAlpha = 0.719;
1331 constexpr double hBeta = 2.;
1332
1333 m_eMobility = eMuMin1 +
1334 (m_eLatticeMobility - eMuMin2) /
1335 (1. + pow(m_dopingConcentration / eCr, eAlpha)) -
1336 eMu1 / (1. + pow(eCs / m_dopingConcentration, eBeta));
1337
1338 m_hMobility = hMuMin1 * exp(-hPc / m_dopingConcentration) +
1339 (m_hLatticeMobility - hMuMin2) /
1340 (1. + pow(m_dopingConcentration / hCr, hAlpha)) -
1341 hMu1 / (1. + pow(hCs / m_dopingConcentration, hBeta));
1342}
1343
1344void MediumSilicon::UpdateSaturationVelocity() {
1345
1346 switch (m_saturationVelocityModel) {
1347 case SaturationVelocity::Minimos:
1348 // - R. Quay, C. Moglestue, V. Palankovski, S. Selberherr,
1349 // Materials Science in Semiconductor Processing 3 (2000), 149
1350 // - Minimos NT User Guide (2004)
1351 m_eSatVel = 1.e-2 / (1. + 0.74 * (m_temperature / 300. - 1.));
1352 m_hSatVel = 0.704e-2 / (1. + 0.37 * (m_temperature / 300. - 1.));
1353 break;
1354 case SaturationVelocity::Canali:
1355 // - C. Canali, G. Majni, R. Minder, G. Ottaviani,
1356 // IEEE Transactions on Electron Devices 22 (1975), 1045
1357 // - Sentaurus Device User Guide (2007)
1358 m_eSatVel = 1.07e-2 * pow(300. / m_temperature, 0.87);
1359 m_hSatVel = 8.37e-3 * pow(300. / m_temperature, 0.52);
1360 break;
1361 case SaturationVelocity::Reggiani:
1362 // M. A. Omar, L. Reggiani, Solid State Electronics 30 (1987), 693
1363 m_eSatVel = 1.470e-2 * sqrt(tanh(150. / m_temperature));
1364 m_hSatVel = 0.916e-2 * sqrt(tanh(300. / m_temperature));
1365 break;
1366 default:
1367 std::cerr << m_className << "::UpdateSaturationVelocity:\n"
1368 << " Unknown saturation velocity model. Program bug!\n";
1369 break;
1370 }
1371}
1372
1373void MediumSilicon::UpdateHighFieldMobilityCanali() {
1374 // References:
1375 // - C. Canali, G. Majni, R. Minder, G. Ottaviani,
1376 // IEEE Transactions on Electron Devices 22 (1975), 1045-1047
1377 // - Sentaurus Device User Guide (2007)
1378
1379 // Temperature dependent exponent in high-field mobility formula
1380 m_eBetaCanali = 1.109 * pow(m_temperature / 300., 0.66);
1381 m_hBetaCanali = 1.213 * pow(m_temperature / 300., 0.17);
1382 m_eBetaCanaliInv = 1. / m_eBetaCanali;
1383 m_hBetaCanaliInv = 1. / m_hBetaCanali;
1384}
1385
1386void MediumSilicon::UpdateImpactIonisation() {
1387
1388 if (m_impactIonisationModel == ImpactIonisation::VanOverstraeten ||
1389 m_impactIonisationModel == ImpactIonisation::Grant) {
1390
1391 // Temperature dependence as in Sentaurus Device
1392 // Optical phonon energy
1393 constexpr double hbarOmega = 0.063;
1394 // Temperature scaling coefficient
1395 const double gamma =
1396 tanh(hbarOmega / (2. * BoltzmannConstant * 300.)) /
1397 tanh(hbarOmega / (2. * BoltzmannConstant * m_temperature));
1398
1399 if (m_impactIonisationModel == ImpactIonisation::VanOverstraeten) {
1400 // - R. van Overstraeten and H. de Man,
1401 // Solid State Electronics 13 (1970), 583
1402 // - W. Maes, K. de Meyer and R. van Overstraeten,
1403 // Solid State Electronics 33 (1990), 705
1404 // - Sentaurus Device User Guide (2016)
1405
1406 // Low field coefficients taken from Maes, de Meyer, van Overstraeten
1407 // eImpactA0 = gamma * 3.318e5;
1408 // eImpactB0 = gamma * 1.135e6;
1409 m_eImpactA0 = gamma * 7.03e5;
1410 m_eImpactB0 = gamma * 1.231e6;
1411 m_eImpactA1 = gamma * 7.03e5;
1412 m_eImpactB1 = gamma * 1.231e6;
1413
1414 m_hImpactA0 = gamma * 1.582e6;
1415 m_hImpactB0 = gamma * 2.036e6;
1416 m_hImpactA1 = gamma * 6.71e5;
1417 m_hImpactB1 = gamma * 1.693e6;
1418 } else {
1419 // W. N. Grant, Solid State Electronics 16 (1973), 1189
1420 // Sentaurus Device User Guide (2007)
1421 m_eImpactA0 = 2.60e6 * gamma;
1422 m_eImpactB0 = 1.43e6 * gamma;
1423 m_eImpactA1 = 6.20e5 * gamma;
1424 m_eImpactB1 = 1.08e6 * gamma;
1425 m_eImpactA2 = 5.05e5 * gamma;
1426 m_eImpactB2 = 9.90e5 * gamma;
1427
1428 m_hImpactA0 = 2.00e6 * gamma;
1429 m_hImpactB0 = 1.97e6 * gamma;
1430 m_hImpactA1 = 5.60e5 * gamma;
1431 m_hImpactB1 = 1.32e6 * gamma;
1432 }
1433 } else if (m_impactIonisationModel == ImpactIonisation::Massey) {
1434 // D. J. Massey, J. P. R. David, and G. J. Rees,
1435 // IEEE Trans. Electron Devices 53 (2006), 2328
1436 m_eImpactA0 = 4.43e5;
1437 m_eImpactB0 = 9.66e5 + 4.99e2 * m_temperature;
1438
1439 m_hImpactA0 = 1.13e6;
1440 m_hImpactB0 = 1.71e6 + 1.09e3 * m_temperature;
1441 } else if (m_impactIonisationModel == ImpactIonisation::Okuto) {
1442 const double dt = m_temperature - 300.;
1443 m_eImpactA0 = 0.426 * (1. + 3.05e-4 * dt);
1444 m_hImpactA0 = 0.243 * (1. + 5.35e-4 * dt);
1445 m_eImpactB0 = 4.81e5 * (1. + 6.86e-4 * dt);
1446 m_hImpactB0 = 6.53e5 * (1. + 5.67e-4 * dt);
1447 } else {
1448 std::cerr << m_className << "::UpdateImpactIonisation:\n"
1449 << " Unknown impact ionisation model. Program bug!\n";
1450 }
1451}
1452
1453double MediumSilicon::ElectronMobility(const double emag) const {
1454
1455 if (emag < Small) return 0.;
1456
1457 if (m_highFieldMobilityModel == HighFieldMobility::Minimos) {
1458 // Minimos User's Guide (1999)
1459 const double r = 2 * m_eMobility * emag / m_eSatVel;
1460 return 2. * m_eMobility / (1. + sqrt(1. + r * r));
1461 } else if (m_highFieldMobilityModel == HighFieldMobility::Canali) {
1462 // Sentaurus Device User Guide (2007)
1463 const double r = m_eMobility * emag / m_eSatVel;
1464 return m_eMobility / pow(1. + pow(r, m_eBetaCanali), m_eBetaCanaliInv);
1465 } else if (m_highFieldMobilityModel == HighFieldMobility::Reggiani) {
1466 // M. A. Omar, L. Reggiani, Solid State Electronics 30 (1987), 693
1467 const double r = m_eMobility * emag / m_eSatVel;
1468 constexpr double k = 1. / 1.5;
1469 return m_eMobility / pow(1. + pow(r, 1.5), k);
1470 }
1471 return m_eMobility;
1472}
1473
1474double MediumSilicon::ElectronAlpha(const double emag) const {
1475
1476 if (emag < Small) return 0.;
1477
1478 if (m_impactIonisationModel == ImpactIonisation::VanOverstraeten) {
1479 // - R. van Overstraeten and H. de Man,
1480 // Solid State Electronics 13 (1970), 583
1481 // - W. Maes, K. de Meyer and R. van Overstraeten,
1482 // Solid State Electronics 33 (1990), 705
1483 // - Sentaurus Device User Guide (2016)
1484 if (emag < 4e5) {
1485 return m_eImpactA0 * exp(-m_eImpactB0 / emag);
1486 } else {
1487 return m_eImpactA1 * exp(-m_eImpactB1 / emag);
1488 }
1489 } else if (m_impactIonisationModel == ImpactIonisation::Grant) {
1490 // W. N. Grant, Solid State Electronics 16 (1973), 1189
1491 if (emag < 2.4e5) {
1492 return m_eImpactA0 * exp(-m_eImpactB0 / emag);
1493 } else if (emag < 5.3e5) {
1494 return m_eImpactA1 * exp(-m_eImpactB1 / emag);
1495 } else {
1496 return m_eImpactA2 * exp(-m_eImpactB2 / emag);
1497 }
1498 } else if (m_impactIonisationModel == ImpactIonisation::Massey) {
1499 return m_eImpactA0 * exp(-m_eImpactB0 / emag);
1500 } else if (m_impactIonisationModel == ImpactIonisation::Okuto) {
1501 const double f = m_eImpactB0 / emag;
1502 return m_eImpactA0 * emag * exp(-f * f);
1503 }
1504 std::cerr << m_className << "::ElectronAlpha: Unknown model. Program bug!\n";
1505 return 0.;
1506}
1507
1508double MediumSilicon::HoleMobility(const double emag) const {
1509
1510 if (emag < Small) return 0.;
1511
1512 if (m_highFieldMobilityModel == HighFieldMobility::Minimos) {
1513 // Minimos User's Guide (1999)
1514 return m_hMobility / (1. + m_hMobility * emag / m_eSatVel);
1515 } else if (m_highFieldMobilityModel == HighFieldMobility::Canali) {
1516 // Sentaurus Device User Guide (2007)
1517 const double r = m_hMobility * emag / m_hSatVel;
1518 return m_hMobility / pow(1. + pow(r, m_hBetaCanali), m_hBetaCanaliInv);
1519 } else if (m_highFieldMobilityModel == HighFieldMobility::Reggiani) {
1520 // M. A. Omar, L. Reggiani, Solid State Electronics 30 (1987), 693
1521 const double r = m_hMobility * emag / m_hSatVel;
1522 return m_hMobility / sqrt(1. + r * r);
1523 }
1524 return m_hMobility;
1525}
1526
1527double MediumSilicon::HoleAlpha(const double emag) const {
1528
1529 if (emag < Small) return 0.;
1530
1531 if (m_impactIonisationModel == ImpactIonisation::VanOverstraeten) {
1532 // - R. van Overstraeten and H. de Man,
1533 // Solid State Electronics 13 (1970), 583
1534 // - Sentaurus Device User Guide (2016)
1535 if (emag < 4e5) {
1536 return m_hImpactA0 * exp(-m_hImpactB0 / emag);
1537 } else {
1538 return m_hImpactA1 * exp(-m_hImpactB1 / emag);
1539 }
1540 } else if (m_impactIonisationModel == ImpactIonisation::Grant) {
1541 // W. N. Grant, Solid State Electronics 16 (1973), 1189
1542 if (emag < 5.3e5) {
1543 return m_hImpactA0 * exp(-m_hImpactB0 / emag);
1544 } else {
1545 return m_hImpactA1 * exp(-m_hImpactB1 / emag);
1546 }
1547 } else if (m_impactIonisationModel == ImpactIonisation::Massey) {
1548 return m_hImpactA0 * exp(-m_hImpactB0 / emag);
1549 } else if (m_impactIonisationModel == ImpactIonisation::Okuto) {
1550 const double f = m_hImpactB0 / emag;
1551 return m_hImpactA0 * emag * exp(-f * f);
1552 }
1553 std::cerr << m_className << "::HoleAlpha: Unknown model. Program bug!\n";
1554 return 0.;
1555}
1556
1557bool MediumSilicon::LoadOpticalData(const std::string& filename) {
1558 // Clear the optical data table.
1559 m_opticalDataEnergies.clear();
1560 m_opticalDataEpsilon.clear();
1561
1562 // Get the path to the data directory.
1563 char* pPath = getenv("GARFIELD_HOME");
1564 if (pPath == 0) {
1565 std::cerr << m_className << "::LoadOpticalData:\n"
1566 << " Environment variable GARFIELD_HOME is not set.\n";
1567 return false;
1568 }
1569 const std::string filepath = std::string(pPath) + "/Data/" + filename;
1570
1571 // Open the file.
1572 std::ifstream infile(filepath);
1573 // Make sure the file could actually be opened.
1574 if (!infile) {
1575 std::cerr << m_className << "::LoadOpticalData:\n"
1576 << " Error opening file " << filename << ".\n";
1577 return false;
1578 }
1579
1580 double lastEnergy = -1.;
1581 double energy, eps1, eps2, loss;
1582 // Read the file line by line.
1583 std::string line;
1584 std::istringstream dataStream;
1585 int i = 0;
1586 while (!infile.eof()) {
1587 ++i;
1588 // Read the next line.
1589 std::getline(infile, line);
1590 // Strip white space from the beginning of the line.
1591 ltrim(line);
1592 if (line.empty()) continue;
1593 // Skip comments.
1594 if (line[0] == '#' || line[0] == '*' || (line[0] == '/' && line[1] == '/'))
1595 continue;
1596 // Extract the values.
1597 dataStream.str(line);
1598 dataStream >> energy >> eps1 >> eps2 >> loss;
1599 if (dataStream.eof()) break;
1600 // Check if the data has been read correctly.
1601 if (infile.fail()) {
1602 std::cerr << m_className << "::LoadOpticalData:\n Error reading file "
1603 << filename << " (line " << i << ").\n";
1604 return false;
1605 }
1606 // Reset the stringstream.
1607 dataStream.str("");
1608 dataStream.clear();
1609 // Make sure the values make sense.
1610 // The table has to be in ascending order
1611 // with respect to the photon energy.
1612 if (energy <= lastEnergy) {
1613 std::cerr << m_className << "::LoadOpticalData:\n Table is not in "
1614 << "monotonically increasing order (line " << i << ").\n"
1615 << " " << lastEnergy << " " << energy << " " << eps1
1616 << " " << eps2 << "\n";
1617 return false;
1618 }
1619 // The imaginary part of the dielectric function has to be positive.
1620 if (eps2 < 0.) {
1621 std::cerr << m_className << "::LoadOpticalData:\n Negative value "
1622 << "of the loss function (line " << i << ").\n";
1623 return false;
1624 }
1625 // Ignore negative photon energies.
1626 if (energy <= 0.) continue;
1627 // Add the values to the list.
1628 m_opticalDataEnergies.emplace_back(energy);
1629 m_opticalDataEpsilon.emplace_back(std::make_pair(eps1, eps2));
1630 lastEnergy = energy;
1631 }
1632
1633 if (m_opticalDataEnergies.empty()) {
1634 std::cerr << m_className << "::LoadOpticalData:\n"
1635 << " Import of data from file " << filepath << "failed.\n"
1636 << " No valid data found.\n";
1637 return false;
1638 }
1639
1640 if (m_debug) {
1641 std::cout << m_className << "::LoadOpticalData:\n Read "
1642 << m_opticalDataEnergies.size() << " values from file "
1643 << filepath << "\n";
1644 }
1645 return true;
1646}
1647
1648bool MediumSilicon::ElectronScatteringRates() {
1649 // Reset the scattering rates
1650 m_cfTotElectronsX.assign(nEnergyStepsXL, 0.);
1651 m_cfTotElectronsL.assign(nEnergyStepsXL, 0.);
1652 m_cfTotElectronsG.assign(nEnergyStepsG, 0.);
1653 m_cfElectronsX.assign(nEnergyStepsXL, std::vector<double>());
1654 m_cfElectronsL.assign(nEnergyStepsXL, std::vector<double>());
1655 m_cfElectronsG.assign(nEnergyStepsG, std::vector<double>());
1656 m_energyLossElectronsX.clear();
1657 m_energyLossElectronsL.clear();
1658 m_energyLossElectronsG.clear();
1659 m_scatTypeElectronsX.clear();
1660 m_scatTypeElectronsL.clear();
1661 m_scatTypeElectronsG.clear();
1662 m_cfNullElectronsX = 0.;
1663 m_cfNullElectronsL = 0.;
1664 m_cfNullElectronsG = 0.;
1665
1666 m_nLevelsX = 0;
1667 m_nLevelsL = 0;
1668 m_nLevelsG = 0;
1669 // Fill the scattering rate tables.
1670 ElectronAcousticScatteringRates();
1671 ElectronOpticalScatteringRates();
1672 ElectronImpurityScatteringRates();
1673 ElectronIntervalleyScatteringRatesXX();
1674 ElectronIntervalleyScatteringRatesXL();
1675 ElectronIntervalleyScatteringRatesLL();
1676 ElectronIntervalleyScatteringRatesXGLG();
1677 ElectronIonisationRatesXL();
1678 ElectronIonisationRatesG();
1679
1680 if (m_debug) {
1681 std::cout << m_className << "::ElectronScatteringRates:\n"
1682 << " " << m_nLevelsX << " X-valley scattering terms\n"
1683 << " " << m_nLevelsL << " L-valley scattering terms\n"
1684 << " " << m_nLevelsG << " higher band scattering terms\n";
1685 }
1686
1687 std::ofstream outfileX;
1688 std::ofstream outfileL;
1689 if (m_cfOutput) {
1690 outfileX.open("ratesX.txt", std::ios::out);
1691 outfileL.open("ratesL.txt", std::ios::out);
1692 }
1693
1694 m_ieMinL = int(m_eMinL / m_eStepXL) + 1;
1695 for (int i = 0; i < nEnergyStepsXL; ++i) {
1696 // Sum up the scattering rates of all processes.
1697 for (int j = m_nLevelsX; j--;) m_cfTotElectronsX[i] += m_cfElectronsX[i][j];
1698 for (int j = m_nLevelsL; j--;) m_cfTotElectronsL[i] += m_cfElectronsL[i][j];
1699
1700 if (m_cfOutput) {
1701 outfileX << i * m_eStepXL << " " << m_cfTotElectronsX[i] << " ";
1702 for (int j = 0; j < m_nLevelsX; ++j) {
1703 outfileX << m_cfElectronsX[i][j] << " ";
1704 }
1705 outfileX << "\n";
1706 outfileL << i * m_eStepXL << " " << m_cfTotElectronsL[i] << " ";
1707 for (int j = 0; j < m_nLevelsL; ++j) {
1708 outfileL << m_cfElectronsL[i][j] << " ";
1709 }
1710 outfileL << "\n";
1711 }
1712
1713 if (m_cfTotElectronsX[i] > m_cfNullElectronsX) {
1714 m_cfNullElectronsX = m_cfTotElectronsX[i];
1715 }
1716 if (m_cfTotElectronsL[i] > m_cfNullElectronsL) {
1717 m_cfNullElectronsL = m_cfTotElectronsL[i];
1718 }
1719
1720 // Make sure the total scattering rate is positive.
1721 if (m_cfTotElectronsX[i] <= 0.) {
1722 std::cerr << m_className << "::ElectronScatteringRates:\n X-valley "
1723 << "scattering rate at " << i * m_eStepXL << " eV <= 0.\n";
1724 return false;
1725 }
1726 // Normalise the rates.
1727 for (int j = 0; j < m_nLevelsX; ++j) {
1728 m_cfElectronsX[i][j] /= m_cfTotElectronsX[i];
1729 if (j > 0) m_cfElectronsX[i][j] += m_cfElectronsX[i][j - 1];
1730 }
1731
1732 // Make sure the total scattering rate is positive.
1733 if (m_cfTotElectronsL[i] <= 0.) {
1734 if (i < m_ieMinL) continue;
1735 std::cerr << m_className << "::ElectronScatteringRates:\n L-valley "
1736 << "scattering rate at " << i * m_eStepXL << " eV <= 0.\n";
1737 return false;
1738 }
1739 // Normalise the rates.
1740 for (int j = 0; j < m_nLevelsL; ++j) {
1741 m_cfElectronsL[i][j] /= m_cfTotElectronsL[i];
1742 if (j > 0) m_cfElectronsL[i][j] += m_cfElectronsL[i][j - 1];
1743 }
1744 }
1745
1746 if (m_cfOutput) {
1747 outfileX.close();
1748 outfileL.close();
1749 }
1750
1751 std::ofstream outfileG;
1752 if (m_cfOutput) {
1753 outfileG.open("ratesG.txt", std::ios::out);
1754 }
1755 m_ieMinG = int(m_eMinG / m_eStepG) + 1;
1756 for (int i = 0; i < nEnergyStepsG; ++i) {
1757 // Sum up the scattering rates of all processes.
1758 for (int j = m_nLevelsG; j--;) m_cfTotElectronsG[i] += m_cfElectronsG[i][j];
1759
1760 if (m_cfOutput) {
1761 outfileG << i * m_eStepG << " " << m_cfTotElectronsG[i] << " ";
1762 for (int j = 0; j < m_nLevelsG; ++j) {
1763 outfileG << m_cfElectronsG[i][j] << " ";
1764 }
1765 outfileG << "\n";
1766 }
1767
1768 if (m_cfTotElectronsG[i] > m_cfNullElectronsG) {
1769 m_cfNullElectronsG = m_cfTotElectronsG[i];
1770 }
1771
1772 // Make sure the total scattering rate is positive.
1773 if (m_cfTotElectronsG[i] <= 0.) {
1774 if (i < m_ieMinG) continue;
1775 std::cerr << m_className << "::ElectronScatteringRates:\n Higher "
1776 << "band scattering rate at " << i * m_eStepG << " eV <= 0.\n";
1777 }
1778 // Normalise the rates.
1779 for (int j = 0; j < m_nLevelsG; ++j) {
1780 m_cfElectronsG[i][j] /= m_cfTotElectronsG[i];
1781 if (j > 0) m_cfElectronsG[i][j] += m_cfElectronsG[i][j - 1];
1782 }
1783 }
1784
1785 if (m_cfOutput) {
1786 outfileG.close();
1787 }
1788
1789 return true;
1790}
1791
1792bool MediumSilicon::ElectronAcousticScatteringRates() {
1793 // Reference:
1794 // - C. Jacoboni and L. Reggiani,
1795 // Rev. Mod. Phys. 55, 645-705
1796
1797 // Mass density [(eV/c2)/cm3]
1798 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
1799 // Lattice temperature [eV]
1800 const double kbt = BoltzmannConstant * m_temperature;
1801
1802 // Acoustic phonon intraband scattering
1803 // Acoustic deformation potential [eV]
1804 constexpr double defpot2 = 9. * 9.;
1805 // Longitudinal velocity of sound [cm/ns]
1806 constexpr double u = 9.04e-4;
1807 // Prefactor for acoustic deformation potential scattering
1808 const double cIntra = TwoPi * SpeedOfLight * SpeedOfLight * kbt * defpot2 /
1809 (Hbar * u * u * rho);
1810
1811 // Fill the scattering rate tables.
1812 double en = Small;
1813 for (int i = 0; i < nEnergyStepsXL; ++i) {
1814 const double dosX = GetConductionBandDensityOfStates(en, 0);
1815 const double dosL = GetConductionBandDensityOfStates(en, m_nValleysX);
1816
1817 m_cfElectronsX[i].push_back(cIntra * dosX);
1818 m_cfElectronsL[i].push_back(cIntra * dosL);
1819 en += m_eStepXL;
1820 }
1821
1822 en = Small;
1823 for (int i = 0; i < nEnergyStepsG; ++i) {
1824 const double dosG =
1825 GetConductionBandDensityOfStates(en, m_nValleysX + m_nValleysL);
1826 m_cfElectronsG[i].push_back(cIntra * dosG);
1827 en += m_eStepG;
1828 }
1829
1830 // Assume that energy loss is negligible.
1831 m_energyLossElectronsX.push_back(0.);
1832 m_energyLossElectronsL.push_back(0.);
1833 m_energyLossElectronsG.push_back(0.);
1834 m_scatTypeElectronsX.push_back(ElectronCollisionTypeAcousticPhonon);
1835 m_scatTypeElectronsL.push_back(ElectronCollisionTypeAcousticPhonon);
1836 m_scatTypeElectronsG.push_back(ElectronCollisionTypeAcousticPhonon);
1837 ++m_nLevelsX;
1838 ++m_nLevelsL;
1839 ++m_nLevelsG;
1840
1841 return true;
1842}
1843
1844bool MediumSilicon::ElectronOpticalScatteringRates() {
1845 // Reference:
1846 // - K. Hess (editor),
1847 // Monte Carlo device simulation: full band and beyond
1848 // Chapter 5
1849 // - C. Jacoboni and L. Reggiani,
1850 // Rev. Mod. Phys. 55, 645-705
1851 // - M. Lundstrom,
1852 // Fundamentals of carrier transport
1853
1854 // Mass density [(eV/c2)/cm3]
1855 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
1856 // Lattice temperature [eV]
1857 const double kbt = BoltzmannConstant * m_temperature;
1858
1859 // Coupling constant [eV/cm]
1860 constexpr double dtk = 2.2e8;
1861 // Phonon energy [eV]
1862 constexpr double eph = 63.0e-3;
1863 // Phonon cccupation numbers
1864 const double nocc = 1. / (exp(eph / kbt) - 1);
1865 // Prefactors
1866 const double c0 = HbarC * SpeedOfLight * Pi / rho;
1867 double c = c0 * dtk * dtk / eph;
1868
1869 double en = 0.;
1870 // L valleys
1871 /*
1872 for (int i = 0; i < nEnergyStepsXL; ++i) {
1873 // Absorption
1874 if (en > m_eMinL) {
1875 double dos = GetConductionBandDensityOfStates(en + eph, m_nValleysX);
1876 m_cfElectronsL[i].push_back(c * nocc * dos);
1877 } else {
1878 m_cfElectronsL[i].push_back(0.);
1879 }
1880 // Emission
1881 if (en > m_eMinL + eph) {
1882 double dos = GetConductionBandDensityOfStates(en - eph, m_nValleysX);
1883 m_cfElectronsL[i].push_back(c * (nocc + 1) * dos);
1884 } else {
1885 m_cfElectronsL[i].push_back(0.);
1886 }
1887 en += m_eStepXL;
1888 }
1889 //*/
1890
1891 en = 0.;
1892 // Higher band(s)
1893 for (int i = 0; i < nEnergyStepsG; ++i) {
1894 // Absorption
1895 if (en > m_eMinG) {
1896 double dos =
1897 GetConductionBandDensityOfStates(en + eph, m_nValleysX + m_nValleysL);
1898 m_cfElectronsG[i].push_back(c * nocc * dos);
1899 } else {
1900 m_cfElectronsG[i].push_back(0.);
1901 }
1902 // Emission
1903 if (en > m_eMinG + eph) {
1904 double dos =
1905 GetConductionBandDensityOfStates(en - eph, m_nValleysX + m_nValleysL);
1906 m_cfElectronsG[i].push_back(c * (nocc + 1) * dos);
1907 } else {
1908 m_cfElectronsG[i].push_back(0.);
1909 }
1910 en += m_eStepG;
1911 }
1912
1913 // Absorption
1914 // m_energyLossElectronsL.push_back(-eph);
1915 m_energyLossElectronsG.push_back(-eph);
1916 // Emission
1917 // m_energyLossElectronsL.push_back(eph);
1918 m_energyLossElectronsG.push_back(eph);
1919 // m_scatTypeElectronsL.push_back(ElectronCollisionTypeOpticalPhonon);
1920 // m_scatTypeElectronsL.push_back(ElectronCollisionTypeOpticalPhonon);
1921 m_scatTypeElectronsG.push_back(ElectronCollisionTypeOpticalPhonon);
1922 m_scatTypeElectronsG.push_back(ElectronCollisionTypeOpticalPhonon);
1923
1924 // m_nLevelsL += 2;
1925 m_nLevelsG += 2;
1926
1927 return true;
1928}
1929
1930bool MediumSilicon::ElectronIntervalleyScatteringRatesXX() {
1931 // Reference:
1932 // - C. Jacoboni and L. Reggiani,
1933 // Rev. Mod. Phys. 55, 645-705
1934
1935 // Mass density [(eV/c2)/cm3]
1936 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
1937 // Lattice temperature [eV]
1938 const double kbt = BoltzmannConstant * m_temperature;
1939
1940 constexpr unsigned int nPhonons = 6;
1941 // f-type scattering: transition between orthogonal axes (multiplicity 4)
1942 // g-type scattering: transition between opposite axes (multiplicity 1)
1943 // Sequence of transitions in the table:
1944 // TA (g) - LA (g) - LO (g) - TA (f) - LA (f) - TO (f)
1945 // Coupling constants [eV/cm]
1946 constexpr double dtk[nPhonons] = {0.5e8, 0.8e8, 1.1e9, 0.3e8, 2.0e8, 2.0e8};
1947 // Phonon energies [eV]
1948 constexpr double eph[nPhonons] = {12.06e-3, 18.53e-3, 62.04e-3,
1949 18.86e-3, 47.39e-3, 59.03e-3};
1950 // Phonon cccupation numbers
1951 double nocc[nPhonons];
1952 // Prefactors
1953 const double c0 = HbarC * SpeedOfLight * Pi / rho;
1954 double c[nPhonons];
1955
1956 for (unsigned int j = 0; j < nPhonons; ++j) {
1957 nocc[j] = 1. / (exp(eph[j] / kbt) - 1);
1958 c[j] = c0 * dtk[j] * dtk[j] / eph[j];
1959 if (j > 2) c[j] *= 4;
1960 }
1961
1962 double en = 0.;
1963 for (int i = 0; i < nEnergyStepsXL; ++i) {
1964 for (unsigned int j = 0; j < nPhonons; ++j) {
1965 // Absorption
1966 double dos = GetConductionBandDensityOfStates(en + eph[j], 0);
1967 m_cfElectronsX[i].push_back(c[j] * nocc[j] * dos);
1968 // Emission
1969 if (en > eph[j]) {
1970 dos = GetConductionBandDensityOfStates(en - eph[j], 0);
1971 m_cfElectronsX[i].push_back(c[j] * (nocc[j] + 1) * dos);
1972 } else {
1973 m_cfElectronsX[i].push_back(0.);
1974 }
1975 }
1976 en += m_eStepXL;
1977 }
1978
1979 for (unsigned int j = 0; j < nPhonons; ++j) {
1980 // Absorption
1981 m_energyLossElectronsX.push_back(-eph[j]);
1982 // Emission
1983 m_energyLossElectronsX.push_back(eph[j]);
1984 if (j <= 2) {
1985 m_scatTypeElectronsX.push_back(ElectronCollisionTypeIntervalleyG);
1986 m_scatTypeElectronsX.push_back(ElectronCollisionTypeIntervalleyG);
1987 } else {
1988 m_scatTypeElectronsX.push_back(ElectronCollisionTypeIntervalleyF);
1989 m_scatTypeElectronsX.push_back(ElectronCollisionTypeIntervalleyF);
1990 }
1991 }
1992
1993 m_nLevelsX += 2 * nPhonons;
1994
1995 return true;
1996}
1997
1998bool MediumSilicon::ElectronIntervalleyScatteringRatesXL() {
1999 // Reference:
2000 // - M. Lundstrom, Fundamentals of carrier transport
2001 // - M. Martin et al.,
2002 // Semicond. Sci. Technol. 8, 1291-1297
2003
2004 // Mass density [(eV/c2)/cm3]
2005 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
2006 // Lattice temperature [eV]
2007 const double kbt = BoltzmannConstant * m_temperature;
2008
2009 constexpr unsigned int nPhonons = 4;
2010
2011 // Coupling constants [eV/cm]
2012 constexpr double dtk[nPhonons] = {2.e8, 2.e8, 2.e8, 2.e8};
2013 // Phonon energies [eV]
2014 constexpr double eph[nPhonons] = {58.e-3, 55.e-3, 41.e-3, 17.e-3};
2015 // Number of equivalent valleys
2016 constexpr unsigned int zX = 6;
2017 constexpr unsigned int zL = 8;
2018
2019 // Phonon cccupation numbers
2020 double nocc[nPhonons] = {0.};
2021 // Prefactors
2022 const double c0 = HbarC * SpeedOfLight * Pi / rho;
2023 double c[nPhonons];
2024
2025 for (unsigned int j = 0; j < nPhonons; ++j) {
2026 nocc[j] = 1. / (exp(eph[j] / kbt) - 1);
2027 c[j] = c0 * dtk[j] * dtk[j] / eph[j];
2028 }
2029
2030 double en = 0.;
2031 for (int i = 0; i < nEnergyStepsXL; ++i) {
2032 for (unsigned int j = 0; j < nPhonons; ++j) {
2033 // XL
2034 // Absorption
2035 if (en + eph[j] > m_eMinL) {
2036 double dos = GetConductionBandDensityOfStates(en + eph[j], m_nValleysX);
2037 m_cfElectronsX[i].push_back(zL * c[j] * nocc[j] * dos);
2038 } else {
2039 m_cfElectronsX[i].push_back(0.);
2040 }
2041 // Emission
2042 if (en - eph[j] > m_eMinL) {
2043 double dos = GetConductionBandDensityOfStates(en - eph[j], m_nValleysX);
2044 m_cfElectronsX[i].push_back(zL * c[j] * (nocc[j] + 1) * dos);
2045 } else {
2046 m_cfElectronsX[i].push_back(0.);
2047 }
2048 // LX
2049 if (en > m_eMinL) {
2050 // Absorption
2051 double dos = GetConductionBandDensityOfStates(en + eph[j], 0);
2052 m_cfElectronsL[i].push_back(zX * c[j] * nocc[j] * dos);
2053 // Emission
2054 dos = GetConductionBandDensityOfStates(en - eph[j], 0);
2055 m_cfElectronsL[i].push_back(zX * c[j] * (nocc[j] + 1) * dos);
2056 } else {
2057 m_cfElectronsL[i].push_back(0.);
2058 m_cfElectronsL[i].push_back(0.);
2059 }
2060 }
2061 en += m_eStepXL;
2062 }
2063
2064 for (unsigned int j = 0; j < nPhonons; ++j) {
2065 // Absorption
2066 m_energyLossElectronsX.push_back(-eph[j]);
2067 m_energyLossElectronsL.push_back(-eph[j]);
2068 // Emission
2069 m_energyLossElectronsX.push_back(eph[j]);
2070 m_energyLossElectronsL.push_back(eph[j]);
2071 m_scatTypeElectronsX.push_back(ElectronCollisionTypeInterbandXL);
2072 m_scatTypeElectronsX.push_back(ElectronCollisionTypeInterbandXL);
2073 m_scatTypeElectronsL.push_back(ElectronCollisionTypeInterbandXL);
2074 m_scatTypeElectronsL.push_back(ElectronCollisionTypeInterbandXL);
2075 }
2076
2077 m_nLevelsX += 2 * nPhonons;
2078 m_nLevelsL += 2 * nPhonons;
2079
2080 return true;
2081}
2082
2083bool MediumSilicon::ElectronIntervalleyScatteringRatesLL() {
2084 // Reference:
2085 // - K. Hess (editor),
2086 // Monte Carlo device simulation: full band and beyond
2087 // Chapter 5
2088 // - M. J. Martin et al.,
2089 // Semicond. Sci. Technol. 8, 1291-1297
2090
2091 // Mass density [(eV/c2)/cm3]
2092 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
2093 // Lattice temperature [eV]
2094 const double kbt = BoltzmannConstant * m_temperature;
2095
2096 const int nPhonons = 1;
2097 // Coupling constant [eV/cm]
2098 const double dtk[nPhonons] = {2.63e8};
2099 // Phonon energy [eV]
2100 const double eph[nPhonons] = {38.87e-3};
2101 // Phonon cccupation numbers
2102 double nocc[nPhonons];
2103 // Prefactors
2104 const double c0 = HbarC * SpeedOfLight * Pi / rho;
2105 double c[nPhonons];
2106
2107 for (int j = 0; j < nPhonons; ++j) {
2108 nocc[j] = 1. / (exp(eph[j] / kbt) - 1);
2109 c[j] = c0 * dtk[j] * dtk[j] / eph[j];
2110 c[j] *= 7;
2111 }
2112
2113 double en = 0.;
2114 double dos = 0.;
2115 for (int i = 0; i < nEnergyStepsXL; ++i) {
2116 for (int j = 0; j < nPhonons; ++j) {
2117 // Absorption
2118 dos = GetConductionBandDensityOfStates(en + eph[j], m_nValleysX);
2119 m_cfElectronsL[i].push_back(c[j] * nocc[j] * dos);
2120 // Emission
2121 if (en > m_eMinL + eph[j]) {
2122 dos = GetConductionBandDensityOfStates(en - eph[j], m_nValleysX);
2123 m_cfElectronsL[i].push_back(c[j] * (nocc[j] + 1) * dos);
2124 } else {
2125 m_cfElectronsL[i].push_back(0.);
2126 }
2127 }
2128 en += m_eStepXL;
2129 }
2130
2131 for (int j = 0; j < nPhonons; ++j) {
2132 // Absorption
2133 m_energyLossElectronsL.push_back(-eph[j]);
2134 // Emission
2135 m_energyLossElectronsL.push_back(eph[j]);
2136 m_scatTypeElectronsL.push_back(ElectronCollisionTypeIntervalleyF);
2137 m_scatTypeElectronsL.push_back(ElectronCollisionTypeIntervalleyF);
2138 }
2139
2140 m_nLevelsL += 2 * nPhonons;
2141
2142 return true;
2143}
2144
2145bool MediumSilicon::ElectronIntervalleyScatteringRatesXGLG() {
2146 // Reference:
2147 // - K. Hess (editor),
2148 // Monte Carlo device simulation: full band and beyond
2149 // Chapter 5
2150
2151 // Mass density [(eV/c2)/cm3]
2152 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
2153 // Lattice temperature [eV]
2154 const double kbt = BoltzmannConstant * m_temperature;
2155
2156 const int nPhonons = 1;
2157
2158 // Coupling constants [eV/cm]
2159 // Average of XG and LG
2160 const double dtk[nPhonons] = {2.43e8};
2161 // Phonon energies [eV]
2162 const double eph[nPhonons] = {37.65e-3};
2163 // Number of equivalent valleys
2164 const int zX = 6;
2165 const int zL = 8;
2166 const int zG = 1;
2167
2168 // Phonon cccupation numbers
2169 double nocc[nPhonons] = {0.};
2170 // Prefactors
2171 const double c0 = HbarC * SpeedOfLight * Pi / rho;
2172 double c[nPhonons];
2173
2174 for (int j = 0; j < nPhonons; ++j) {
2175 nocc[j] = 1. / (exp(eph[j] / kbt) - 1);
2176 c[j] = c0 * dtk[j] * dtk[j] / eph[j];
2177 }
2178
2179 double en = 0.;
2180 double dos = 0.;
2181 // XG, LG
2182 for (int i = 0; i < nEnergyStepsXL; ++i) {
2183 for (int j = 0; j < nPhonons; ++j) {
2184 // Absorption
2185 if (en + eph[j] > m_eMinG) {
2186 dos = GetConductionBandDensityOfStates(en + eph[j],
2187 m_nValleysX + m_nValleysL);
2188 m_cfElectronsX[i].push_back(zG * c[j] * nocc[j] * dos);
2189 m_cfElectronsL[i].push_back(zG * c[j] * nocc[j] * dos);
2190 } else {
2191 m_cfElectronsX[i].push_back(0.);
2192 m_cfElectronsL[i].push_back(0.);
2193 }
2194 // Emission
2195 if (en - eph[j] > m_eMinG) {
2196 dos = GetConductionBandDensityOfStates(en - eph[j],
2197 m_nValleysX + m_nValleysL);
2198 m_cfElectronsX[i].push_back(zG * c[j] * (nocc[j] + 1) * dos);
2199 m_cfElectronsL[i].push_back(zG * c[j] * (nocc[j] + 1) * dos);
2200 } else {
2201 m_cfElectronsX[i].push_back(0.);
2202 m_cfElectronsL[i].push_back(0.);
2203 }
2204 }
2205 en += m_eStepXL;
2206 }
2207
2208 // GX, GL
2209 en = 0.;
2210 double dosX = 0., dosL = 0.;
2211 for (int i = 0; i < nEnergyStepsG; ++i) {
2212 for (int j = 0; j < nPhonons; ++j) {
2213 // Absorption
2214 dosX = GetConductionBandDensityOfStates(en + eph[j], 0);
2215 dosL = GetConductionBandDensityOfStates(en + eph[j], m_nValleysX);
2216 m_cfElectronsG[i].push_back(zX * c[j] * nocc[j] * dosX);
2217 if (en > m_eMinL) {
2218 m_cfElectronsG[i].push_back(zL * c[j] * nocc[j] * dosL);
2219 } else {
2220 m_cfElectronsG[i].push_back(0.);
2221 }
2222 // Emission
2223 dosX = GetConductionBandDensityOfStates(en - eph[j], 0);
2224 dosL = GetConductionBandDensityOfStates(en - eph[j], m_nValleysX);
2225 if (en > eph[j]) {
2226 m_cfElectronsG[i].push_back(zX * c[j] * (nocc[j] + 1) * dosX);
2227 } else {
2228 m_cfElectronsG[i].push_back(0.);
2229 }
2230 if (en - eph[j] > m_eMinL) {
2231 m_cfElectronsG[i].push_back(zL * c[j] * (nocc[j] + 1) * dosL);
2232 } else {
2233 m_cfElectronsG[i].push_back(0.);
2234 }
2235 }
2236 en += m_eStepG;
2237 }
2238
2239 for (int j = 0; j < nPhonons; ++j) {
2240 // Absorption (XL)
2241 m_energyLossElectronsX.push_back(-eph[j]);
2242 m_energyLossElectronsL.push_back(-eph[j]);
2243 // Emission (XL)
2244 m_energyLossElectronsX.push_back(eph[j]);
2245 m_energyLossElectronsL.push_back(eph[j]);
2246 // Absorption (G)
2247 m_energyLossElectronsG.push_back(-eph[j]);
2248 m_energyLossElectronsG.push_back(-eph[j]);
2249 // Emission (G)
2250 m_energyLossElectronsG.push_back(eph[j]);
2251 m_energyLossElectronsG.push_back(eph[j]);
2252
2253 m_scatTypeElectronsX.push_back(ElectronCollisionTypeInterbandXG);
2254 m_scatTypeElectronsX.push_back(ElectronCollisionTypeInterbandXG);
2255 m_scatTypeElectronsL.push_back(ElectronCollisionTypeInterbandLG);
2256 m_scatTypeElectronsL.push_back(ElectronCollisionTypeInterbandLG);
2257
2258 m_scatTypeElectronsG.push_back(ElectronCollisionTypeInterbandXG);
2259 m_scatTypeElectronsG.push_back(ElectronCollisionTypeInterbandLG);
2260 m_scatTypeElectronsG.push_back(ElectronCollisionTypeInterbandXG);
2261 m_scatTypeElectronsG.push_back(ElectronCollisionTypeInterbandLG);
2262 }
2263
2264 m_nLevelsX += 2 * nPhonons;
2265 m_nLevelsL += 2 * nPhonons;
2266 m_nLevelsG += 4 * nPhonons;
2267
2268 return true;
2269}
2270
2271bool MediumSilicon::ElectronIonisationRatesXL() {
2272 // References:
2273 // - E. Cartier, M. V. Fischetti, E. A. Eklund and F. R. McFeely,
2274 // Appl. Phys. Lett 62, 3339-3341
2275 // - DAMOCLES web page: www.research.ibm.com/DAMOCLES
2276
2277 // Coefficients [ns-1]
2278 constexpr double p[3] = {6.25e1, 3.e3, 6.8e5};
2279 // Threshold energies [eV]
2280 constexpr double eth[3] = {1.2, 1.8, 3.45};
2281
2282 double en = 0.;
2283 for (int i = 0; i < nEnergyStepsXL; ++i) {
2284 double fIon = 0.;
2285 if (en > eth[0]) {
2286 fIon += p[0] * (en - eth[0]) * (en - eth[0]);
2287 }
2288 if (en > eth[1]) {
2289 fIon += p[1] * (en - eth[1]) * (en - eth[1]);
2290 }
2291 if (en > eth[2]) {
2292 fIon += p[2] * (en - eth[2]) * (en - eth[2]);
2293 }
2294 m_cfElectronsX[i].push_back(fIon);
2295 m_cfElectronsL[i].push_back(fIon);
2296 en += m_eStepXL;
2297 }
2298
2299 m_energyLossElectronsX.push_back(eth[0]);
2300 m_energyLossElectronsL.push_back(eth[0]);
2301 m_scatTypeElectronsX.push_back(ElectronCollisionTypeIonisation);
2302 m_scatTypeElectronsL.push_back(ElectronCollisionTypeIonisation);
2303 ++m_nLevelsX;
2304 ++m_nLevelsL;
2305
2306 return true;
2307}
2308
2309bool MediumSilicon::ElectronIonisationRatesG() {
2310 // References:
2311 // - E. Cartier, M. V. Fischetti, E. A. Eklund and F. R. McFeely,
2312 // Appl. Phys. Lett 62, 3339-3341
2313 // - S. Tanuma, C. J. Powell and D. R. Penn
2314 // Surf. Interface Anal. (2010)
2315
2316 // Coefficients [ns-1]
2317 constexpr double p[3] = {6.25e1, 3.e3, 6.8e5};
2318 // Threshold energies [eV]
2319 constexpr double eth[3] = {1.2, 1.8, 3.45};
2320
2321 double en = 0.;
2322 for (int i = 0; i < nEnergyStepsG; ++i) {
2323 double fIon = 0.;
2324 if (en > eth[0]) {
2325 fIon += p[0] * (en - eth[0]) * (en - eth[0]);
2326 }
2327 if (en > eth[1]) {
2328 fIon += p[1] * (en - eth[1]) * (en - eth[1]);
2329 }
2330 if (en > eth[2]) {
2331 fIon += p[2] * (en - eth[2]) * (en - eth[2]);
2332 }
2333 if (en >= m_eMinG) {
2334 m_cfElectronsG[i].push_back(fIon);
2335 } else {
2336 m_cfElectronsG[i].push_back(0.);
2337 }
2338 en += m_eStepG;
2339 }
2340
2341 m_energyLossElectronsG.push_back(eth[0]);
2342 m_scatTypeElectronsG.push_back(ElectronCollisionTypeIonisation);
2343 ++m_nLevelsG;
2344
2345 return true;
2346}
2347
2348bool MediumSilicon::ElectronImpurityScatteringRates() {
2349 // Lattice temperature [eV]
2350 const double kbt = BoltzmannConstant * m_temperature;
2351
2352 // Band parameters
2353 // Density of states effective masses
2354 const double mdX =
2355 ElectronMass * pow(m_mLongX * m_mTransX * m_mTransX, 1. / 3.);
2356 const double mdL =
2357 ElectronMass * pow(m_mLongL * m_mTransL * m_mTransL, 1. / 3.);
2358
2359 // Dielectric constant
2360 const double eps = GetDielectricConstant();
2361 // Impurity concentration
2362 const double impurityConcentration = m_dopingConcentration;
2363 if (impurityConcentration < Small) return true;
2364
2365 // Screening length
2366 const double ls = sqrt(eps * kbt / (4 * Pi * FineStructureConstant * HbarC *
2367 impurityConcentration));
2368 const double ebX = 0.5 * HbarC * HbarC / (mdX * ls * ls);
2369 const double ebL = 0.5 * HbarC * HbarC / (mdL * ls * ls);
2370
2371 // Prefactor
2372 // const double c = pow(2., 2.5) * Pi * impurityConcentration *
2373 // pow(FineStructureConstant * HbarC, 2) *
2374 // SpeedOfLight / (eps * eps * sqrt(md) * eb * eb);
2375 // Use momentum-transfer cross-section
2376 const double cX = impurityConcentration * Pi *
2377 pow(FineStructureConstant * HbarC, 2) * SpeedOfLight /
2378 (sqrt(2 * mdX) * eps * eps);
2379 const double cL = impurityConcentration * Pi *
2380 pow(FineStructureConstant * HbarC, 2) * SpeedOfLight /
2381 (sqrt(2 * mdL) * eps * eps);
2382
2383 double en = 0.;
2384 for (int i = 0; i < nEnergyStepsXL; ++i) {
2385 const double gammaX = en * (1. + m_alphaX * en);
2386 const double gammaL = (en - m_eMinL) * (1. + m_alphaL * (en - m_eMinL));
2387 // m_cfElectrons[i][iLevel] = c * sqrt(gamma) * (1. + 2 * alpha * en) /
2388 // (1. + 4. * gamma / eb);
2389 if (gammaX <= 0.) {
2390 m_cfElectronsX[i].push_back(0.);
2391 } else {
2392 const double b = 4 * gammaX / ebX;
2393 m_cfElectronsX[i].push_back((cX / pow(gammaX, 1.5)) *
2394 (log(1. + b) - b / (1. + b)));
2395 }
2396 if (en <= m_eMinL || gammaL <= 0.) {
2397 m_cfElectronsL[i].push_back(0.);
2398 } else {
2399 const double b = 4 * gammaL / ebL;
2400 m_cfElectronsL[i].push_back((cL / pow(gammaL, 1.5)) *
2401 (log(1. + b) - b / (1. + b)));
2402 }
2403 en += m_eStepXL;
2404 }
2405
2406 m_energyLossElectronsX.push_back(0.);
2407 m_energyLossElectronsL.push_back(0.);
2408 m_scatTypeElectronsX.push_back(ElectronCollisionTypeImpurity);
2409 m_scatTypeElectronsL.push_back(ElectronCollisionTypeImpurity);
2410 ++m_nLevelsX;
2411 ++m_nLevelsL;
2412
2413 return true;
2414}
2415
2416bool MediumSilicon::HoleScatteringRates() {
2417 // Reset the scattering rates
2418 m_cfTotHoles.assign(nEnergyStepsV, 0.);
2419 m_cfHoles.assign(nEnergyStepsV, std::vector<double>());
2420 m_energyLossHoles.clear();
2421 m_scatTypeHoles.clear();
2422 m_cfNullHoles = 0.;
2423
2424 m_nLevelsV = 0;
2425 // Fill the scattering rates table
2426 HoleAcousticScatteringRates();
2427 HoleOpticalScatteringRates();
2428 // HoleImpurityScatteringRates();
2429 HoleIonisationRates();
2430
2431 std::ofstream outfile;
2432 if (m_cfOutput) {
2433 outfile.open("ratesV.txt", std::ios::out);
2434 }
2435
2436 for (int i = 0; i < nEnergyStepsV; ++i) {
2437 // Sum up the scattering rates of all processes.
2438 for (int j = m_nLevelsV; j--;) m_cfTotHoles[i] += m_cfHoles[i][j];
2439
2440 if (m_cfOutput) {
2441 outfile << i * m_eStepV << " " << m_cfTotHoles[i] << " ";
2442 for (int j = 0; j < m_nLevelsV; ++j) {
2443 outfile << m_cfHoles[i][j] << " ";
2444 }
2445 outfile << "\n";
2446 }
2447
2448 if (m_cfTotHoles[i] > m_cfNullHoles) {
2449 m_cfNullHoles = m_cfTotHoles[i];
2450 }
2451
2452 // Make sure the total scattering rate is positive.
2453 if (m_cfTotHoles[i] <= 0.) {
2454 std::cerr << m_className << "::HoleScatteringRates:\n"
2455 << " Scattering rate at " << i * m_eStepV << " eV <= 0.\n";
2456 return false;
2457 }
2458 // Normalise the rates.
2459 for (int j = 0; j < m_nLevelsV; ++j) {
2460 m_cfHoles[i][j] /= m_cfTotHoles[i];
2461 if (j > 0) m_cfHoles[i][j] += m_cfHoles[i][j - 1];
2462 }
2463 }
2464
2465 if (m_cfOutput) {
2466 outfile.close();
2467 }
2468
2469 return true;
2470}
2471
2472bool MediumSilicon::HoleAcousticScatteringRates() {
2473 // Reference:
2474 // - C. Jacoboni and L. Reggiani,
2475 // Rev. Mod. Phys. 55, 645-705
2476 // - DAMOCLES web page: www.research.ibm.com/DAMOCLES
2477 // - M. Lundstrom, Fundamentals of carrier transport
2478
2479 // Mass density [(eV/c2)/cm3]
2480 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
2481 // Lattice temperature [eV]
2482 const double kbt = BoltzmannConstant * m_temperature;
2483
2484 // Acoustic phonon intraband scattering
2485 // Acoustic deformation potential [eV]
2486 // DAMOCLES: 4.6 eV; Lundstrom: 5 eV
2487 constexpr double defpot2 = 4.6 * 4.6;
2488 // Longitudinal velocity of sound [cm/ns]
2489 constexpr double u = 9.04e-4;
2490 // Prefactor for acoustic deformation potential scattering
2491 const double cIntra = TwoPi * SpeedOfLight * SpeedOfLight * kbt * defpot2 /
2492 (Hbar * u * u * rho);
2493
2494 // Fill the scattering rate tables.
2495 double en = Small;
2496 for (int i = 0; i < nEnergyStepsV; ++i) {
2497 const double dos = GetValenceBandDensityOfStates(en, 0);
2498 m_cfHoles[i].push_back(cIntra * dos);
2499 en += m_eStepV;
2500 }
2501
2502 // Assume that energy loss is negligible.
2503 m_energyLossHoles.push_back(0.);
2504 m_scatTypeHoles.push_back(ElectronCollisionTypeAcousticPhonon);
2505 ++m_nLevelsV;
2506
2507 return true;
2508}
2509
2510bool MediumSilicon::HoleOpticalScatteringRates() {
2511 // Reference:
2512 // - C. Jacoboni and L. Reggiani,
2513 // Rev. Mod. Phys. 55, 645-705
2514 // - DAMOCLES web page: www.research.ibm.com/DAMOCLES
2515 // - M. Lundstrom, Fundamentals of carrier transport
2516
2517 // Mass density [(eV/c2)/cm3]
2518 const double rho = m_density * m_a * AtomicMassUnitElectronVolt;
2519 // Lattice temperature [eV]
2520 const double kbt = BoltzmannConstant * m_temperature;
2521
2522 // Coupling constant [eV/cm]
2523 // DAMOCLES: 6.6, Lundstrom: 6.0
2524 constexpr double dtk = 6.6e8;
2525 // Phonon energy [eV]
2526 constexpr double eph = 63.0e-3;
2527 // Phonon cccupation numbers
2528 const double nocc = 1. / (exp(eph / kbt) - 1);
2529 // Prefactors
2530 const double c0 = HbarC * SpeedOfLight * Pi / rho;
2531 double c = c0 * dtk * dtk / eph;
2532
2533 double en = 0.;
2534 for (int i = 0; i < nEnergyStepsV; ++i) {
2535 // Absorption
2536 double dos = GetValenceBandDensityOfStates(en + eph, 0);
2537 m_cfHoles[i].push_back(c * nocc * dos);
2538 // Emission
2539 if (en > eph) {
2540 dos = GetValenceBandDensityOfStates(en - eph, 0);
2541 m_cfHoles[i].push_back(c * (nocc + 1) * dos);
2542 } else {
2543 m_cfHoles[i].push_back(0.);
2544 }
2545 en += m_eStepV;
2546 }
2547
2548 // Absorption
2549 m_energyLossHoles.push_back(-eph);
2550 // Emission
2551 m_energyLossHoles.push_back(eph);
2552 m_scatTypeHoles.push_back(ElectronCollisionTypeOpticalPhonon);
2553 m_scatTypeHoles.push_back(ElectronCollisionTypeOpticalPhonon);
2554
2555 m_nLevelsV += 2;
2556
2557 return true;
2558}
2559
2560bool MediumSilicon::HoleIonisationRates() {
2561 // References:
2562 // - DAMOCLES web page: www.research.ibm.com/DAMOCLES
2563
2564 // Coefficients [ns-1]
2565 constexpr double p[2] = {2., 1.e3};
2566 // Threshold energies [eV]
2567 constexpr double eth[2] = {1.1, 1.45};
2568 // Exponents
2569 constexpr double b[2] = {6., 4.};
2570
2571 double en = 0.;
2572 for (int i = 0; i < nEnergyStepsV; ++i) {
2573 double fIon = 0.;
2574 if (en > eth[0]) {
2575 fIon += p[0] * pow(en - eth[0], b[0]);
2576 }
2577 if (en > eth[1]) {
2578 fIon += p[1] * pow(en - eth[1], b[1]);
2579 }
2580 m_cfHoles[i].push_back(fIon);
2581 en += m_eStepV;
2582 }
2583
2584 m_energyLossHoles.push_back(eth[0]);
2585 m_scatTypeHoles.push_back(ElectronCollisionTypeIonisation);
2586 ++m_nLevelsV;
2587
2588 return true;
2589}
2590
2592 const int band) {
2593 if (band < 0) {
2594 int iE = int(e / m_eStepDos);
2595 const int nPoints = m_fbDosConduction.size();
2596 if (iE >= nPoints || iE < 0) {
2597 return 0.;
2598 } else if (iE == nPoints - 1) {
2599 return m_fbDosConduction[nPoints - 1];
2600 }
2601
2602 const double dos = m_fbDosConduction[iE] +
2603 (m_fbDosConduction[iE + 1] - m_fbDosConduction[iE]) *
2604 (e / m_eStepDos - iE);
2605 return dos * 1.e21;
2606
2607 } else if (band < m_nValleysX) {
2608 // X valleys
2609 if (e <= 0.) return 0.;
2610 // Density-of-states effective mass (cube)
2611 const double md3 = pow(ElectronMass, 3) * m_mLongX * m_mTransX * m_mTransX;
2612
2613 if (m_fullBandDos) {
2614 if (e < m_eMinL) {
2615 return GetConductionBandDensityOfStates(e, -1) / m_nValleysX;
2616 } else if (e < m_eMinG) {
2617 // Subtract the fraction of the full-band density of states
2618 // attributed to the L valleys.
2619 const double dosX =
2621 GetConductionBandDensityOfStates(e, m_nValleysX) * m_nValleysL;
2622 return dosX / m_nValleysX;
2623 } else {
2624 // Subtract the fraction of the full-band density of states
2625 // attributed to the L valleys and the higher bands.
2626 const double dosX =
2628 GetConductionBandDensityOfStates(e, m_nValleysX) * m_nValleysL -
2629 GetConductionBandDensityOfStates(e, m_nValleysX + m_nValleysL);
2630 if (dosX <= 0.) return 0.;
2631 return dosX / m_nValleysX;
2632 }
2633 } else if (m_nonParabolic) {
2634 const double alpha = m_alphaX;
2635 return sqrt(md3 * e * (1. + alpha * e) / 2.) * (1. + 2 * alpha * e) /
2636 (Pi2 * pow(HbarC, 3.));
2637 } else {
2638 return sqrt(md3 * e / 2.) / (Pi2 * pow(HbarC, 3.));
2639 }
2640 } else if (band < m_nValleysX + m_nValleysL) {
2641 // L valleys
2642 if (e <= m_eMinL) return 0.;
2643
2644 // Density-of-states effective mass (cube)
2645 const double md3 = pow(ElectronMass, 3) * m_mLongL * m_mTransL * m_mTransL;
2646 // Non-parabolicity parameter
2647 const double alpha = m_alphaL;
2648
2649 if (m_fullBandDos) {
2650 // Energy up to which the non-parabolic approximation is used.
2651 const double ej = m_eMinL + 0.5;
2652 if (e <= ej) {
2653 return sqrt(md3 * (e - m_eMinL) * (1. + alpha * (e - m_eMinL))) *
2654 (1. + 2 * alpha * (e - m_eMinL)) /
2655 (Sqrt2 * Pi2 * pow(HbarC, 3.));
2656 } else {
2657 // Fraction of full-band density of states attributed to L valleys
2658 double fL = sqrt(md3 * (ej - m_eMinL) * (1. + alpha * (ej - m_eMinL))) *
2659 (1. + 2 * alpha * (ej - m_eMinL)) /
2660 (Sqrt2 * Pi2 * pow(HbarC, 3.));
2661 fL = m_nValleysL * fL / GetConductionBandDensityOfStates(ej, -1);
2662
2663 double dosXL = GetConductionBandDensityOfStates(e, -1);
2664 if (e > m_eMinG) {
2665 dosXL -=
2666 GetConductionBandDensityOfStates(e, m_nValleysX + m_nValleysL);
2667 }
2668 if (dosXL <= 0.) return 0.;
2669 return fL * dosXL / 8.;
2670 }
2671 } else if (m_nonParabolic) {
2672 return sqrt(md3 * (e - m_eMinL) * (1. + alpha * (e - m_eMinL))) *
2673 (1. + 2 * alpha * (e - m_eMinL)) / (Sqrt2 * Pi2 * pow(HbarC, 3.));
2674 } else {
2675 return sqrt(md3 * (e - m_eMinL) / 2.) / (Pi2 * pow(HbarC, 3.));
2676 }
2677 } else if (band == m_nValleysX + m_nValleysL) {
2678 // Higher bands
2679 const double ej = 2.7;
2680 if (m_eMinG >= ej) {
2681 std::cerr << m_className << "::GetConductionBandDensityOfStates:\n"
2682 << " Cannot determine higher band density-of-states.\n"
2683 << " Program bug. Check offset energy!\n";
2684 return 0.;
2685 }
2686 if (e < m_eMinG) {
2687 return 0.;
2688 } else if (e < ej) {
2689 // Coexistence of XL and higher bands.
2690 const double dj = GetConductionBandDensityOfStates(ej, -1);
2691 // Assume linear increase of density-of-states.
2692 return dj * (e - m_eMinG) / (ej - m_eMinG);
2693 } else {
2695 }
2696 }
2697
2698 std::cerr << m_className << "::GetConductionBandDensityOfStates:\n"
2699 << " Band index (" << band << ") out of range.\n";
2700 return ElectronMass * sqrt(ElectronMass * e / 2.) / (Pi2 * pow(HbarC, 3.));
2701}
2702
2704 const int band) {
2705 if (band <= 0) {
2706 // Total (full-band) density of states.
2707 const int nPoints = m_fbDosValence.size();
2708 int iE = int(e / m_eStepDos);
2709 if (iE >= nPoints || iE < 0) {
2710 return 0.;
2711 } else if (iE == nPoints - 1) {
2712 return m_fbDosValence[nPoints - 1];
2713 }
2714
2715 const double dos =
2716 m_fbDosValence[iE] +
2717 (m_fbDosValence[iE + 1] - m_fbDosValence[iE]) * (e / m_eStepDos - iE);
2718 return dos * 1.e21;
2719 }
2720
2721 std::cerr << m_className << "::GetConductionBandDensityOfStates:\n"
2722 << " Band index (" << band << ") out of range.\n";
2723 return 0.;
2724}
2725
2726void MediumSilicon::ComputeSecondaries(const double e0, double& ee,
2727 double& eh) {
2728 const int nPointsValence = m_fbDosValence.size();
2729 const int nPointsConduction = m_fbDosConduction.size();
2730 const double widthValenceBand = m_eStepDos * nPointsValence;
2731 const double widthConductionBand = m_eStepDos * nPointsConduction;
2732
2733 bool ok = false;
2734 while (!ok) {
2735 // Sample a hole energy according to the valence band DOS.
2736 eh = RndmUniformPos() * std::min(widthValenceBand, e0);
2737 int ih = std::min(int(eh / m_eStepDos), nPointsValence - 1);
2738 while (RndmUniform() > m_fbDosValence[ih] / m_fbDosMaxV) {
2739 eh = RndmUniformPos() * std::min(widthValenceBand, e0);
2740 ih = std::min(int(eh / m_eStepDos), nPointsValence - 1);
2741 }
2742 // Sample an electron energy according to the conduction band DOS.
2743 ee = RndmUniformPos() * std::min(widthConductionBand, e0);
2744 int ie = std::min(int(ee / m_eStepDos), nPointsConduction - 1);
2745 while (RndmUniform() > m_fbDosConduction[ie] / m_fbDosMaxC) {
2746 ee = RndmUniformPos() * std::min(widthConductionBand, e0);
2747 ie = std::min(int(ee / m_eStepDos), nPointsConduction - 1);
2748 }
2749 // Calculate the energy of the primary electron.
2750 const double ep = e0 - m_bandGap - eh - ee;
2751 if (ep < Small) continue;
2752 if (ep > 5.) return;
2753 // Check if the primary electron energy is consistent with the DOS.
2754 int ip = std::min(int(ep / m_eStepDos), nPointsConduction - 1);
2755 if (RndmUniform() > m_fbDosConduction[ip] / m_fbDosMaxC) continue;
2756 ok = true;
2757 }
2758}
2759
2760void MediumSilicon::InitialiseDensityOfStates() {
2761 m_eStepDos = 0.1;
2762
2763 m_fbDosValence = {
2764 {0., 1.28083, 2.08928, 2.70763, 3.28095, 3.89162, 4.50547, 5.15043,
2765 5.89314, 6.72667, 7.67768, 8.82725, 10.6468, 12.7003, 13.7457, 14.0263,
2766 14.2731, 14.5527, 14.8808, 15.1487, 15.4486, 15.7675, 16.0519, 16.4259,
2767 16.7538, 17.0589, 17.3639, 17.6664, 18.0376, 18.4174, 18.2334, 16.7552,
2768 15.1757, 14.2853, 13.6516, 13.2525, 12.9036, 12.7203, 12.6104, 12.6881,
2769 13.2862, 14.0222, 14.9366, 13.5084, 9.77808, 6.15266, 3.47839, 2.60183,
2770 2.76747, 3.13985, 3.22524, 3.29119, 3.40868, 3.6118, 3.8464, 4.05776,
2771 4.3046, 4.56219, 4.81553, 5.09909, 5.37616, 5.67297, 6.04611, 6.47252,
2772 6.9256, 7.51254, 8.17923, 8.92351, 10.0309, 11.726, 16.2853, 18.2457,
2773 12.8879, 7.86019, 6.02275, 5.21777, 4.79054, 3.976, 3.11855, 2.46854,
2774 1.65381, 0.830278, 0.217735}};
2775
2776 m_fbDosConduction = {
2777 {0., 1.5114, 2.71026, 3.67114, 4.40173, 5.05025, 5.6849, 6.28358,
2778 6.84628, 7.43859, 8.00204, 8.80658, 9.84885, 10.9579, 12.0302, 13.2051,
2779 14.6948, 16.9879, 18.4492, 18.1933, 17.6747, 16.8135, 15.736, 14.4965,
2780 13.1193, 12.1817, 12.6109, 15.3148, 19.4936, 23.0093, 24.4106, 22.2834,
2781 19.521, 18.9894, 18.8015, 17.9363, 17.0252, 15.9871, 14.8486, 14.3797,
2782 14.2426, 14.3571, 14.7271, 14.681, 14.3827, 14.2789, 14.144, 14.1684,
2783 14.1418, 13.9237, 13.7558, 13.5691, 13.4567, 13.2693, 12.844, 12.4006,
2784 12.045, 11.7729, 11.3607, 11.14, 11.0586, 10.5475, 9.73786, 9.34423,
2785 9.4694, 9.58071, 9.6967, 9.84854, 10.0204, 9.82705, 9.09102, 8.30665,
2786 7.67306, 7.18925, 6.79675, 6.40713, 6.21687, 6.33267, 6.5223, 6.17877,
2787 5.48659, 4.92208, 4.44239, 4.02941, 3.5692, 3.05953, 2.6428, 2.36979,
2788 2.16273, 2.00627, 1.85206, 1.71265, 1.59497, 1.46681, 1.34913, 1.23951,
2789 1.13439, 1.03789, 0.924155, 0.834962, 0.751017}};
2790
2791 auto it = std::max_element(m_fbDosValence.begin(), m_fbDosValence.end());
2792 m_fbDosMaxV = *it;
2793 it = std::max_element(m_fbDosConduction.begin(), m_fbDosConduction.end());
2794 m_fbDosMaxC = *it;
2795}
2796}
bool GetDielectricFunction(const double e, double &eps1, double &eps2, const unsigned int i=0) override
Get the complex dielectric function at a given energy.
void SetLatticeMobilityModelReggiani()
Calculate the lattice mobility using the Reggiani model.
void SetDoping(const char type, const double c)
Set doping concentration [cm-3] and type ('i', 'n', 'p').
void SetImpactIonisationModelOkutoCrowell()
Calculate α using the Okuto-Crowell model.
void GetDoping(char &type, double &c) const
Retrieve doping concentration.
bool ElectronTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha) override
Ionisation coefficient [cm-1].
void SetHighFieldMobilityModelReggiani()
Parameterize the high-field mobility using the Reggiani model.
double GetElectronCollisionRate(const double e, const int band) override
Collision rate [ns-1] for given electron energy.
bool HoleVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz) override
Drift velocity [cm / ns].
unsigned int GetNumberOfLevels() const
void SetSaturationVelocityModelReggiani()
Calculate the saturation velocities using the Reggiani model.
bool ElectronAttachment(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &eta) override
Attachment coefficient [cm-1].
double GetValenceBandDensityOfStates(const double e, const int band=-1)
void SetDopingMobilityModelMasetti()
Use the Masetti model for the doping-dependence of the mobility (default).
void SetTrapDensity(const double n)
Trap density [cm-3], by default set to zero.
void SetHighFieldMobilityModelMinimos()
Parameterize the high-field mobility using the Minimos model.
MediumSilicon()
Constructor.
void SetSaturationVelocity(const double vsate, const double vsath)
Specify the saturation velocities of electrons and holes.
bool ElectronVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz) override
Drift velocity [cm / ns].
void ComputeSecondaries(const double e0, double &ee, double &eh)
double HoleMobility() override
Low-field mobility [cm2 V-1 ns-1].
double GetConductionBandDensityOfStates(const double e, const int band=0)
bool ElectronCollision(const double e, int &type, int &level, double &e1, double &dx, double &dy, double &dz, std::vector< std::pair< Particle, double > > &secondaries, int &ndxc, int &band) override
Sample the collision type. Update energy and direction vector.
bool SetMaxElectronEnergy(const double e)
bool HoleTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha) override
Ionisation coefficient [cm-1].
void SetHighFieldMobilityModelConstant()
Make the velocity proportional to the electric field (no saturation).
void SetLatticeMobilityModelSentaurus()
Calculate the lattice mobility using the Sentaurus model (default).
void SetSaturationVelocityModelCanali()
Calculate the saturation velocities using the Canali model (default).
void SetHighFieldMobilityModelCanali()
Parameterize the high-field mobility using the Canali model (default).
void GetElectronMomentum(const double e, double &px, double &py, double &pz, int &band) override
void SetImpactIonisationModelVanOverstraetenDeMan()
Calculate α using the van Overstraeten-de Man model (default).
void SetTrappingTime(const double etau, const double htau)
Set time constant for trapping of electrons and holes [ns].
void SetSaturationVelocityModelMinimos()
Calculate the saturation velocities using the Minimos model.
void SetLowFieldMobility(const double mue, const double muh)
Specify the low field values of the electron and hole mobilities.
bool HoleAttachment(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &eta) override
Attachment coefficient [cm-1].
double GetElectronEnergy(const double px, const double py, const double pz, double &vx, double &vy, double &vz, const int band=0) override
Dispersion relation (energy vs. wave vector)
double ElectronMobility() override
Low-field mobility [cm2 V-1 ns-1].
bool GetOpticalDataRange(double &emin, double &emax, const unsigned int i=0) override
Get the energy range [eV] of the available optical data.
double GetElectronNullCollisionRate(const int band) override
Null-collision rate [ns-1].
unsigned int GetNumberOfElectronCollisions() const
void SetLatticeMobilityModelMinimos()
Calculate the lattice mobility using the Minimos model.
void SetTrapCrossSection(const double ecs, const double hcs)
Trapping cross-sections for electrons and holes.
int GetElectronBandPopulation(const int band)
void SetImpactIonisationModelGrant()
Calculate α using the Grant model.
void SetDopingMobilityModelMinimos()
Use the Minimos model for the doping-dependence of the mobility.
void SetImpactIonisationModelMassey()
Calculate α using the Massey model.
unsigned int GetNumberOfElectronBands() const
double m_density
Definition Medium.hh:550
void SetTemperature(const double t)
Set the temperature [K].
Definition Medium.cc:72
virtual bool HoleTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha)
Ionisation coefficient [cm-1].
Definition Medium.cc:599
virtual bool HoleVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
Drift velocity [cm / ns].
Definition Medium.cc:578
std::vector< std::vector< std::vector< double > > > m_eAlp
Definition Medium.hh:582
std::vector< std::vector< std::vector< double > > > m_hAlp
Definition Medium.hh:594
double GetDielectricConstant() const
Get the relative static dielectric constant.
Definition Medium.hh:45
std::string m_name
Definition Medium.hh:538
virtual bool ElectronVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
Drift velocity [cm / ns].
Definition Medium.cc:444
virtual void SetAtomicNumber(const double z)
Set the effective atomic number.
Definition Medium.cc:115
std::vector< std::vector< std::vector< double > > > m_eVelE
Definition Medium.hh:577
void SetDielectricConstant(const double eps)
Set the relative static dielectric constant.
Definition Medium.cc:92
virtual bool ElectronTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha)
Ionisation coefficient [cm-1].
Definition Medium.cc:468
std::vector< std::vector< std::vector< double > > > m_eAtt
Definition Medium.hh:583
virtual void SetMassDensity(const double rho)
Set the mass density [g/cm3].
Definition Medium.cc:145
Medium()
Constructor.
Definition Medium.cc:61
std::vector< std::vector< std::vector< double > > > m_hVelE
Definition Medium.hh:589
virtual bool ElectronAttachment(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &eta)
Attachment coefficient [cm-1].
Definition Medium.cc:481
std::vector< std::vector< std::vector< double > > > m_hAtt
Definition Medium.hh:595
virtual void SetAtomicWeight(const double a)
Set the effective atomic weight.
Definition Medium.cc:125
std::string m_className
Definition Medium.hh:529
virtual bool HoleAttachment(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &eta)
Attachment coefficient [cm-1].
Definition Medium.cc:612
double m_temperature
Definition Medium.hh:540
static void Langevin(const double ex, const double ey, const double ez, double bx, double by, double bz, const double mu, double &vx, double &vy, double &vz)
Definition Medium.cc:301
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition Random.hh:14
void ltrim(std::string &line)
Definition Utilities.hh:12
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition Random.hh:128
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
Definition Random.hh:17
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337
DoubleAc exp(const DoubleAc &f)
Definition DoubleAc.cpp:377
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314