13double AvalancheMC::c1 = ElectronMass / (SpeedOfLight * SpeedOfLight);
21 m_hasTimeWindow(false),
29 m_useInducedCharge(false),
30 m_useEquilibration(true),
32 m_useAttachment(true),
35 m_withElectrons(true),
37 m_scaleElectronSignal(1.),
38 m_scaleHoleSignal(1.),
40 m_useTcadTrapping(false),
41 m_useTcadVelocity(false),
44 m_className =
"AvalancheMC";
46 m_drift.reserve(10000);
52 std::cerr << m_className <<
"::SetSensor:\n Null pointer.\n";
62 std::cerr << m_className <<
"::EnablePlotting:\n Null pointer.\n";
73 std::cerr << m_className <<
"::SetTimeSteps:\n "
74 <<
"Step size is too small. Using default (20 ps) instead.\n";
79 std::cout << m_className <<
"::SetTimeSteps:\n"
80 <<
" Step size set to " << d <<
" ns.\n";
89 std::cerr << m_className <<
"::SetDistanceSteps:\n "
90 <<
"Step size is too small. Using default (10 um) instead.\n";
95 std::cout << m_className <<
"::SetDistanceSteps:\n"
96 <<
" Step size set to " << d <<
" cm.\n";
105 std::cerr << m_className <<
"::SetCollisionSteps:\n "
106 <<
"Number of collisions set to default value (100).\n";
111 std::cout << m_className <<
"::SetCollisionSteps:\n "
112 <<
"Number of collisions to be skipped set to " << n <<
".\n";
119 if (fabs(t1 - t0) < Small) {
120 std::cerr << m_className <<
"::SetTimeWindow:\n"
121 <<
" Time interval must be greater than zero.\n";
125 m_tMin = std::min(t0, t1);
126 m_tMax = std::max(t0, t1);
127 m_hasTimeWindow =
true;
131 double& z,
double& t)
const {
133 if (i >= m_drift.size()) {
134 std::cerr << m_className <<
"::GetDriftLinePoint:\n"
135 <<
" Drift line point " << i <<
" does not exist.\n";
146 double& z0,
double& t0,
double& x1,
147 double& y1,
double& z1,
double& t1,
150 if (i >= m_endpointsHoles.size()) {
151 std::cerr << m_className <<
"::GetHoleEndpoint:\n"
152 <<
" Endpoint " << i <<
" does not exist.\n";
156 x0 = m_endpointsHoles[i].x0;
157 y0 = m_endpointsHoles[i].y0;
158 z0 = m_endpointsHoles[i].z0;
159 t0 = m_endpointsHoles[i].t0;
160 x1 = m_endpointsHoles[i].x1;
161 y1 = m_endpointsHoles[i].y1;
162 z1 = m_endpointsHoles[i].z1;
163 t1 = m_endpointsHoles[i].t1;
164 status = m_endpointsHoles[i].status;
168 double& z0,
double& t0,
double& x1,
double& y1,
169 double& z1,
double& t1,
int& status)
const {
171 if (i >= m_endpointsIons.size()) {
172 std::cerr << m_className <<
"::GetIonEndpoint:\n"
173 <<
" Endpoint " << i <<
" does not exist.\n";
177 x0 = m_endpointsIons[i].x0;
178 y0 = m_endpointsIons[i].y0;
179 z0 = m_endpointsIons[i].z0;
180 t0 = m_endpointsIons[i].t0;
181 x1 = m_endpointsIons[i].x1;
182 y1 = m_endpointsIons[i].y1;
183 z1 = m_endpointsIons[i].z1;
184 t1 = m_endpointsIons[i].t1;
185 status = m_endpointsIons[i].status;
189 double& y0,
double& z0,
double& t0,
190 double& x1,
double& y1,
double& z1,
191 double& t1,
int& status)
const {
193 if (i >= m_endpointsElectrons.size()) {
194 std::cerr << m_className <<
"::GetElectronEndpoint:\n"
195 <<
" Endpoint " << i <<
" does not exist.\n";
199 x0 = m_endpointsElectrons[i].x0;
200 y0 = m_endpointsElectrons[i].y0;
201 z0 = m_endpointsElectrons[i].z0;
202 t0 = m_endpointsElectrons[i].t0;
203 x1 = m_endpointsElectrons[i].x1;
204 y1 = m_endpointsElectrons[i].y1;
205 z1 = m_endpointsElectrons[i].z1;
206 t1 = m_endpointsElectrons[i].t1;
207 status = m_endpointsElectrons[i].status;
211 const double z0,
const double t0) {
214 std::cerr << m_className <<
"::DriftElectron:\n"
215 <<
" Sensor is not defined.\n";
219 m_endpointsElectrons.clear();
220 m_endpointsHoles.clear();
221 m_endpointsIons.clear();
227 return DriftLine(x0, y0, z0, t0, -1);
234 std::cerr << m_className <<
"::DriftHole:\n Sensor is not defined.\n";
238 m_endpointsElectrons.clear();
239 m_endpointsHoles.clear();
240 m_endpointsIons.clear();
246 return DriftLine(x0, y0, z0, t0, 1);
253 std::cerr << m_className <<
"::DriftIon:\n Sensor is not defined.\n";
257 m_endpointsElectrons.clear();
258 m_endpointsHoles.clear();
259 m_endpointsIons.clear();
265 return DriftLine(x0, y0, z0, t0, 2);
268bool AvalancheMC::DriftLine(
const double x0,
const double y0,
const double z0,
269 const double t0,
const int type,
const bool aval) {
271 const std::string hdr = m_className +
"::DriftLine:\n ";
275 double x = x0, y = y0, z = z0;
289 while (0 == status) {
291 double ex = 0., ey = 0., ez = 0.;
292 double bx = 0., by = 0., bz = 0.;
294 status = GetField(x, y, z, ex, ey, ez, bx, by, bz, medium);
295 if (status == StatusCalculationAbandoned) {
297 std::cerr << hdr <<
"Abandoning the calculation.\n";
298 }
else if (status == StatusLeftDriftMedium || medium == NULL) {
300 if (m_drift.empty()) {
301 std::cerr << hdr <<
"Initial position (" << x <<
", " << y <<
", "
302 << z <<
") is not inside a drift medium.\n";
305 TerminateLine(point.x, point.y, point.z, point.t, x, y, z, t);
307 std::cout << hdr <<
"Particle left the drift medium at ("
308 << x <<
", " << y <<
", " << z <<
").\n";
311 }
else if (!m_sensor->
IsInArea(x, y, z)) {
313 status = StatusLeftDriftArea;
314 if (m_drift.empty()) {
315 std::cerr << hdr <<
"Initial position (" << x <<
", " << y <<
", "
316 << z <<
") is not inside the drift area.\n";
319 TerminateLine(point.x, point.y, point.z, point.t, x, y, z, t);
321 std::cout << hdr <<
"Particle left the drift area at ("
322 << x <<
", " << y <<
", " << z <<
").\n";
325 }
else if (!m_drift.empty()) {
330 if (m_sensor->
IsWireCrossed(xc, yc, zc, x, y, z, xc, yc, zc)) {
331 status = StatusLeftDriftMedium;
337 const double dxc = xc - point.x;
338 const double dyc = yc - point.y;
339 const double dzc = zc - point.z;
340 const double dxp = x - point.x;
341 const double dyp = y - point.y;
342 const double dzp = z - point.z;
343 const double dsc =
sqrt(dxc * dxc + dyc * dyc + dzc * dzc);
344 const double dsp =
sqrt(dxp * dxp + dyp * dyp + dzp * dzp);
345 t = point.t + (t - point.t) * dsc / dsp;
347 std::cout << hdr <<
"Particle hit a wire at ("
348 << xc <<
", " << yc <<
", " << zc <<
").\n";
354 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
355 status = StatusOutsideTimeWindow;
356 if (m_drift.empty()) {
357 std::cerr << hdr <<
"Initial time (" << t0 <<
") is not inside the "
358 <<
"time window (" << m_tMin <<
", " << m_tMax <<
").\n";
367 m_drift.push_back(point);
370 if (status != 0)
break;
373 const double emag =
sqrt(ex * ex + ey * ey + ez * ez);
375 std::cerr << hdr <<
"Electric field at (" << x <<
", " << y <<
", " << z
376 <<
") is too small.\n";
377 status = StatusCalculationAbandoned;
381 double vx = 0., vy = 0., vz = 0.;
382 if (!GetVelocity(type, medium, x, y, z, ex, ey, ez, bx, by, bz,
384 status = StatusCalculationAbandoned;
385 std::cerr << hdr <<
"Abandoning the calculation.\n";
389 const double vmag =
sqrt(vx * vx + vy * vy + vz * vz);
391 std::cerr << hdr <<
"Drift velocity at (" << x <<
", " << y <<
", " << z
392 <<
") is too small.\n";
393 status = StatusCalculationAbandoned;
399 switch (m_stepModel) {
413 std::cerr << hdr <<
"Unknown stepping model. Program bug!\n";
414 status = StatusCalculationAbandoned;
423 if (m_useDiffusion) {
424 if (!AddDiffusion(type, medium,
sqrt(vmag * dt), x, y, z, vx, vy, vz,
425 ex, ey, ez, bx, by, bz)) {
426 status = StatusCalculationAbandoned;
427 std::cerr << hdr <<
"Abandoning the calculation.\n";
432 std::cout << hdr <<
"New point: " << x <<
", " << y <<
", " << z <<
"\n";
437 unsigned int nElectronsOld = m_nElectrons;
438 unsigned int nHolesOld = m_nHoles;
439 unsigned int nIonsOld = m_nIons;
441 if ((type == -1 || type == 1) && (aval || m_useAttachment)) {
442 ComputeGainLoss(type, status);
443 if (status == StatusAttached && m_debug) {
444 std::cout << hdr <<
"Particle attached at (" << m_drift.back().x <<
", "
445 << m_drift.back().y <<
", " << m_drift.back().z <<
"\n";
455 endPoint.x1 = m_drift.back().x;
456 endPoint.y1 = m_drift.back().y;
457 endPoint.z1 = m_drift.back().z;
458 endPoint.t1 = m_drift.back().t;
459 endPoint.status = status;
461 m_endpointsElectrons.push_back(endPoint);
462 }
else if (type == 1) {
463 m_endpointsHoles.push_back(endPoint);
464 }
else if (type == 2) {
465 m_endpointsIons.push_back(endPoint);
468 const int nNewElectrons = m_nElectrons - nElectronsOld;
469 const int nNewHoles = m_nHoles - nHolesOld;
470 const int nNewIons = m_nIons - nIonsOld;
471 std::cout << hdr <<
"Produced\n"
472 <<
" " << nNewElectrons <<
" electrons,\n"
473 <<
" " << nNewHoles <<
" holes, and\n"
474 <<
" " << nNewIons <<
" ions\n"
475 <<
" along the drift line from \n"
476 <<
" (" << endPoint.x0 <<
", " << endPoint.y0 <<
", "
477 << endPoint.z0 <<
") to \n"
478 <<
" (" << endPoint.x1 <<
", " << endPoint.y1 <<
", "
479 << endPoint.z1 <<
").\n";
483 const double scale = type == -1 ? -m_scaleElectronSignal :
484 type == 1 ? m_scaleHoleSignal : m_scaleIonSignal;
485 if (m_useSignal) ComputeSignal(scale);
486 if (m_useInducedCharge) ComputeInducedCharge(scale);
489 if (m_viewer && !m_drift.empty()) {
490 const unsigned int nPoints = m_drift.size();
495 }
else if (type == 1) {
501 for (
unsigned int i = 0; i < nPoints; ++i) {
507 if (status == StatusCalculationAbandoned)
return false;
512 const double z0,
const double t0,
516 return Avalanche(x0, y0, z0, t0, 1, 0, 0);
520 const double z0,
const double t0,
521 const bool electrons) {
523 m_withElectrons = electrons;
524 return Avalanche(x0, y0, z0, t0, 0, 1, 0);
528 const double z0,
const double t0) {
530 m_withElectrons = m_withHoles =
true;
531 return Avalanche(x0, y0, z0, t0, 1, 1, 0);
534bool AvalancheMC::Avalanche(
const double x0,
const double y0,
const double z0,
536 const unsigned int ne0,
const unsigned int nh0,
537 const unsigned int ni0) {
539 const std::string hdr = m_className +
"::Avalanche:\n ";
541 m_endpointsElectrons.clear();
542 m_endpointsHoles.clear();
543 m_endpointsIons.clear();
547 std::cerr << hdr <<
"Sensor is not defined.\n";
552 std::vector<AvalPoint> aval;
561 aval.push_back(point);
567 if (!m_withHoles && !m_withElectrons) {
568 std::cerr << hdr <<
"Neither electron nor hole/ion component requested.\n";
571 std::vector<AvalPoint> newAval;
572 while (!aval.empty()) {
573 std::vector<AvalPoint>::iterator it;
574 std::vector<AvalPoint>::iterator end = aval.end();
575 for (it = aval.begin(); it != end; ++it) {
576 if (m_withElectrons) {
578 const unsigned int ne = (*it).ne;
579 for (
unsigned int i = 0; i < ne; ++i) {
581 if (!DriftLine((*it).x, (*it).y, (*it).z, (*it).t, -1,
true)) {
585 const unsigned int nPoints = m_drift.size();
587 for (
unsigned int j = 0; j < nPoints - 2; ++j) {
588 if (m_drift[j].ne > 0 || m_drift[j].nh > 0 || m_drift[j].ni > 0) {
590 point.x = m_drift[j + 1].x;
591 point.y = m_drift[j + 1].y;
592 point.z = m_drift[j + 1].z;
593 point.t = m_drift[j + 1].t;
594 point.ne = m_drift[j].ne;
595 point.nh = m_drift[j].nh;
596 point.ni = m_drift[j].ni;
597 newAval.push_back(point);
605 const unsigned int ni = (*it).ni;
606 for (
unsigned int i = 0; i < ni; ++i) {
608 DriftLine((*it).x, (*it).y, (*it).z, (*it).t, 2,
false);
612 const unsigned int nh = (*it).nh;
613 for (
unsigned int i = 0; i < nh; ++i) {
615 if (!DriftLine((*it).x, (*it).y, (*it).z, (*it).t, +1,
true)) {
619 const unsigned int nPoints = m_drift.size();
620 for (
unsigned int j = 0; j < nPoints - 1; ++j) {
621 if (m_drift[j].ne > 0 || m_drift[j].nh > 0 || m_drift[j].ni > 0) {
623 point.x = m_drift[j + 1].x;
624 point.y = m_drift[j + 1].y;
625 point.z = m_drift[j + 1].z;
626 point.t = m_drift[j + 1].t;
627 point.ne = m_drift[j].ne;
628 point.nh = m_drift[j].nh;
629 point.ni = m_drift[j].ni;
630 newAval.push_back(point);
642int AvalancheMC::GetField(
const double x,
const double y,
const double z,
643 double& ex,
double& ey,
double& ez,
644 double& bx,
double& by,
double& bz,
649 m_sensor->
ElectricField(x, y, z, ex, ey, ez, medium, status);
651 if (status != 0)
return StatusLeftDriftMedium;
656 bx *= Tesla2Internal;
657 by *= Tesla2Internal;
658 bz *= Tesla2Internal;
663bool AvalancheMC::GetVelocity(
const int type, Medium* medium,
664 const double x,
const double y,
const double z,
665 const double ex,
const double ey,
const double ez,
666 const double bx,
const double by,
const double bz,
667 double& vx,
double& vy,
double& vz) {
669 if (type != -1 && type != 1 && type != 2) {
670 std::cerr << m_className <<
"::GetVelocity:\n "
671 <<
"Unknown drift line type (" << type <<
"). Program bug!\n";
674 if (m_useTcadVelocity && type != 2) {
677 for (
unsigned int i = 0; i < nComponents; ++i) {
679 if (!cmp->IsVelocityActive())
continue;
684 }
else if (type == 1) {
685 cmp->HoleVelocity(x, y, z, vx, vy, vz, m, status);
688 const std::string eh = type < 0 ?
"electron" :
"hole";
689 std::cerr << m_className <<
"::GetVelocity:\n "
690 <<
"Error calculating " << eh <<
" TCAD velocity at ("
691 << x <<
", " << y <<
", " << z <<
")\n";
696 std::cout << m_className <<
"::GetVelocity:\n "
697 <<
"TCAD drift velocity at (" << x <<
", " << y <<
", " << z
698 <<
"): " << vx <<
", " << vy <<
", " << vz <<
"\n";
705 ok = medium->ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
706 }
else if (type == 1) {
707 ok = medium->HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
708 }
else if (type == 2) {
709 ok = medium->IonVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
712 const std::string ehi = type < 0 ?
"electron" : type == 1 ?
"hole" :
"ion";
713 std::cerr << m_className <<
"::GetVelocity:\n Error calculating " << ehi
714 <<
" velocity at (" << x <<
", " << y <<
", " << z <<
")\n";
718 std::cout << m_className <<
"::GetVelocity:\n "
719 <<
"Drift velocity at (" << x <<
", " << y <<
", " << z <<
"): "
720 << vx <<
", " << vy <<
", " << vz <<
"\n";
725bool AvalancheMC::AddDiffusion(
const int type, Medium* medium,
726 const double step,
double& x,
double& y,
double& z,
727 const double vx,
const double vy,
const double vz,
728 const double ex,
const double ey,
const double ez,
729 const double bx,
const double by,
const double bz) {
732 double dl = 0., dt = 0.;
734 ok = medium->ElectronDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
735 }
else if (type == 1) {
736 ok = medium->HoleDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
737 }
else if (type == 2) {
738 ok = medium->IonDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
741 const std::string ehi = type < 0 ?
"electron" : type == 1 ?
"hole" :
"ion";
742 std::cerr << m_className <<
"::AddDiffusion:\n Error calculating " << ehi
743 <<
" diffusion at (" << x <<
", " << y <<
", " << z <<
")\n";
752 std::cout << m_className <<
"::AddDiffusion:\n Adding diffusion step "
753 << dx <<
", " << dy <<
", " << dz <<
"\n";
756 const double vt =
sqrt(vx * vx + vy * vy);
757 const double phi = vt > Small ? atan2(vy, vx) : 0.;
758 const double theta = vt > Small ? atan2(vz, vt) : vz < 0. ? -HalfPi : HalfPi;
759 const double cphi =
cos(phi);
760 const double sphi =
sin(phi);
761 const double ctheta =
cos(theta);
762 const double stheta =
sin(theta);
764 x += cphi * ctheta * dx - sphi * dy - cphi * stheta * dz;
765 y += sphi * ctheta * dx + cphi * dy - sphi * stheta * dz;
766 z += stheta * dx + ctheta * dz;
770void AvalancheMC::TerminateLine(
double x0,
double y0,
double z0,
double t0,
771 double& x,
double& y,
double& z,
double& t)
const {
777 double ds =
sqrt(dx * dx + dy * dy + dz * dz);
779 const double scale = 1. / ds;
789 while (ds > BoundaryDistance) {
792 const double xm = x + dx * ds;
793 const double ym = y + dy * ds;
794 const double zm = z + dz * ds;
796 double ex = 0., ey = 0., ez = 0.;
798 Medium* medium = NULL;
799 m_sensor->
ElectricField(xm, ym, zm, ex, ey, ez, medium, status);
800 if (status == 0 && m_sensor->
IsInArea(xm, ym, zm)) {
814bool AvalancheMC::ComputeGainLoss(
const int type,
int& status) {
816 const unsigned int nPoints = m_drift.size();
817 std::vector<double> alphas(nPoints, 0.);
818 std::vector<double> etas(nPoints, 0.);
820 if (!ComputeAlphaEta(type, alphas, etas))
return false;
823 const double probth = 0.01;
828 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
833 const int nDiv = std::max(
int((alphas[i] + etas[i]) / probth), 1);
835 const double palpha = std::max(alphas[i] / nDiv, 0.);
836 const double peta = std::max(etas[i] / nDiv, 0.);
838 const int neInit = ne;
839 const int niInit = ni;
841 for (
int j = 0; j < nDiv; ++j) {
844 const int gain = int(
852 for (
int k = ne; k--;) {
862 status = StatusAttached;
865 }
else if (type == 1) {
870 m_drift.resize(i + 2);
871 m_drift[i + 1].x = 0.5 * (m_drift[i].x + m_drift[i + 1].x);
872 m_drift[i + 1].y = 0.5 * (m_drift[i].y + m_drift[i + 1].y);
873 m_drift[i + 1].z = 0.5 * (m_drift[i].z + m_drift[i + 1].z);
879 if (ne - neInit >= 1) {
881 m_drift[i].ne = ne - neInit;
882 m_nElectrons += ne - neInit;
883 }
else if (type == 1) {
884 m_drift[i].nh = ne - neInit;
885 m_nHoles += ne - neInit;
887 m_drift[i].ni = ne - neInit;
890 if (ni - niInit >= 1) {
893 m_drift[i].ni = ni - niInit;
894 m_nIons += ni - niInit;
896 m_drift[i].nh = ni - niInit;
897 m_nHoles += ni - niInit;
900 m_drift[i].ne = ni - niInit;
901 m_nElectrons += ni - niInit;
905 if (status == StatusAttached)
return true;
910bool AvalancheMC::ComputeAlphaEta(
const int type, std::vector<double>& alphas,
911 std::vector<double>& etas)
const {
913 const double tg[6] = {-0.932469514203152028, -0.661209386466264514,
914 -0.238619186083196909, 0.238619186083196909,
915 0.661209386466264514, 0.932469514203152028};
916 const double wg[6] = {0.171324492379170345, 0.360761573048138608,
917 0.467913934572691047, 0.467913934572691047,
918 0.360761573048138608, 0.171324492379170345};
920 const unsigned int nPoints = m_drift.size();
921 alphas.resize(nPoints, 0.);
922 etas.resize(nPoints, 0.);
923 if (nPoints < 2)
return true;
925 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
926 const DriftPoint& p0 = m_drift[i];
927 const DriftPoint& p1 = m_drift[i + 1];
929 const double delx = p1.x - p0.x;
930 const double dely = p1.y - p0.y;
931 const double delz = p1.z - p0.z;
932 const double del =
sqrt(delx * delx + dely * dely + delz * delz);
937 for (
unsigned int j = 0; j < 6; ++j) {
938 const double x = p0.x + 0.5 * (1. + tg[j]) * delx;
939 const double y = p0.y + 0.5 * (1. + tg[j]) * dely;
940 const double z = p0.z + 0.5 * (1. + tg[j]) * delz;
942 double ex = 0., ey = 0., ez = 0.;
943 Medium* medium = NULL;
945 m_sensor->
ElectricField(x, y, z, ex, ey, ez, medium, status);
949 if (i < nPoints - 2) {
950 std::cerr << m_className <<
"::ComputeAlphaEta: Got status \n"
951 << status <<
" at segment " << j + 1
952 <<
"/6, drift point " << i + 1 <<
"/" << nPoints <<
".\n";
958 double bx = 0., by = 0., bz = 0.;
961 bx *= Tesla2Internal;
962 by *= Tesla2Internal;
963 bz *= Tesla2Internal;
966 double vx = 0., vy = 0., vz = 0.;
967 double alpha = 0., eta = 0.;
968 if (m_useTcadTrapping) {
972 for (
unsigned int k = 0; k < nComponents; ++k) {
974 if (!trapCmp->IsTrapActive())
continue;
975 Medium* trapMed = trapCmp->
GetMedium(x, y, z);
977 if (trapCmp->IsVelocityActive()) {
980 trapMed->ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
982 trapMed->ElectronTownsend(ex, ey, ez, bx, by, bz, alpha);
983 trapCmp->ElectronAttachment(x, y, z, eta);
985 if (trapCmp->IsVelocityActive()) {
986 trapCmp->HoleVelocity(x, y, z, vx, vy, vz, trapMed, status);
988 trapMed->HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
990 trapMed->HoleTownsend(ex, ey, ez, bx, by, bz, alpha);
991 trapCmp->HoleAttachment(x, y, z, eta);
996 medium->ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
997 medium->ElectronTownsend(ex, ey, ez, bx, by, bz, alpha);
998 medium->ElectronAttachment(ex, ey, ez, bx, by, bz, eta);
1000 medium->HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
1001 medium->HoleTownsend(ex, ey, ez, bx, by, bz, alpha);
1002 medium->HoleAttachment(ex, ey, ez, bx, by, bz, eta);
1008 alphas[i] += wg[j] *
alpha;
1009 etas[i] += wg[j] * eta;
1013 if (m_useEquilibration) {
1014 const double vd =
sqrt(vdx * vdx + vdy * vdy + vdz * vdz);
1015 if (vd * del <= 0.) {
1018 const double dinv = delx * vdx + dely * vdy + delz * vdz;
1022 scale = (delx * vdx + dely * vdy + delz * vdz) / (vd * del);
1026 alphas[i] *= 0.5 * del * scale;
1027 etas[i] *= 0.5 * del * scale;
1031 if (!m_useEquilibration)
return true;
1032 if (!Equilibrate(alphas)) {
1034 std::cerr << m_className <<
"::ComputeAlphaEta:\n Unable to even out "
1035 <<
"alpha steps. Calculation is probably inaccurate.\n";
1039 if (!Equilibrate(etas)) {
1041 std::cerr << m_className <<
"::ComputeAlphaEta:\n Unable to even out "
1042 <<
"eta steps. Calculation is probably inaccurate.\n";
1050bool AvalancheMC::Equilibrate(std::vector<double>& alphas)
const {
1052 const unsigned int nPoints = alphas.size();
1054 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
1056 if (alphas[i] >= 0.)
continue;
1058 double sub1 = -0.5 * alphas[i];
1063 for (
unsigned int j = 0; j < i - 1; ++j) {
1064 if (alphas[i - j] > sub1) {
1065 alphas[i - j] -= sub1;
1070 }
else if (alphas[i - j] > 0.) {
1071 alphas[i] += alphas[i - j];
1072 sub1 -= alphas[i - j];
1077 for (
unsigned int j = 0; j < nPoints - i - 1; ++j) {
1078 if (alphas[i + j] > sub2) {
1079 alphas[i + j] -= sub2;
1084 }
else if (alphas[i + j] > 0.) {
1085 alphas[i] += alphas[i + j];
1086 sub2 -= alphas[i + j];
1098 for (
unsigned int j = 0; j < i - 1; ++j) {
1099 if (alphas[i - j] > sub1) {
1100 alphas[i - j] -= sub1;
1105 }
else if (alphas[i - j] > 0.) {
1106 alphas[i] += alphas[i - j];
1107 sub1 -= alphas[i - j];
1114 for (
unsigned int j = 0; j < nPoints - i - 1; ++j) {
1115 if (alphas[i + j] > sub2) {
1116 alphas[i + j] -= sub2;
1121 }
else if (alphas[i + j] > 0.) {
1122 alphas[i] += alphas[i + j];
1123 sub2 -= alphas[i + j];
1129 if (!done)
return false;
1135void AvalancheMC::ComputeSignal(
const double q)
const {
1137 const unsigned int nPoints = m_drift.size();
1138 if (nPoints < 2)
return;
1139 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
1140 const DriftPoint& p0 = m_drift[i];
1141 const DriftPoint& p1 = m_drift[i + 1];
1142 const double dt = p1.t - p0.t;
1143 const double dx = p1.x - p0.x;
1144 const double dy = p1.y - p0.y;
1145 const double dz = p1.z - p0.z;
1146 const double x = p0.x + 0.5 * dx;
1147 const double y = p0.y + 0.5 * dy;
1148 const double z = p0.z + 0.5 * dz;
1149 const double s = 1. / dt;
1150 m_sensor->
AddSignal(q, p0.t, dt, x, y, z,
1151 dx * s, dy * s, dz * s);
1155void AvalancheMC::ComputeInducedCharge(
const double q)
const {
1157 if (m_drift.size() < 2)
return;
1158 const DriftPoint& p0 = m_drift.front();
1159 const DriftPoint& p1 = m_drift.back();
void GetDriftLinePoint(const unsigned int i, double &x, double &y, double &z, double &t) const
Return the coordinates and time of a point along the last drift line.
void EnablePlotting(ViewDrift *view)
Switch on drift line plotting.
void SetTimeSteps(const double d=0.02)
Use fixed-time steps (default 20 ps).
bool AvalancheElectronHole(const double x0, const double y0, const double z0, const double t0)
bool DriftIon(const double x0, const double y0, const double z0, const double t0)
void SetTimeWindow(const double t0, const double t1)
Define a time interval (only carriers inside the interval are drifted).
void GetIonEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
bool DriftElectron(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an electron with a given starting point.
void GetElectronEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
void SetCollisionSteps(const int n=100)
void GetHoleEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
AvalancheMC()
Constructor.
void SetSensor(Sensor *s)
Set the sensor.
bool AvalancheElectron(const double x0, const double y0, const double z0, const double t0, const bool hole=false)
Simulate an avalanche initiated by an electron with given starting point.
void SetDistanceSteps(const double d=0.001)
Use fixed distance steps (default 10 um).
bool AvalancheHole(const double x0, const double y0, const double z0, const double t0, const bool electron=false)
bool DriftHole(const double x0, const double y0, const double z0, const double t0)
virtual void ElectronVelocity(const double, const double, const double, double &vx, double &vy, double &vz, Medium *&, int &status)
Get the electron drift velocity.
virtual Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).
Abstract base class for media.
virtual bool ElectronVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
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)
ComponentBase * GetComponent(const unsigned int componentNumber)
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)
unsigned int GetNumberOfComponents() const
Visualize drift lines and tracks.
void NewElectronDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
void SetDriftLinePoint(const unsigned int iL, const unsigned int iP, const double x, const double y, const double z)
void NewHoleDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
void NewIonDriftLine(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).
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
double RndmGaussian()
Draw a Gaussian random variate with mean zero and standard deviation one.
DoubleAc cos(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)