14double Interpolate(
const std::vector<double>& y,
15 const std::vector<double>& x,
16 const double xx,
const unsigned int order) {
18 if (xx <
x.front() || xx >
x.back())
return 0.;
22 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
23 if (it1 ==
x.cend())
return y.back();
24 const auto it0 = std::prev(it1);
25 const double dx = (*it1 - *it0);
26 if (dx < Garfield::Small)
return y[it0 -
x.cbegin()];
27 const double f = (xx - *it0) / dx;
28 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
31double Trapezoid2(
const std::vector<std::pair<double, double> >& f) {
33 const unsigned int n = f.size();
34 if (n < 2)
return -1.;
36 const double x0 = f[0].first;
37 const double y0 = f[0].second;
38 const double x1 = f[1].first;
39 const double y1 = f[1].second;
41 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
43 const double x2 = f[2].first;
44 const double y2 = f[2].second;
45 sum = (x1 - x0) * y0 * y0 + (x2 - x0) * y1 * y1 + (x2 - x1) * y2 * y2;
47 sum = (x1 - x0) * y0 * y0 + (f[2].first - x0) * y1 * y1;
48 const double xm = f[n - 2].first;
49 const double ym = f[n - 2].second;
50 const double xn = f[n - 1].first;
51 const double yn = f[n - 1].second;
52 sum += (xn - f[n - 3].first) * ym * ym + (xn - xm) * yn * yn;
54 for (
unsigned int k = 2; k < n - 2; ++k) {
55 const double y = f[k].second;
56 sum += (f[k + 1].first - f[k - 1].first) * y * y;
68 if (i >= m_components.size()) {
69 std::cerr << m_className <<
"::GetComponent: Index out of range.\n";
72 return m_components[i];
76 double& ex,
double& ey,
double& ez,
double& v,
77 Medium*& medium,
int& status) {
78 ex = ey = ez = v = 0.;
81 double fx = 0., fy = 0., fz = 0., p = 0.;
85 for (
auto component : m_components) {
86 component->ElectricField(x, y, z, fx, fy, fz, p, med, stat);
101 double& ex,
double& ey,
double& ez,
Medium*& medium,
106 double fx = 0., fy = 0., fz = 0.;
110 for (
auto component : m_components) {
111 component->ElectricField(x, y, z, fx, fy, fz, med, stat);
125 double& bx,
double& by,
double& bz,
int& status) {
127 double fx = 0., fy = 0., fz = 0.;
129 for (
auto component : m_components) {
130 component->MagneticField(x, y, z, fx, fy, fz, status);
131 if (status != 0)
continue;
139 double& wx,
double& wy,
double& wz,
140 const std::string& label) {
143 for (
const auto& electrode : m_electrodes) {
144 if (electrode.label == label) {
145 double fx = 0., fy = 0., fz = 0.;
146 electrode.comp->WeightingField(x, y, z, fx, fy, fz, label);
155 const double z,
const std::string& label) {
158 for (
const auto& electrode : m_electrodes) {
159 if (electrode.label == label) {
160 v += electrode.comp->WeightingPotential(x, y, z, label);
171 if (m_components.empty())
return false;
174 if (m_lastComponent) {
179 for (
auto component : m_components) {
180 m = component->GetMedium(x, y, z);
182 m_lastComponent = component;
190 if (!GetBoundingBox(m_xMinUser, m_yMinUser, m_zMinUser, m_xMaxUser,
191 m_yMaxUser, m_zMaxUser)) {
192 std::cerr << m_className <<
"::SetArea: Bounding box is not known.\n";
196 std::cout << m_className <<
"::SetArea:\n"
197 <<
" " << m_xMinUser <<
" < x [cm] < " << m_xMaxUser <<
"\n"
198 <<
" " << m_yMinUser <<
" < y [cm] < " << m_yMaxUser <<
"\n"
199 <<
" " << m_zMinUser <<
" < z [cm] < " << m_zMaxUser <<
"\n";
200 if (std::isinf(m_xMinUser) || std::isinf(m_xMaxUser)) {
201 std::cerr << m_className <<
"::SetArea: Warning. Infinite x-range.\n";
203 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
204 std::cerr << m_className <<
"::SetArea: Warning. Infinite y-range.\n";
206 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
207 std::cerr << m_className <<
"::SetArea: Warning. Infinite z-range.\n";
209 m_hasUserArea =
true;
214 const double xmax,
const double ymax,
const double zmax) {
215 if (fabs(xmax - xmin) < Small || fabs(ymax - ymin) < Small ||
216 fabs(zmax - zmin) < Small) {
217 std::cerr << m_className <<
"::SetArea: Invalid range.\n";
221 m_xMinUser = std::min(xmin, xmax);
222 m_yMinUser = std::min(ymin, ymax);
223 m_zMinUser = std::min(zmin, zmax);
224 m_xMaxUser = std::max(xmax, xmin);
225 m_yMaxUser = std::max(ymax, ymin);
226 m_zMaxUser = std::max(zmax, zmin);
228 m_hasUserArea =
true;
233 double& ymax,
double& zmax) {
259 if (!m_hasUserArea) {
261 std::cerr << m_className <<
"::IsInArea: User area could not be set.\n";
264 m_hasUserArea =
true;
267 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
268 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
273 std::cout << m_className <<
"::IsInArea: (" << x <<
", " << y <<
", " << z
281 const double x1,
const double y1,
const double z1,
282 double& xc,
double& yc,
double& zc,
283 const bool centre,
double& rc) {
284 for (
auto component : m_components) {
285 if (component->IsWireCrossed(x0, y0, z0, x1, y1, z1,
286 xc, yc, zc, centre, rc)) {
294 double z0,
double& xw,
double& yw,
double& rw) {
295 for (
auto component : m_components) {
296 if (component->IsInTrapRadius(q0, x0, y0, z0, xw, yw, rw))
return true;
303 std::cerr << m_className <<
"::AddComponent: Null pointer.\n";
307 m_components.push_back(comp);
312 std::cerr << m_className <<
"::AddElectrode: Null pointer.\n";
315 for (
const auto& electrode : m_electrodes) {
316 if (electrode.label == label) {
317 std::cout << m_className <<
"::AddElectrode:\n"
318 <<
" Warning: An electrode with label \"" << label
319 <<
"\" exists already. Weighting fields will be summed up.\n";
325 electrode.comp = comp;
326 electrode.label = label;
327 electrode.signal.assign(m_nTimeBins, 0.);
328 electrode.electronsignal.assign(m_nTimeBins, 0.);
329 electrode.ionsignal.assign(m_nTimeBins, 0.);
330 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
331 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
332 m_electrodes.push_back(std::move(electrode));
333 std::cout << m_className <<
"::AddElectrode:\n"
334 <<
" Added readout electrode \"" << label <<
"\".\n"
335 <<
" All signals are reset.\n";
340 m_components.clear();
341 m_lastComponent =
nullptr;
342 m_electrodes.clear();
347 m_hasUserArea =
false;
348 m_fTransfer =
nullptr;
350 m_fTransferTab.clear();
352 m_fTransferFFT.clear();
359 for (
auto component : m_components) {
360 double umin = 0., umax = 0.;
361 if (!component->GetVoltageRange(umin, umax))
continue;
363 vmin = std::min(umin, vmin);
364 vmax = std::max(umax, vmax);
374 std::cerr << m_className <<
"::GetVoltageRange:\n"
375 <<
" Sensor voltage range not known.\n";
381 std::cout << m_className <<
"::GetVoltageRange: " << vmin <<
" < V < "
388 for (
auto& electrode : m_electrodes) {
389 electrode.charge = 0.;
390 electrode.signal.assign(m_nTimeBins, 0.);
391 electrode.electronsignal.assign(m_nTimeBins, 0.);
392 electrode.ionsignal.assign(m_nTimeBins, 0.);
393 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
394 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
397 m_integrated =
false;
401 if (!std::is_sorted(ts.begin(), ts.end())) {
402 std::cerr << m_className <<
"::SetDelayedSignalTimes:\n"
403 <<
" Times are not in ascending order.\n";
406 m_delayedSignalTimes = ts;
410 const double x0,
const double y0,
const double z0,
411 const double x1,
const double y1,
const double z1,
412 const bool integrateWeightingField,
413 const bool useWeightingPotential) {
414 if (m_debug) std::cout << m_className <<
"::AddSignal: ";
417 if (m_debug) std::cout <<
"Time " << t0 <<
" out of range.\n";
420 const double dt = t1 - t0;
422 if (m_debug) std::cout <<
"Time step too small.\n";
425 const int bin = int((t0 - m_tStart) / m_tStep);
427 if (bin < 0 || bin >= (
int)m_nTimeBins) {
428 if (m_debug) std::cout <<
"Bin " << bin <<
" out of range.\n";
431 if (m_nEvents <= 0) m_nEvents = 1;
433 const bool electron = q < 0;
434 const double dx = x1 - x0;
435 const double dy = y1 - y0;
436 const double dz = z1 - z0;
437 const double vx = dx / dt;
438 const double vy = dy / dt;
439 const double vz = dz / dt;
441 std::cout <<
" Time: " << t0 <<
"\n"
442 <<
" Step: " << dt <<
"\n"
443 <<
" Charge: " << q <<
"\n"
444 <<
" Velocity: (" << vx <<
", " << vy <<
", " << vz <<
")\n";
448 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
449 -0.238619186083196909, 0.238619186083196909,
450 0.661209386466264514, 0.932469514203152028};
451 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
452 0.467913934572691047, 0.467913934572691047,
453 0.360761573048138608, 0.171324492379170345};
454 for (
auto& electrode : m_electrodes) {
455 const std::string lbl = electrode.label;
456 if (m_debug) std::cout <<
" Electrode " << electrode.label <<
":\n";
459 if (useWeightingPotential) {
460 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, lbl);
461 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, lbl);
462 current = q * (w1 - w0) / dt;
464 double wx = 0., wy = 0., wz = 0.;
466 if (integrateWeightingField) {
467 for (
unsigned int j = 0; j < 6; ++j) {
468 const double s = 0.5 * (1. + tg[j]);
469 const double x = x0 + s * dx;
470 const double y = y0 + s * dy;
471 const double z = z0 + s * dz;
472 double fx = 0., fy = 0., fz = 0.;
473 electrode.comp->WeightingField(x, y, z, fx, fy, fz, lbl);
482 const double x = x0 + 0.5 * dx;
483 const double y = y0 + 0.5 * dy;
484 const double z = z0 + 0.5 * dz;
485 electrode.comp->WeightingField(x, y, z, wx, wy, wz, lbl);
488 std::cout <<
" Weighting field: (" << wx <<
", " << wy <<
", "
492 current = -q * (wx * vx + wy * vy + wz * vz);
495 if (m_debug) std::cout <<
" Induced charge: " << current * dt <<
"\n";
496 double delta = m_tStart + (bin + 1) * m_tStep - t0;
499 FillBin(electrode, bin, current * delta, electron,
false);
502 while (delta > m_tStep && bin + j < m_nTimeBins) {
503 FillBin(electrode, bin + j, current * m_tStep, electron,
false);
507 if (bin + j < m_nTimeBins) {
508 FillBin(electrode, bin + j, current * delta, electron,
false);
511 FillBin(electrode, bin, current * dt, electron,
false);
514 if (!m_delayedSignal)
return;
515 if (m_delayedSignalTimes.empty())
return;
516 const unsigned int nd = m_delayedSignalTimes.size();
518 std::vector<double> td(nd);
519 for (
unsigned int i = 0; i < nd; ++i) {
520 td[i] = t0 + m_delayedSignalTimes[i];
523 for (
auto& electrode : m_electrodes) {
524 const std::string lbl = electrode.label;
525 std::vector<double> id(nd, 0.);
526 for (
unsigned int i = 0; i < nd; ++i) {
528 const double step = std::min(m_delayedSignalTimes[i], dt);
529 const double scale = step / dt;
531 for (
unsigned int j = 0; j < 6; ++j) {
532 double s = 0.5 * (1. + tg[j]);
533 const double t = m_delayedSignalTimes[i] - s * step;
535 const double x = x0 + s * dx;
536 const double y = y0 + s * dy;
537 const double z = z0 + s * dz;
539 double wx = 0., wy = 0., wz = 0.;
540 electrode.comp->DelayedWeightingField(x, y, z, t, wx, wy, wz, lbl);
541 sum += (wx * vx + wy * vy + wz * vz) * wg[j];
543 id[i] = -q * 0.5 * sum * step;
545 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
551 const std::vector<std::array<double, 3> >& xs,
552 const std::vector<std::array<double, 3> >& vs,
553 const std::vector<double>& ns,
const int navg) {
556 if (ts.size() < 2)
return;
557 if (ts.size() != xs.size() || ts.size() != vs.size()) {
558 std::cerr << m_className <<
"::AddSignal: Mismatch in vector size.\n";
561 const bool aval = ns.size() == ts.size();
562 const unsigned int nPoints = ts.size();
564 std::cout << m_className <<
"::AddSignal: Adding a " << nPoints
565 <<
"-vector (charge " << q <<
").\n";
568 if (m_nEvents <= 0) m_nEvents = 1;
569 for (
auto& electrode : m_electrodes) {
570 const std::string label = electrode.label;
571 std::vector<double> signal(nPoints, 0.);
572 for (
unsigned int i = 0; i < nPoints; ++i) {
574 const auto& x = xs[i];
575 double wx = 0., wy = 0., wz = 0.;
576 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
578 const auto& v = vs[i];
579 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
580 if (aval) signal[i] *= ns[i];
582 FillSignal(electrode, q, ts, signal, navg);
585 if (!m_delayedSignal)
return;
586 if (m_delayedSignalTimes.empty())
return;
588 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
589 -0.238619186083196909, 0.238619186083196909,
590 0.661209386466264514, 0.932469514203152028};
591 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
592 0.467913934572691047, 0.467913934572691047,
593 0.360761573048138608, 0.171324492379170345};
594 const unsigned int nd = m_delayedSignalTimes.size();
595 for (
unsigned int k = 0; k < nPoints - 1; ++k) {
596 const double t0 = ts[k];
597 const double t1 = ts[k + 1];
598 const double dt = t1 - t0;
599 if (dt < Small)
continue;
600 const auto& x0 = xs[k];
601 const auto& x1 = xs[k + 1];
602 const auto& v = vs[k];
603 std::vector<double> td(nd);
604 for (
unsigned int i = 0; i < nd; ++i) {
605 td[i] = t0 + m_delayedSignalTimes[i];
608 for (
auto& electrode : m_electrodes) {
609 const std::string lbl = electrode.label;
610 std::vector<double> id(nd, 0.);
611 for (
unsigned int i = 0; i < nd; ++i) {
613 const double step = std::min(m_delayedSignalTimes[i], dt);
614 const double scale = step / dt;
615 const double dx = scale * (x1[0] - x0[0]);
616 const double dy = scale * (x1[1] - x0[1]);
617 const double dz = scale * (x1[2] - x0[2]);
619 for (
unsigned int j = 0; j < 6; ++j) {
620 const double f = 0.5 * (1. + tg[j]);
621 const double t = m_delayedSignalTimes[i] - f * step;
623 double wx = 0., wy = 0., wz = 0.;
624 electrode.comp->DelayedWeightingField(x0[0] + f * dx,
628 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
630 id[i] = -q * 0.5 * sum * step;
632 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
638void Sensor::FillSignal(Electrode& electrode,
const double q,
639 const std::vector<double>& ts,
640 const std::vector<double>& is,
const int navg,
641 const bool delayed) {
643 const bool electron = q < 0.;
645 constexpr unsigned int k = 1;
646 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
647 const double t0 = m_tStart + i * m_tStep;
648 const double t1 = t0 + m_tStep;
649 if (ts.front() > t1)
continue;
650 if (ts.back() < t0)
break;
652 const double tmin = std::max(ts.front(), t0);
653 const double tmax = std::min(ts.back(), t1);
656 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
658 const double h = 0.5 * (tmax - tmin) / navg;
659 for (
int j = -navg; j <= navg; ++j) {
660 const int jj = j + navg;
661 const double t = t0 + jj * h;
662 if (t < ts.front() || t > ts.back())
continue;
663 if (j == -navg || j == navg) {
664 sum += Interpolate(is, ts, t, k);
665 }
else if (jj == 2 * (jj /2)) {
666 sum += 2 * Interpolate(is, ts, t, k);
668 sum += 4 * Interpolate(is, ts, t, k);
674 FillBin(electrode, i, sum, electron, delayed);
679 const double z0,
const double x1,
const double y1,
681 if (m_debug) std::cout << m_className <<
"::AddInducedCharge:\n";
682 for (
auto& electrode : m_electrodes) {
684 auto cmp = electrode.comp;
685 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
687 const double w1 = cmp->WeightingPotential(x1, y1, z1, electrode.label);
688 electrode.charge += q * (w1 - w0);
690 std::cout <<
" Electrode " << electrode.label <<
":\n"
691 <<
" Weighting potential at (" << x0 <<
", " << y0 <<
", "
692 << z0 <<
"): " << w0 <<
"\n"
693 <<
" Weighting potential at (" << x1 <<
", " << y1 <<
", "
694 << z1 <<
"): " << w1 <<
"\n"
695 <<
" Induced charge: " << electrode.charge <<
"\n";
701 const unsigned int nsteps) {
704 std::cerr << m_className <<
"::SetTimeWindow: Start time out of range.\n";
710 std::cerr << m_className <<
"::SetTimeWindow: Invalid number of bins.\n";
712 m_nTimeBins = nsteps;
716 std::cout << m_className <<
"::SetTimeWindow: " << m_tStart
717 <<
" < t [ns] < " << m_tStart + m_nTimeBins * m_tStep <<
"\n"
718 <<
" Step size: " << m_tStep <<
" ns\n";
721 std::cout << m_className <<
"::SetTimeWindow: Resetting all signals.\n";
722 for (
auto& electrode : m_electrodes) {
723 electrode.signal.assign(m_nTimeBins, 0.);
724 electrode.electronsignal.assign(m_nTimeBins, 0.);
725 electrode.ionsignal.assign(m_nTimeBins, 0.);
730 m_fTransferFFT.clear();
734 const unsigned int bin) {
735 if (m_nEvents == 0)
return 0.;
736 if (bin >= m_nTimeBins)
return 0.;
738 for (
const auto& electrode : m_electrodes) {
739 if (electrode.label == label) sig += electrode.electronsignal[bin];
741 return ElementaryCharge * sig / (m_nEvents * m_tStep);
745 if (m_nEvents == 0)
return 0.;
746 if (bin >= m_nTimeBins)
return 0.;
748 for (
const auto& electrode : m_electrodes) {
749 if (electrode.label == label) sig += electrode.ionsignal[bin];
751 return ElementaryCharge * sig / (m_nEvents * m_tStep);
755 const unsigned int bin) {
756 if (m_nEvents == 0)
return 0.;
757 if (bin >= m_nTimeBins)
return 0.;
759 for (
const auto& electrode : m_electrodes) {
760 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
762 return ElementaryCharge * sig / (m_nEvents * m_tStep);
766 const unsigned int bin) {
767 if (m_nEvents == 0)
return 0.;
768 if (bin >= m_nTimeBins)
return 0.;
770 for (
const auto& electrode : m_electrodes) {
771 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
773 return ElementaryCharge * sig / (m_nEvents * m_tStep);
777 const double signal) {
778 if (bin >= m_nTimeBins)
return;
779 if (m_nEvents == 0) m_nEvents = 1;
780 for (
auto& electrode : m_electrodes) {
781 if (electrode.label == label) {
782 electrode.signal[bin] = m_nEvents * m_tStep * signal / ElementaryCharge;
789 if (m_nEvents == 0)
return 0.;
790 if (bin >= m_nTimeBins)
return 0.;
792 for (
const auto& electrode : m_electrodes) {
793 if (electrode.label == label) sig += electrode.signal[bin];
795 return ElementaryCharge * sig / (m_nEvents * m_tStep);
799 if (m_nEvents == 0)
return 0.;
801 for (
const auto& electrode : m_electrodes) {
802 if (electrode.label == label) charge += electrode.charge;
805 return charge / m_nEvents;
810 std::cerr << m_className <<
"::SetTransferFunction: Null pointer.\n";
815 m_fTransferTab.clear();
817 m_fTransferFFT.clear();
821 const std::vector<double>& values) {
822 if (times.empty() || values.empty()) {
823 std::cerr << m_className <<
"::SetTransferFunction: Empty vector.\n";
825 }
else if (times.size() != values.size()) {
826 std::cerr << m_className <<
"::SetTransferFunction:\n"
827 <<
" Time and value vectors must have same size.\n";
830 const auto n = times.size();
831 m_fTransferTab.clear();
832 for (
unsigned int i = 0; i < n; ++i) {
833 m_fTransferTab.emplace_back(std::make_pair(times[i], values[i]));
835 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
836 m_fTransfer =
nullptr;
839 m_fTransferFFT.clear();
844 m_fTransfer =
nullptr;
845 m_fTransferTab.clear();
847 m_fTransferFFT.clear();
850double Sensor::InterpolateTransferFunctionTable(
const double t)
const {
851 if (m_fTransferTab.empty())
return 0.;
853 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
857 const auto begin = m_fTransferTab.cbegin();
858 const auto it1 = std::upper_bound(begin, m_fTransferTab.cend(), std::make_pair(t, 0.));
859 if (it1 == begin)
return m_fTransferTab.front().second;
860 const auto it0 = std::prev(it1);
861 const double t0 = (*it0).first;
862 const double t1 = (*it1).first;
863 const double f = t - t0 / (t1 - t0);
865 return (*it0).second * (1. - f) + (*it1).second * f;
870 return m_fTransfer(t);
871 }
else if (m_shaper) {
872 return m_shaper->
Shape(t);
874 return InterpolateTransferFunctionTable(t);
878 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
879 std::cerr << m_className <<
"::ConvoluteSignal: "
880 <<
"Transfer function not set.\n";
883 if (m_nEvents == 0) {
884 std::cerr << m_className <<
"::ConvoluteSignal: No signals present.\n";
888 if (fft)
return ConvoluteSignalFFT();
891 constexpr double cnvMin = 0.;
892 constexpr double cnvMax = 1.e10;
894 std::vector<double> cnvTab(2 * m_nTimeBins - 1, 0.);
895 const unsigned int offset = m_nTimeBins - 1;
897 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
899 double t = (-int(i)) * m_tStep;
900 if (t < cnvMin || t > cnvMax) {
901 cnvTab[offset - i] = 0.;
905 if (i == 0)
continue;
908 if (t < cnvMin || t > cnvMax) {
909 cnvTab[offset + i] = 0.;
914 std::vector<double> tmpSignal(m_nTimeBins, 0.);
916 for (
auto& electrode : m_electrodes) {
918 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
920 for (
unsigned int k = 0; k < m_nTimeBins; ++k) {
921 tmpSignal[j] += m_tStep * cnvTab[offset + j - k] * electrode.signal[k];
924 electrode.signal.swap(tmpSignal);
930bool Sensor::ConvoluteSignalFFT() {
933 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
935 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
937 m_fTransferFFT.assign(2 * (nn + 1), 0.);
938 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
941 FFT(m_fTransferFFT,
false, nn);
944 const double scale = m_tStep / nn;
945 for (
auto& electrode : m_electrodes) {
946 std::vector<double> g(2 * (nn + 1), 0.);
947 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
948 g[2 * i + 1] = electrode.signal[i];
951 for (
unsigned int i = 0; i < nn; ++i) {
952 const double fr = m_fTransferFFT[2 * i + 1];
953 const double fi = m_fTransferFFT[2 * i + 2];
954 const double gr = g[2 * i + 1];
955 const double gi = g[2 * i + 2];
956 g[2 * i + 1] = fr * gr - fi * gi;
957 g[2 * i + 2] = fr * gi + gr * fi;
960 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
961 electrode.signal[i] = scale * g[2 * i + 1];
969 if (m_nEvents == 0) {
970 std::cerr << m_className <<
"::IntegrateSignal: No signals present.\n";
974 for (
auto& electrode : m_electrodes) {
975 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
976 electrode.signal[j] *= m_tStep;
977 electrode.electronsignal[j] *= m_tStep;
978 electrode.ionsignal[j] *= m_tStep;
980 electrode.signal[j] += electrode.signal[j - 1];
981 electrode.electronsignal[j] += electrode.electronsignal[j - 1];
982 electrode.ionsignal[j] += electrode.ionsignal[j - 1];
992 const int offset = int(td / m_tStep);
993 for (
auto& electrode : m_electrodes) {
994 std::vector<double> signal1(m_nTimeBins, 0.);
995 std::vector<double> signal2(m_nTimeBins, 0.);
996 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
997 signal2[j] = f * electrode.signal[j];
998 const int bin = j - offset;
999 if (bin < 0 || bin >= (
int)m_nTimeBins)
continue;
1000 signal1[j] = electrode.signal[bin];
1002 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1003 electrode.signal[j] = signal1[j] - signal2[j];
1011 std::cerr << m_className <<
"::SetNoiseFunction: Null pointer.\n";
1019 std::cerr << m_className <<
"::AddNoise: Noise function not set.\n";
1022 if (m_nEvents == 0) m_nEvents = 1;
1024 for (
auto& electrode : m_electrodes) {
1025 double t = m_tStart + 0.5 * m_tStep;
1026 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1027 const double noise = m_fNoise(t);
1028 if (total) electrode.signal[j] += noise;
1029 if (electron) electrode.electronsignal[j] += noise;
1030 if (ion) electrode.ionsignal[j] += noise;
1039 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1040 std::cerr << m_className <<
"::AddWhiteNoise: Transfer function not set.\n";
1043 if (m_nEvents == 0) m_nEvents = 1;
1045 const double f2 = TransferFunctionSq();
1047 std::cerr << m_className <<
"::AddWhiteNoise:\n"
1048 <<
" Could not calculate transfer function integral.\n";
1054 const double nu = (enc * enc / (q0 * q0)) / f2;
1056 const double avg = nu * m_tStep * m_nTimeBins;
1058 for (
auto& electrode : m_electrodes) {
1060 for (
int j = 0; j < nPulses; ++j) {
1061 const int bin =
static_cast<int>(m_nTimeBins *
RndmUniform());
1062 electrode.signal[bin] += q0;
1064 const double offset = q0 * nu * m_tStep;
1065 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1066 electrode.signal[j] -= offset;
1071 const double sigma = enc * sqrt(m_tStep / f2);
1072 for (
auto& electrode : m_electrodes) {
1073 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1080double Sensor::TransferFunctionSq() {
1082 if (m_fTransferSq >= 0.)
return m_fTransferSq;
1083 double integral = -1.;
1085 std::function<double(
double)> fsq = [
this](
double x) {
1086 const double y = m_fTransfer(x);
1089 constexpr double epsrel = 1.e-8;
1091 unsigned int stat = 0;
1093 }
else if (m_shaper) {
1096 integral = Trapezoid2(m_fTransferTab);
1098 if (m_cacheTransferFunction) m_fTransferSq = integral;
1103 const std::string& label,
int& n) {
1105 m_thresholdCrossings.clear();
1106 m_thresholdLevel = thr;
1111 if (m_nEvents == 0) {
1112 std::cerr << m_className <<
"::ComputeThresholdCrossings: "
1113 <<
"No signals present.\n";
1116 if (!m_integrated) {
1117 std::cerr << m_className <<
"::ComputeThresholdCrossings:\n "
1118 <<
"Warning: signal has not been integrated/convoluted.\n";
1121 std::vector<double> signal(m_nTimeBins, 0.);
1123 bool foundLabel =
false;
1124 for (
const auto& electrode : m_electrodes) {
1125 if (electrode.label == label) {
1127 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1128 signal[i] += electrode.signal[i];
1133 std::cerr << m_className <<
"::ComputeThresholdCrossings: Electrode "
1134 << label <<
" not found.\n";
1137 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1138 for (
unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1141 const double vMin = *std::min_element(std::begin(signal), std::end(signal));
1142 const double vMax = *std::max_element(std::begin(signal), std::end(signal));
1143 if (m_debug) std::cout << m_className <<
"::ComputeThresholdCrossings:\n";
1144 if (thr < vMin && thr > vMax) {
1146 std::cout <<
" Threshold outside the range [" << vMin <<
", "
1153 constexpr std::array<int, 2> directions = {1, -1};
1154 for (
const auto dir : directions) {
1155 const bool up = dir > 0;
1158 std::cout <<
" Hunting for rising edges.\n";
1160 std::cout <<
" Hunting for falling edges.\n";
1164 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1165 std::vector<double> vs = {signal[0]};
1167 for (
unsigned int i = 1; i < m_nTimeBins; ++i) {
1169 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1170 const double vNew = signal[i];
1172 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1178 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1179 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1182 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1192 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1193 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1195 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1198 n = m_thresholdCrossings.size();
1201 std::cout <<
" Found " << n <<
" crossings.\n";
1202 if (n > 0) std::cout <<
" Time [ns] Direction\n";
1203 for (
const auto& crossing : m_thresholdCrossings) {
1204 std::cout <<
" " << crossing.first <<
" ";
1205 if (crossing.second) {
1206 std::cout <<
"rising\n";
1208 std::cout <<
"falling\n";
1217 double& level,
bool& rise)
const {
1218 level = m_thresholdLevel;
1220 if (i >= m_thresholdCrossings.size()) {
1221 std::cerr << m_className <<
"::GetThresholdCrossing: Index out of range.\n";
1222 time = m_tStart + m_nTimeBins * m_tStep;
1226 time = m_thresholdCrossings[i].first;
1227 rise = m_thresholdCrossings[i].second;
1231bool Sensor::GetBoundingBox(
double& xmin,
double& ymin,
double& zmin,
1232 double& xmax,
double& ymax,
double& zmax) {
1236 double x0, y0, z0, x1, y1, z1;
1237 for (
auto component : m_components) {
1238 if (!component->GetBoundingBox(x0, y0, z0, x1, y1, z1))
continue;
1240 if (x0 < xmin) xmin = x0;
1241 if (y0 < ymin) ymin = y0;
1242 if (z0 < zmin) zmin = z0;
1243 if (x1 > xmax) xmax = x1;
1244 if (y1 > ymax) ymax = y1;
1245 if (z1 > zmax) zmax = z1;
1259 std::cerr << m_className <<
"::GetBoundingBox:\n"
1260 <<
" Sensor bounding box not known.\n";
1261 xmin = ymin = zmin = 0.;
1262 xmax = ymax = zmax = 0.;
1267 std::cout << m_className <<
"::GetBoundingBox:\n"
1268 <<
" " << xmin <<
" < x [cm] < " << xmax <<
"\n"
1269 <<
" " << ymin <<
" < y [cm] < " << ymax <<
"\n"
1270 <<
" " << zmin <<
" < z [cm] < " << zmax <<
"\n";
1275void Sensor::FFT(std::vector<double>& data,
const bool inverse,
1283 const int n = 2 * nn;
1286 for (
int i = 1; i < n; i += 2) {
1289 std::swap(data[j],data[i]);
1290 std::swap(data[j+1],data[i+1]);
1293 while (m >= 2 && j > m) {
1300 const int isign = inverse ? -1 : 1;
1303 const int step = 2 * mmax;
1304 const double theta = isign * TwoPi / mmax;
1305 double wtemp =
sin(0.5 * theta);
1306 const double wpr = -2. * wtemp * wtemp;
1307 const double wpi =
sin(theta);
1310 for (
int m = 1; m < mmax; m += 2) {
1311 for (
int i = m; i <= n;i += step) {
1313 double tr = wr * data[j] - wi * data[j + 1];
1314 double ti = wr * data[j + 1] + wi * data[j];
1315 data[j] = data[i] - tr;
1316 data[j + 1] = data[i + 1] - ti;
1320 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1321 wi = wi * wpr + wtemp * wpi + wi;
Abstract base class for components.
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.
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).
double GetElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the electron signal for a given electrode and time bin.
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
void AddWhiteNoise(const double enc, const bool poisson=true, const double q0=1.)
void SetDelayedSignalTimes(const std::vector< double > &ts)
Set the points in time at which to evaluate the delayed weighting field.
void SetTimeWindow(const double tstart, const double tstep, const unsigned int nsteps)
bool IntegrateSignal()
Replace the current signal curve by its integral.
void SetSignal(const std::string &label, const unsigned int bin, const double signal)
Set/override the signal in a given time bin explicitly.
double GetDelayedIonSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed ion/hole signal for a given electrode and time bin.
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
bool DelayAndSubtractFraction(const double td, const double f)
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
Get the weighting field at (x, y, z).
double GetTransferFunction(const double t)
Evaluate the transfer function at a given time.
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 SetArea()
Set the user area to the default.
double GetSignal(const std::string &label, const unsigned int bin)
Retrieve the total signal for a given electrode and time bin.
void SetNoiseFunction(double(*f)(double t))
Set the function to be used for evaluating the noise component.
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
void AddElectrode(ComponentBase *comp, const std::string &label)
Add an electrode.
double GetInducedCharge(const std::string &label)
void AddNoise(const bool total=true, const bool electron=false, const bool ion=false)
Add noise to the induced signal.
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Get the weighting potential at (x, y, z).
bool ComputeThresholdCrossings(const double thr, const std::string &label, int &n)
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
void Clear()
Remove all components, electrodes and reset the sensor.
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 ConvoluteSignal(const bool fft=false)
Convolute the induced current with the transfer function.
void SetTransferFunction(double(*f)(double t))
Set the function to be used for evaluating the transfer function.
bool GetThresholdCrossing(const unsigned int i, double &time, double &level, bool &rise) const
void AddComponent(ComponentBase *comp)
Add a component.
double GetDelayedElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed electron signal for a given electrode and time bin.
bool IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc)
void ClearSignal()
Reset signals and induced charges of all electrodes.
void AddInducedCharge(const double q, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
Add the induced charge from a charge carrier drift between two points.
double GetIonSignal(const std::string &label, const unsigned int bin)
Retrieve the ion or hole signal for a given electrode and time bin.
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
ComponentBase * GetComponent(const unsigned int i)
Retrieve the pointer to a given component.
Class for signal processing.
double TransferFuncSq() const
Return the integral of the transfer function squared.
double Shape(const double t) const
Evaluate the transfer function.
void qagi(std::function< double(double)> f, double bound, const int inf, const double epsabs, const double epsrel, double &result, double &abserr, unsigned int &status)
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
double RndmUniform()
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.
int RndmPoisson(const double mean)
Draw a random number from a Poisson distribution.
DoubleAc sin(const DoubleAc &f)