13std::string PrintVec(
const std::array<double, 3>& x) {
15 return "(" + std::to_string(x[0]) +
", " + std::to_string(x[1]) +
", " +
16 std::to_string(x[2]) +
")";
19double Mag(
const std::array<double, 3>& x) {
21 return sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
24double Mag(
const double x,
const double y,
const double z) {
26 return sqrt(x * x + y * y + z * z);
29double Mag(
const double x,
const double y) {
31 return sqrt(x * x + y * y);
34double Mag2(
const double x,
const double y) {
39double Dist(
const std::array<double, 3>& x0,
40 const std::array<double, 3>& x1) {
42 return Mag(x1[0] - x0[0], x1[1] - x0[1], x1[2] - x0[2]);
48using Vec = std::array<double, 3>;
57 std::cerr << m_className <<
"::SetSensor: Null pointer.\n";
67 std::cerr << m_className <<
"::SetIntegrationAccuracy:\n"
68 <<
" Accuracy must be greater than zero.\n";
75 m_useStepSizeLimit =
true;
77 std::cerr << m_className <<
"::SetMaximumStepSize:\n"
78 <<
" Step size must be greater than zero.\n";
84 std::cerr << m_className <<
"::EnablePlotting: Null pointer.\n";
95 std::cout << m_className <<
"::SetGainFluctuationsFixed: "
96 <<
"Avalanche size set to " << gain <<
".\n";
98 std::cout << m_className <<
"::SetGainFluctuationsFixed:\n "
99 <<
"Avalanche size will be given by "
100 <<
"the integrated Townsend coefficient.\n";
103 m_gainFluctuations = GainFluctuations::None;
110 std::cerr << m_className <<
"::SetGainFluctuationsPolya: "
111 <<
"Shape parameter must be >= 0.\n";
115 std::cout << m_className <<
"::SetGainFluctuationsPolya: "
116 <<
"Mean avalanche size set to " << mean <<
".\n";
118 std::cout << m_className <<
"::SetGainFluctuationsPolya:\n "
119 <<
"Mean avalanche size will be given by "
120 <<
"the integrated Townsend coefficient.\n";
124 m_gainFluctuations = GainFluctuations::Polya;
128 const double z0,
const double t0) {
129 m_particle = Particle::Electron;
130 if (!DriftLine(x0, y0, z0, t0, Particle::Electron, m_t, m_x, m_status)) {
133 std::vector<double> ne(m_t.size(), 1.);
134 std::vector<double> ni(m_t.size(), 0.);
136 if (m_doAvalanche) Avalanche(Particle::Electron, m_x, ne, ni, scale);
138 ComputeSignal(Particle::Electron, scale * m_scaleE, m_t, m_x, ne);
140 if (m_doAvalanche && m_doIonTail) AddIonTail(m_t, m_x, ni, scale);
144bool DriftLineRKF::AddIonTail(
const std::vector<double>& te,
145 const std::vector<std::array<double, 3> >& xe,
146 const std::vector<double>& ni,
147 const double scale) {
149 const unsigned int nPoints = te.size();
150 if (nPoints < 2)
return false;
151 if (ni.size() != nPoints)
return false;
153 for (
unsigned int i = 1; i < nPoints; ++i) {
155 if (scale * ni[i] < 1.)
continue;
159 const auto& x0 = xe[i];
160 std::vector<double> ti;
161 std::vector<std::array<double, 3> > xi;
163 if (!DriftLine(x0[0], x0[1], x0[2], te[i], Particle::Ion, ti, xi, stat)) {
164 std::cerr << m_className <<
"::AddIonTail:\n"
165 <<
" Unable to obtain an ion tail; tail not added.\n";
169 std::cout << m_className <<
"::AddIonTail: Origin = " << PrintVec(x0)
170 <<
", n = " << ti.size() <<
", status = " << stat <<
"\n";
173 ComputeSignal(Particle::Ion, scale * m_scaleI * ni[i], ti, xi, {});
179 const double z0,
const double t0) {
181 m_particle = Particle::Positron;
182 if (!DriftLine(x0, y0, z0, t0, Particle::Positron, m_t, m_x, m_status)) {
186 ComputeSignal(Particle::Positron, m_scaleE, m_t, m_x, {});
193 m_particle = Particle::Hole;
194 if (!DriftLine(x0, y0, z0, t0, Particle::Hole, m_t, m_x, m_status)) {
197 if (m_doSignal) ComputeSignal(Particle::Hole, m_scaleH, m_t, m_x, {});
203 m_particle = Particle::Ion;
204 if (!DriftLine(x0, y0, z0, t0, Particle::Ion, m_t, m_x, m_status)) {
207 if (m_doSignal) ComputeSignal(Particle::Ion, m_scaleI, m_t, m_x, {});
212 const double z0,
const double t0) {
214 m_particle = Particle::NegativeIon;
215 if (!DriftLine(x0, y0, z0, t0, Particle::NegativeIon, m_t, m_x, m_status)) {
219 ComputeSignal(Particle::NegativeIon, m_scaleI, m_t, m_x, {});
224bool DriftLineRKF::DriftLine(
const double xi,
const double yi,
const double zi,
225 const double ti,
const Particle particle,
226 std::vector<double>& ts,
227 std::vector<Vec>& xs,
int& flag) {
253 std::cerr << m_className <<
"::DriftLine: Sensor is not defined.\n";
254 flag = StatusCalculationAbandoned;
259 double xmin = 0., xmax = 0.;
260 double ymin = 0., ymax = 0.;
261 double zmin = 0., zmax = 0.;
262 bool bbox = m_sensor->
GetArea(xmin, ymin, zmin, xmax, ymax, zmax);
265 Vec x0 = {xi, yi, zi};
267 double ex = 0., ey = 0., ez = 0.;
268 double bx = 0., by = 0., bz = 0.;
269 Medium* medium =
nullptr;
270 if (GetField(x0, ex, ey, ez, bx, by, bz, medium) != 0) {
271 std::cerr << m_className <<
"::DriftLine:\n"
272 <<
" No valid field at initial position.\n";
273 flag = StatusLeftDriftMedium;
278 constexpr double c10 = 214. / 891.;
279 constexpr double c11 = 1. / 33.;
280 constexpr double c12 = 650. / 891.;
281 constexpr double c20 = 533. / 2106.;
282 constexpr double c22 = 800. / 1053.;
283 constexpr double c23 = -1. / 78.;
285 constexpr double b10 = 1. / 4.;
286 constexpr double b20 = -189. / 800.;
287 constexpr double b21 = 729. / 800.;
288 constexpr double b30 = 214. / 891.;
289 constexpr double b31 = 1. / 33.;
290 constexpr double b32 = 650. / 891.;
293 const double charge = particle == Particle::Electron ? -1 : 1;
296 Vec v0 = {0., 0., 0.};
297 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, v0)) {
298 std::cerr << m_className <<
"::DriftLine:\n"
299 <<
" Cannot retrieve drift velocity.\n";
303 const double speed0 = Mag(v0);
304 if (speed0 < Small) {
305 std::cerr << m_className <<
"::DriftLine:\n"
306 <<
" Zero velocity at initial position.\n";
311 double h = m_accuracy / speed0;
324 for (
unsigned int i = 0; i < 3; ++i) {
325 x1[i] += h * b10 * v0[i];
329 if (!GetVelocity(x1, particle, v1, stat)) {
330 flag = StatusCalculationAbandoned;
332 }
else if (stat != 0) {
334 std::cout << m_className <<
"::DriftLine: Point 1 outside.\n";
336 if (!Terminate(x0, x1, particle, ts, xs)) {
337 flag = StatusCalculationAbandoned;
345 for (
unsigned int i = 0; i < 3; ++i) {
346 x2[i] += h * (b20 * v0[i] + b21 * v1[i]);
349 if (!GetVelocity(x2, particle, v2, stat)) {
350 flag = StatusCalculationAbandoned;
352 }
else if (stat != 0) {
354 std::cout << m_className <<
"::DriftLine: Point 2 outside.\n";
356 if (!Terminate(x0, x2, particle, ts, xs)) {
357 flag = StatusCalculationAbandoned;
365 for (
unsigned int i = 0; i < 3; ++i) {
366 x3[i] += h * (b30 * v0[i] + b31 * v1[i] + b32 * v2[i]);
369 if (!GetVelocity(x3, particle, v3, stat)) {
370 flag = StatusCalculationAbandoned;
372 }
else if (stat != 0) {
374 std::cout << m_className <<
"::DriftLine: Point 3 outside.\n";
376 if (!Terminate(x0, x3, particle, ts, xs)) {
377 flag = StatusCalculationAbandoned;
384 double xw = 0., yw = 0., zw = 0., rw = 0.;
386 x1[0], x1[1], x1[2], xw, yw, zw,
true, rw) ||
388 x2[0], x2[1], x2[2], xw, yw, zw,
true, rw) ||
390 x3[0], x3[1], x3[2], xw, yw, zw,
true, rw)) {
392 std::cout << m_className <<
"::DriftLine: Crossed wire.\n";
394 if (DriftToWire(xw, yw, rw, particle, ts, xs, stat)) {
396 }
else if (h > Small) {
400 std::cerr << m_className <<
"::DriftLine: Step size too small. Stop.\n";
401 flag = StatusCalculationAbandoned;
406 if (particle != Particle::Ion) {
407 if (m_sensor->
IsInTrapRadius(charge, x1[0], x1[1], x1[2], xw, yw, rw)) {
408 if (!DriftToWire(xw, yw, rw, particle, ts, xs, flag)) {
409 flag = StatusCalculationAbandoned;
413 if (m_sensor->
IsInTrapRadius(charge, x2[0], x2[1], x2[2], xw, yw, rw)) {
414 if (!DriftToWire(xw, yw, rw, particle, ts, xs, flag)) {
415 flag = StatusCalculationAbandoned;
419 if (m_sensor->
IsInTrapRadius(charge, x3[0], x3[1], x3[2], xw, yw, rw)) {
420 if (!DriftToWire(xw, yw, rw, particle, ts, xs, flag)) {
421 flag = StatusCalculationAbandoned;
427 Vec phi1 = {0., 0., 0.};
428 Vec phi2 = {0., 0., 0.};
429 for (
unsigned int i = 0; i < 3; ++i) {
430 phi1[i] = c10 * v0[i] + c11 * v1[i] + c12 * v2[i];
431 phi2[i] = c20 * v0[i] + c22 * v2[i] + c23 * v3[i];
434 const double phi1mag = Mag(phi1);
435 if (phi1mag < Small) {
436 std::cerr << m_className <<
"::DriftLine: Step has zero length. Stop.\n";
437 flag = StatusCalculationAbandoned;
439 }
else if (m_useStepSizeLimit && h * phi1mag > m_maxStepSize) {
441 std::cout << m_className <<
"::DriftLine: Step is considered too long. "
442 <<
"H is reduced.\n";
444 h = 0.5 * m_maxStepSize / phi1mag;
448 if (h *
fabs(phi1[0]) > 0.1 *
fabs(xmax - xmin) ||
449 h *
fabs(phi1[1]) > 0.1 *
fabs(ymax - ymin)) {
452 std::cout << m_className <<
"::DriftLine: Step is considered too long. "
453 <<
"H is divided by two.\n";
457 }
else if (m_rejectKinks && xs.size() > 1) {
458 const unsigned int np = xs.size();
459 const auto&
x = xs[np - 1];
460 const auto& xp = xs[np - 2];
461 if (phi1[0] * (x[0] - xp[0]) + phi1[1] * (x[1] - xp[1]) +
462 phi1[2] * (x[2] - xp[2]) < 0.) {
463 std::cerr << m_className <<
"::DriftLine: Bending angle > 90 degree.\n";
464 flag = StatusSharpKink;
468 if (m_debug) std::cout << m_className <<
"::DriftLine: Step size ok.\n";
470 for (
unsigned int i = 0; i < 3; ++i) x0[i] += h * phi1[i];
473 m_sensor->
ElectricField(x0[0], x0[1], x0[2], ex, ey, ez, medium, stat);
478 std::cout << m_className <<
"::DriftLine: Point outside. Terminate.\n";
480 if (!Terminate(xs.back(), x0, particle, ts, xs)) {
481 flag = StatusCalculationAbandoned;
490 const double dphi =
fabs(phi1[0] - phi2[0]) +
fabs(phi1[1] - phi2[1]) +
491 fabs(phi1[2] - phi2[2]);
493 h =
sqrt(h * m_accuracy / dphi);
495 std::cout << m_className <<
"::DriftLine: Adapting H to " << h <<
".\n";
500 std::cout << m_className <<
"::DriftLine: H increased by factor two.\n";
505 std::cerr << m_className <<
"::DriftLine: Step size is zero. Stop.\n";
506 flag = StatusCalculationAbandoned;
510 if (initCycle > 0 && h < 0.2 * hprev) {
512 std::cout << m_className <<
"::DriftLine: Reinitialise step size.\n";
523 if (h > 10 * hprev) {
526 std::cout << m_className <<
"::DriftLine: H restricted to 10 times "
527 <<
"the previous value.\n";
531 if (h * (
fabs(phi1[0]) +
fabs(phi1[1]) +
fabs(phi1[2])) < m_accuracy) {
532 std::cerr << m_className <<
"::DriftLine: Step size has become smaller "
533 <<
"than int. accuracy. Stop.\n";
534 flag = StatusCalculationAbandoned;
543 const size_t nPoints = m_x.size();
544 if (particle == Particle::Ion || particle == Particle::NegativeIon) {
546 }
else if (particle == Particle::Electron ||
547 particle == Particle::Positron) {
549 }
else if (particle == Particle::Hole) {
552 for (
size_t i = 0; i < nPoints; ++i) {
553 const auto&
x = m_x[i];
557 if (flag == StatusCalculationAbandoned)
return false;
561bool DriftLineRKF::Avalanche(
const Particle particle,
562 const std::vector<Vec>& xs,
563 std::vector<double>& ne,
564 std::vector<double>& ni,
double& scale) {
567 const unsigned int nPoints = xs.size();
568 if (nPoints < 2)
return true;
570 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
571 -0.238619186083196909, 0.238619186083196909,
572 0.661209386466264514, 0.932469514203152028};
573 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
574 0.467913934572691047, 0.467913934572691047,
575 0.360761573048138608, 0.171324492379170345};
577 ne.assign(nPoints, 1.);
578 ni.assign(nPoints, 0.);
580 bool overflow =
false;
582 for (
unsigned int i = 1; i < nPoints; ++i) {
583 const auto& xp = xs[i - 1];
584 const auto&
x = xs[i];
585 const Vec dx = {
x[0] - xp[0],
x[1] - xp[1],
x[2] - xp[2]};
589 for (
unsigned int j = 0; j < 6; ++j) {
590 const double f = 0.5 * (1. + tg[j]);
592 for (
unsigned int k = 0; k < 3; ++k) xj[k] += f * dx[k];
593 double ex = 0., ey = 0., ez = 0.;
594 double bx = 0., by = 0., bz = 0.;
595 Medium* medium =
nullptr;
596 if (GetField(xj, ex, ey, ez, bx, by, bz, medium) != 0) {
597 std::cerr << m_className <<
"::Avalanche:\n Invalid field at "
598 <<
"drift line point " << i <<
", segment " << j <<
".\n";
602 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alp)) {
603 std::cerr << m_className <<
"::Avalanche:\n Cannot retrieve alpha at "
604 <<
"drift line point " << i <<
", segment " << j <<
".\n";
608 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, eta)) {
609 std::cerr << m_className <<
"::Avalanche:\n Cannot retrieve eta at "
610 <<
"drift line point " << i <<
", segment " << j <<
".\n";
613 alpsum += wg[j] * alp;
614 etasum += wg[j] * eta;
618 if (alpsum > 1.e-6 && !start) {
620 std::cout << m_className <<
"::Avalanche: Avalanche starts at step "
625 const double d = Mag(dx);
627 constexpr double expmax = 30.;
628 const double logp = log(std::max(1., ne[i - 1]));
629 if (logp + d * (alpsum - etasum) > expmax) {
633 ne[i] = ne[i - 1] *
exp(d * (alpsum - etasum));
635 if (logp + d * alpsum > expmax) {
639 ni[i] = ne[i - 1] * (
exp(d * alpsum) - 1);
643 std::cerr << m_className <<
"::Avalanche:\n "
644 <<
"Warning: Integrating the Townsend coefficients "
645 <<
"would lead to exponential overflow.\n "
646 <<
"Avalanche truncated.\n";
648 const double qe = ne.back();
649 const double qi = std::accumulate(ni.begin(), ni.end(), 0.);
652 !(m_gainFluctuations == GainFluctuations::None && m_gain < 1.)) {
653 const double gain = m_gain > 1. ? m_gain :
GetGain();
655 if (m_gainFluctuations == GainFluctuations::Polya) {
656 for (
unsigned int i = 0; i < 100; ++i) {
660 q1 = std::max(q1, 1.);
663 scale = (q1 + 1.) / (qi + 1.);
666 std::cout << m_className <<
"::Avalanche:\n "
667 <<
"Final number of electrons: " << qe <<
"\n "
668 <<
"Number of ions: " << qi <<
"\n "
669 <<
"Charge scaling factor: " << scale <<
"\n "
670 <<
"Avalanche development:\n Step Electrons Ions\n";
671 for (
unsigned int i = 0; i < nPoints; ++i) {
672 std::printf(
"%6d %15.7f %15.7f\n", i, scale * ne[i], scale * ni[i]);
688 const unsigned int nPoints = m_x.size();
690 if (nPoints < 2)
return 0.;
691 const Particle particle = m_particle;
696 for (
unsigned int i = 0; i < nPoints; ++i) {
698 double ex = 0., ey = 0., ez = 0.;
699 double bx = 0., by = 0., bz = 0.;
701 if (GetField(m_x[i], ex, ey, ez, bx, by, bz, medium) != 0) {
702 std::cerr << m_className <<
"::GetArrivalTimeSpread:\n"
703 <<
" Invalid drift line point " << i <<
".\n";
707 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, v)) {
708 std::cerr << m_className <<
"::GetArrivalTimeSpread:\n"
709 <<
" Cannot retrieve drift velocity at point " << i <<
".\n";
712 const double speed = Mag(v);
714 std::cerr << m_className <<
"::GetArrivalTimeSpread:\n"
715 <<
" Zero drift velocity at point " << i <<
".\n";
718 double dl = 0., dt = 0.;
719 if (!GetDiffusion(ex, ey, ez, bx, by, bz, medium, particle, dl, dt)) {
720 std::cerr << m_className <<
"::GetArrivalTimeSpread:\n"
721 <<
" Cannot retrieve diffusion at point " << i <<
".\n";
724 const double sigma = dl / speed;
725 const double var = sigma * sigma;
727 const auto& x = m_x[i];
728 const auto& xPrev = m_x[i - 1];
729 const double d = Mag(x[0] - xPrev[0], x[1] - xPrev[1], x[2] - xPrev[2]);
730 crude += 0.5 * d * (var + varPrev);
736 const double tol = eps * crude;
738 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
739 sum += IntegrateDiffusion(m_x[i], m_x[i + 1], particle, tol);
752 const unsigned int nPoints = m_x.size();
754 if (nPoints < 2)
return 1.;
755 const Particle particle = m_particle;
756 if (particle == Particle::Ion)
return 1.;
757 if (m_status == StatusCalculationAbandoned)
return 1.;
761 double alphaPrev = 0.;
762 for (
unsigned int i = 0; i < nPoints; ++i) {
764 double ex = 0., ey = 0., ez = 0.;
765 double bx = 0., by = 0., bz = 0.;
767 if (GetField(m_x[i], ex, ey, ez, bx, by, bz, medium) != 0) {
768 std::cerr << m_className <<
"::GetGain:\n"
769 <<
" Invalid drift line point " << i <<
".\n";
773 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alpha)) {
774 std::cerr << m_className <<
"::GetGain:\n"
775 <<
" Cannot retrieve alpha at point " << i <<
".\n";
779 const auto& x = m_x[i];
780 const auto& xPrev = m_x[i - 1];
781 const double d = Mag(x[0] - xPrev[0], x[1] - xPrev[1], x[2] - xPrev[2]);
782 crude += 0.5 * d * (alpha + alphaPrev);
787 if (crude < Small)
return 1.;
790 const double tol = eps * crude;
792 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
793 sum += IntegrateAlpha(m_x[i], m_x[i + 1], particle, tol);
806 const unsigned int nPoints = m_x.size();
808 if (nPoints < 2)
return 1.;
809 const Particle particle = m_particle;
810 if (particle == Particle::Ion)
return 1.;
811 if (m_status == StatusCalculationAbandoned)
return 1.;
816 for (
unsigned int i = 0; i < nPoints; ++i) {
818 double ex = 0., ey = 0., ez = 0.;
819 double bx = 0., by = 0., bz = 0.;
821 if (GetField(m_x[i], ex, ey, ez, bx, by, bz, medium) != 0) {
822 std::cerr << m_className <<
"::GetLoss:\n"
823 <<
" Invalid drift line point " << i <<
".\n";
827 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, eta)) {
828 std::cerr << m_className <<
"::GetLoss:\n"
829 <<
" Cannot retrieve eta at point " << i <<
".\n";
833 const auto& x = m_x[i];
834 const auto& xPrev = m_x[i - 1];
835 const double d = Mag(x[0] - xPrev[0], x[1] - xPrev[1], x[2] - xPrev[2]);
836 crude += 0.5 * d * (eta + etaPrev);
842 const double tol = eps * crude;
844 for (
unsigned int i = 0; i < nPoints - 1; ++i) {
845 sum += IntegrateEta(m_x[i], m_x[i + 1], particle, tol);
850int DriftLineRKF::GetField(
const std::array<double, 3>& x,
851 double& ex,
double& ey,
double& ez,
852 double& bx,
double& by,
double& bz,
855 m_sensor->
MagneticField(x[0], x[1], x[2], bx, by, bz, status);
856 m_sensor->
ElectricField(x[0], x[1], x[2], ex, ey, ez, medium, status);
860bool DriftLineRKF::GetVelocity(
const std::array<double, 3>& x,
861 const Particle particle,
862 std::array<double, 3>& v,
int& status)
const {
864 double ex = 0., ey = 0., ez = 0.;
865 double bx = 0., by = 0., bz = 0.;
866 Medium* medium =
nullptr;
868 status = GetField(x, ex, ey, ez, bx, by, bz, medium);
869 if (status != 0)
return true;
871 if (!m_sensor->
IsInArea(x[0], x[1], x[2])) {
872 status = StatusLeftDriftArea;
875 if (particle == Particle::Electron) {
876 return medium->ElectronVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
877 }
else if (particle == Particle::Ion) {
878 return medium->IonVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
879 }
else if (particle == Particle::Hole) {
880 return medium->HoleVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
881 }
else if (particle == Particle::Positron) {
882 const bool ok = medium->ElectronVelocity(ex, ey, ez, bx, by, bz,
884 for (
unsigned int i = 0; i < 3; ++i) v[i] *= -1;
886 }
else if (particle == Particle::NegativeIon) {
887 const bool ok = medium->IonVelocity(ex, ey, ez, bx, by, bz,
889 for (
unsigned int i = 0; i < 3; ++i) v[i] *= -1;
892 std::cerr << m_className <<
"::GetVelocity:\n"
893 <<
" Cannot retrieve drift velocity at " << PrintVec(x) <<
".\n";
897bool DriftLineRKF::GetVelocity(
const double ex,
const double ey,
898 const double ez,
const double bx,
899 const double by,
const double bz,
900 Medium* medium,
const Particle particle,
901 std::array<double, 3>& v)
const {
903 if (particle == Particle::Electron) {
904 return medium->ElectronVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
905 }
else if (particle == Particle::Ion) {
906 return medium->IonVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
907 }
else if (particle == Particle::Hole) {
908 return medium->HoleVelocity(ex, ey, ez, bx, by, bz, v[0], v[1], v[2]);
909 }
else if (particle == Particle::Positron) {
910 const bool ok = medium->ElectronVelocity(ex, ey, ez, bx, by, bz,
912 for (
unsigned int i = 0; i < 3; ++i) v[i] *= -1;
914 }
else if (particle == Particle::NegativeIon) {
915 const bool ok = medium->IonVelocity(ex, ey, ez, bx, by, bz,
917 for (
unsigned int i = 0; i < 3; ++i) v[i] *= -1;
923bool DriftLineRKF::GetDiffusion(
const double ex,
const double ey,
924 const double ez,
const double bx,
925 const double by,
const double bz,
926 Medium* medium,
const Particle particle,
927 double& dl,
double& dt)
const {
928 if (particle == Particle::Electron || particle == Particle::Positron) {
929 return medium->ElectronDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
930 }
else if (particle == Particle::Ion || particle == Particle::NegativeIon) {
931 return medium->IonDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
932 }
else if (particle == Particle::Hole) {
933 return medium->HoleDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
938bool DriftLineRKF::GetAlpha(
const double ex,
const double ey,
const double ez,
939 const double bx,
const double by,
const double bz,
940 Medium* medium,
const Particle particle,
941 double& alpha)
const {
942 if (particle == Particle::Electron || particle == Particle::Positron) {
943 return medium->ElectronTownsend(ex, ey, ez, bx, by, bz, alpha);
944 }
else if (particle == Particle::Hole) {
945 return medium->HoleTownsend(ex, ey, ez, bx, by, bz, alpha);
950bool DriftLineRKF::GetEta(
const double ex,
const double ey,
const double ez,
951 const double bx,
const double by,
const double bz,
952 Medium* medium,
const Particle particle,
954 if (particle == Particle::Electron) {
955 return medium->ElectronAttachment(ex, ey, ez, bx, by, bz, eta);
956 }
else if (particle == Particle::Hole) {
957 return medium->HoleAttachment(ex, ey, ez, bx, by, bz, eta);
962bool DriftLineRKF::Terminate(
const std::array<double, 3>& xx0,
963 const std::array<double, 3>& xx1,
964 const Particle particle,
965 std::vector<double>& ts,
966 std::vector<Vec>& xs) {
976 Vec vv0 = {0., 0., 0.};
978 if (!GetVelocity(xx0, particle, vv0, status)) {
979 std::cerr << m_className <<
"::Terminate:\n"
980 <<
" Cannot retrieve initial drift velocity.\n";
982 }
else if (status != 0) {
983 std::cerr << m_className <<
"::Terminate:\n"
984 <<
" No valid field at initial point. Program bug!\n";
987 double speed = Mag(vv0);
989 std::cerr << m_className <<
"::Terminate:\n"
990 <<
" Zero velocity at initial position.\n";
999 const unsigned int nBisections = 20;
1000 for (
unsigned int i = 0; i < nBisections; ++i) {
1003 for (
unsigned int j = 0; j < 3; ++j) {
1004 if (
fabs(x1[j] - x0[j]) > 1.e-6 * (
fabs(x0[j]) +
fabs(x1[j]))) {
1011 std::cout << m_className <<
"::Terminate:\n"
1012 <<
" Bisection ended at cycle " << i <<
".\n";
1018 for (
unsigned int j = 0; j < 3; ++j) xm[j] = 0.5 * (x0[j] + x1[j]);
1019 double ex = 0., ey = 0., ez = 0.;
1020 Medium* medium =
nullptr;
1021 m_sensor->
ElectricField(xm[0], xm[1], xm[2], ex, ey, ez, medium, status);
1022 if (!m_sensor->
IsInArea(xm[0], xm[1], xm[2])) status = -1;
1032 if (!GetVelocity(x0, particle, v0, status) || status != 0) {
1033 std::cerr << m_className <<
"::Terminate:\n"
1034 <<
" Warning: Unable to compute mean velocity at last step.\n";
1036 speed = 0.5 * (speed + Mag(v0));
1039 const double dt = Mag(x0[0] - xx0[0], x0[1] - xx0[1], x0[2] - xx0[2]) / speed;
1041 ts.push_back(ts.back() + dt);
1043 m_status = StatusLeftDriftMedium;
1047bool DriftLineRKF::DriftToWire(
const double xw,
const double yw,
1048 const double rw,
const Particle particle,
1049 std::vector<double>& ts,
1050 std::vector<Vec>& xs,
int& stat) {
1061 double t0 = ts.back() - ts.front();
1063 std::cout << m_className <<
"::DriftToWire:\n Drifting from ("
1064 << x0[0] <<
", " << x0[1] <<
") to wire at ("
1065 << xw <<
", " << yw <<
") with radius " << rw <<
" cm.\n";
1071 if (!GetVelocity(x0, particle, v0, status)) {
1072 std::cerr << m_className <<
"::DriftToWire:\n"
1073 <<
" Cannot retrieve initial drift velocity.\n";
1075 }
else if (status != 0) {
1076 std::cerr << m_className <<
"::DriftToWire:\n"
1077 <<
" No valid field at initial point. Program bug!\n";
1083 double dt = (Mag(xw - x0[0], yw - x0[1]) - rw) / Mag(v0[0], v0[1]);
1085 std::cout << m_className <<
"::DriftToWire: "
1086 <<
"Estimated time needed to reach the wire: " << dt <<
" ns.\n";
1089 constexpr unsigned int nMaxSplit = 10;
1090 unsigned int nSplit = 0;
1092 bool onwire =
false;
1093 const double r2 = rw * rw;
1094 while (!onwire && dt > 1.e-6 * t0) {
1097 for (
unsigned int j = 0; j < 3; ++j) x1[j] += dt * v0[j];
1099 const double xinp0 = (x1[0] - x0[0]) * (xw - x0[0]) +
1100 (x1[1] - x0[1]) * (yw - x0[1]);
1103 std::cerr << m_className <<
"::DriftToWire:\n"
1104 <<
" Particle moves away from the wire. Quit.\n";
1109 const double xinp1 = (x0[0] - x1[0]) * (xw - x1[0]) +
1110 (x0[1] - x1[1]) * (yw - x1[1]);
1112 if (Mag2(xw - x1[0], yw - x1[1]) <= r2) {
1114 if (m_debug) std::cout << m_className <<
"::DriftToWire: Inside.\n";
1117 if (m_debug) std::cout << m_className <<
"::DriftToWire: Wire crossed.\n";
1121 const double dw0 = Mag(xw - x0[0], yw - x0[1]);
1122 x1[0] = xw - (rw + BoundaryDistance) * (xw - x0[0]) / dw0;
1123 x1[1] = yw - (rw + BoundaryDistance) * (yw - x0[1]) / dw0;
1124 dt = Mag(x1[0] - x0[0], x1[1] - x0[1]) / Mag(v0[0], v0[1]);
1125 x1[2] = x0[2] + dt * v0[2];
1129 if (!GetVelocity(x1, particle, v1, status)) {
1130 std::cerr << m_className <<
"::DriftToWire:\n"
1131 <<
" Cannot retrieve drift velocity at end point. Quit.\n";
1133 }
else if (status != 0) {
1134 std::cerr << m_className <<
"::DriftToWire:\n"
1135 <<
" End point is not in a valid drift medium. Quit.\n";
1140 for (
unsigned int j = 0; j < 3; ++j) xm[j] = 0.5 * (x0[j] + x1[j]);
1143 if (!GetVelocity(xm, particle, vm, status)) {
1144 std::cerr << m_className <<
"::DriftToWire:\n"
1145 <<
" Cannot retrieve drift velocity at mid point. Quit.\n";
1147 }
else if (status != 0) {
1148 std::cerr << m_className <<
"::DriftToWire:\n"
1149 <<
" Mid point is not in a valid drift medium. Quit.\n";
1153 const double speed0 = Mag(v0[0], v0[1]);
1154 const double speed1 = Mag(v1[0], v1[1]);
1155 const double speedm = Mag(vm[0], vm[1]);
1156 if (speed0 < Small || speed1 < Small || speedm < Small) {
1157 std::cerr << m_className <<
"::DriftToWire: Zero velocity. Stop.\n";
1160 const double p0 = 1. / speed0;
1161 const double p1 = 1. / speed1;
1162 const double pm = 1. / speedm;
1164 const double d = Mag(x0[0] - x1[0], x0[1] - x1[1]);
1165 const double tol = 1.e-4 * (1. +
fabs(t0));
1166 if (d *
fabs(p0 - 2 * pm + p1) / 3. > tol && nSplit < nMaxSplit) {
1169 std::cout << m_className <<
"::DriftToWire: Reducing step size.\n";
1176 const double t1 = t0 + d * (p0 + 4 * pm + p1) / 6.;
1178 ts.push_back(ts[0] + t1);
1186 double ex = 0., ey = 0., ez = 0.;
1187 Medium* medium =
nullptr;
1188 m_sensor->
ElectricField(xw, yw, 0., ex, ey, ez, medium, stat);
1194 std::cout << m_className <<
"::PrintDriftLine:\n";
1196 std::cout <<
" No drift line present.\n";
1199 if (m_particle == Particle::Electron) {
1200 std::cout <<
" Particle: electron\n";
1201 }
else if (m_particle == Particle::Ion) {
1202 std::cout <<
" Particle: ion\n";
1203 }
else if (m_particle == Particle::Hole) {
1204 std::cout <<
" Particle: hole\n";
1206 std::cout <<
" Particle: unknown\n";
1208 std::cout <<
" Status: " << m_status <<
"\n"
1209 <<
" Step time [ns] "
1210 <<
"x [cm] y [cm] z [cm]\n";
1211 const unsigned int nPoints = m_x.size();
1212 for (
unsigned int i = 0; i < nPoints; ++i) {
1213 std::printf(
"%6d %15.7f %15.7f %15.7f %15.7f\n",
1214 i, m_t[i], m_x[i][0], m_x[i][1], m_x[i][2]);
1226 const auto& p = m_x.back();
1235 double& z,
double& t)
const {
1236 if (i >= m_x.size()) {
1237 std::cerr << m_className <<
"::GetDriftLinePoint: Index out of range.\n";
1241 const auto& p = m_x[i];
1248double DriftLineRKF::IntegrateDiffusion(
const std::array<double, 3>& xi,
1249 const std::array<double, 3>& xe,
1250 const Particle particle,
1253 double ex = 0., ey = 0., ez = 0.;
1254 double bx = 0., by = 0., bz = 0.;
1255 Medium* medium =
nullptr;
1259 if (GetField(x0, ex, ey, ez, bx, by, bz, medium) != 0) {
1260 std::cerr << m_className <<
"::IntegrateDiffusion: Invalid starting point "
1261 << PrintVec(xi) <<
".\n";
1267 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, v0)) {
1268 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1269 <<
" Cannot retrieve drift velocity at initial point.\n";
1272 double speed0 = Mag(v0);
1273 if (speed0 < Small) {
1274 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1275 <<
" Zero velocity at starting point.\n";
1279 double dl0 = 0., dt0 = 0.;
1280 if (!GetDiffusion(ex, ey, ez, bx, by, bz, medium, particle, dl0, dt0)) {
1281 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1282 <<
" Cannot retrieve diffusion at initial point.\n";
1285 const double sigma0 = dl0 / speed0;
1286 double var0 = sigma0 * sigma0;
1290 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1291 std::cerr << m_className <<
"::IntegrateDiffusion: Invalid end point "
1292 << PrintVec(xe) <<
".\n";
1297 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, v1)) {
1298 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1299 <<
" Cannot retrieve drift velocity at end point.\n";
1302 double speed1 = Mag(v1);
1303 if (speed1 < Small) {
1304 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1305 <<
" Zero velocity at end point.\n";
1309 double dl1 = 0., dt1 = 0.;
1310 if (!GetDiffusion(ex, ey, ez, bx, by, bz, medium, particle, dl1, dt1)) {
1311 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1312 <<
" Cannot retrieve diffusion at initial point.\n";
1315 double sigma1 = dl1 / speed1;
1316 double var1 = sigma1 * sigma1;
1318 double integral = 0.;
1319 while (Dist(xe, x0) > 1.e-6) {
1320 const double d = Dist(x1, x0);
1324 std::cout << m_className <<
"::IntegrateDiffusion: Small step.\n";
1326 integral += var0 * d;
1333 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1334 std::cerr << m_className <<
"::IntegrateDiffusion: Invalid end point.\n";
1337 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, v1)) {
1338 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1339 <<
" Cannot retrieve drift velocity at end point.\n";
1343 if (speed1 < Small) {
1344 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1345 <<
" Zero drift velocity at end point.\n";
1348 if (!GetDiffusion(ex, ey, ez, bx, by, bz, medium, particle, dl1, dt1)) {
1349 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1350 <<
" Cannot retrieve diffusion at end point.\n";
1355 for (
unsigned int i = 0; i < 3; ++i) xm[i] = 0.5 * (x0[i] + x1[i]);
1356 if (GetField(xm, ex, ey, ez, bx, by, bz, medium) != 0) {
1357 std::cerr << m_className <<
"::IntegrateDiffusion: Invalid mid point.\n";
1361 if (!GetVelocity(ex, ey, ez, bx, by, bz, medium, particle, vm)) {
1362 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1363 <<
" Cannot retrieve drift velocity at mid point.\n";
1366 const double speedm = Mag(vm);
1367 if (speedm < Small) {
1368 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1369 <<
" Zero drift velocity at mid point.\n";
1372 double dlm = 0., dtm = 0.;
1373 if (!GetDiffusion(ex, ey, ez, bx, by, bz, medium, particle, dlm, dtm)) {
1374 std::cerr << m_className <<
"::IntegrateDiffusion:\n"
1375 <<
" Cannot retrieve diffusion at mid point.\n";
1378 sigma1 = dl1 / speed1;
1379 var1 = sigma1 * sigma1;
1380 const double sigmam = dlm / speedm;
1381 const double varm = sigmam * sigmam;
1384 if (
fabs(var0 - 2 * varm + var1) *
sqrt(d * 2 / (var0 + var1)) / 6. < tol) {
1386 integral += d * (var0 + 4 * varm + var1) / 6.;
1400double DriftLineRKF::IntegrateAlpha(
const std::array<double, 3>& xi,
1401 const std::array<double, 3>& xe,
1402 const Particle particle,
const double tol) {
1404 double ex = 0., ey = 0., ez = 0.;
1405 double bx = 0., by = 0., bz = 0.;
1406 Medium* medium =
nullptr;
1410 if (GetField(x0, ex, ey, ez, bx, by, bz, medium) != 0) {
1411 std::cerr << m_className <<
"::IntegrateAlpha: Invalid starting point "
1412 << PrintVec(xi) <<
".\n";
1417 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alpha0)) {
1418 std::cerr << m_className <<
"::IntegrateAlpha:\n"
1419 <<
" Cannot retrieve Townsend coefficient at initial point.\n";
1424 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1425 std::cerr << m_className <<
"::IntegrateAlpha: Invalid end point "
1426 << PrintVec(xe) <<
".\n";
1431 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alpha1)) {
1432 std::cerr << m_className <<
"::IntegrateAlpha:\n"
1433 <<
" Cannot retrieve Townsend coefficient at end point.\n";
1436 double integral = 0.;
1437 while (Dist(xe, x0) > 1.e-6) {
1438 const double d = Dist(x1, x0);
1442 std::cout << m_className <<
"::IntegrateAlpha: Small step.\n";
1444 integral += alpha0 * d;
1451 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1452 std::cerr << m_className <<
"::IntegrateAlpha: Invalid end point.\n";
1455 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alpha1)) {
1456 std::cerr << m_className <<
"::IntegrateAlpha:\n"
1457 <<
" Cannot retrieve Townsend coefficient at end point.\n";
1462 for (
unsigned int i = 0; i < 3; ++i) xm[i] = 0.5 * (x0[i] + x1[i]);
1463 if (GetField(xm, ex, ey, ez, bx, by, bz, medium) != 0) {
1464 std::cerr << m_className <<
"::IntegrateAlpha: Invalid mid point.\n";
1468 if (!GetAlpha(ex, ey, ez, bx, by, bz, medium, particle, alpham)) {
1469 std::cerr << m_className <<
"::IntegrateAlpha:\n"
1470 <<
" Cannot retrieve Townsend coefficient at mid point.\n";
1474 if (d *
fabs(alpha0 - 2 * alpham + alpha1) / 3. < tol) {
1476 integral += d * (alpha0 + 4 * alpham + alpha1) / 6.;
1490double DriftLineRKF::IntegrateEta(
const std::array<double, 3>& xi,
1491 const std::array<double, 3>& xe,
1492 const Particle particle,
const double tol) {
1494 double ex = 0., ey = 0., ez = 0.;
1495 double bx = 0., by = 0., bz = 0.;
1496 Medium* medium =
nullptr;
1500 if (GetField(x0, ex, ey, ez, bx, by, bz, medium) != 0) {
1501 std::cerr << m_className <<
"::IntegrateEta: Invalid starting point "
1502 << PrintVec(xi) <<
".\n";
1507 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, eta0)) {
1508 std::cerr << m_className <<
"::IntegrateEta:\n"
1509 <<
" Cannot retrieve att. coefficient at initial point.\n";
1514 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1515 std::cerr << m_className <<
"::IntegrateEta: Invalid end point "
1516 << PrintVec(xe) <<
".\n";
1521 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, eta1)) {
1522 std::cerr << m_className <<
"::IntegrateEta:\n"
1523 <<
" Cannot retrieve att. coefficient at end point.\n";
1526 double integral = 0.;
1527 while (Dist(xe, x0) > 1.e-6) {
1528 const double d = Dist(x1, x0);
1532 std::cout << m_className <<
"::IntegrateEta: Small step.\n";
1534 integral += eta0 * d;
1541 if (GetField(x1, ex, ey, ez, bx, by, bz, medium) != 0) {
1542 std::cerr << m_className <<
"::IntegrateEta: Invalid end point.\n";
1545 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, eta1)) {
1546 std::cerr << m_className <<
"::IntegrateEta:\n"
1547 <<
" Cannot retrieve att. coefficient at end point.\n";
1552 for (
unsigned int i = 0; i < 3; ++i) xm[i] = 0.5 * (x0[i] + x1[i]);
1553 if (GetField(xm, ex, ey, ez, bx, by, bz, medium) != 0) {
1554 std::cerr << m_className <<
"::IntegrateEta: Invalid mid point.\n";
1558 if (!GetEta(ex, ey, ez, bx, by, bz, medium, particle, etam)) {
1559 std::cerr << m_className <<
"::IntegrateEta:\n"
1560 <<
" Cannot retrieve att. coefficient at mid point.\n";
1564 if (d *
fabs(eta0 - 2 * etam + eta1) / 3. < tol) {
1566 integral += d * (eta0 + 4 * etam + eta1) / 6.;
1580void DriftLineRKF::ComputeSignal(
const Particle particle,
const double scale,
1581 const std::vector<double>& ts,
1582 const std::vector<Vec>& xs,
1583 const std::vector<double>& ne)
const {
1585 const unsigned int nPoints = ts.size();
1586 if (nPoints < 2)
return;
1587 const double q = particle == Particle::Electron ? -1 * scale : scale;
1590 std::vector<std::array<double, 3> > vs;
1591 for (
const auto& x : xs) {
1594 if (!GetVelocity(x, particle, v, stat)) {
1595 std::cerr << m_className <<
"::ComputeSignal:\n"
1596 <<
" Cannot retrieve velocity at " << PrintVec(x) <<
"\n";
1598 vs.push_back(std::move(v));
1600 m_sensor->
AddSignal(q, ts, xs, vs, ne, m_navg);
1604 std::vector<std::array<float, 3> >& xl,
1605 const bool electron) {
1611 std::cerr << m_className <<
"::FieldLine: Sensor is not defined.\n";
1616 double xmin = 0., xmax = 0.;
1617 double ymin = 0., ymax = 0.;
1618 double zmin = 0., zmax = 0.;
1619 bool bbox = m_sensor->
GetArea(xmin, ymin, zmin, xmax, ymax, zmax);
1622 double ex = 0., ey = 0., ez = 0.;
1623 Medium* medium =
nullptr;
1625 m_sensor->
ElectricField(xi, yi, zi, ex, ey, ez, medium, stat);
1626 if (!medium || stat != 0) {
1627 std::cerr << m_className <<
"::FieldLine:\n"
1628 <<
" No valid field at initial position.\n";
1631 Vec x0 = {xi, yi, zi};
1632 Vec f0 = {ex, ey, ez};
1633 if (electron)
for (
auto& f : f0) f *= -1;
1636 constexpr double c10 = 214. / 891.;
1637 constexpr double c11 = 1. / 33.;
1638 constexpr double c12 = 650. / 891.;
1639 constexpr double c20 = 533. / 2106.;
1640 constexpr double c22 = 800. / 1053.;
1641 constexpr double c23 = -1. / 78.;
1643 constexpr double b10 = 1. / 4.;
1644 constexpr double b20 = -189. / 800.;
1645 constexpr double b21 = 729. / 800.;
1646 constexpr double b30 = 214. / 891.;
1647 constexpr double b31 = 1. / 33.;
1648 constexpr double b32 = 650. / 891.;
1650 const double fmag0 = Mag(f0);
1651 if (fmag0 < Small) {
1652 std::cerr << m_className <<
"::FieldLine:\n"
1653 <<
" Zero field at initial position.\n";
1658 double h = m_accuracy / fmag0;
1662 xl.push_back({float(x0[0]), float(x0[1]), float(x0[2])});
1669 for (
unsigned int i = 0; i < 3; ++i) {
1670 x1[i] += h * b10 * f0[i];
1672 m_sensor->
ElectricField(x1[0], x1[1], x1[2], ex, ey, ez, medium, stat);
1675 std::cout << m_className <<
"::FieldLine: Point 1 outside.\n";
1677 Terminate(x0, x1, xl);
1680 Vec f1 = {ex, ey, ez};
1681 if (electron)
for (
auto& f : f1) f *= -1;
1684 for (
unsigned int i = 0; i < 3; ++i) {
1685 x2[i] += h * (b20 * f0[i] + b21 * f1[i]);
1687 m_sensor->
ElectricField(x2[0], x2[1], x2[2], ex, ey, ez, medium, stat);
1690 std::cout << m_className <<
"::FieldLine: Point 2 outside.\n";
1692 Terminate(x0, x2, xl);
1695 Vec f2 = {ex, ey, ez};
1696 if (electron)
for (
auto& f : f2) f *= -1;
1699 for (
unsigned int i = 0; i < 3; ++i) {
1700 x3[i] += h * (b30 * f0[i] + b31 * f1[i] + b32 * f2[i]);
1702 m_sensor->
ElectricField(x3[0], x3[1], x3[2], ex, ey, ez, medium, stat);
1705 std::cout << m_className <<
"::FieldLine: Point 3 outside.\n";
1707 Terminate(x0, x3, xl);
1710 Vec f3 = {ex, ey, ez};
1711 if (electron)
for (
auto& f : f3) f *= -1;
1713 double xw = 0., yw = 0., zw = 0., rw = 0.;
1715 x1[0], x1[1], x1[2], xw, yw, zw,
false, rw) ||
1717 x2[0], x2[1], x2[2], xw, yw, zw,
false, rw) ||
1719 x3[0], x3[1], x3[2], xw, yw, zw,
false, rw)) {
1721 xl.push_back({float(xw), float(yw), float(zw)});
1728 std::cerr << m_className <<
"::FieldLine: Step size too small. Stop.\n";
1733 Vec phi1 = {0., 0., 0.};
1734 Vec phi2 = {0., 0., 0.};
1735 for (
unsigned int i = 0; i < 3; ++i) {
1736 phi1[i] = c10 * f0[i] + c11 * f1[i] + c12 * f2[i];
1737 phi2[i] = c20 * f0[i] + c22 * f2[i] + c23 * f3[i];
1740 const double phi1mag = Mag(phi1);
1741 if (phi1mag < Small) {
1742 std::cerr << m_className <<
"::FieldLine: Step has zero length. Stop.\n";
1744 }
else if (m_useStepSizeLimit && h * phi1mag > m_maxStepSize) {
1746 std::cout << m_className <<
"::FieldLine: Step is considered too long. "
1747 <<
"H is reduced.\n";
1749 h = 0.5 * m_maxStepSize / phi1mag;
1753 if (h * fabs(phi1[0]) > 0.1 * fabs(xmax - xmin) ||
1754 h * fabs(phi1[1]) > 0.1 * fabs(ymax - ymin)) {
1757 std::cout << m_className <<
"::FieldLine: Step is considered too long. "
1758 <<
"H is divided by two.\n";
1763 if (m_debug) std::cout << m_className <<
"::FieldLine: Step size ok.\n";
1765 for (
unsigned int i = 0; i < 3; ++i) x0[i] += h * phi1[i];
1767 m_sensor->
ElectricField(x0[0], x0[1], x0[2], ex, ey, ez, medium, stat);
1772 std::cout << m_className <<
"::FieldLine: Point outside. Terminate.\n";
1774 std::array<double, 3> xp = {xl.back()[0], xl.back()[1], xl.back()[2]};
1775 Terminate(xp, x0, xl);
1779 xl.push_back({float(x0[0]), float(x0[1]), float(x0[2])});
1782 const double dphi = fabs(phi1[0] - phi2[0]) + fabs(phi1[1] - phi2[1]) +
1783 fabs(phi1[2] - phi2[2]);
1785 h = sqrt(h * m_accuracy / dphi);
1787 std::cout << m_className <<
"::FieldLine: Adapting H to " << h <<
".\n";
1792 std::cout << m_className <<
"::FieldLine: H increased by factor two.\n";
1797 std::cerr << m_className <<
"::FieldLine: Step size is zero. Stop.\n";
1801 if (initCycle > 0 && h < 0.2 * hprev) {
1803 std::cout << m_className <<
"::FieldLine: Reinitialise step size.\n";
1808 xl.push_back({float(xi), float(yi), float(zi)});
1813 if (h > 10 * hprev) {
1816 std::cout << m_className <<
"::FieldLine: H restricted to 10 times "
1817 <<
"the previous value.\n";
1821 if (h * (fabs(phi1[0]) + fabs(phi1[1]) + fabs(phi1[2])) < m_accuracy) {
1822 std::cerr << m_className <<
"::FieldLine: Step size has become smaller "
1823 <<
"than int. accuracy. Stop.\n";
1832void DriftLineRKF::Terminate(
const std::array<double, 3>& xx0,
1833 const std::array<double, 3>& xx1,
1834 std::vector<std::array<float, 3> >& xs) {
1841 const unsigned int nBisections = 20;
1842 for (
unsigned int i = 0; i < nBisections; ++i) {
1845 for (
unsigned int j = 0; j < 3; ++j) {
1846 if (fabs(x1[j] - x0[j]) > 1.e-6 * (fabs(x0[j]) + fabs(x1[j]))) {
1853 std::cout << m_className <<
"::Terminate:\n"
1854 <<
" Bisection ended at cycle " << i <<
".\n";
1860 for (
unsigned int j = 0; j < 3; ++j) xm[j] = 0.5 * (x0[j] + x1[j]);
1861 double ex = 0., ey = 0., ez = 0.;
1862 Medium* medium =
nullptr;
1864 m_sensor->
ElectricField(xm[0], xm[1], xm[2], ex, ey, ez, medium, status);
1872 xs.push_back({float(x0[0]), float(x0[1]), float(x0[2])});
void DisablePlotting()
Switch off drift line plotting.
bool DriftNegativeIon(const double x0, const double y0, const double z0, const double t0)
double GetLoss(const double eps=1.e-4)
Compute the attachment loss factor for the current drift line.
bool DriftHole(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of a hole with a given starting point.
bool DriftIon(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an ion with a given starting point.
void SetSensor(Sensor *s)
Set the sensor.
void SetIntegrationAccuracy(const double eps)
Set the accuracy of the Runge Kutta Fehlberg drift line integration.
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 SetGainFluctuationsPolya(const double theta, const double mean=-1.)
double GetGain(const double eps=1.e-4)
Compute the multiplication factor for the current drift line.
void SetGainFluctuationsFixed(const double gain=-1.)
Do not randomize the avalanche size.
bool DriftPositron(const double x0, const double y0, const double z0, const double t0)
void GetDriftLinePoint(const unsigned int i, double &x, double &y, double &z, double &t) const
Get the coordinates and time of a point along the most recent drift line.
void PrintDriftLine() const
Print the trajectory of the most recent drift line.
void EnablePlotting(ViewDrift *view)
Switch on drift line plotting.
DriftLineRKF()
Constructor.
double GetArrivalTimeSpread(const double eps=1.e-4)
void GetEndPoint(double &x, double &y, double &z, double &t, int &st) const
Get the end point and status flag of the most recent drift line.
bool FieldLine(const double xi, const double yi, const double zi, std::vector< std::array< float, 3 > > &xl, const bool electron=true)
void SetMaximumStepSize(const double ms)
Set the maximum step size that is allowed. By default, there is no limit.
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).
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
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 t0, const double t1, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const bool integrateWeightingField, const bool useWeightingPotential=false)
Add the signal from a charge-carrier step.
bool IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc)
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Visualize drift lines and tracks.
void NewHoleDriftLine(const size_t np, size_t &id, const float x0, const float y0, const float z0)
void NewIonDriftLine(const size_t np, size_t &id, const float x0, const float y0, const float z0)
void SetDriftLinePoint(const size_t iL, const size_t iP, const float x, const float y, const float z)
void NewElectronDriftLine(const size_t np, size_t &id, const float x0, const float y0, const float z0)
std::array< double, 3 > Vec
double RndmPolya(const double theta)
Draw a Polya distributed random number.
DoubleAc exp(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)