13double Mag(
const double x,
const double y,
const double z) {
14 return sqrt(x * x + y * y + z * z);
17void Normalise(
double& x,
double& y,
double& z) {
18 const double d = Mag(x, y, z);
20 const double scale = 1. / d;
27void ToLocal(
const std::array<std::array<double, 3>, 3>& rot,
const double xg,
28 const double yg,
const double zg,
double& xl,
double& yl,
30 xl = rot[0][0] * xg + rot[0][1] * yg + rot[0][2] * zg;
31 yl = rot[1][0] * xg + rot[1][1] * yg + rot[1][2] * zg;
32 zl = rot[2][0] * xg + rot[2][1] * yg + rot[2][2] * zg;
35void ToGlobal(
const std::array<std::array<double, 3>, 3>& rot,
const double xl,
36 const double yl,
const double zl,
double& xg,
double& yg,
38 xg = rot[0][0] * xl + rot[1][0] * yl + rot[2][0] * zl;
39 yg = rot[0][1] * xl + rot[1][1] * yl + rot[2][1] * zl;
40 zg = rot[0][2] * xl + rot[1][2] * yl + rot[2][2] * zl;
43void RotationMatrix(
double bx,
double by,
double bz,
const double bmag,
44 const double ex,
const double ey,
const double ez,
45 std::array<std::array<double, 3>, 3>& rot) {
51 std::array<std::array<double, 3>, 3> rB = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
52 if (bmag > Garfield::Small) {
56 const double bt = by * by + bz * bz;
57 if (bt > Garfield::Small) {
58 const double btInv = 1. / bt;
64 rB[1][1] = (bx * by * by + bz * bz) * btInv;
65 rB[2][2] = (bx * bz * bz + by * by) * btInv;
66 rB[1][2] = rB[2][1] = (bx - 1.) * by * bz * btInv;
74 const double fy = rB[1][0] * ex + rB[1][1] * ey + rB[1][2] * ez;
75 const double fz = rB[2][0] * ex + rB[2][1] * ey + rB[2][2] * ez;
76 const double ft =
sqrt(fy * fy + fz * fz);
77 std::array<std::array<double, 3>, 3> rX = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
78 if (ft > Garfield::Small) {
80 rX[1][1] = rX[2][2] = fz / ft;
84 for (
unsigned int i = 0; i < 3; ++i) {
85 for (
unsigned int j = 0; j < 3; ++j) {
87 for (
unsigned int k = 0; k < 3; ++k) {
88 rot[i][j] += rX[i][k] * rB[k][j];
95 const double x,
const double y,
const double z,
const double t,
96 const double energy,
const double dx,
const double dy,
const double dz,
113 const double x,
const double y,
const double z,
const double t,
114 const double energy) {
116 double dx = 0., dy = 0., dz = 1.;
118 return MakePoint(x, y, z, t, energy, dx, dy, dz, 0);
127 m_electrons.reserve(10000);
128 m_holes.reserve(10000);
129 m_photons.reserve(1000);
134 std::cerr << m_className <<
"::SetSensor: Null pointer.\n";
141 const size_t nColl) {
143 std::cerr << m_className <<
"::EnablePlotting: Null pointer.\n";
147 m_nCollPlot = std::max(nColl, 1ul);
152 std::cerr << m_className <<
"::EnableElectronEnergyHistogramming:\n"
153 <<
" Null pointer.\n";
157 m_histElectronEnergy = histo;
162 std::cerr << m_className <<
"::EnableHoleEnergyHistogramming:\n"
163 <<
" Null pointer.\n";
167 m_histHoleEnergy = histo;
172 std::cerr << m_className <<
"::SetDistanceHistogram: Null pointer.\n";
176 m_histDistance = histo;
178 if (opt ==
'x' || opt ==
'y' || opt ==
'z' || opt ==
'r') {
179 m_distanceOption = opt;
181 std::cerr << m_className <<
"::SetDistanceHistogram:";
182 std::cerr <<
" Unknown option " << opt <<
".\n";
183 std::cerr <<
" Valid options are x, y, z, r.\n";
184 std::cerr <<
" Using default value (r).\n";
185 m_distanceOption =
'r';
188 if (m_distanceHistogramType.empty()) {
189 std::cout << m_className <<
"::SetDistanceHistogram:\n";
190 std::cout <<
" Don't forget to call EnableDistanceHistogramming.\n";
197 const unsigned int nDistanceHistogramTypes = m_distanceHistogramType.size();
198 if (nDistanceHistogramTypes > 0) {
199 for (
unsigned int i = 0; i < nDistanceHistogramTypes; ++i) {
200 if (m_distanceHistogramType[i] != type)
continue;
201 std::cout << m_className <<
"::EnableDistanceHistogramming:\n";
202 std::cout <<
" Collision type " << type
203 <<
" is already being histogrammed.\n";
208 m_distanceHistogramType.push_back(type);
209 std::cout << m_className <<
"::EnableDistanceHistogramming:\n";
210 std::cout <<
" Histogramming of collision type " << type <<
" enabled.\n";
211 if (!m_histDistance) {
212 std::cout <<
" Don't forget to set the histogram.\n";
217 if (std::find(m_distanceHistogramType.begin(), m_distanceHistogramType.end(),
218 type) == m_distanceHistogramType.end()) {
219 std::cerr << m_className <<
"::DisableDistanceHistogramming:\n"
220 <<
" Collision type " << type <<
" is not histogrammed.\n";
224 m_distanceHistogramType.erase(
225 std::remove(m_distanceHistogramType.begin(),
226 m_distanceHistogramType.end(), type),
227 m_distanceHistogramType.end());
231 m_histDistance =
nullptr;
232 m_distanceHistogramType.clear();
237 std::cerr << m_className <<
"::EnableSecondaryEnergyHistogramming:\n"
238 <<
" Null pointer.\n";
242 m_histSecondary = histo;
246 if (fabs(t1 - t0) < Small) {
247 std::cerr << m_className <<
"::SetTimeWindow:\n";
248 std::cerr <<
" Time interval must be greater than zero.\n";
252 m_tMin = std::min(t0, t1);
253 m_tMax = std::max(t0, t1);
254 m_hasTimeWindow =
true;
258 double& x0,
double& y0,
double& z0,
double& t0,
double& e0,
259 double& x1,
double& y1,
double& z1,
double& t1,
double& e1,
261 if (i >= m_electrons.size()) {
262 std::cerr << m_className <<
"::GetElectronEndpoint: Index out of range.\n";
266 if (m_electrons[i].path.empty()) {
267 std::cerr << m_className <<
"::GetElectronEndpoint: Empty drift line.\n";
271 x0 = m_electrons[i].path[0].x;
272 y0 = m_electrons[i].path[0].y;
273 z0 = m_electrons[i].path[0].z;
274 t0 = m_electrons[i].path[0].t;
275 e0 = m_electrons[i].path[0].energy;
276 x1 = m_electrons[i].path.back().x;
277 y1 = m_electrons[i].path.back().y;
278 z1 = m_electrons[i].path.back().z;
279 t1 = m_electrons[i].path.back().t;
280 e1 = m_electrons[i].path.back().energy;
281 status = m_electrons[i].status;
285 double& x0,
double& y0,
double& z0,
double& t0,
double& e0,
286 double& x1,
double& y1,
double& z1,
double& t1,
double& e1,
288 if (i >= m_holes.size()) {
289 std::cerr << m_className <<
"::GetHoleEndpoint: Index out of range.\n";
293 if (m_electrons[i].path.empty()) {
294 std::cerr << m_className <<
"::GetHoleEndpoint: Empty drift line.\n";
298 x0 = m_holes[i].path[0].x;
299 y0 = m_holes[i].path[0].y;
300 z0 = m_holes[i].path[0].z;
301 t0 = m_holes[i].path[0].t;
302 e0 = m_holes[i].path[0].energy;
303 x1 = m_holes[i].path.back().x;
304 y1 = m_holes[i].path.back().y;
305 z1 = m_holes[i].path.back().z;
306 t1 = m_holes[i].path.back().t;
307 e1 = m_holes[i].path.back().energy;
308 status = m_holes[i].status;
312 const size_t i)
const {
313 if (i >= m_electrons.size()) {
314 std::cerr << m_className <<
"::GetNumberOfElectronDriftLinePoints: "
315 <<
"Index out of range.\n";
318 return m_electrons[i].path.size();
322 const size_t i)
const {
323 if (i >= m_holes.size()) {
324 std::cerr << m_className <<
"::GetNumberOfHoleDriftLinePoints: "
325 <<
"Index out of range.\n";
328 return m_holes[i].path.size();
332 double& x,
double& y,
double& z,
double& t,
const size_t ip,
333 const size_t ie)
const {
334 if (ie >= m_electrons.size()) {
335 std::cerr << m_className <<
"::GetElectronDriftLinePoint:\n"
336 <<
" Endpoint index (" << ie <<
") out of range.\n";
339 if (ip >= m_electrons[ie].path.size()) {
340 std::cerr << m_className <<
"::GetElectronDriftLinePoint:\n"
341 <<
" Drift line point index (" << ip <<
") out of range.\n";
344 x = m_electrons[ie].path[ip].x;
345 y = m_electrons[ie].path[ip].y;
346 z = m_electrons[ie].path[ip].z;
347 t = m_electrons[ie].path[ip].t;
351 double& z,
double& t,
353 const size_t ih)
const {
354 if (ih >= m_holes.size()) {
355 std::cerr << m_className <<
"::GetHoleDriftLinePoint:\n"
356 <<
" Endpoint index (" << ih <<
") out of range.\n";
359 if (ip >= m_holes[ih].path.size()) {
360 std::cerr << m_className <<
"::GetHoleDriftLinePoint:\n"
361 <<
" Drift line point index (" << ip <<
") out of range.\n";
364 x = m_holes[ih].path[ip].x;
365 y = m_holes[ih].path[ip].y;
366 z = m_holes[ih].path[ip].z;
367 t = m_holes[ih].path[ip].t;
371 double& x0,
double& y0,
double& z0,
double& t0,
372 double& x1,
double& y1,
double& z1,
double& t1,
int& status)
const {
373 if (i >= m_photons.size()) {
374 std::cerr << m_className <<
"::GetPhoton: Index out of range.\n";
378 x0 = m_photons[i].x0;
379 x1 = m_photons[i].x1;
380 y0 = m_photons[i].y0;
381 y1 = m_photons[i].y1;
382 z0 = m_photons[i].z0;
383 z1 = m_photons[i].z1;
384 t0 = m_photons[i].t0;
385 t1 = m_photons[i].t1;
386 status = m_photons[i].status;
387 e = m_photons[i].energy;
391 void (*f)(
double x,
double y,
double z,
double t,
double e,
double dx,
392 double dy,
double dz,
bool hole)) {
394 std::cerr << m_className <<
"::SetUserHandleStep: Null pointer.\n";
397 m_userHandleStep = f;
401 void (*f)(
double x,
double y,
double z,
double t,
int type,
int level,
402 Medium* m,
double e0,
double e1,
double dx0,
double dy0,
403 double dz0,
double dx1,
double dy1,
double dz1)) {
404 m_userHandleCollision = f;
408 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
409 m_userHandleAttachment = f;
413 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
414 m_userHandleInelastic = f;
418 double x,
double y,
double z,
double t,
int type,
int level,
Medium* m)) {
419 m_userHandleIonisation = f;
423 const double x,
const double y,
const double z,
const double t,
424 const double e,
const double dx,
const double dy,
const double dz) {
425 std::vector<std::pair<Point, bool> > particles;
426 Point p = MakePoint(x, y, z, t, e, dx, dy, dz, 0);
427 particles.emplace_back(std::make_pair(std::move(p),
false));
428 return TransportElectrons(particles,
false);
432 const double x,
const double y,
const double z,
const double t,
433 const double e,
const double dx,
const double dy,
const double dz) {
435 std::vector<std::pair<Point, bool> > particles;
436 Point p = MakePoint(x, y, z, t, e, dx, dy, dz, 0);
437 particles.emplace_back(std::make_pair(std::move(p),
false));
438 return TransportElectrons(particles,
true);
442 const double x,
const double y,
const double z,
const double t,
443 const double e,
const double dx,
const double dy,
const double dz) {
446 electron.
status = StatusAlive;
447 electron.
path.emplace_back(MakePoint(x, y, z, t, e, dx, dy, dz, 0));
448 m_electrons.push_back(std::move(electron));
452 std::vector<std::pair<Point, bool> > particles;
453 for (
const auto& p : m_electrons) {
454 if (p.status == StatusAlive || p.status == StatusOutsideTimeWindow) {
455 particles.emplace_back(std::make_pair(p.path.back(),
false));
458 for (
const auto& p : m_holes) {
459 if (p.status == StatusAlive || p.status == StatusOutsideTimeWindow) {
460 particles.emplace_back(std::make_pair(p.path.back(),
true));
463 return TransportElectrons(particles,
true);
466bool AvalancheMicroscopic::TransportElectrons(
467 std::vector<std::pair<Point, bool> >& particles,
const bool aval) {
475 m_nElectrons = m_nHoles = m_nIons = 0;
479 std::cerr << m_className
480 <<
"::TransportElectrons: Sensor is not defined.\n";
492 for (
auto& p : particles) {
494 const double x0 = p.first.x;
495 const double y0 = p.first.y;
496 const double z0 = p.first.z;
497 if (!m_sensor->
IsInArea(x0, y0, z0)) {
498 std::cerr << m_className <<
"::TransportElectrons: "
499 <<
"Starting point is outside the active area.\n";
504 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
505 if (!medium || !medium->IsDriftable() || !medium->IsMicroscopic()) {
506 std::cerr << m_className <<
"::TransportElectrons: "
507 <<
"Starting point is not in a valid medium.\n";
511 const double e0 = std::max(p.first.energy, Small);
513 if (medium->IsSemiconductor() && m_useBandStructure) {
515 if (p.first.band < 0) {
517 medium->GetElectronMomentum(e0, p.first.kx, p.first.ky, p.first.kz,
522 const double kmag = Mag(p.first.kx, p.first.ky, p.first.kz);
523 if (
fabs(kmag) < Small) {
528 const double scale = 1. / kmag;
540 std::vector<std::pair<Point, bool> > newParticles;
541 while (!particles.empty()) {
542 newParticles.clear();
544 for (
const auto& particle : particles) {
545 const bool isHole = particle.second;
546 if (aval && m_sizeCut > 0) {
547 const size_t nH = m_holes.size();
548 const size_t nE = m_electrons.size();
549 if (nH >= m_sizeCut && nE >= m_sizeCut) {
552 if (nH >= m_sizeCut)
continue;
554 if (nE >= m_sizeCut)
continue;
557 std::vector<Point> path;
558 double pathLength = 0.;
561 status = TransportElectronSc(particle.first, isHole, aval,
563 newParticles, pathLength);
564 }
else if (useBfield) {
565 status = TransportElectronBfield(particle.first, isHole, aval,
567 newParticles, pathLength);
569 status = TransportElectron(particle.first, isHole, aval, signal,
570 path, newParticles, pathLength);
575 hole.path = std::move(path);
576 hole.pathLength = pathLength;
577 m_holes.push_back(std::move(hole));
581 electron.path = std::move(path);
582 electron.pathLength = pathLength;
583 m_electrons.push_back(std::move(electron));
586 particles.swap(newParticles);
590 if (m_doInducedCharge) {
591 for (
const auto& p : m_electrons) {
592 m_sensor->AddInducedCharge(-1, p.path[0].x, p.path[0].y, p.path[0].z,
593 p.path.back().x, p.path.back().y, p.path.back().z);
595 for (
const auto& p : m_holes) {
596 m_sensor->AddInducedCharge(+1, p.path[0].x, p.path[0].y, p.path[0].z,
597 p.path.back().x, p.path.back().y, p.path.back().z);
603int AvalancheMicroscopic::TransportElectron(
const Point& p0,
604 const bool hole,
const bool aval,
const bool signal,
605 std::vector<Point>& path,
606 std::vector<std::pair<Point, bool> >& newParticles,
607 double& pathLength) {
614 double en = p0.energy;
630 const double c1 = SpeedOfLight *
sqrt(2. / ElectronMass);
631 const double c2 = 0.25 * c1 * c1;
634 double ex = 0., ey = 0., ez = 0.;
635 Medium* medium =
nullptr;
637 m_sensor->ElectricField(x, y, z, ex, ey, ez, medium, status);
645 std::cout <<
" Drift line starts at ("
646 <<
x <<
", " <<
y <<
", " <<
z <<
").\n"
647 <<
" Status: " << status <<
"\n";
648 if (medium) std::cout <<
" Medium: " << medium->GetName() <<
"\n";
651 if (status != 0 || !medium || !medium->IsDriftable() ||
652 !medium->IsMicroscopic()) {
653 if (m_debug) std::cout <<
" Not in a valid medium.\n";
654 return StatusLeftDriftMedium;
657 auto mid = medium->GetId();
659 double fLim = medium->GetElectronNullCollisionRate(band);
661 std::cerr << m_className
662 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
663 return StatusCalculationAbandoned;
665 double tLim = 1. / fLim;
667 std::vector<std::pair<Particle, double> > secondaries;
672 auto hEnergy = hole ? m_histHoleEnergy : m_histElectronEnergy;
675 size_t nCollPlot = 0;
678 if (en < m_deltaCut) {
679 if (m_debug) std::cout <<
" Kinetic energy below transport cut.\n";
680 status = StatusBelowTransportCut;
685 if (hEnergy) hEnergy->Fill(en);
688 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
689 if (m_debug) std::cout <<
" Outside the time window.\n";
690 status = StatusOutsideTimeWindow;
694 if (medium->GetId() != mid) {
696 if (!medium->IsMicroscopic()) {
698 if (m_debug) std::cout <<
" Not in a microscopic medium.\n";
699 status = StatusLeftDriftMedium;
702 mid = medium->GetId();
706 fLim = medium->GetElectronNullCollisionRate(band);
708 std::cerr << m_className
709 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
710 status = StatusCalculationAbandoned;
717 const double vmag = c1 *
sqrt(en);
718 double vx = vmag * kx;
719 double vy = vmag * ky;
720 double vz = vmag * kz;
721 const double a1 = vx * ex + vy * ey + vz * ez;
722 const double a2 = c2 * (ex * ex + ey * ey + ez * ez);
724 if (m_userHandleStep) {
725 m_userHandleStep(x, y, z, t, en, kx, ky, kz, hole);
732 bool isNullCollision =
true;
733 while (isNullCollision) {
736 dt += -log(r) * tLim;
738 en1 = std::max(en + (a1 + a2 * dt) * dt, Small);
740 const double fReal = medium->GetElectronCollisionRate(en1, band);
742 std::cerr << m_className <<
"::TransportElectron:\n"
743 <<
" Got collision rate <= 0 at " << en1
744 <<
" eV (band " << band <<
").\n";
745 path.emplace_back(MakePoint(x, y, z, t, en1, kx, ky, kz, band));
746 return StatusCalculationAbandoned;
752 std::cerr << m_className <<
"::TransportElectron: "
753 <<
"Increasing null-collision rate by 5%.\n";
758 if (m_useNullCollisionSteps)
break;
760 if (
RndmUniform() <= fReal * tLim) isNullCollision =
false;
768 const double b1 =
sqrt(en / en1);
769 const double b2 = 0.5 * c1 * dt /
sqrt(en1);
770 double kx1 = kx * b1 + ex * b2;
771 double ky1 = ky * b1 + ey * b2;
772 double kz1 = kz * b1 + ez * b2;
775 const double b3 = dt * dt * c2;
776 double x1 =
x + vx * dt + ex * b3;
777 double y1 =
y + vy * dt + ey * b3;
778 double z1 =
z + vz * dt + ez * b3;
782 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, medium, status);
789 double xc =
x, yc =
y, zc =
z, rc = 0.;
794 Terminate(x, y, z, t, x1, y1, z1, t1);
795 if (m_debug) std::cout <<
" Left the drift medium.\n";
796 status = StatusLeftDriftMedium;
797 }
else if (!m_sensor->IsInArea(x1, y1, z1)) {
798 Terminate(x, y, z, t, x1, y1, z1, t1);
799 if (m_debug) std::cout <<
" Left the drift area.\n";
800 status = StatusLeftDriftArea;
801 }
else if (m_sensor->CrossedWire(x, y, z, x1, y1, z1,
802 xc, yc, zc,
false, rc)) {
803 t1 = t + dt * Mag(xc - x, yc - y, zc - z) /
804 Mag(x1 - x, y1 - y, z1 - z);
808 if (m_debug) std::cout <<
" Hit a wire.\n";
809 status = StatusLeftDriftMedium;
810 }
else if (m_sensor->CrossedPlane(x, y, z, x1, y1, z1, xc, yc, zc)) {
811 t1 = t + dt * Mag(xc - x, yc - y, zc - z) /
812 Mag(x1 - x, y1 - y, z1 - z);
816 if (m_debug) std::cout <<
" Hit a plane.\n";
817 status = StatusHitPlane;
821 if (signal) AddSignal(x, y, z, t, x1, y1, z1, t1, hole);
824 if (m_computePathLength) pathLength += Mag(x1 - x, y1 - y, z1 - z);
838 if (isNullCollision) {
851 medium->ElectronCollision(en1, cstype, level, en, kx1, ky1, kz1,
852 secondaries, ndxc, band);
854 if (m_debug) std::cout <<
" Collision type " << cstype <<
".\n";
857 if (m_histDistance) {
858 FillDistanceHistogram(cstype, x, y, z, xLast, yLast, zLast);
861 if (m_userHandleCollision) {
862 m_userHandleCollision(x, y, z, t, cstype, level, medium, en1, en, kx,
863 ky, kz, kx1, ky1, kz1);
867 case ElectronCollisionTypeElastic:
870 case ElectronCollisionTypeIonisation:
871 if (m_userHandleIonisation) {
872 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
874 for (
const auto& secondary : secondaries) {
876 const double esec = std::max(secondary.second, Small);
877 if (m_histSecondary) m_histSecondary->Fill(esec);
882 newParticles.emplace_back(std::make_pair(
883 MakePoint(x, y, z, t, esec),
false));
885 const double esec = std::max(secondary.second, Small);
890 newParticles.emplace_back(std::make_pair(
891 MakePoint(x, y, z, t, esec),
true));
898 case ElectronCollisionTypeAttachment:
899 if (m_userHandleAttachment) {
900 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
907 path.emplace_back(MakePoint(x, y, z, t, en, kx1, ky1, kz1, band));
908 return StatusAttached;
911 case ElectronCollisionTypeInelastic:
912 if (m_userHandleInelastic) {
913 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
917 case ElectronCollisionTypeExcitation:
918 if (m_userHandleInelastic) {
919 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
921 if (ndxc <= 0)
break;
923 for (
int j = ndxc; j--;) {
924 double tdx = 0., sdx = 0., edx = 0.;
926 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, edx)) {
927 std::cerr << m_className <<
"::TransportElectron: "
928 <<
"Cannot retrieve deexcitation product " << j
929 <<
"/" << ndxc <<
".\n";
933 if (typedx == DxcProdTypeElectron) {
935 double xp =
x, yp =
y, zp =
z;
938 double dxp = 0., dyp = 0., dzp = 0.;
945 Medium* med =
nullptr;
946 double fx = 0., fy = 0., fz = 0.;
947 m_sensor->ElectricField(xp, yp, zp, fx, fy, fz, med, status);
949 if (status != 0 || !m_sensor->IsInArea(xp, yp, zp))
continue;
951 if (m_sensor->CrossedWire(x, y, z, xp, yp, zp, xc, yc, zc,
958 if (m_userHandleIonisation) {
959 m_userHandleIonisation(xp, yp, zp, t, cstype, level, medium);
963 newParticles.emplace_back(std::make_pair(
964 MakePoint(xp, yp, zp, t + tdx, std::max(edx, Small)),
false));
965 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
968 if (aval) TransportPhoton(x, y, z, t + tdx, edx, newParticles);
973 case ElectronCollisionTypeSuperelastic:
976 case ElectronCollisionTypeVirtual:
979 case ElectronCollisionTypeAcousticPhonon:
982 case ElectronCollisionTypeOpticalPhonon:
985 case ElectronCollisionTypeIntervalleyG:
986 case ElectronCollisionTypeIntervalleyF:
987 case ElectronCollisionTypeInterbandXL:
988 case ElectronCollisionTypeInterbandXG:
989 case ElectronCollisionTypeInterbandLG:
992 case ElectronCollisionTypeImpurity:
995 std::cerr << m_className
996 <<
"::TransportElectron: Unknown collision type.\n";
999 if (m_viewer) PlotCollision(cstype, did, x, y, z, nCollPlot);
1007 if (nColl % 100 == 0) Normalise(kx, ky, kz);
1010 if (m_storeDriftLines && nColl >= m_nCollSkip) {
1011 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1014 if (m_viewer && nCollPlot >= m_nCollPlot) {
1015 m_viewer->AddDriftLinePoint(did, x, y, z);
1021 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1023 if (m_viewer && nCollPlot > 0) {
1024 m_viewer->AddDriftLinePoint(did, x, y, z);
1027 std::cout <<
" Drift line stops at ("
1028 <<
x <<
", " <<
y <<
", " <<
z <<
").\n";
1033int AvalancheMicroscopic::TransportElectronBfield(
const Point& p0,
1034 const bool hole,
const bool aval,
const bool signal,
1035 std::vector<Point>& path,
1036 std::vector<std::pair<Point, bool> >& newParticles,
1037 double& pathLength) {
1044 double en = p0.energy;
1060 const double c1 = SpeedOfLight *
sqrt(2. / ElectronMass);
1061 const double c2 = 0.25 * c1 * c1;
1064 double ex = 0., ey = 0., ez = 0.;
1065 Medium* medium =
nullptr;
1067 m_sensor->ElectricField(x, y, z, ex, ey, ez, medium, status);
1075 std::cout <<
" Drift line starts at ("
1076 <<
x <<
", " <<
y <<
", " <<
z <<
").\n"
1077 <<
" Status: " << status <<
"\n";
1078 if (medium) std::cout <<
" Medium: " << medium->GetName() <<
"\n";
1081 if (status != 0 || !medium || !medium->IsDriftable() ||
1082 !medium->IsMicroscopic()) {
1083 if (m_debug) std::cout <<
" Not in a valid medium.\n";
1084 return StatusLeftDriftMedium;
1087 auto mid = medium->GetId();
1089 double fLim = medium->GetElectronNullCollisionRate(band);
1091 std::cerr << m_className
1092 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
1093 return StatusCalculationAbandoned;
1095 double tLim = 1. / fLim;
1097 std::array<std::array<double, 3>, 3> rot;
1099 double bx = 0., by = 0., bz = 0.;
1101 m_sensor->MagneticField(x, y, z, bx, by, bz, st);
1102 const double scale = hole ? Tesla2Internal : -Tesla2Internal;
1106 double bmag = Mag(bx, by, bz);
1109 RotationMatrix(bx, by, bz, bmag, ex, ey, ez, rot);
1111 double omega = OmegaCyclotronOverB *
bmag;
1113 ToLocal(rot, ex, ey, ez, ex, ey, ez);
1115 double ezovb =
bmag > Small ? ez /
bmag : 0.;
1117 std::vector<std::pair<Particle, double> > secondaries;
1122 auto hEnergy = hole ? m_histHoleEnergy : m_histElectronEnergy;
1125 size_t nCollPlot = 0;
1128 if (en < m_deltaCut) {
1129 if (m_debug) std::cout <<
" Kinetic energy below transport cut.\n";
1130 status = StatusBelowTransportCut;
1135 if (hEnergy) hEnergy->Fill(en);
1138 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
1139 if (m_debug) std::cout <<
" Outside the time window.\n";
1140 status = StatusOutsideTimeWindow;
1144 if (medium->GetId() != mid) {
1146 if (!medium->IsMicroscopic()) {
1148 if (m_debug) std::cout <<
" Not in a microscopic medium.\n";
1149 status = StatusLeftDriftMedium;
1152 mid = medium->GetId();
1154 fLim = medium->GetElectronNullCollisionRate(band);
1156 std::cerr << m_className
1157 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
1158 status = StatusCalculationAbandoned;
1165 const double vmag = c1 *
sqrt(en);
1166 double vx = 0., vy = 0., vz = 0.;
1167 ToLocal(rot, vmag * kx, vmag * ky, vmag * kz, vx, vy, vz);
1168 double a1 = vx * ex;
1169 double a2 = c2 * ex * ex;
1170 if (omega > Small) {
1177 if (m_userHandleStep) {
1178 m_userHandleStep(x, y, z, t, en, kx, ky, kz, hole);
1186 double cphi = 1., sphi = 0.;
1187 double a3 = 0., a4 = 0.;
1188 bool isNullCollision =
true;
1189 while (isNullCollision) {
1192 dt += -log(r) * tLim;
1194 en1 = en + (a1 + a2 * dt) * dt;
1195 if (omega > Small) {
1196 const double phi = omega * dt;
1200 a4 = (1. - cphi) / omega;
1201 en1 += ez * (vz * a3 - vy * a4);
1203 en1 = std::max(en1, Small);
1205 const double fReal = medium->GetElectronCollisionRate(en1, band);
1207 std::cerr << m_className <<
"::TransportElectron:\n"
1208 <<
" Got collision rate <= 0 at " << en1
1209 <<
" eV (band " << band <<
").\n";
1210 path.emplace_back(MakePoint(x, y, z, t, en1, kx, ky, kz, band));
1211 return StatusCalculationAbandoned;
1215 dt += log(r) * tLim;
1217 std::cerr << m_className <<
"::TransportElectron: "
1218 <<
"Increasing null-collision rate by 5%.\n";
1223 if (m_useNullCollisionSteps)
break;
1225 if (
RndmUniform() <= fReal * tLim) isNullCollision =
false;
1234 double kx1 = 0., ky1 = 0., kz1 = 0.;
1235 double dx = 0., dy = 0., dz = 0.;
1237 double vx1 = vx + 2. * c2 * ex * dt;
1238 double vy1 = vy * cphi + vz * sphi + ezovb;
1239 double vz1 = vz * cphi - vy * sphi;
1240 if (omega < Small) vz1 += 2. * c2 * ez * dt;
1242 ToGlobal(rot, vx1, vy1, vz1, kx1, ky1, kz1);
1243 Normalise(kx1, ky1, kz1);
1245 dx = vx * dt + c2 * ex * dt * dt;
1246 if (omega > Small) {
1247 dy = vy * a3 + vz * a4 + ezovb * dt;
1248 dz = vz * a3 - vy * a4;
1251 dz = vz * dt + c2 * ez * dt * dt;
1254 ToGlobal(rot, dx, dy, dz, dx, dy, dz);
1261 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, medium, status);
1268 double xc =
x, yc =
y, zc =
z, rc = 0.;
1273 Terminate(x, y, z, t, x1, y1, z1, t1);
1274 if (m_debug) std::cout <<
" Left the drift medium.\n";
1275 status = StatusLeftDriftMedium;
1276 }
else if (!m_sensor->IsInArea(x1, y1, z1)) {
1277 Terminate(x, y, z, t, x1, y1, z1, t1);
1278 if (m_debug) std::cout <<
" Left the drift area.\n";
1279 status = StatusLeftDriftArea;
1280 }
else if (m_sensor->CrossedWire(x, y, z, x1, y1, z1,
1281 xc, yc, zc,
false, rc)) {
1282 t1 = t + dt * Mag(xc - x, yc - y, zc - z) / Mag(dx, dy, dz);
1286 if (m_debug) std::cout <<
" Hit a wire.\n";
1287 status = StatusLeftDriftMedium;
1288 }
else if (m_sensor->CrossedPlane(x, y, z, x1, y1, z1, xc, yc, zc)) {
1289 t1 = t + dt * Mag(xc - x, yc - y, zc - z) / Mag(dx, dy, dz);
1293 if (m_debug) std::cout <<
" Hit a plane.\n";
1294 status = StatusHitPlane;
1298 if (signal) AddSignal(x, y, z, t, x1, y1, z1, t1, hole);
1301 if (m_computePathLength) pathLength += Mag(x1 - x, y1 - y, z1 - z);
1316 m_sensor->MagneticField(x, y, z, bx, by, bz, st);
1320 bmag = Mag(bx, by, bz);
1322 RotationMatrix(bx, by, bz, bmag, ex, ey, ez, rot);
1323 omega = OmegaCyclotronOverB *
bmag;
1325 ToLocal(rot, ex, ey, ez, ex, ey, ez);
1326 ezovb =
bmag > Small ? ez /
bmag : 0.;
1328 if (isNullCollision) {
1340 secondaries.clear();
1341 medium->ElectronCollision(en1, cstype, level, en, kx1, ky1, kz1,
1342 secondaries, ndxc, band);
1344 if (m_debug) std::cout <<
" Collision type " << cstype <<
".\n";
1347 if (m_histDistance) {
1348 FillDistanceHistogram(cstype, x, y, z, xLast, yLast, zLast);
1351 if (m_userHandleCollision) {
1352 m_userHandleCollision(x, y, z, t, cstype, level, medium, en1, en, kx,
1353 ky, kz, kx1, ky1, kz1);
1357 case ElectronCollisionTypeElastic:
1360 case ElectronCollisionTypeIonisation:
1361 if (m_userHandleIonisation) {
1362 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
1364 for (
const auto& secondary : secondaries) {
1366 const double esec = std::max(secondary.second, Small);
1367 if (m_histSecondary) m_histSecondary->Fill(esec);
1370 if (!aval)
continue;
1372 newParticles.emplace_back(std::make_pair(
1373 MakePoint(x, y, z, t, esec),
false));
1375 const double esec = std::max(secondary.second, Small);
1378 if (!aval)
continue;
1380 newParticles.emplace_back(std::make_pair(
1381 MakePoint(x, y, z, t, esec),
true));
1388 case ElectronCollisionTypeAttachment:
1389 if (m_userHandleAttachment) {
1390 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
1397 path.emplace_back(MakePoint(x, y, z, t, en, kx1, ky1, kz1, band));
1398 return StatusAttached;
1401 case ElectronCollisionTypeInelastic:
1402 if (m_userHandleInelastic) {
1403 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1407 case ElectronCollisionTypeExcitation:
1408 if (m_userHandleInelastic) {
1409 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1411 if (ndxc <= 0)
break;
1413 for (
int j = ndxc; j--;) {
1414 double tdx = 0., sdx = 0., edx = 0.;
1416 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, edx)) {
1417 std::cerr << m_className <<
"::TransportElectron: "
1418 <<
"Cannot retrieve deexcitation product " << j
1419 <<
"/" << ndxc <<
".\n";
1423 if (typedx == DxcProdTypeElectron) {
1425 double xp =
x, yp =
y, zp =
z;
1428 double dxp = 0., dyp = 0., dzp = 0.;
1435 Medium* med =
nullptr;
1436 double fx = 0., fy = 0., fz = 0.;
1437 m_sensor->ElectricField(xp, yp, zp, fx, fy, fz, med, status);
1439 if (status != 0 || !m_sensor->IsInArea(xp, yp, zp))
continue;
1441 if (m_sensor->CrossedWire(x, y, z, xp, yp, zp, xc, yc, zc,
1448 if (m_userHandleIonisation) {
1449 m_userHandleIonisation(xp, yp, zp, t, cstype, level, medium);
1451 if (!aval)
continue;
1453 newParticles.emplace_back(std::make_pair(
1454 MakePoint(xp, yp, zp, t + tdx, std::max(edx, Small)),
false));
1455 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1458 if (aval) TransportPhoton(x, y, z, t + tdx, edx, newParticles);
1463 case ElectronCollisionTypeSuperelastic:
1466 case ElectronCollisionTypeVirtual:
1469 case ElectronCollisionTypeAcousticPhonon:
1472 case ElectronCollisionTypeOpticalPhonon:
1475 case ElectronCollisionTypeIntervalleyG:
1476 case ElectronCollisionTypeIntervalleyF:
1477 case ElectronCollisionTypeInterbandXL:
1478 case ElectronCollisionTypeInterbandXG:
1479 case ElectronCollisionTypeInterbandLG:
1482 case ElectronCollisionTypeImpurity:
1485 std::cerr << m_className
1486 <<
"::TransportElectron: Unknown collision type.\n";
1489 if (m_viewer) PlotCollision(cstype, did, x, y, z, nCollPlot);
1497 if (nColl % 100 == 0) Normalise(kx, ky, kz);
1500 if (m_storeDriftLines && nColl >= m_nCollSkip) {
1501 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1504 if (m_viewer && nCollPlot >= m_nCollPlot) {
1505 m_viewer->AddDriftLinePoint(did, x, y, z);
1511 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1513 if (m_viewer && nCollPlot > 0) {
1514 m_viewer->AddDriftLinePoint(did, x, y, z);
1517 std::cout <<
" Drift line stops at ("
1518 <<
x <<
", " <<
y <<
", " <<
z <<
").\n";
1523int AvalancheMicroscopic::TransportElectronSc(
const Point& p0,
1524 const bool hole,
const bool aval,
const bool signal,
1525 std::vector<Point>& path,
1526 std::vector<std::pair<Point, bool> >& newParticles,
1527 double& pathLength) {
1534 double en = p0.energy;
1550 double ex = 0., ey = 0., ez = 0.;
1551 Medium* medium =
nullptr;
1553 m_sensor->ElectricField(x, y, z, ex, ey, ez, medium, status);
1561 std::cout <<
" Drift line starts at ("
1562 <<
x <<
", " <<
y <<
", " <<
z <<
").\n"
1563 <<
" Status: " << status <<
"\n";
1564 if (medium) std::cout <<
" Medium: " << medium->GetName() <<
"\n";
1567 if (status != 0 || !medium || !medium->IsDriftable() ||
1568 !medium->IsMicroscopic()) {
1569 if (m_debug) std::cout <<
" Not in a valid medium.\n";
1570 return StatusLeftDriftMedium;
1573 auto mid = medium->GetId();
1577 double fLim = medium->GetElectronNullCollisionRate(band);
1579 std::cerr << m_className
1580 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
1581 return StatusCalculationAbandoned;
1583 double tLim = 1. / fLim;
1585 std::vector<std::pair<Particle, double> > secondaries;
1590 auto hEnergy = hole ? m_histHoleEnergy : m_histElectronEnergy;
1593 size_t nCollPlot = 0;
1596 if (en < m_deltaCut) {
1597 if (m_debug) std::cout <<
" Kinetic energy below transport cut.\n";
1598 status = StatusBelowTransportCut;
1603 if (hEnergy) hEnergy->Fill(en);
1606 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
1607 if (m_debug) std::cout <<
" Outside the time window.\n";
1608 status = StatusOutsideTimeWindow;
1612 if (medium->GetId() != mid) {
1614 if (!medium->IsMicroscopic()) {
1616 if (m_debug) std::cout <<
" Not in a microscopic medium.\n";
1617 status = StatusLeftDriftMedium;
1620 mid = medium->GetId();
1624 fLim = medium->GetElectronNullCollisionRate(band);
1626 std::cerr << m_className
1627 <<
"::TransportElectron: Got null-collision rate <= 0.\n";
1628 status = StatusCalculationAbandoned;
1635 double vx = 0., vy = 0., vz = 0.;
1636 en = medium->GetElectronEnergy(kx, ky, kz, vx, vy, vz, band);
1638 if (m_userHandleStep) {
1639 m_userHandleStep(x, y, z, t, en, kx, ky, kz, hole);
1646 bool isNullCollision =
true;
1647 while (isNullCollision) {
1650 dt += -log(r) * tLim;
1652 const double cdt = dt * SpeedOfLight;
1653 const double kx1 = kx + ex * cdt;
1654 const double ky1 = ky + ey * cdt;
1655 const double kz1 = kz + ez * cdt;
1656 double vx1 = 0., vy1 = 0., vz1 = 0.;
1657 en1 = medium->GetElectronEnergy(kx1, ky1, kz1, vx1, vy1, vz1, band);
1658 en1 = std::max(en1, Small);
1660 const double fReal = medium->GetElectronCollisionRate(en1, band);
1662 std::cerr << m_className <<
"::TransportElectron:\n"
1663 <<
" Got collision rate <= 0 at " << en1
1664 <<
" eV (band " << band <<
").\n";
1665 path.emplace_back(MakePoint(x, y, z, t, en1, kx, ky, kz, band));
1666 return StatusCalculationAbandoned;
1670 dt += log(r) * tLim;
1672 std::cerr << m_className <<
"::TransportElectron: "
1673 <<
"Increasing null-collision rate by 5% (band "
1679 if (m_useNullCollisionSteps)
break;
1681 if (
RndmUniform() <= fReal * tLim) isNullCollision =
false;
1690 double kx1 = 0., ky1 = 0., kz1 = 0.;
1691 double dx = 0., dy = 0., dz = 0.;
1693 const double cdt = dt * SpeedOfLight;
1694 kx1 = kx + ex * cdt;
1695 ky1 = ky + ey * cdt;
1696 kz1 = kz + ez * cdt;
1697 double vx1 = 0., vy1 = 0, vz1 = 0.;
1698 en1 = medium->GetElectronEnergy(kx1, ky1, kz1, vx1, vy1, vz1, band);
1699 dx = 0.5 * (vx + vx1) * dt;
1700 dy = 0.5 * (vy + vy1) * dt;
1701 dz = 0.5 * (vz + vz1) * dt;
1708 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, medium, status);
1715 double xc =
x, yc =
y, zc =
z, rc = 0.;
1720 Terminate(x, y, z, t, x1, y1, z1, t1);
1721 if (m_debug) std::cout <<
" Left the drift medium.\n";
1722 status = StatusLeftDriftMedium;
1723 }
else if (!m_sensor->IsInArea(x1, y1, z1)) {
1724 Terminate(x, y, z, t, x1, y1, z1, t1);
1725 if (m_debug) std::cout <<
" Left the drift area.\n";
1726 status = StatusLeftDriftArea;
1727 }
else if (m_sensor->CrossedWire(x, y, z, x1, y1, z1,
1728 xc, yc, zc,
false, rc)) {
1729 t1 = t + dt * Mag(xc - x, yc - y, zc - z) / Mag(dx, dy, dz);
1733 if (m_debug) std::cout <<
" Hit a wire.\n";
1734 status = StatusLeftDriftMedium;
1735 }
else if (m_sensor->CrossedPlane(x, y, z, x1, y1, z1, xc, yc, zc)) {
1736 t1 = t + dt * Mag(xc - x, yc - y, zc - z) / Mag(dx, dy, dz);
1740 if (m_debug) std::cout <<
" Hit a plane.\n";
1741 status = StatusHitPlane;
1745 if (signal) AddSignal(x, y, z, t, x1, y1, z1, t1, hole);
1748 if (m_computePathLength) pathLength += Mag(x1 - x, y1 - y, z1 - z);
1762 if (isNullCollision) {
1774 secondaries.clear();
1775 medium->ElectronCollision(en1, cstype, level, en, kx1, ky1, kz1,
1776 secondaries, ndxc, band);
1778 if (m_debug) std::cout <<
" Collision type " << cstype <<
".\n";
1781 if (m_histDistance) {
1782 FillDistanceHistogram(cstype, x, y, z, xLast, yLast, zLast);
1785 if (m_userHandleCollision) {
1786 m_userHandleCollision(x, y, z, t, cstype, level, medium, en1, en, kx,
1787 ky, kz, kx1, ky1, kz1);
1791 case ElectronCollisionTypeElastic:
1794 case ElectronCollisionTypeIonisation:
1795 if (m_userHandleIonisation) {
1796 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
1798 for (
const auto& secondary : secondaries) {
1800 const double esec = std::max(secondary.second, Small);
1801 if (m_histSecondary) m_histSecondary->Fill(esec);
1804 if (!aval)
continue;
1806 double kxs = 0., kys = 0., kzs = 0.;
1808 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1809 newParticles.emplace_back(std::make_pair(
1810 MakePoint(x, y, z, t, esec, kxs, kys, kzs, bs),
false));
1812 const double esec = std::max(secondary.second, Small);
1815 if (!aval)
continue;
1817 double kxs = 0., kys = 0., kzs = 0.;
1819 medium->GetElectronMomentum(esec, kxs, kys, kzs, bs);
1820 newParticles.emplace_back(std::make_pair(
1821 MakePoint(x, y, z, t, esec, kxs, kys, kzs, bs),
true));
1828 case ElectronCollisionTypeAttachment:
1829 if (m_userHandleAttachment) {
1830 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
1837 path.emplace_back(MakePoint(x, y, z, t, en, kx1, ky1, kz1, band));
1838 return StatusAttached;
1841 case ElectronCollisionTypeInelastic:
1842 if (m_userHandleInelastic) {
1843 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1847 case ElectronCollisionTypeExcitation:
1848 if (m_userHandleInelastic) {
1849 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1851 if (ndxc <= 0)
break;
1853 for (
int j = ndxc; j--;) {
1854 double tdx = 0., sdx = 0., edx = 0.;
1856 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, edx)) {
1857 std::cerr << m_className <<
"::TransportElectron: "
1858 <<
"Cannot retrieve deexcitation product " << j
1859 <<
"/" << ndxc <<
".\n";
1863 if (typedx == DxcProdTypeElectron) {
1865 double xp =
x, yp =
y, zp =
z;
1868 double dxp = 0., dyp = 0., dzp = 0.;
1875 Medium* med =
nullptr;
1876 double fx = 0., fy = 0., fz = 0.;
1877 m_sensor->ElectricField(xp, yp, zp, fx, fy, fz, med, status);
1879 if (status != 0 || !m_sensor->IsInArea(xp, yp, zp))
continue;
1881 if (m_sensor->CrossedWire(x, y, z, xp, yp, zp, xc, yc, zc,
1888 if (m_userHandleIonisation) {
1889 m_userHandleIonisation(xp, yp, zp, t, cstype, level, medium);
1891 if (!aval)
continue;
1893 newParticles.emplace_back(std::make_pair(
1894 MakePoint(xp, yp, zp, t + tdx, std::max(edx, Small)),
false));
1895 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
1898 if (aval) TransportPhoton(x, y, z, t + tdx, edx, newParticles);
1903 case ElectronCollisionTypeSuperelastic:
1906 case ElectronCollisionTypeVirtual:
1909 case ElectronCollisionTypeAcousticPhonon:
1912 case ElectronCollisionTypeOpticalPhonon:
1915 case ElectronCollisionTypeIntervalleyG:
1916 case ElectronCollisionTypeIntervalleyF:
1917 case ElectronCollisionTypeInterbandXL:
1918 case ElectronCollisionTypeInterbandXG:
1919 case ElectronCollisionTypeInterbandLG:
1922 case ElectronCollisionTypeImpurity:
1925 std::cerr << m_className
1926 <<
"::TransportElectron: Unknown collision type.\n";
1929 if (m_viewer) PlotCollision(cstype, did, x, y, z, nCollPlot);
1937 if (m_storeDriftLines && nColl >= m_nCollSkip) {
1938 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1941 if (m_viewer && nCollPlot >= m_nCollPlot) {
1942 m_viewer->AddDriftLinePoint(did, x, y, z);
1948 path.emplace_back(MakePoint(x, y, z, t, en, kx, ky, kz, band));
1950 if (m_viewer && nCollPlot > 0) {
1951 m_viewer->AddDriftLinePoint(did, x, y, z);
1954 std::cout <<
" Drift line stops at ("
1955 <<
x <<
", " <<
y <<
", " <<
z <<
").\n";
1960void AvalancheMicroscopic::PlotCollision(
const int cstype,
const size_t did,
1961 const double x,
const double y,
const double z,
1962 size_t& nCollPlot)
const {
1963 if (!m_viewer)
return;
1964 if (cstype == ElectronCollisionTypeIonisation) {
1965 if (m_plotIonisations) {
1966 m_viewer->AddIonisation(x, y, z);
1967 m_viewer->AddDriftLinePoint(did, x, y, z);
1970 }
else if (cstype == ElectronCollisionTypeExcitation) {
1971 if (m_plotExcitations) {
1972 m_viewer->AddExcitation(x, y, z);
1973 m_viewer->AddDriftLinePoint(did, x, y, z);
1976 }
else if (cstype == ElectronCollisionTypeAttachment) {
1977 if (m_plotAttachments) {
1978 m_viewer->AddAttachment(x, y, z);
1979 m_viewer->AddDriftLinePoint(did, x, y, z);
1984void AvalancheMicroscopic::FillDistanceHistogram(
const int cstype,
1985 const double x,
const double y,
const double z,
1986 double& xLast,
double& yLast,
double& zLast) {
1988 for (
const auto& htype : m_distanceHistogramType) {
1989 if (htype != cstype)
continue;
1990 if (m_debug) std::cout <<
" Filling distance histogram.\n";
1991 switch (m_distanceOption) {
1993 m_histDistance->Fill(xLast - x);
1996 m_histDistance->Fill(yLast - y);
1999 m_histDistance->Fill(zLast - z);
2002 m_histDistance->Fill(Mag(xLast - x, yLast - y, zLast - z));
2012void AvalancheMicroscopic::AddSignal(
2013 const double x0,
const double y0,
const double z0,
const double t0,
2014 const double x1,
const double y1,
const double z1,
const double t1,
2015 const bool hole)
const {
2017 const int q = hole ? 1 : -1;
2018 m_sensor->AddSignal(q, t0, t1, x0, y0, z0, x1, y1, z1,
2019 m_integrateWeightingField,
2020 m_useWeightingPotential);
2023void AvalancheMicroscopic::TransportPhoton(
2024 const double x0,
const double y0,
const double z0,
const double t0,
2025 const double e0, std::vector<std::pair<Point, bool> > & stack) {
2028 std::cerr << m_className <<
"::TransportPhoton: Sensor is not defined.\n";
2033 if (!m_sensor->IsInArea(x0, y0, z0)) {
2034 std::cerr << m_className <<
"::TransportPhoton:\n"
2035 <<
" No valid field at initial position.\n";
2040 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
2042 std::cerr << m_className <<
"::TransportPhoton:\n"
2043 <<
" No medium at initial position.\n";
2048 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
2049 std::cerr << m_className <<
"::TransportPhoton:\n"
2050 <<
" Medium at initial position does not provide "
2051 <<
" microscopic tracking data.\n";
2056 int id = medium->GetId();
2059 double x = x0,
y = y0,
z = z0;
2062 double dx = 0., dy = 0., dz = 0.;
2068 double f = medium->GetPhotonCollisionRate(e);
2069 if (f <= 0.)
return;
2079 medium = m_sensor->GetMedium(x, y, z);
2080 if (!medium || medium->GetId() !=
id) {
2089 double delta = Mag(dx, dy, dz);
2096 double xM =
x, yM =
y, zM =
z;
2097 while (delta > BoundaryDistance) {
2100 xM =
x + delta * dx;
2101 yM =
y + delta * dy;
2102 zM =
z + delta * dz;
2104 medium = m_sensor->GetMedium(xM, yM, zM);
2105 if (medium && medium->GetId() ==
id) {
2119 newPhoton.energy = e0;
2120 newPhoton.status = StatusLeftDriftMedium;
2121 m_photons.push_back(std::move(newPhoton));
2122 if (m_viewer) m_viewer->AddPhoton(x0, y0, z0, x, y, z);
2131 if (!medium->GetPhotonCollision(e, type, level, e1, ctheta, nsec, esec))
2134 if (type == PhotonCollisionTypeIonisation) {
2136 if (m_sizeCut == 0 || stack.size() < m_sizeCut) {
2137 stack.emplace_back(std::make_pair(
2138 MakePoint(x, y, z, t, std::max(esec, Small)),
false));
2143 }
else if (type == PhotonCollisionTypeExcitation) {
2147 std::vector<double> tPhotons;
2148 std::vector<double> ePhotons;
2149 for (
int j = nsec; j--;) {
2150 if (!medium->GetDeexcitationProduct(j, tdx, sdx, typedx, esec))
continue;
2151 if (typedx == DxcProdTypeElectron) {
2153 stack.emplace_back(std::make_pair(
2154 MakePoint(x, y, z, t + tdx, std::max(esec, Small)),
false));
2158 }
else if (typedx == DxcProdTypePhoton && m_usePhotons &&
2159 esec > m_gammaCut) {
2161 tPhotons.push_back(t + tdx);
2162 ePhotons.push_back(esec);
2166 const int nSizePhotons = tPhotons.size();
2167 for (
int k = nSizePhotons; k--;) {
2168 TransportPhoton(x, y, z, tPhotons[k], ePhotons[k], stack);
2179 newPhoton.energy = e0;
2180 newPhoton.status = -2;
2181 if (m_viewer) m_viewer->AddPhoton(x0, y0, z0, x, y, z);
2182 m_photons.push_back(std::move(newPhoton));
2185void AvalancheMicroscopic::Terminate(
double x0,
double y0,
double z0,
double t0,
2186 double& x1,
double& y1,
double& z1,
2188 const double dx = x1 - x0;
2189 const double dy = y1 - y0;
2190 const double dz = z1 - z0;
2191 double d = Mag(dx, dy, dz);
2192 while (d > BoundaryDistance) {
2194 const double xm = 0.5 * (x0 + x1);
2195 const double ym = 0.5 * (y0 + y1);
2196 const double zm = 0.5 * (z0 + z1);
2197 const double tm = 0.5 * (t0 + t1);
2199 if (m_sensor->IsInside(xm, ym, zm) && m_sensor->IsInArea(xm, ym, zm)) {
2212 bool outsideMedium =
true;
2213 while (outsideMedium) {
2215 const double xm = 0.5 * (x0 + x1);
2216 const double ym = 0.5 * (y0 + y1);
2217 const double zm = 0.5 * (z0 + z1);
2218 const double tm = 0.5 * (t0 + t1);
2220 if (m_sensor->IsInside(xm, ym, zm) && m_sensor->IsInArea(xm, ym, zm)) {
2221 outsideMedium =
false;
void EnableDistanceHistogramming(const int type)
Fill distance distribution histograms for a given collision type.
void GetHoleDriftLinePoint(double &x, double &y, double &z, double &t, const size_t ip, const size_t ih=0) const
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 callback function to be called at every step.
void EnableHoleEnergyHistogramming(TH1 *histo)
Fill a histogram with the hole energy distribution.
void AddElectron(const double x, const double y, const double z, const double t, const double e, const double dx=0., const double dy=0., const double dz=0.)
Add an electron to the list of particles to be transported.
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 callback function to be called at every (real) collision.
void GetHoleEndpoint(const size_t 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 SetUserHandleIonisation(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
size_t GetNumberOfElectronDriftLinePoints(const size_t i=0) const
void GetPhoton(const size_t i, double &e, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
bool DriftElectron(const double x, const double y, const double z, const double t, const double e, const double dx=0., const double dy=0., const double dz=0.)
AvalancheMicroscopic()
Default 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.
bool AvalancheElectron(const double x, const double y, const double z, const double t, const double e, const double dx=0., const double dy=0., const double dz=0.)
Calculate an avalanche initiated by a given electron.
void EnableElectronEnergyHistogramming(TH1 *histo)
Fill a histogram with the electron energy distribution.
bool ResumeAvalanche()
Continue the avalanche simulation from the current set of electrons.
void DisableDistanceHistogramming()
Stop filling distance distribution histograms.
void GetElectronEndpoint(const size_t 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 EnableSecondaryEnergyHistogramming(TH1 *histo)
Fill histograms of the energy of electrons emitted in ionising collisions.
void EnablePlotting(ViewDrift *view, const size_t nColl=100)
Switch on drift line plotting.
size_t GetNumberOfHoleDriftLinePoints(const size_t 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 GetElectronDriftLinePoint(double &x, double &y, double &z, double &t, const size_t ip, const size_t ie=0) const
void SetTimeWindow(const double t0, const double t1)
Define a time interval (only carriers inside the interval are simulated).
Abstract base class for media.
bool HasMagneticField() const
Does the sensor have a non-zero magnetic field?
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
size_t GetNumberOfElectrodes() const
Get the number of electrodes attached to the sensor.
Visualize drift lines and tracks.
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)
std::vector< Point > path
Drift line.
double energy
Kinetic energy.
double kz
Direction/wave vector.