13void PrintStatus(
const std::string& hdr,
const std::string& status,
14 const double x,
const double y,
const double z,
17 const std::string eh = hole ?
"Hole " :
"Electron ";
18 std::cout << hdr << eh << status <<
" at " << x <<
", " << y <<
", " << z <<
"\n";
32 m_plotExcitations(true),
33 m_plotIonisations(true),
34 m_plotAttachments(true),
35 m_histElectronEnergy(NULL),
36 m_histHoleEnergy(NULL),
38 m_distanceOption(
'r'),
39 m_histSecondary(NULL),
41 m_useInducedCharge(false),
42 m_useDriftLines(false),
44 m_useBandStructureDefault(true),
45 m_useNullCollisionSteps(false),
64 m_hasTimeWindow(false),
67 m_hasUserHandleStep(false),
68 m_hasUserHandleAttachment(false),
69 m_hasUserHandleInelastic(false),
70 m_hasUserHandleIonisation(false),
72 m_userHandleAttachment(0),
73 m_userHandleInelastic(0),
74 m_userHandleIonisation(0),
77 m_className =
"AvalancheMicroscopic";
79 m_endpointsElectrons.reserve(10000);
80 m_endpointsHoles.reserve(10000);
81 m_photons.reserve(1000);
88 std::cerr << m_className <<
"::SetSensor:\n Null pointer.\n";
97 std::cerr << m_className <<
"::EnablePlotting:\n Null pointer.\n";
102 m_usePlotting =
true;
103 if (!m_useDriftLines) {
104 std::cout << m_className <<
"::EnablePlotting:\n"
105 <<
" Enabling storage of drift line.\n";
113 m_usePlotting =
false;
119 std::cerr << m_className <<
"::EnableElectronEnergyHistogramming:\n"
120 <<
" Null pointer.\n";
124 m_histElectronEnergy = histo;
130 std::cerr << m_className <<
"::EnableHoleEnergyHistogramming:\n"
131 <<
" Null pointer.\n";
135 m_histHoleEnergy = histo;
141 std::cerr << m_className <<
"::SetDistanceHistogram:\n Null pointer.\n";
145 m_histDistance = histo;
147 if (opt ==
'x' || opt ==
'y' || opt ==
'z' || opt ==
'r') {
148 m_distanceOption = opt;
150 std::cerr << m_className <<
"::SetDistanceHistogram:";
151 std::cerr <<
" Unknown option " << opt <<
".\n";
152 std::cerr <<
" Valid options are x, y, z, r.\n";
153 std::cerr <<
" Using default value (r).\n";
154 m_distanceOption =
'r';
157 if (m_distanceHistogramType.empty()) {
158 std::cout << m_className <<
"::SetDistanceHistogram:\n";
159 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";
188 if (m_distanceHistogramType.empty()) {
189 std::cerr << m_className <<
"::DisableDistanceHistogramming:\n";
190 std::cerr <<
" Collision type " << type <<
" is not histogrammed.\n";
193 const unsigned int nDistanceHistogramTypes = m_distanceHistogramType.size();
194 for (
unsigned int i = 0; i < nDistanceHistogramTypes; ++i) {
195 if (m_distanceHistogramType[i] != type)
continue;
196 m_distanceHistogramType.erase(m_distanceHistogramType.begin() + i);
197 std::cout <<
" Histogramming of collision type " << type
202 std::cerr << m_className <<
"::DisableDistanceHistogramming:\n";
203 std::cerr <<
" Collision type " << type <<
" is not histogrammed.\n";
208 m_histDistance = NULL;
209 m_distanceHistogramType.clear();
215 std::cerr << m_className <<
"::EnableSecondaryEnergyHistogramming:\n"
216 <<
" Null pointer.\n";
220 m_histSecondary = histo;
225 if (fabs(t1 - t0) < Small) {
226 std::cerr << m_className <<
"::SetTimeWindow:\n";
227 std::cerr <<
" Time interval must be greater than zero.\n";
231 m_tMin = std::min(t0, t1);
232 m_tMax = std::max(t0, t1);
233 m_hasTimeWindow =
true;
239 double& y0,
double& z0,
240 double& t0,
double& e0,
241 double& x1,
double& y1,
242 double& z1,
double& t1,
243 double& e1,
int& status)
const {
245 if (i >= m_endpointsElectrons.size()) {
246 std::cerr << m_className <<
"::GetElectronEndpoint:\n";
247 std::cerr <<
" Endpoint index " << i <<
" out of range.\n";
248 x0 = y0 = z0 = t0 = e0 = 0.;
249 x1 = y1 = z1 = t1 = e1 = 0.;
254 x0 = m_endpointsElectrons[i].x0;
255 y0 = m_endpointsElectrons[i].y0;
256 z0 = m_endpointsElectrons[i].z0;
257 t0 = m_endpointsElectrons[i].t0;
258 e0 = m_endpointsElectrons[i].e0;
259 x1 = m_endpointsElectrons[i].x;
260 y1 = m_endpointsElectrons[i].y;
261 z1 = m_endpointsElectrons[i].z;
262 t1 = m_endpointsElectrons[i].t;
263 e1 = m_endpointsElectrons[i].energy;
264 status = m_endpointsElectrons[i].status;
268 const unsigned int i,
double& x0,
double& y0,
double& z0,
double& t0,
double& e0,
269 double& x1,
double& y1,
double& z1,
double& t1,
double& e1,
double& dx1,
270 double& dy1,
double& dz1,
int& status)
const {
272 if (i >= m_endpointsElectrons.size()) {
273 std::cerr << m_className <<
"::GetElectronEndpoint:\n";
274 std::cerr <<
" Endpoint index " << i <<
" out of range.\n";
275 x0 = y0 = z0 = t0 = e0 = 0.;
276 x1 = y1 = z1 = t1 = e1 = 0.;
277 dx1 = dy1 = dz1 = 0.;
282 x0 = m_endpointsElectrons[i].x0;
283 y0 = m_endpointsElectrons[i].y0;
284 z0 = m_endpointsElectrons[i].z0;
285 t0 = m_endpointsElectrons[i].t0;
286 e0 = m_endpointsElectrons[i].e0;
287 x1 = m_endpointsElectrons[i].x;
288 y1 = m_endpointsElectrons[i].y;
289 z1 = m_endpointsElectrons[i].z;
290 t1 = m_endpointsElectrons[i].t;
291 e1 = m_endpointsElectrons[i].energy;
292 dx1 = m_endpointsElectrons[i].kx;
293 dy1 = m_endpointsElectrons[i].ky;
294 dz1 = m_endpointsElectrons[i].kz;
295 status = m_endpointsElectrons[i].status;
299 double& z0,
double& t0,
double& e0,
300 double& x1,
double& y1,
double& z1,
301 double& t1,
double& e1,
304 if (i >= m_endpointsHoles.size()) {
305 std::cerr << m_className <<
"::GetHoleEndpoint:\n";
306 std::cerr <<
" Endpoint index " << i <<
" out of range.\n";
307 x0 = y0 = z0 = t0 = e0 = 0.;
308 x1 = y1 = z1 = t1 = e1 = 0.;
313 x0 = m_endpointsHoles[i].x0;
314 y0 = m_endpointsHoles[i].y0;
315 z0 = m_endpointsHoles[i].z0;
316 t0 = m_endpointsHoles[i].t0;
317 e0 = m_endpointsHoles[i].e0;
318 x1 = m_endpointsHoles[i].x;
319 y1 = m_endpointsHoles[i].y;
320 z1 = m_endpointsHoles[i].z;
321 t1 = m_endpointsHoles[i].t;
322 e1 = m_endpointsHoles[i].energy;
323 status = m_endpointsHoles[i].status;
329 if (i >= m_endpointsElectrons.size()) {
330 std::cerr << m_className <<
"::GetNumberOfElectronDriftLinePoints:\n";
331 std::cerr <<
" Endpoint index (" << i <<
") out of range.\n";
335 if (!m_useDriftLines)
return 2;
337 return m_endpointsElectrons[i].driftLine.size() + 2;
342 if (i >= m_endpointsHoles.size()) {
343 std::cerr << m_className <<
"::GetNumberOfHoleDriftLinePoints:\n";
344 std::cerr <<
" Endpoint index (" << i <<
") out of range.\n";
348 if (!m_useDriftLines)
return 2;
350 return m_endpointsHoles[i].driftLine.size() + 2;
354 double& z,
double& t,
356 const unsigned int iel)
const {
358 if (iel >= m_endpointsElectrons.size()) {
359 std::cerr << m_className <<
"::GetElectronDriftLinePoint:\n";
360 std::cerr <<
" Endpoint index (" << iel <<
") out of range.\n";
365 x = m_endpointsElectrons[iel].x0;
366 y = m_endpointsElectrons[iel].y0;
367 z = m_endpointsElectrons[iel].z0;
368 t = m_endpointsElectrons[iel].t0;
372 const int np = m_endpointsElectrons[iel].driftLine.size();
374 x = m_endpointsElectrons[iel].x;
375 y = m_endpointsElectrons[iel].y;
376 z = m_endpointsElectrons[iel].z;
377 t = m_endpointsElectrons[iel].t;
381 x = m_endpointsElectrons[iel].driftLine[ip - 1].x;
382 y = m_endpointsElectrons[iel].driftLine[ip - 1].y;
383 z = m_endpointsElectrons[iel].driftLine[ip - 1].z;
384 t = m_endpointsElectrons[iel].driftLine[ip - 1].t;
388 double& z,
double& t,
390 const unsigned int ih)
const {
392 if (ih >= m_endpointsHoles.size()) {
393 std::cerr << m_className <<
"::GetHoleDriftLinePoint:\n";
394 std::cerr <<
" Endpoint index (" << ih <<
") out of range.\n";
399 x = m_endpointsHoles[ih].x0;
400 y = m_endpointsHoles[ih].y0;
401 z = m_endpointsHoles[ih].z0;
402 t = m_endpointsHoles[ih].t0;
406 const int np = m_endpointsHoles[ih].driftLine.size();
408 x = m_endpointsHoles[ih].x;
409 y = m_endpointsHoles[ih].y;
410 z = m_endpointsHoles[ih].z;
411 t = m_endpointsHoles[ih].t;
415 x = m_endpointsHoles[ih].driftLine[ip - 1].x;
416 y = m_endpointsHoles[ih].driftLine[ip - 1].y;
417 z = m_endpointsHoles[ih].driftLine[ip - 1].z;
418 t = m_endpointsHoles[ih].driftLine[ip - 1].t;
422 double& y0,
double& z0,
double& t0,
423 double& x1,
double& y1,
double& z1,
424 double& t1,
int& status)
const {
426 if (i >= m_photons.size()) {
427 std::cerr << m_className <<
"::GetPhoton:\n Index out of range.\n";
431 x0 = m_photons[i].x0;
432 x1 = m_photons[i].x1;
433 y0 = m_photons[i].y0;
434 y1 = m_photons[i].y1;
435 z0 = m_photons[i].z0;
436 z1 = m_photons[i].z1;
437 t0 = m_photons[i].t0;
438 t1 = m_photons[i].t1;
439 status = m_photons[i].status;
440 e = m_photons[i].energy;
444 void (*f)(
double x,
double y,
double z,
double t,
double e,
double dx,
445 double dy,
double dz,
bool hole)) {
448 std::cerr << m_className <<
"::SetUserHandleStep:\n";
449 std::cerr <<
" Function pointer is a null pointer.\n";
452 m_userHandleStep = f;
453 m_hasUserHandleStep =
true;
458 m_userHandleStep = 0;
459 m_hasUserHandleStep =
false;
463 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
465 m_userHandleAttachment = f;
466 m_hasUserHandleAttachment =
true;
471 m_userHandleAttachment = 0;
472 m_hasUserHandleAttachment =
false;
476 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
478 m_userHandleInelastic = f;
479 m_hasUserHandleInelastic =
true;
484 m_userHandleInelastic = 0;
485 m_hasUserHandleInelastic =
false;
489 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
491 m_userHandleIonisation = f;
492 m_hasUserHandleIonisation =
true;
497 m_userHandleIonisation = 0;
498 m_hasUserHandleIonisation =
false;
502 const double z0,
const double t0,
503 const double e0,
const double dx0,
504 const double dy0,
const double dz0) {
507 m_endpointsElectrons.clear();
508 m_endpointsHoles.clear();
512 m_nElectrons = m_nHoles = m_nIons = 0;
514 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0,
false,
false);
518 const double z0,
const double t0,
519 const double e0,
const double dx0,
524 m_endpointsElectrons.clear();
525 m_endpointsHoles.clear();
529 m_nElectrons = m_nHoles = m_nIons = 0;
531 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0,
true,
false);
534bool AvalancheMicroscopic::TransportElectron(
const double x0,
const double y0,
535 const double z0,
const double t0,
536 double e0,
const double dx0,
537 const double dy0,
const double dz0,
538 const bool aval,
bool hole0) {
540 const std::string hdr = m_className +
"::TransportElectron: ";
543 std::cerr << hdr <<
"Sensor is not defined.\n";
548 Medium* medium = NULL;
549 if (!m_sensor->
GetMedium(x0, y0, z0, medium) || !medium) {
550 std::cerr << hdr <<
"No medium at initial position.\n";
555 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
556 std::cerr << hdr <<
"Medium does not have cross-section data.\n";
561 bool useBandStructure = medium->IsSemiconductor() && m_useBandStructureDefault;
563 std::cout << hdr <<
"Start drifting in medium "
564 << medium->GetName() <<
".\n";
568 int id = medium->GetId();
571 const double c1 = SpeedOfLight *
sqrt(2. / ElectronMass);
572 const double c2 = c1 * c1 / 4.;
575 double ex = 0., ey = 0., ez = 0.;
576 double bx = 0., by = 0., bz = 0.,
bmag = 0.;
579 double cwt = 1., swt = 0.;
585 double newKx = 0., newKy = 0., newKz = 0.;
586 double newVx = 0., newVy = 0., newVz = 0.;
587 double newEnergy = 0.;
590 double a1 = 0., a2 = 0., a3 = 0., a4 = 0.;
593 e0 = std::max(e0, Small);
595 std::vector<Electron> stackOld;
596 std::vector<Electron> stackNew;
597 stackOld.reserve(10000);
598 stackNew.reserve(1000);
599 std::vector<std::pair<double, double> > stackPhotons;
602 if (useBandStructure) {
606 double kx = 0., ky = 0., kz = 0.;
607 medium->GetElectronMomentum(e0, kx, ky, kz, band);
608 AddToStack(x0, y0, z0, t0, e0, kx, ky, kz, band, hole0, stackOld);
614 const double k =
sqrt(kx * kx + ky * ky + kz * kz);
615 if (
fabs(k) < Small) {
624 AddToStack(x0, y0, z0, t0, e0, kx, ky, kz, 0, hole0, stackOld);
633 double fLim = medium->GetElectronNullCollisionRate(stackOld.front().band);
635 std::cerr << hdr <<
"Got null-collision rate <= 0.\n";
638 double fInv = 1. / fLim;
642 stackOld.erase(std::remove_if(stackOld.begin(), stackOld.end(), IsInactive),
645 if (aval && m_sizeCut > 0) {
647 if (stackOld.size() > m_sizeCut) {
649 }
else if (stackOld.size() + stackNew.size() > m_sizeCut) {
650 stackNew.resize(m_sizeCut - stackOld.size());
653 stackOld.insert(stackOld.end(), stackNew.begin(), stackNew.end());
656 if (stackOld.empty())
break;
658 const std::vector<Electron>::const_iterator end = stackOld.end();
659 std::vector<Electron>::iterator it;
660 for (it = stackOld.begin(); it != end; ++it) {
666 double energy = (*it).energy;
667 int band = (*it).band;
668 double kx = (*it).kx;
669 double ky = (*it).ky;
670 double kz = (*it).kz;
671 bool hole = (*it).hole;
676 unsigned int nCollTemp = 0;
679 m_sensor->
ElectricField(x, y, z, ex, ey, ez, medium, status);
688 const std::string eh = hole ?
"hole " :
"electron ";
689 std::cout << hdr <<
"\n Drifting " << eh << it - stackOld.begin()
690 <<
".\n Field [V/cm] at (" << x <<
", " << y <<
", " << z
691 <<
"): " << ex <<
", " << ey <<
", " << ez <<
"\n Status: "
693 if (medium) std::cout <<
" Medium: " << medium->GetName() <<
"\n";
698 Update(it, x, y, z, t, energy, kx, ky, kz, band);
699 (*it).status = StatusLeftDriftMedium;
700 AddToEndPoints(*it, hole);
701 if (m_debug) PrintStatus(hdr,
"left the drift medium", x, y, z, hole);
708 const double scale = hole ? Tesla2Internal : -Tesla2Internal;
713 bmag =
sqrt(bx * bx + by * by + bz * bz);
714 const double emag2 = ex * ex + ey * ey + ez * ez;
715 bOk = (
bmag > Small && emag2 > Small);
721 bool isNullCollision =
false;
724 if (energy < m_deltaCut) {
725 Update(it, x, y, z, t, energy, kx, ky, kz, band);
726 (*it).status = StatusBelowTransportCut;
727 AddToEndPoints(*it, hole);
729 std::cout << hdr <<
"Kinetic energy (" << energy
730 <<
") below transport cut.\n";
737 if (hole && m_histHoleEnergy) {
738 m_histHoleEnergy->Fill(energy);
739 }
else if (!hole && m_histElectronEnergy) {
740 m_histElectronEnergy->Fill(energy);
744 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
745 Update(it, x, y, z, t, energy, kx, ky, kz, band);
746 (*it).status = StatusOutsideTimeWindow;
747 AddToEndPoints(*it, hole);
748 if (m_debug) PrintStatus(hdr,
"left the time window", x, y, z, hole);
753 if (medium->GetId() !=
id) {
755 if (!medium->IsMicroscopic()) {
757 Update(it, x, y, z, t, energy, kx, ky, kz, band);
758 (*it).status = StatusLeftDriftMedium;
759 AddToEndPoints(*it, hole);
762 std::cout << hdr <<
"\n Medium at "
763 << x <<
", " << y <<
", " << z
764 <<
" does not have cross-section data.\n";
768 id = medium->GetId();
769 useBandStructure = (medium->IsSemiconductor() && m_useBandStructureDefault);
771 fLim = medium->GetElectronNullCollisionRate(band);
773 std::cerr << hdr <<
"Got null-collision rate <= 0.\n";
779 double vx = 0., vy = 0., vz = 0.;
780 if (m_useBfield && bOk) {
782 wb = OmegaCyclotronOverB *
bmag;
784 ComputeRotationMatrix(bx, by, bz, bmag, ex, ey, ez);
785 RotateGlobal2Local(kx, ky, kz);
787 RotateGlobal2Local(ex, ey, ez);
789 const double v = c1 *
sqrt(energy);
797 }
else if (useBandStructure) {
798 energy = medium->GetElectronEnergy(kx, ky, kz, vx, vy, vz, band);
802 const double v = c1 *
sqrt(energy);
807 a1 = vx * ex + vy * ey + vz * ez;
808 a2 = c2 * (ex * ex + ey * ey + ez * ez);
811 if (m_hasUserHandleStep) {
812 m_userHandleStep(x, y, z, t, energy, kx, ky, kz, hole);
820 dt += -log(r) * fInv;
822 if (m_useBfield && bOk) {
825 newEnergy = std::max(energy + (a1 + a2 * dt) * dt +
826 a4 * (a3 * (1. - cwt) + vz * swt),
828 }
else if (useBandStructure) {
829 const double cdt = dt * SpeedOfLight;
830 newEnergy = std::max(
831 medium->GetElectronEnergy(kx + ex * cdt, ky + ey * cdt,
832 kz + ez * cdt, newVx, newVy, newVz, band),
835 newEnergy = std::max(energy + (a1 + a2 * dt) * dt, Small);
838 double fReal = medium->GetElectronCollisionRate(newEnergy, band);
840 std::cerr << hdr <<
"Got collision rate <= 0 at " << newEnergy
841 <<
" eV (band " << band <<
").\n";
848 std::cerr << hdr <<
"Increasing null-collision rate by 5%.\n";
849 if (useBandStructure) std::cerr <<
" Band " << band <<
"\n";
856 if (m_useNullCollisionSteps) {
857 isNullCollision =
true;
868 if (m_useBfield && bOk) {
870 newVx = vx + 2. * c2 * ex * dt;
871 newVy = vz * swt - a3 * cwt + ez /
bmag;
872 newVz = vz * cwt + a3 * swt;
874 const double v =
sqrt(newVx * newVx + newVy * newVy + newVz * newVz);
878 RotateLocal2Global(newKx, newKy, newKz);
881 ky = (vz * (1. - cwt) - a3 * swt) / (
wb * dt) + ez / bmag;
882 kz = (vz * swt + a3 * (1. - cwt)) / (
wb * dt);
886 RotateLocal2Global(vx, vy, vz);
887 }
else if (useBandStructure) {
889 newKx = kx + ex * dt * SpeedOfLight;
890 newKy = ky + ey * dt * SpeedOfLight;
891 newKz = kz + ez * dt * SpeedOfLight;
893 vx = 0.5 * (vx + newVx);
894 vy = 0.5 * (vy + newVy);
895 vz = 0.5 * (vz + newVz);
898 a1 =
sqrt(energy / newEnergy);
899 a2 = 0.5 * c1 * dt /
sqrt(newEnergy);
900 newKx = kx * a1 + ex * a2;
901 newKy = ky * a1 + ey * a2;
902 newKz = kz * a1 + ez * a2;
905 a1 = c1 *
sqrt(energy);
907 vx = kx * a1 + ex * a2;
908 vy = ky * a1 + ey * a2;
909 vz = kz * a1 + ez * a2;
912 double x1 = x + vx * dt;
913 double y1 = y + vy * dt;
914 double z1 = z + vz * dt;
917 m_sensor->
ElectricField(x1, y1, z1, ex, ey, ez, medium, status);
925 if (status != 0 || !m_sensor->
IsInArea(x1, y1, z1)) {
928 Terminate(x, y, z, t, x1, y1, z1, t1);
930 const int q = hole ? 1 : -1;
932 0.5 * (x + x1), 0.5 * (y + y1),
933 0.5 * (z + z1), vx, vy, vz);
935 Update(it, x1, y1, z1, t1, energy, newKx, newKy, newKz, band);
937 (*it).status = StatusLeftDriftMedium;
938 if (m_debug) PrintStatus(hdr,
"left the drift medium", x1, y1, z1, hole);
940 (*it).status = StatusLeftDriftArea;
941 if (m_debug) PrintStatus(hdr,
"left the drift area", x1, y1, z1, hole);
943 AddToEndPoints(*it, hole);
949 double xc = x, yc = y, zc = z;
950 if (m_sensor->
IsWireCrossed(x, y, z, x1, y1, z1, xc, yc, zc)) {
953 const double dx = xc - x;
954 const double dy = yc - y;
955 const double dz = zc - z;
956 dt =
sqrt(dx * dx + dy * dy + dz * dz) /
957 sqrt(vx * vx + vy * vy + vz * vz);
958 const int q = hole ? 1 : -1;
959 m_sensor->
AddSignal(q, t, dt, 0.5 * (x + xc), 0.5 * (y + yc),
960 0.5 * (z + zc), vx, vy, vz);
962 Update(it, xc, yc, zc, t + dt, energy, newKx, newKy, newKz, band);
963 (*it).status = StatusLeftDriftMedium;
964 AddToEndPoints(*it, hole);
966 if (m_debug) PrintStatus(hdr,
"hit a wire", x, y, z, hole);
972 const int q = hole ? 1 : -1;
973 m_sensor->
AddSignal(q, t, dt, 0.5 * (x + x1), 0.5 * (y + y1),
974 0.5 * (z + z1), vx, vy, vz);
986 const double scale = hole ? Tesla2Internal : -Tesla2Internal;
991 bmag =
sqrt(bx * bx + by * by + bz * bz);
992 const double emag2 = ex * ex + ey * ey + ez * ez;
993 bOk = (
bmag > Small && emag2 > Small);
996 if (isNullCollision) {
1009 medium->GetElectronCollision(newEnergy, cstype, level, energy, newKx,
1010 newKy, newKz, nion, ndxc, band);
1014 if (m_histDistance && !m_distanceHistogramType.empty()) {
1015 const int nDistanceHistogramTypes = m_distanceHistogramType.size();
1016 for (
int iType = nDistanceHistogramTypes; iType--;) {
1017 if (m_distanceHistogramType[iType] != cstype)
continue;
1019 std::cout << m_className <<
"::TransportElectron:\n";
1020 std::cout <<
" Collision type: " << cstype <<
"\n";
1021 std::cout <<
" Fill distance histogram.\n";
1024 switch (m_distanceOption) {
1026 m_histDistance->Fill((*it).xLast - x);
1029 m_histDistance->Fill((*it).yLast - y);
1032 m_histDistance->Fill((*it).zLast - z);
1035 const double r2 =
pow((*it).xLast - x, 2) +
1036 pow((*it).yLast - y, 2) +
1037 pow((*it).zLast - z, 2);
1038 m_histDistance->Fill(
sqrt(r2));
1050 case ElectronCollisionTypeElastic:
1053 case ElectronCollisionTypeIonisation:
1054 if (m_usePlotting && m_plotIonisations) {
1057 if (m_hasUserHandleIonisation) {
1058 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
1060 for (
int j = nion; j--;) {
1063 medium->GetIonisationProduct(j, itype, esec);
1064 if (itype == IonProdTypeElectron) {
1065 esec = std::max(esec, Small);
1066 if (m_histSecondary) m_histSecondary->Fill(esec);
1069 if (!aval)
continue;
1071 if (useBandStructure) {
1072 double kxs = 0., kys = 0., kzs = 0.;
1074 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1075 AddToStack(x, y, z, t, esec, kxs, kys, kzs, bs,
false, stackNew);
1077 AddToStack(x, y, z, t, esec,
false, stackNew);
1079 }
else if (itype == IonProdTypeHole) {
1080 esec = std::max(esec, Small);
1083 if (!aval)
continue;
1085 if (useBandStructure) {
1086 double kxs = 0., kys = 0., kzs = 0.;
1088 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1089 AddToStack(x, y, z, t, esec, kxs, kys, kzs, bs,
true, stackNew);
1091 AddToStack(x, y, z, t, esec,
true, stackNew);
1093 }
else if (itype == IonProdTypeIon) {
1097 if (m_debug) PrintStatus(hdr,
"ionised", x, y, z, hole);
1100 case ElectronCollisionTypeAttachment:
1101 if (m_usePlotting && m_plotAttachments) {
1104 if (m_hasUserHandleAttachment) {
1105 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
1108 Update(it, x, y, z, t, energy, newKx, newKy, newKz, band);
1109 (*it).status = StatusAttached;
1111 m_endpointsHoles.push_back(*it);
1114 m_endpointsElectrons.push_back(*it);
1120 case ElectronCollisionTypeInelastic:
1121 if (m_hasUserHandleInelastic) {
1122 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1126 case ElectronCollisionTypeExcitation:
1127 if (m_usePlotting && m_plotExcitations) {
1130 if (m_hasUserHandleInelastic) {
1131 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1133 if (ndxc <= 0)
break;
1135 stackPhotons.clear();
1136 for (
int j = ndxc; j--;) {
1137 double tdx = 0., sdx = 0., edx = 0.;
1139 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, edx)) {
1140 std::cerr << hdr <<
"Cannot retrieve deexcitation product "
1141 << j <<
"/" << ndxc <<
".\n";
1145 if (typedx == DxcProdTypeElectron) {
1147 double xp = x, yp = y, zp = z;
1150 double dxp = 0., dyp = 0., dzp = 0.;
1158 double fx = 0., fy = 0., fz = 0.;
1159 m_sensor->
ElectricField(xp, yp, zp, fx, fy, fz, med, status);
1161 if (status != 0 || !m_sensor->
IsInArea(xp, yp, zp))
continue;
1166 if (m_sensor->
IsWireCrossed(x, y, z, xp, yp, zp, xc, yc, zc)) {
1169 if (!aval)
continue;
1171 AddToStack(xp, yp, zp, t + tdx, std::max(edx, Small),
false, stackNew);
1172 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1175 stackPhotons.push_back(std::make_pair(t + tdx, edx));
1181 const int nSizePhotons = stackPhotons.size();
1182 for (
int j = nSizePhotons; j--;) {
1183 TransportPhoton(x, y, z, stackPhotons[j].first, stackPhotons[j].second, stackNew);
1188 case ElectronCollisionTypeSuperelastic:
1191 case ElectronCollisionTypeAcousticPhonon:
1194 case ElectronCollisionTypeOpticalPhonon:
1197 case ElectronCollisionTypeIntervalleyG:
1198 case ElectronCollisionTypeIntervalleyF:
1199 case ElectronCollisionTypeInterbandXL:
1200 case ElectronCollisionTypeInterbandXG:
1201 case ElectronCollisionTypeInterbandLG:
1204 case ElectronCollisionTypeImpurity:
1207 std::cerr << hdr <<
"Unknown collision type.\n";
1213 if (!ok || nCollTemp > m_nCollSkip ||
1214 cstype == ElectronCollisionTypeIonisation ||
1215 (m_plotExcitations && cstype == ElectronCollisionTypeExcitation) ||
1216 (m_plotAttachments && cstype == ElectronCollisionTypeAttachment)) {
1226 if (!useBandStructure) {
1228 const double k =
sqrt(kx * kx + ky * ky + kz * kz);
1234 Update(it, x, y, z, t, energy, kx, ky, kz, band);
1236 if (m_useDriftLines) {
1242 (*it).driftLine.push_back(newPoint);
1248 if (m_useInducedCharge) {
1249 const unsigned int nElectronEndpoints = m_endpointsElectrons.size();
1250 for (
unsigned int i = 0; i < nElectronEndpoints; ++i) {
1251 const Electron& p = m_endpointsElectrons[i];
1254 const unsigned int nHoleEndpoints = m_endpointsHoles.size();
1255 for (
unsigned int i = 0; i < nHoleEndpoints; ++i) {
1256 const Electron& p = m_endpointsHoles[i];
1262 if (m_usePlotting) {
1264 const unsigned int nElectronEndpoints = m_endpointsElectrons.size();
1265 for (
unsigned int i = 0; i < nElectronEndpoints; ++i) {
1268 if (np <= 0)
continue;
1269 const Electron& p = m_endpointsElectrons[i];
1271 for (
int jP = np; jP--;) {
1272 double x = 0., y = 0., z = 0., t = 0.;
1278 const unsigned int nHoleEndpoints = m_endpointsHoles.size();
1279 for (
unsigned int i = 0; i < nHoleEndpoints; ++i) {
1282 if (np <= 0)
continue;
1283 const Electron& p = m_endpointsHoles[i];
1285 for (
int jP = np; jP--;) {
1286 double x = 0., y = 0., z = 0., t = 0.;
1292 const unsigned int nPhotons = m_photons.size();
1293 for (
unsigned int i = 0; i < nPhotons; ++i) {
1294 const photon& p = m_photons[i];
1301void AvalancheMicroscopic::TransportPhoton(
const double x0,
const double y0,
1302 const double z0,
const double t0,
1304 std::vector<Electron>& stack) {
1308 std::cerr << m_className <<
"::TransportPhoton: Sensor is not defined.\n";
1314 if (!m_sensor->
GetMedium(x0, y0, z0, medium)) {
1315 std::cerr << m_className <<
"::TransportPhoton:\n"
1316 <<
" No medium at initial position.\n";
1321 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
1322 std::cerr << m_className <<
"::TransportPhoton:\n"
1323 <<
" Medium at initial position does not provide "
1324 <<
" microscopic tracking data.\n";
1329 int id = medium->GetId();
1332 double x = x0, y = y0, z = z0;
1335 double dx = 0., dy = 0., dz = 0.;
1342 double f = medium->GetPhotonCollisionRate(e);
1343 if (f <= 0.)
return;
1353 if (!m_sensor->
GetMedium(x, y, z, medium) || medium->GetId() !=
id) {
1362 double delta =
sqrt(dx * dx + dy * dy + dz * dz);
1369 double xM = x, yM = y, zM = z;
1370 while (delta > BoundaryDistance) {
1373 xM = x + delta * dx;
1374 yM = y + delta * dy;
1375 zM = z + delta * dz;
1377 if (m_sensor->
GetMedium(xM, yM, zM, medium) && medium->GetId() ==
id) {
1391 newPhoton.energy = e0;
1392 newPhoton.status = StatusLeftDriftMedium;
1393 m_photons.push_back(newPhoton);
1402 if (!medium->GetPhotonCollision(e, type, level, e1, ctheta, nsec, esec))
1405 if (type == PhotonCollisionTypeIonisation) {
1407 if (m_sizeCut == 0 || stack.size() < m_sizeCut) {
1408 AddToStack(x, y, z, t, std::max(esec, Small),
false, stack);
1413 }
else if (type == PhotonCollisionTypeExcitation) {
1417 std::vector<double> tPhotons;
1418 std::vector<double> ePhotons;
1419 for (
int j = nsec; j--;) {
1420 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, esec))
1422 if (typedx == DxcProdTypeElectron) {
1424 AddToStack(x, y, z, t + tdx, std::max(esec, Small),
false, stack);
1428 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1429 esec > m_gammaCut) {
1431 tPhotons.push_back(t + tdx);
1432 ePhotons.push_back(esec);
1436 const int nSizePhotons = tPhotons.size();
1437 for (
int k = nSizePhotons; k--;) {
1438 TransportPhoton(x, y, z, tPhotons[k], ePhotons[k], stack);
1449 newPhoton.energy = e0;
1450 newPhoton.status = -2;
1451 m_photons.push_back(newPhoton);
1454void AvalancheMicroscopic::ComputeRotationMatrix(
1455 const double bx,
const double by,
const double bz,
const double bmag,
1456 const double ex,
const double ey,
const double ez) {
1463 const double bt = by * by + bz * bz;
1466 m_rb11 = m_rb22 = m_rb33 = 1.;
1467 m_rb12 = m_rb13 = m_rb21 = m_rb23 = m_rb31 = m_rb32 = 0.;
1469 const double btInv = 1. / bt;
1475 m_rb22 = (m_rb11 * by * by + bz * bz) * btInv;
1476 m_rb33 = (m_rb11 * bz * bz + by * by) * btInv;
1477 m_rb23 = m_rb32 = (m_rb11 - 1.) * by * bz * btInv;
1480 const double fy = m_rb21 * ex + m_rb22 * ey + m_rb23 * ez;
1481 const double fz = m_rb31 * ex + m_rb32 * ey + m_rb33 * ez;
1482 const double ft =
sqrt(fy * fy + fz * fz);
1485 m_rx22 = m_rx33 = 1.;
1486 m_rx23 = m_rx32 = 0.;
1488 m_rx22 = m_rx33 = fz / ft;
1494void AvalancheMicroscopic::RotateGlobal2Local(
double& dx,
double& dy,
1497 const double dx1 = m_rb11 * dx + m_rb12 * dy + m_rb13 * dz;
1498 const double dy1 = m_rb21 * dx + m_rb22 * dy + m_rb23 * dz;
1499 const double dz1 = m_rb31 * dx + m_rb32 * dy + m_rb33 * dz;
1502 dy = m_rx22 * dy1 + m_rx23 * dz1;
1503 dz = m_rx32 * dy1 + m_rx33 * dz1;
1506void AvalancheMicroscopic::RotateLocal2Global(
double& dx,
double& dy,
1509 const double dx1 = dx;
1510 const double dy1 = m_rx22 * dy + m_rx32 * dz;
1511 const double dz1 = m_rx23 * dy + m_rx33 * dz;
1513 dx = m_rb11 * dx1 + m_rb21 * dy1 + m_rb31 * dz1;
1514 dy = m_rb12 * dx1 + m_rb22 * dy1 + m_rb32 * dz1;
1515 dz = m_rb13 * dx1 + m_rb23 * dy1 + m_rb33 * dz1;
1518void AvalancheMicroscopic::Update(std::vector<Electron>::iterator it,
1519 const double x,
const double y,
1520 const double z,
const double t,
1521 const double energy,
1522 const double kx,
const double ky,
1523 const double kz,
const int band) {
1529 (*it).energy = energy;
1536void AvalancheMicroscopic::AddToStack(
const double x,
const double y,
1537 const double z,
const double t,
1538 const double energy,
1540 std::vector<Electron>& container)
const {
1543 double dx = 0., dy = 0., dz = 1.;
1545 AddToStack(x, y, z, t, energy, dx, dy, dz, 0, hole, container);
1548void AvalancheMicroscopic::AddToStack(
const double x,
const double y,
1549 const double z,
const double t,
1550 const double energy,
1551 const double dx,
const double dy,
1552 const double dz,
const int band,
1554 std::vector<Electron>& container)
const {
1557 electron.status = 0;
1558 electron.hole = hole;
1563 electron.e0 = energy;
1568 electron.energy = energy;
1572 electron.band = band;
1577 electron.driftLine.reserve(1000);
1578 container.push_back(electron);
1581void AvalancheMicroscopic::Terminate(
double x0,
double y0,
double z0,
double t0,
1582 double& x1,
double& y1,
1583 double& z1,
double& t1) {
1585 const double dx = x1 - x0;
1586 const double dy = y1 - y0;
1587 const double dz = z1 - z0;
1588 double d =
sqrt(dx * dx + dy * dy + dz * dz);
1589 while (d > BoundaryDistance) {
1591 const double xm = 0.5 * (x0 + x1);
1592 const double ym = 0.5 * (y0 + y1);
1593 const double zm = 0.5 * (z0 + z1);
1594 const double tm = 0.5 * (t0 + t1);
1596 double ex = 0., ey = 0., ez = 0.;
1597 Medium* medium = NULL;
1599 m_sensor->
ElectricField(xm, ym, zm, ex, ey, ez, medium, status);
1600 if (status == 0 && m_sensor->
IsInArea(xm, ym, zm)) {
void EnableDistanceHistogramming(const int 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)
void EnablePlotting(ViewDrift *view)
Switch on drift line plotting.
void SetUserHandleIonisation(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 ionising collision.
void UnsetUserHandleAttachment()
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 EnableDriftLines()
Switch on storage of drift lines.
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 UnsetUserHandleStep()
void EnableElectronEnergyHistogramming(TH1 *histo)
Switch on filling histograms for electron energy distribution.
void UnsetUserHandleIonisation()
void DisableDistanceHistogramming()
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 UnsetUserHandleInelastic()
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.
void AddSignal(const double q, const double t, const double dt, const double x, const double y, const double z, const double vx, const double vy, const double vz)
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
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)
void AddInducedCharge(const double q, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
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 pow(const DoubleAc &f, double p)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)