13double Mag(
const double x,
const double y,
const double z) {
14 return sqrt(x * x + y * y + z * z);
17void ToLocal(
const std::array<std::array<double, 3>, 3>& rot,
18 const double xg,
const double yg,
const double zg,
19 double& xl,
double& yl,
double& zl) {
20 xl = rot[0][0] * xg + rot[0][1] * yg + rot[0][2] * zg;
21 yl = rot[1][0] * xg + rot[1][1] * yg + rot[1][2] * zg;
22 zl = rot[2][0] * xg + rot[2][1] * yg + rot[2][2] * zg;
25void ToGlobal(
const std::array<std::array<double, 3>, 3>& rot,
26 const double xl,
const double yl,
const double zl,
27 double& xg,
double& yg,
double& zg) {
28 xg = rot[0][0] * xl + rot[1][0] * yl + rot[2][0] * zl;
29 yg = rot[0][1] * xl + rot[1][1] * yl + rot[2][1] * zl;
30 zg = rot[0][2] * xl + rot[1][2] * yl + rot[2][2] * zl;
33void RotationMatrix(
double bx,
double by,
double bz,
const double bmag,
34 const double ex,
const double ey,
const double ez,
35 std::array<std::array<double, 3>, 3>& rot) {
41 std::array<std::array<double, 3>, 3> rB = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
42 if (bmag > Garfield::Small) {
46 const double bt = by * by + bz * bz;
47 if (bt > Garfield::Small) {
48 const double btInv = 1. / bt;
54 rB[1][1] = (bx * by * by + bz * bz) * btInv;
55 rB[2][2] = (bx * bz * bz + by * by) * btInv;
56 rB[1][2] = rB[2][1] = (bx - 1.) * by * bz * btInv;
60 const double fy = rB[1][0] * ex + rB[1][1] * ey + rB[1][2] * ez;
61 const double fz = rB[2][0] * ex + rB[2][1] * ey + rB[2][2] * ez;
62 const double ft =
sqrt(fy * fy + fz * fz);
63 std::array<std::array<double, 3>, 3> rX = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
64 if (ft > Garfield::Small) {
66 rX[1][1] = rX[2][2] = fz / ft;
70 for (
unsigned int i = 0; i < 3; ++i) {
71 for (
unsigned int j = 0; j < 3; ++j) {
73 for (
unsigned int k = 0; k < 3; ++k) {
74 rot[i][j] += rX[i][k] * rB[k][j];
80void PrintStatus(
const std::string& hdr,
const std::string& status,
81 const double x,
const double y,
const double z,
83 const std::string eh = hole ?
"Hole " :
"Electron ";
84 std::cout << hdr << eh << status <<
" at " <<
x <<
", " <<
y <<
", " <<
z
92 m_endpointsElectrons.reserve(10000);
93 m_endpointsHoles.reserve(10000);
94 m_photons.reserve(1000);
100 std::cerr << m_className <<
"::SetSensor: Null pointer.\n";
108 std::cerr << m_className <<
"::EnablePlotting: Null pointer.\n";
113 if (!m_useDriftLines) {
114 std::cout << m_className <<
"::EnablePlotting:\n"
115 <<
" Enabling storage of drift line.\n";
122 std::cerr << m_className <<
"::EnableElectronEnergyHistogramming:\n"
123 <<
" Null pointer.\n";
127 m_histElectronEnergy = histo;
132 std::cerr << m_className <<
"::EnableHoleEnergyHistogramming:\n"
133 <<
" Null pointer.\n";
137 m_histHoleEnergy = histo;
142 std::cerr << m_className <<
"::SetDistanceHistogram: Null pointer.\n";
146 m_histDistance = histo;
148 if (opt ==
'x' || opt ==
'y' || opt ==
'z' || opt ==
'r') {
149 m_distanceOption = opt;
151 std::cerr << m_className <<
"::SetDistanceHistogram:";
152 std::cerr <<
" Unknown option " << opt <<
".\n";
153 std::cerr <<
" Valid options are x, y, z, r.\n";
154 std::cerr <<
" Using default value (r).\n";
155 m_distanceOption =
'r';
158 if (m_distanceHistogramType.empty()) {
159 std::cout << m_className <<
"::SetDistanceHistogram:\n";
160 std::cout <<
" Don't forget to call EnableDistanceHistogramming.\n";
167 const unsigned int nDistanceHistogramTypes = m_distanceHistogramType.size();
168 if (nDistanceHistogramTypes > 0) {
169 for (
unsigned int i = 0; i < nDistanceHistogramTypes; ++i) {
170 if (m_distanceHistogramType[i] != type)
continue;
171 std::cout << m_className <<
"::EnableDistanceHistogramming:\n";
172 std::cout <<
" Collision type " << type
173 <<
" is already being histogrammed.\n";
178 m_distanceHistogramType.push_back(type);
179 std::cout << m_className <<
"::EnableDistanceHistogramming:\n";
180 std::cout <<
" Histogramming of collision type " << type <<
" enabled.\n";
181 if (!m_histDistance) {
182 std::cout <<
" Don't forget to set the histogram.\n";
187 if (std::find(m_distanceHistogramType.begin(), m_distanceHistogramType.end(),
188 type) == m_distanceHistogramType.end()) {
189 std::cerr << m_className <<
"::DisableDistanceHistogramming:\n"
190 <<
" Collision type " << type <<
" is not histogrammed.\n";
194 m_distanceHistogramType.erase(
195 std::remove(m_distanceHistogramType.begin(),
196 m_distanceHistogramType.end(), type),
197 m_distanceHistogramType.end());
201 m_histDistance =
nullptr;
202 m_distanceHistogramType.clear();
207 std::cerr << m_className <<
"::EnableSecondaryEnergyHistogramming:\n"
208 <<
" Null pointer.\n";
212 m_histSecondary = histo;
216 if (fabs(t1 - t0) < Small) {
217 std::cerr << m_className <<
"::SetTimeWindow:\n";
218 std::cerr <<
" Time interval must be greater than zero.\n";
222 m_tMin = std::min(t0, t1);
223 m_tMax = std::max(t0, t1);
224 m_hasTimeWindow =
true;
228 double& y0,
double& z0,
229 double& t0,
double& e0,
230 double& x1,
double& y1,
231 double& z1,
double& t1,
232 double& e1,
int& status)
const {
233 if (i >= m_endpointsElectrons.size()) {
234 std::cerr << m_className <<
"::GetElectronEndpoint: Index out of range.\n";
235 x0 = y0 = z0 = t0 = e0 = 0.;
236 x1 = y1 = z1 = t1 = e1 = 0.;
241 x0 = m_endpointsElectrons[i].x0;
242 y0 = m_endpointsElectrons[i].y0;
243 z0 = m_endpointsElectrons[i].z0;
244 t0 = m_endpointsElectrons[i].t0;
245 e0 = m_endpointsElectrons[i].e0;
246 x1 = m_endpointsElectrons[i].x;
247 y1 = m_endpointsElectrons[i].y;
248 z1 = m_endpointsElectrons[i].z;
249 t1 = m_endpointsElectrons[i].t;
250 e1 = m_endpointsElectrons[i].energy;
251 status = m_endpointsElectrons[i].status;
255 const unsigned int i,
double& x0,
double& y0,
double& z0,
double& t0,
256 double& e0,
double& x1,
double& y1,
double& z1,
double& t1,
double& e1,
257 double& dx1,
double& dy1,
double& dz1,
int& status)
const {
258 if (i >= m_endpointsElectrons.size()) {
259 std::cerr << m_className <<
"::GetElectronEndpoint: Index out of range.\n";
260 x0 = y0 = z0 = t0 = e0 = 0.;
261 x1 = y1 = z1 = t1 = e1 = 0.;
262 dx1 = dy1 = dz1 = 0.;
267 x0 = m_endpointsElectrons[i].x0;
268 y0 = m_endpointsElectrons[i].y0;
269 z0 = m_endpointsElectrons[i].z0;
270 t0 = m_endpointsElectrons[i].t0;
271 e0 = m_endpointsElectrons[i].e0;
272 x1 = m_endpointsElectrons[i].x;
273 y1 = m_endpointsElectrons[i].y;
274 z1 = m_endpointsElectrons[i].z;
275 t1 = m_endpointsElectrons[i].t;
276 e1 = m_endpointsElectrons[i].energy;
277 dx1 = m_endpointsElectrons[i].kx;
278 dy1 = m_endpointsElectrons[i].ky;
279 dz1 = m_endpointsElectrons[i].kz;
280 status = m_endpointsElectrons[i].status;
284 double& y0,
double& z0,
double& t0,
285 double& e0,
double& x1,
double& y1,
286 double& z1,
double& t1,
double& e1,
288 if (i >= m_endpointsHoles.size()) {
289 std::cerr << m_className <<
"::GetHoleEndpoint: Index out of range.\n";
290 x0 = y0 = z0 = t0 = e0 = 0.;
291 x1 = y1 = z1 = t1 = e1 = 0.;
296 x0 = m_endpointsHoles[i].x0;
297 y0 = m_endpointsHoles[i].y0;
298 z0 = m_endpointsHoles[i].z0;
299 t0 = m_endpointsHoles[i].t0;
300 e0 = m_endpointsHoles[i].e0;
301 x1 = m_endpointsHoles[i].x;
302 y1 = m_endpointsHoles[i].y;
303 z1 = m_endpointsHoles[i].z;
304 t1 = m_endpointsHoles[i].t;
305 e1 = m_endpointsHoles[i].energy;
306 status = m_endpointsHoles[i].status;
310 const unsigned int i)
const {
311 if (i >= m_endpointsElectrons.size()) {
312 std::cerr << m_className <<
"::GetNumberOfElectronDriftLinePoints:\n";
313 std::cerr <<
" Endpoint index (" << i <<
") out of range.\n";
317 if (!m_useDriftLines)
return 2;
319 return m_endpointsElectrons[i].driftLine.size() + 2;
323 const unsigned int i)
const {
324 if (i >= m_endpointsHoles.size()) {
325 std::cerr << m_className <<
"::GetNumberOfHoleDriftLinePoints:\n";
326 std::cerr <<
" Endpoint index (" << i <<
") out of range.\n";
330 if (!m_useDriftLines)
return 2;
332 return m_endpointsHoles[i].driftLine.size() + 2;
336 double& x,
double& y,
double& z,
double& t,
const int ip,
337 const unsigned int iel)
const {
338 if (iel >= m_endpointsElectrons.size()) {
339 std::cerr << m_className <<
"::GetElectronDriftLinePoint:\n";
340 std::cerr <<
" Endpoint index (" << iel <<
") out of range.\n";
345 x = m_endpointsElectrons[iel].x0;
346 y = m_endpointsElectrons[iel].y0;
347 z = m_endpointsElectrons[iel].z0;
348 t = m_endpointsElectrons[iel].t0;
352 const int np = m_endpointsElectrons[iel].driftLine.size();
354 x = m_endpointsElectrons[iel].x;
355 y = m_endpointsElectrons[iel].y;
356 z = m_endpointsElectrons[iel].z;
357 t = m_endpointsElectrons[iel].t;
361 x = m_endpointsElectrons[iel].driftLine[ip - 1].x;
362 y = m_endpointsElectrons[iel].driftLine[ip - 1].y;
363 z = m_endpointsElectrons[iel].driftLine[ip - 1].z;
364 t = m_endpointsElectrons[iel].driftLine[ip - 1].t;
368 double& z,
double& t,
370 const unsigned int ih)
const {
371 if (ih >= m_endpointsHoles.size()) {
372 std::cerr << m_className <<
"::GetHoleDriftLinePoint:\n";
373 std::cerr <<
" Endpoint index (" << ih <<
") out of range.\n";
378 x = m_endpointsHoles[ih].x0;
379 y = m_endpointsHoles[ih].y0;
380 z = m_endpointsHoles[ih].z0;
381 t = m_endpointsHoles[ih].t0;
385 const int np = m_endpointsHoles[ih].driftLine.size();
387 x = m_endpointsHoles[ih].x;
388 y = m_endpointsHoles[ih].y;
389 z = m_endpointsHoles[ih].z;
390 t = m_endpointsHoles[ih].t;
394 x = m_endpointsHoles[ih].driftLine[ip - 1].x;
395 y = m_endpointsHoles[ih].driftLine[ip - 1].y;
396 z = m_endpointsHoles[ih].driftLine[ip - 1].z;
397 t = m_endpointsHoles[ih].driftLine[ip - 1].t;
401 double& x0,
double& y0,
double& z0,
402 double& t0,
double& x1,
double& y1,
403 double& z1,
double& t1,
405 if (i >= m_photons.size()) {
406 std::cerr << m_className <<
"::GetPhoton: Index out of range.\n";
410 x0 = m_photons[i].x0;
411 x1 = m_photons[i].x1;
412 y0 = m_photons[i].y0;
413 y1 = m_photons[i].y1;
414 z0 = m_photons[i].z0;
415 z1 = m_photons[i].z1;
416 t0 = m_photons[i].t0;
417 t1 = m_photons[i].t1;
418 status = m_photons[i].status;
419 e = m_photons[i].energy;
423 void (*f)(
double x,
double y,
double z,
double t,
double e,
double dx,
424 double dy,
double dz,
bool hole)) {
426 std::cerr << m_className <<
"::SetUserHandleStep: Null pointer.\n";
429 m_userHandleStep = f;
433 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m,
434 double e0,
double e1,
double dx0,
double dy0,
double dz0,
435 double dx1,
double dy1,
double dz1)) {
436 m_userHandleCollision = f;
440 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
441 m_userHandleAttachment = f;
445 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
446 m_userHandleInelastic = f;
450 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
451 m_userHandleIonisation = f;
455 const double z0,
const double t0,
456 const double e0,
const double dx0,
457 const double dy0,
const double dz0) {
459 m_endpointsElectrons.clear();
460 m_endpointsHoles.clear();
464 m_nElectrons = m_nHoles = m_nIons = 0;
466 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0,
false,
false);
470 const double z0,
const double t0,
471 const double e0,
const double dx0,
475 m_endpointsElectrons.clear();
476 m_endpointsHoles.clear();
480 m_nElectrons = m_nHoles = m_nIons = 0;
482 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0,
true,
false);
485bool AvalancheMicroscopic::TransportElectron(
const double x0,
const double y0,
486 const double z0,
const double t0,
487 double e0,
const double dx0,
488 const double dy0,
const double dz0,
489 const bool aval,
bool hole0) {
490 const std::string hdr = m_className +
"::TransportElectron: ";
493 std::cerr << hdr <<
"Sensor is not defined.\n";
498 Medium* medium =
nullptr;
499 if (!m_sensor->
GetMedium(x0, y0, z0, medium) || !medium) {
500 std::cerr << hdr <<
"No medium at initial position.\n";
505 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
506 std::cerr << hdr <<
"Medium does not have cross-section data.\n";
511 bool useBandStructure =
512 medium->IsSemiconductor() && m_useBandStructureDefault;
514 std::cout << hdr <<
"Start drifting in medium " << medium->GetName()
519 int id = medium->GetId();
522 const double c1 = SpeedOfLight *
sqrt(2. / ElectronMass);
523 const double c2 = c1 * c1 / 4.;
526 e0 = std::max(e0, Small);
528 std::vector<Electron> stackOld;
529 std::vector<Electron> stackNew;
530 stackOld.reserve(10000);
531 stackNew.reserve(1000);
532 std::vector<std::pair<double, double> > stackPhotons;
533 std::vector<std::pair<int, double> > secondaries;
536 if (useBandStructure) {
540 double kx = 0., ky = 0., kz = 0.;
541 medium->GetElectronMomentum(e0, kx, ky, kz, band);
542 AddToStack(x0, y0, z0, t0, e0, kx, ky, kz, band, hole0, stackOld);
548 const double kmag = Mag(kx, ky, kz);
549 if (
fabs(kmag) < Small) {
558 AddToStack(x0, y0, z0, t0, e0, kx, ky, kz, 0, hole0, stackOld);
567 double fLim = medium->GetElectronNullCollisionRate(stackOld.front().band);
569 std::cerr << hdr <<
"Got null-collision rate <= 0.\n";
572 double fInv = 1. / fLim;
576 stackOld.erase(std::remove_if(stackOld.begin(), stackOld.end(), IsInactive),
579 if (aval && m_sizeCut > 0) {
581 if (stackOld.size() > m_sizeCut) {
583 }
else if (stackOld.size() + stackNew.size() > m_sizeCut) {
584 stackNew.resize(m_sizeCut - stackOld.size());
587 stackOld.insert(stackOld.end(), std::make_move_iterator(stackNew.begin()),
588 std::make_move_iterator(stackNew.end()));
591 if (stackOld.empty())
break;
593 for (
auto it = stackOld.begin(), end = stackOld.end(); it != end; ++it) {
599 double en = (*it).energy;
600 int band = (*it).band;
601 double kx = (*it).kx;
602 double ky = (*it).ky;
603 double kz = (*it).kz;
604 bool hole = (*it).hole;
609 unsigned int nCollTemp = 0;
612 double ex = 0., ey = 0., ez = 0.;
614 m_sensor->
ElectricField(x, y, z, ex, ey, ez, medium, status);
622 const std::string eh = hole ?
"hole " :
"electron ";
623 std::cout << hdr +
"Drifting " + eh << it - stackOld.begin() <<
".\n"
624 <<
" Field [V/cm] at (" <<
x <<
", " <<
y <<
", " <<
z
625 <<
"): " << ex <<
", " << ey <<
", " << ez
626 <<
"\n Status: " << status <<
"\n";
627 if (medium) std::cout <<
" Medium: " << medium->GetName() <<
"\n";
632 Update(it, x, y, z, t, en, kx, ky, kz, band);
633 (*it).status = StatusLeftDriftMedium;
634 AddToEndPoints(*it, hole);
635 if (m_debug) PrintStatus(hdr,
"left the drift medium", x, y, z, hole);
640 double bx = 0., by = 0., bz = 0.;
645 std::array<std::array<double, 3>, 3> rot;
648 const double scale = hole ? Tesla2Internal : -Tesla2Internal;
652 const double bmag = Mag(bx, by, bz);
655 RotationMatrix(bx, by, bz, bmag, ex, ey, ez, rot);
657 omega = OmegaCyclotronOverB *
bmag;
659 ToLocal(rot, ex, ey, ez, ex, ey, ez);
660 ezovb =
bmag > Small ? ez /
bmag : 0.;
665 bool isNullCollision =
false;
668 if (en < m_deltaCut) {
669 Update(it, x, y, z, t, en, kx, ky, kz, band);
670 (*it).status = StatusBelowTransportCut;
671 AddToEndPoints(*it, hole);
673 std::cout << hdr <<
"Kinetic energy (" << en
674 <<
") below transport cut.\n";
681 if (hole && m_histHoleEnergy) {
682 m_histHoleEnergy->Fill(en);
683 }
else if (!hole && m_histElectronEnergy) {
684 m_histElectronEnergy->Fill(en);
688 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
689 Update(it, x, y, z, t, en, kx, ky, kz, band);
690 (*it).status = StatusOutsideTimeWindow;
691 AddToEndPoints(*it, hole);
692 if (m_debug) PrintStatus(hdr,
"left the time window", x, y, z, hole);
697 if (medium->GetId() !=
id) {
699 if (!medium->IsMicroscopic()) {
701 Update(it, x, y, z, t, en, kx, ky, kz, band);
702 (*it).status = StatusLeftDriftMedium;
703 AddToEndPoints(*it, hole);
706 std::cout << hdr <<
"\n Medium at " <<
x <<
", " <<
y <<
", "
707 <<
z <<
" does not have cross-section data.\n";
711 id = medium->GetId();
713 (medium->IsSemiconductor() && m_useBandStructureDefault);
715 fLim = medium->GetElectronNullCollisionRate(band);
717 std::cerr << hdr <<
"Got null-collision rate <= 0.\n";
723 double a1 = 0., a2 = 0.;
725 double vx = 0., vy = 0., vz = 0.;
728 const double vmag = c1 *
sqrt(en);
729 ToLocal(rot, vmag * kx, vmag * ky, vmag * kz, vx, vy, vz);
738 }
else if (useBandStructure) {
739 en = medium->GetElectronEnergy(kx, ky, kz, vx, vy, vz, band);
743 const double vmag = c1 *
sqrt(en);
747 a1 = vx * ex + vy * ey + vz * ez;
748 a2 = c2 * (ex * ex + ey * ey + ez * ez);
751 if (m_userHandleStep) {
752 m_userHandleStep(x, y, z, t, en, kx, ky, kz, hole);
760 double cphi = 1., sphi = 0.;
761 double a3 = 0., a4 = 0.;
765 dt += -log(r) * fInv;
768 en1 = en + (a1 + a2 * dt) * dt;
770 cphi =
cos(omega * dt);
771 sphi =
sin(omega * dt);
773 a4 = (1. - cphi) / omega;
774 en1 += ez * (vz * a3 - vy * a4);
776 }
else if (useBandStructure) {
777 const double cdt = dt * SpeedOfLight;
778 const double kx1 = kx + ex * cdt;
779 const double ky1 = ky + ey * cdt;
780 const double kz1 = kz + ez * cdt;
781 double vx1 = 0., vy1 = 0., vz1 = 0.;
782 en1 = medium->GetElectronEnergy(kx1, ky1, kz1,
783 vx1, vy1, vz1, band);
785 en1 = en + (a1 + a2 * dt) * dt;
787 en1 = std::max(en1, Small);
789 double fReal = medium->GetElectronCollisionRate(en1, band);
791 std::cerr << hdr <<
"Got collision rate <= 0 at " << en1
792 <<
" eV (band " << band <<
").\n";
799 std::cerr << hdr <<
"Increasing null-collision rate by 5%.\n";
800 if (useBandStructure) std::cerr <<
" Band " << band <<
"\n";
807 if (m_useNullCollisionSteps) {
808 isNullCollision =
true;
819 double kx1 = 0., ky1 = 0., kz1 = 0.;
820 double dx = 0., dy = 0., dz = 0.;
823 double vx1 = vx + 2. * c2 * ex * dt;
824 double vy1 = vy * cphi + vz * sphi + ezovb;
825 double vz1 = vz * cphi - vy * sphi;
826 if (omega < Small) vz1 += 2. * c2 * ez * dt;
828 ToGlobal(rot, vx1, vy1, vz1, kx1, ky1, kz1);
829 const double scale = 1. / Mag(kx1, ky1, kz1);
834 dx = vx * dt + c2 * ex * dt * dt;
836 dy = vy * a3 + vz * a4 + ezovb * dt;
837 dz = vz * a3 - vy * a4;
840 dz = vz * dt + c2 * ez * dt * dt;
843 ToGlobal(rot, dx, dy, dz, dx, dy, dz);
844 }
else if (useBandStructure) {
846 const double cdt = dt * SpeedOfLight;
850 double vx1 = 0., vy1 = 0, vz1 = 0.;
851 en1 = medium->GetElectronEnergy(kx1, ky1, kz1,
852 vx1, vy1, vz1, band);
853 dx = 0.5 * (vx + vx1) * dt;
854 dy = 0.5 * (vy + vy1) * dt;
855 dz = 0.5 * (vz + vz1) * dt;
858 const double b1 =
sqrt(en / en1);
859 const double b2 = 0.5 * c1 * dt /
sqrt(en1);
860 kx1 = kx * b1 + ex * b2;
861 ky1 = ky * b1 + ey * b2;
862 kz1 = kz * b1 + ez * b2;
865 const double b3 = dt * dt * c2;
866 dx = vx * dt + ex * b3;
867 dy = vy * dt + ey * b3;
868 dz = vz * dt + ez * b3;
875 m_sensor->
ElectricField(x1, y1, z1, ex, ey, ez, medium, status);
882 if (status != 0 || !m_sensor->
IsInArea(x1, y1, z1)) {
885 Terminate(x, y, z, t, x1, y1, z1, t1);
887 const int q = hole ? 1 : -1;
888 m_sensor->
AddSignal(q, t, t1, x, y, z, x1, y1, z1,
889 m_integrateWeightingField,
890 m_useWeightingPotential);
892 Update(it, x1, y1, z1, t1, en, kx1, ky1, kz1, band);
894 (*it).status = StatusLeftDriftMedium;
896 PrintStatus(hdr,
"left the drift medium", x1, y1, z1, hole);
898 (*it).status = StatusLeftDriftArea;
900 PrintStatus(hdr,
"left the drift area", x1, y1, z1, hole);
902 AddToEndPoints(*it, hole);
908 double xc =
x, yc =
y, zc =
z;
911 xc, yc, zc,
false, rc)) {
912 const double dc = Mag(xc - x, yc - y, zc - z);
913 const double tc = t + dt * dc / Mag(dx, dy, dz);
916 const int q = hole ? 1 : -1;
917 m_sensor->
AddSignal(q, t, tc, x, y, z, xc, yc, zc,
918 m_integrateWeightingField,
919 m_useWeightingPotential);
921 Update(it, xc, yc, zc, tc, en, kx1, ky1, kz1, band);
922 (*it).status = StatusLeftDriftMedium;
923 AddToEndPoints(*it, hole);
925 if (m_debug) PrintStatus(hdr,
"hit a wire", x, y, z, hole);
931 const int q = hole ? 1 : -1;
932 m_sensor->
AddSignal(q, t, t + dt, x, y, z, x1, y1, z1,
933 m_integrateWeightingField,
934 m_useWeightingPotential);
946 const double scale = hole ? Tesla2Internal : -Tesla2Internal;
950 const double bmag = Mag(bx, by, bz);
952 RotationMatrix(bx, by, bz, bmag, ex, ey, ez, rot);
953 omega = OmegaCyclotronOverB *
bmag;
955 ToLocal(rot, ex, ey, ez, ex, ey, ez);
956 ezovb =
bmag > Small ? ez /
bmag : 0.;
959 if (isNullCollision) {
971 medium->GetElectronCollision(en1, cstype, level, en, kx1,
972 ky1, kz1, secondaries, ndxc, band);
975 if (m_histDistance && !m_distanceHistogramType.empty()) {
976 for (
const auto& htype : m_distanceHistogramType) {
977 if (htype != cstype)
continue;
979 std::cout << m_className <<
"::TransportElectron: Collision type "
980 << cstype <<
". Fill distance histogram.\n";
983 switch (m_distanceOption) {
985 m_histDistance->Fill((*it).xLast - x);
988 m_histDistance->Fill((*it).yLast - y);
991 m_histDistance->Fill((*it).zLast - z);
994 m_histDistance->Fill(Mag((*it).xLast - x,
1006 if (m_userHandleCollision) {
1007 m_userHandleCollision(x, y, z, t, cstype, level, medium, en1,
1008 en, kx, ky, kz, kx1, ky1, kz1);
1012 case ElectronCollisionTypeElastic:
1015 case ElectronCollisionTypeIonisation:
1016 if (m_viewer && m_plotIonisations) {
1019 if (m_userHandleIonisation) {
1020 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
1022 for (
const auto& secondary : secondaries) {
1023 if (secondary.first == IonProdTypeElectron) {
1024 const double esec = std::max(secondary.second, Small);
1025 if (m_histSecondary) m_histSecondary->Fill(esec);
1028 if (!aval)
continue;
1030 if (useBandStructure) {
1031 double kxs = 0., kys = 0., kzs = 0.;
1033 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1034 AddToStack(x, y, z, t, esec, kxs, kys, kzs, bs,
false,
1037 AddToStack(x, y, z, t, esec,
false, stackNew);
1039 }
else if (secondary.first == IonProdTypeHole) {
1040 const double esec = std::max(secondary.second, Small);
1043 if (!aval)
continue;
1045 if (useBandStructure) {
1046 double kxs = 0., kys = 0., kzs = 0.;
1048 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1049 AddToStack(x, y, z, t, esec, kxs, kys, kzs, bs,
true,
1052 AddToStack(x, y, z, t, esec,
true, stackNew);
1054 }
else if (secondary.first == IonProdTypeIon) {
1058 secondaries.clear();
1059 if (m_debug) PrintStatus(hdr,
"ionised", x, y, z, hole);
1062 case ElectronCollisionTypeAttachment:
1063 if (m_viewer && m_plotAttachments) {
1066 if (m_userHandleAttachment) {
1067 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
1069 Update(it, x, y, z, t, en, kx1, ky1, kz1, band);
1070 (*it).status = StatusAttached;
1072 m_endpointsHoles.push_back(*it);
1075 m_endpointsElectrons.push_back(*it);
1081 case ElectronCollisionTypeInelastic:
1082 if (m_userHandleInelastic) {
1083 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1087 case ElectronCollisionTypeExcitation:
1088 if (m_viewer && m_plotExcitations) {
1091 if (m_userHandleInelastic) {
1092 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1094 if (ndxc <= 0)
break;
1096 stackPhotons.clear();
1097 for (
int j = ndxc; j--;) {
1098 double tdx = 0., sdx = 0., edx = 0.;
1100 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, edx)) {
1101 std::cerr << hdr <<
"Cannot retrieve deexcitation product " << j
1102 <<
"/" << ndxc <<
".\n";
1106 if (typedx == DxcProdTypeElectron) {
1108 double xp =
x, yp =
y, zp =
z;
1111 double dxp = 0., dyp = 0., dzp = 0.;
1118 Medium* med =
nullptr;
1119 double fx = 0., fy = 0., fz = 0.;
1120 m_sensor->
ElectricField(xp, yp, zp, fx, fy, fz, med, status);
1122 if (status != 0 || !m_sensor->
IsInArea(xp, yp, zp))
continue;
1124 if (m_sensor->
IsWireCrossed(x, y, z, xp, yp, zp, xc, yc, zc,
1131 if (m_userHandleIonisation) {
1132 m_userHandleIonisation(xp, yp, zp, t, cstype, level, medium);
1134 if (!aval)
continue;
1136 AddToStack(xp, yp, zp, t + tdx, std::max(edx, Small),
false,
1138 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1141 stackPhotons.emplace_back(std::make_pair(t + tdx, edx));
1147 for (
const auto& ph : stackPhotons) {
1148 TransportPhoton(x, y, z, ph.first, ph.second, stackNew);
1153 case ElectronCollisionTypeSuperelastic:
1156 case ElectronCollisionTypeVirtual:
1159 case ElectronCollisionTypeAcousticPhonon:
1162 case ElectronCollisionTypeOpticalPhonon:
1165 case ElectronCollisionTypeIntervalleyG:
1166 case ElectronCollisionTypeIntervalleyF:
1167 case ElectronCollisionTypeInterbandXL:
1168 case ElectronCollisionTypeInterbandXG:
1169 case ElectronCollisionTypeInterbandLG:
1172 case ElectronCollisionTypeImpurity:
1175 std::cerr << hdr <<
"Unknown collision type.\n";
1181 if (!ok || nCollTemp > m_nCollSkip ||
1182 cstype == ElectronCollisionTypeIonisation ||
1183 (m_plotExcitations && cstype == ElectronCollisionTypeExcitation) ||
1184 (m_plotAttachments && cstype == ElectronCollisionTypeAttachment)) {
1194 if (!useBandStructure) {
1196 const double scale = 1. / Mag(kx, ky, kz);
1202 Update(it, x, y, z, t, en, kx, ky, kz, band);
1204 if (m_useDriftLines) {
1210 (*it).driftLine.push_back(std::move(newPoint));
1216 if (m_doInducedCharge) {
1217 for (
const auto& ep : m_endpointsElectrons) {
1220 for (
const auto& ep : m_endpointsHoles) {
1228 const unsigned int nElectronEndpoints = m_endpointsElectrons.size();
1229 for (
unsigned int i = 0; i < nElectronEndpoints; ++i) {
1232 if (np <= 0)
continue;
1233 const Electron& p = m_endpointsElectrons[i];
1235 for (
int jP = np; jP--;) {
1236 double x = 0.,
y = 0.,
z = 0., t = 0.;
1242 const unsigned int nHoleEndpoints = m_endpointsHoles.size();
1243 for (
unsigned int i = 0; i < nHoleEndpoints; ++i) {
1246 if (np <= 0)
continue;
1247 const Electron& p = m_endpointsHoles[i];
1249 for (
int jP = np; jP--;) {
1250 double x = 0.,
y = 0.,
z = 0., t = 0.;
1256 for (
const auto& ph : m_photons) {
1257 m_viewer->
NewPhotonTrack(ph.x0, ph.y0, ph.z0, ph.x1, ph.y1, ph.z1);
1263void AvalancheMicroscopic::TransportPhoton(
const double x0,
const double y0,
1264 const double z0,
const double t0,
1266 std::vector<Electron>& stack) {
1269 std::cerr << m_className <<
"::TransportPhoton: Sensor is not defined.\n";
1275 if (!m_sensor->
GetMedium(x0, y0, z0, medium)) {
1276 std::cerr << m_className <<
"::TransportPhoton:\n"
1277 <<
" No medium at initial position.\n";
1282 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
1283 std::cerr << m_className <<
"::TransportPhoton:\n"
1284 <<
" Medium at initial position does not provide "
1285 <<
" microscopic tracking data.\n";
1290 int id = medium->GetId();
1293 double x = x0,
y = y0,
z = z0;
1296 double dx = 0., dy = 0., dz = 0.;
1302 double f = medium->GetPhotonCollisionRate(e);
1303 if (f <= 0.)
return;
1313 if (!m_sensor->
GetMedium(x, y, z, medium) || medium->GetId() !=
id) {
1322 double delta = Mag(dx, dy, dz);
1329 double xM =
x, yM =
y, zM =
z;
1330 while (delta > BoundaryDistance) {
1333 xM =
x + delta * dx;
1334 yM =
y + delta * dy;
1335 zM =
z + delta * dz;
1337 if (m_sensor->
GetMedium(xM, yM, zM, medium) && medium->GetId() ==
id) {
1351 newPhoton.energy = e0;
1352 newPhoton.status = StatusLeftDriftMedium;
1353 m_photons.push_back(std::move(newPhoton));
1362 if (!medium->GetPhotonCollision(e, type, level, e1, ctheta, nsec, esec))
1365 if (type == PhotonCollisionTypeIonisation) {
1367 if (m_sizeCut == 0 || stack.size() < m_sizeCut) {
1368 AddToStack(x, y, z, t, std::max(esec, Small),
false, stack);
1373 }
else if (type == PhotonCollisionTypeExcitation) {
1377 std::vector<double> tPhotons;
1378 std::vector<double> ePhotons;
1379 for (
int j = nsec; j--;) {
1380 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, esec))
continue;
1381 if (typedx == DxcProdTypeElectron) {
1383 AddToStack(x, y, z, t + tdx, std::max(esec, Small),
false, stack);
1387 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1388 esec > m_gammaCut) {
1390 tPhotons.push_back(t + tdx);
1391 ePhotons.push_back(esec);
1395 const int nSizePhotons = tPhotons.size();
1396 for (
int k = nSizePhotons; k--;) {
1397 TransportPhoton(x, y, z, tPhotons[k], ePhotons[k], stack);
1408 newPhoton.energy = e0;
1409 newPhoton.status = -2;
1410 m_photons.push_back(std::move(newPhoton));
1413void AvalancheMicroscopic::Update(std::vector<Electron>::iterator it,
1414 const double x,
const double y,
1415 const double z,
const double t,
1416 const double energy,
const double kx,
1417 const double ky,
const double kz,
1423 (*it).energy = energy;
1430void AvalancheMicroscopic::AddToStack(
const double x,
const double y,
1431 const double z,
const double t,
1432 const double energy,
const bool hole,
1433 std::vector<Electron>& container)
const {
1435 double dx = 0., dy = 0., dz = 1.;
1437 AddToStack(x, y, z, t, energy, dx, dy, dz, 0, hole, container);
1440void AvalancheMicroscopic::AddToStack(
const double x,
const double y,
1441 const double z,
const double t,
1442 const double energy,
const double dx,
1443 const double dy,
const double dz,
1444 const int band,
const bool hole,
1445 std::vector<Electron>& container)
const {
1447 electron.status = 0;
1448 electron.hole = hole;
1453 electron.e0 = energy;
1458 electron.energy = energy;
1462 electron.band = band;
1467 electron.driftLine.reserve(1000);
1468 container.push_back(std::move(electron));
1471void AvalancheMicroscopic::Terminate(
double x0,
double y0,
double z0,
double t0,
1472 double& x1,
double& y1,
double& z1,
1474 const double dx = x1 - x0;
1475 const double dy = y1 - y0;
1476 const double dz = z1 - z0;
1477 double d = Mag(dx, dy, dz);
1478 while (d > BoundaryDistance) {
1480 const double xm = 0.5 * (x0 + x1);
1481 const double ym = 0.5 * (y0 + y1);
1482 const double zm = 0.5 * (z0 + z1);
1483 const double tm = 0.5 * (t0 + t1);
1485 double ex = 0., ey = 0., ez = 0.;
1486 Medium* medium =
nullptr;
1488 m_sensor->
ElectricField(xm, ym, zm, ex, ey, ez, medium, status);
1489 if (status == 0 && m_sensor->
IsInArea(xm, ym, zm)) {
void EnableDistanceHistogramming(const int type)
Fill distance distribution histograms for a given collision type.
void SetDistanceHistogram(TH1 *histo, const char opt='r')
void SetUserHandleStep(void(*f)(double x, double y, double z, double t, double e, double dx, double dy, double dz, bool hole))
Set a user handling procedure. This function is called at every step.
void EnableHoleEnergyHistogramming(TH1 *histo)
Fill a histogram with the hole energy distribution.
void SetUserHandleCollision(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m, double e0, double e1, double dx0, double dy0, double dz0, double dx1, double dy1, double dz1))
Set a user handling procedure, to be called at every (real) collision.
void EnablePlotting(ViewDrift *view)
Switch on drift line plotting.
void EnableDriftLines(const bool on=true)
Switch on storage of drift lines (default: off).
void SetUserHandleIonisation(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
bool DriftElectron(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0=0., const double dy0=0., const double dz0=0.)
unsigned int GetNumberOfElectronDriftLinePoints(const unsigned int i=0) const
AvalancheMicroscopic()
Constructor.
void SetUserHandleAttachment(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
Set a user handling procedure, to be called at every attachment.
void SetSensor(Sensor *sensor)
Set the sensor.
void GetHoleDriftLinePoint(double &x, double &y, double &z, double &t, const int ip, const unsigned int iel=0) const
void GetElectronEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &e0, double &x1, double &y1, double &z1, double &t1, double &e1, int &status) const
void GetHoleEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &e0, double &x1, double &y1, double &z1, double &t1, double &e1, int &status) const
void GetElectronDriftLinePoint(double &x, double &y, double &z, double &t, const int ip, const unsigned int iel=0) const
void EnableElectronEnergyHistogramming(TH1 *histo)
Fill a histogram with the electron energy distribution.
void DisableDistanceHistogramming()
Stop filling distance distribution histograms.
void GetPhoton(const unsigned int i, double &e, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
void EnableSecondaryEnergyHistogramming(TH1 *histo)
Fill histograms of the energy of electrons emitted in ionising collisions.
bool AvalancheElectron(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0=0., const double dy0=0., const double dz0=0.)
Calculate an avalanche initiated by a given electron.
unsigned int GetNumberOfHoleDriftLinePoints(const unsigned int i=0) const
void SetUserHandleInelastic(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
Set a user handling procedure, to be called at every inelastic collision.
void SetTimeWindow(const double t0, const double t1)
Define a time interval (only carriers inside the interval are simulated).
Abstract base class for media.
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Get the magnetic field at (x, y, z).
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&medium, int &status)
Get the drift field and potential at (x, y, z).
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
void AddSignal(const double q, const double t0, const double t1, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const bool integrateWeightingField, const bool useWeightingPotential=false)
Add the signal from a charge-carrier step.
bool IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc)
void AddInducedCharge(const double q, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
Add the induced charge from a charge carrier drift between two points.
Visualize drift lines and tracks.
void NewElectronDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
void AddExcitationMarker(const double x, const double y, const double z)
void AddIonisationMarker(const double x, const double y, const double z)
void SetDriftLinePoint(const unsigned int iL, const unsigned int iP, const double x, const double y, const double z)
void AddAttachmentMarker(const double x, const double y, const double z)
void NewPhotonTrack(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
void NewHoleDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
DoubleAc cos(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)