20double Interpolate(
const std::vector<double> &y,
const std::vector<double> &x,
21 const double xx,
const unsigned int order) {
22 if (xx <
x.front() || xx >
x.back())
return 0.;
26 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
27 if (it1 ==
x.cend())
return y.back();
28 const auto it0 = std::prev(it1);
29 const double dx = (*it1 - *it0);
30 if (dx < Garfield::Small)
return y[it0 -
x.cbegin()];
31 const double f = (xx - *it0) / dx;
32 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
35double Trapezoid2(
const std::vector<std::pair<double, double>> &f) {
36 const size_t n = f.size();
37 if (n < 2)
return -1.;
39 const double x0 = f[0].first;
40 const double y0 = f[0].second;
41 const double x1 = f[1].first;
42 const double y1 = f[1].second;
44 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
46 const double x2 = f[2].first;
47 const double y2 = f[2].second;
48 sum = (x1 - x0) * y0 * y0 + (x2 - x0) * y1 * y1 + (x2 - x1) * y2 * y2;
50 sum = (x1 - x0) * y0 * y0 + (f[2].first - x0) * y1 * y1;
51 const double xm = f[n - 2].first;
52 const double ym = f[n - 2].second;
53 const double xn = f[n - 1].first;
54 const double yn = f[n - 1].second;
55 sum += (xn - f[n - 3].first) * ym * ym + (xn - xm) * yn * yn;
57 for (
size_t k = 2; k < n - 2; ++k) {
58 const double y = f[k].second;
59 sum += (f[k + 1].first - f[k - 1].first) * y * y;
71 double &ex,
double &ey,
double &ez,
double &v,
72 Medium *&medium,
int &status) {
73 ex = ey = ez = v = 0.;
76 double fx = 0., fy = 0., fz = 0., p = 0.;
80 for (
const auto &cmp : m_components) {
81 if (!std::get<1>(cmp))
continue;
82 std::get<0>(cmp)->ElectricField(x, y, z, fx, fy, fz, p, med, stat);
97 double &ex,
double &ey,
double &ez,
Medium *&medium,
102 double fx = 0., fy = 0., fz = 0.;
106 for (
const auto &cmp : m_components) {
107 if (!std::get<1>(cmp))
continue;
108 std::get<0>(cmp)->ElectricField(x, y, z, fx, fy, fz, med, stat);
122 double &bx,
double &by,
double &bz,
int &status) {
124 double fx = 0., fy = 0., fz = 0.;
126 for (
const auto &cmp : m_components) {
127 if (!std::get<2>(cmp))
continue;
128 std::get<0>(cmp)->MagneticField(x, y, z, fx, fy, fz, status);
129 if (status != 0)
continue;
137 double &wx,
double &wy,
double &wz,
138 const std::string &label) {
141 for (
const auto &electrode : m_electrodes) {
142 if (electrode.label == label) {
143 double fx = 0., fy = 0., fz = 0.;
144 electrode.comp->WeightingField(x, y, z, fx, fy, fz, label);
153 const double z,
const std::string &label) {
156 for (
const auto &electrode : m_electrodes) {
157 if (electrode.label == label) {
158 v += std::max(electrode.comp->WeightingPotential(x, y, z, label), 0.);
165 const double z,
const double t,
166 const std::string &label) {
169 for (
const auto &electrode : m_electrodes) {
170 if (electrode.label == label) {
172 electrode.comp->DelayedWeightingPotential(x, y, z, t, label), 0.);
179 for (
const auto &cmp : m_components) {
180 if (!std::get<1>(cmp))
continue;
181 Medium *medium = std::get<0>(cmp)->GetMedium(x, y, z);
182 if (medium)
return medium;
188 std::lock_guard<std::mutex> guard(m_mutex);
189 if (!GetBoundingBox(m_xMinUser, m_yMinUser, m_zMinUser, m_xMaxUser,
190 m_yMaxUser, m_zMaxUser)) {
191 std::cerr << m_className <<
"::SetArea: Bounding box is not known.\n";
195 if (verbose || m_debug) {
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";
201 if (std::isinf(m_xMinUser) || std::isinf(m_xMaxUser)) {
202 std::cerr << m_className <<
"::SetArea: Warning. Infinite x-range.\n";
204 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
205 std::cerr << m_className <<
"::SetArea: Warning. Infinite y-range.\n";
207 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
208 std::cerr << m_className <<
"::SetArea: Warning. Infinite z-range.\n";
210 m_hasUserArea =
true;
215 const double xmax,
const double ymax,
const double zmax) {
216 std::lock_guard<std::mutex> guard(m_mutex);
217 if (fabs(xmax - xmin) < Small || fabs(ymax - ymin) < Small ||
218 fabs(zmax - zmin) < Small) {
219 std::cerr << m_className <<
"::SetArea: Invalid range.\n";
223 m_xMinUser = std::min(xmin, xmax);
224 m_yMinUser = std::min(ymin, ymax);
225 m_zMinUser = std::min(zmin, zmax);
226 m_xMaxUser = std::max(xmax, xmin);
227 m_yMaxUser = std::max(ymax, ymin);
228 m_zMaxUser = std::max(zmax, zmin);
230 m_hasUserArea =
true;
235 double &ymax,
double &zmax) {
261 if (!m_hasUserArea) {
263 std::cerr << m_className <<
"::IsInArea: User area could not be set.\n";
266 m_hasUserArea =
true;
269 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
270 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
275 std::cout << m_className <<
"::IsInArea: (" << x <<
", " << y <<
", " << z
283 double ex = 0., ey = 0., ez = 0.;
287 if (status != 0 || !medium)
return false;
293 const double x1,
const double y1,
const double z1,
294 double &xc,
double &yc,
double &zc,
const bool centre,
296 for (
const auto &cmp : m_components) {
297 if (!std::get<1>(cmp))
continue;
298 if (std::get<0>(cmp)->
CrossedWire(x0, y0, z0, x1, y1, z1, xc, yc, zc,
307 double z0,
double &xw,
double &yw,
double &rw) {
308 for (
const auto &cmp : m_components) {
309 if (!std::get<1>(cmp))
continue;
310 if (std::get<0>(cmp)->
InTrapRadius(q0, x0, y0, z0, xw, yw, rw)) {
318 const double x1,
const double y1,
const double z1,
319 double &xc,
double &yc,
double &zc) {
320 for (
const auto &cmp : m_components) {
321 if (!std::get<1>(cmp))
continue;
322 if (std::get<0>(cmp)->
CrossedPlane(x0, y0, z0, x1, y1, z1, xc, yc, zc)) {
331 double dmin = std::numeric_limits<double>::max();
332 for (
const auto &cmp : m_components) {
333 if (!std::get<1>(cmp))
continue;
334 const double d = std::get<0>(cmp)->StepSizeHint();
335 if (d > 0.) dmin = std::min(dmin, d);
337 return dmin < std::numeric_limits<double>::max() ? dmin : -1.;
341 const double z0,
const double x1,
342 const double y1,
const double z1,
343 const double xp,
const double yp,
344 const double zp,
const unsigned int nI,
347 for (
const auto &cmp : m_components) {
348 if (!std::get<1>(cmp))
continue;
349 q += std::get<0>(cmp)->IntegrateFluxLine(x0, y0, z0, x1, y1, z1, xp, yp, zp,
357 std::cerr << m_className <<
"::AddComponent: Null pointer.\n";
361 m_components.push_back(std::make_tuple(cmp,
true,
true));
365 if (i >= m_components.size()) {
366 std::cerr << m_className <<
"::GetComponent: Index out of range.\n";
369 return std::get<0>(m_components[i]);
373 if (i >= m_components.size()) {
374 std::cerr << m_className <<
"::EnableComponent: Index out of range.\n";
377 std::get<1>(m_components[i]) = on;
381 if (i >= m_components.size()) {
382 std::cerr << m_className <<
"::EnableMagneticField: Index out of range.\n";
385 std::get<2>(m_components[i]) = on;
389 for (
const auto &cmp : m_components) {
390 if (!std::get<1>(cmp) || !std::get<2>(cmp))
continue;
398 std::cerr << m_className <<
"::AddElectrode: Null pointer.\n";
401 for (
const auto &electrode : m_electrodes) {
402 if (electrode.label == label) {
403 std::cout << m_className <<
"::AddElectrode:\n"
404 <<
" Warning: An electrode with label \"" << label
405 <<
"\" exists already. Weighting fields will be summed up.\n";
411 electrode.comp = cmp;
412 electrode.label = label;
413 electrode.signal.assign(m_nTimeBins, 0.);
414 electrode.electronSignal.assign(m_nTimeBins, 0.);
415 electrode.ionSignal.assign(m_nTimeBins, 0.);
416 electrode.delayedSignal.assign(m_nTimeBins, 0.);
417 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
418 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
419 m_electrodes.push_back(std::move(electrode));
420 std::cout << m_className <<
"::AddElectrode:\n"
421 <<
" Added readout electrode \"" << label <<
"\".\n"
422 <<
" All signals are reset.\n";
427 std::lock_guard<std::mutex> guard(m_mutex);
428 m_components.clear();
429 m_electrodes.clear();
434 m_hasUserArea =
false;
435 m_fTransfer =
nullptr;
437 m_fTransferTab.clear();
439 m_fTransferFFT.clear();
446 for (
const auto &cmp : m_components) {
447 if (!std::get<1>(cmp))
continue;
448 double umin = 0., umax = 0.;
451 vmin = std::min(umin, vmin);
452 vmax = std::max(umax, vmax);
462 std::cerr << m_className <<
"::GetVoltageRange:\n"
463 <<
" Sensor voltage range not known.\n";
469 std::cout << m_className <<
"::GetVoltageRange: " << vmin <<
" < V < "
476 for (
auto &electrode : m_electrodes) {
477 electrode.charge = 0.;
478 electrode.signal.assign(m_nTimeBins, 0.);
479 electrode.delayedSignal.assign(m_nTimeBins, 0.);
480 electrode.electronSignal.assign(m_nTimeBins, 0.);
481 electrode.ionSignal.assign(m_nTimeBins, 0.);
482 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
483 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
484 electrode.integrated =
false;
490 if (!std::is_sorted(ts.begin(), ts.end())) {
491 std::cerr << m_className <<
"::SetDelayedSignalTimes:\n"
492 <<
" Times are not in ascending order.\n";
495 m_delayedSignalTimes = ts;
499 const double x0,
const double y0,
const double z0,
500 const double x1,
const double y1,
const double z1,
501 const bool integrateWeightingField,
502 const bool useWeightingPotential) {
503 if (m_debug) std::cout << m_className <<
"::AddSignal: ";
506 if (m_debug) std::cout <<
"Time " << t0 <<
" out of range.\n";
509 const double dt = t1 - t0;
511 if (m_debug) std::cout <<
"Time step too small.\n";
514 const double invdt = 1. / dt;
516 const int bin = int((t0 - m_tStart) / m_tStep);
518 if (bin < 0 || bin >= (
int)m_nTimeBins) {
519 if (m_debug) std::cout <<
"Bin " << bin <<
" out of range.\n";
522 if (m_nEvents <= 0) m_nEvents = 1;
523 const bool electron = q < 0;
524 const double dx = x1 - x0;
525 const double dy = y1 - y0;
526 const double dz = z1 - z0;
527 const double vx = dx * invdt;
528 const double vy = dy * invdt;
529 const double vz = dz * invdt;
531 std::cout <<
" Time: " << t0 <<
"\n"
532 <<
" Step: " << dt <<
"\n"
533 <<
" Charge: " << q <<
"\n"
534 <<
" Velocity: (" << vx <<
", " << vy <<
", " << vz <<
")\n";
537 constexpr size_t nG = 6;
540 for (
auto &electrode : m_electrodes) {
541 const std::string lbl = electrode.label;
542 if (m_debug) std::cout <<
" Electrode " << electrode.label <<
":\n";
545 if (useWeightingPotential) {
546 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, lbl);
547 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, lbl);
549 std::cout <<
" Weighting potentials: " << w0 <<
", " << w1 <<
"\n";
551 if (w0 > -0.5 && w1 > -0.5) current = q * (w1 - w0) * invdt;
553 double wx = 0., wy = 0., wz = 0.;
555 if (integrateWeightingField) {
556 for (
size_t j = 0; j < nG; ++j) {
557 const double s = 0.5 * (1. + tg[j]);
558 const double x = x0 + s * dx;
559 const double y = y0 + s * dy;
560 const double z = z0 + s * dz;
561 double fx = 0., fy = 0., fz = 0.;
562 electrode.comp->WeightingField(x, y, z, fx, fy, fz, lbl);
571 const double x = x0 + 0.5 * dx;
572 const double y = y0 + 0.5 * dy;
573 const double z = z0 + 0.5 * dz;
574 electrode.comp->WeightingField(x, y, z, wx, wy, wz, lbl);
577 std::cout <<
" Weighting field: (" << wx <<
", " << wy <<
", " << wz
581 current = -q * (wx * vx + wy * vy + wz * vz);
583 if (m_debug) std::cout <<
" Induced charge: " << current * dt <<
"\n";
584 double delta = m_tStart + (bin + 1) * m_tStep - t0;
587 FillBin(electrode, bin, current * delta, electron,
false);
590 while (delta > m_tStep && bin + j < m_nTimeBins) {
591 FillBin(electrode, bin + j, current * m_tStep, electron,
false);
595 if (bin + j < m_nTimeBins) {
596 FillBin(electrode, bin + j, current * delta, electron,
false);
599 FillBin(electrode, bin, current * dt, electron,
false);
602 if (!m_delayedSignal)
return;
603 if (m_delayedSignalTimes.empty())
return;
604 const size_t nd = m_delayedSignalTimes.size();
606 std::vector<double> td(nd);
607 for (
size_t i = 0; i < nd; ++i) {
608 td[i] = t0 + m_delayedSignalTimes[i];
611 for (
auto &electrode : m_electrodes) {
612 const std::string lbl = electrode.label;
613 std::vector<double> id(nd, 0.);
614 if (useWeightingPotential) {
616 double chargeHolder = 0.;
617 double currentHolder = 0.;
620 for (
size_t i = 0; i < nd; ++i) {
621 double delayedtime = m_delayedSignalTimes[i] - t0;
622 if (delayedtime < 0)
continue;
624 int bin2 = int((m_delayedSignalTimes[i] - m_tStart) / m_tStep);
626 double dp0 = electrode.comp->DelayedWeightingPotential(
627 x0, y0, z0, delayedtime, lbl);
628 double dp1 = electrode.comp->DelayedWeightingPotential(
629 x1, y1, z1, delayedtime, lbl);
631 double charge = q * (dp1 - dp0);
634 if (std::isnan(charge)) {
640 double dtt = m_delayedSignalTimes[i] - m_delayedSignalTimes[i - 1];
642 double current2 = m_tStep * (charge - chargeHolder) / dtt;
644 if (std::abs(current2) < 1e-16) current2 = 0.;
645 electrode.delayedSignal[bin2] += current2;
646 electrode.signal[bin2] += current2;
648 electrode.electronSignal[bin2] += current2;
649 electrode.delayedElectronSignal[bin2] += current2;
651 electrode.ionSignal[bin2] += current2;
652 electrode.delayedIonSignal[bin2] += current2;
656 if (binHolder > 0 && binHolder + 1 < bin2) {
657 const int diffBin = bin2 - binHolder;
658 for (
int j = binHolder + 1; j < bin2; j++) {
659 electrode.delayedSignal[j] +=
660 (j - binHolder) * (current2 - currentHolder) / diffBin +
662 electrode.signal[j] +=
663 (j - binHolder) * (current2 - currentHolder) / diffBin +
667 currentHolder = current2;
671 chargeHolder = charge;
675 for (
size_t i = 0; i < nd; ++i) {
677 const double step = std::min(m_delayedSignalTimes[i], dt);
678 const double scale = step / dt;
680 for (
size_t j = 0; j < 6; ++j) {
681 double s = 0.5 * (1. + tg[j]);
682 const double t = m_delayedSignalTimes[i] - s * step;
684 const double x = x0 + s * dx;
685 const double y = y0 + s * dy;
686 const double z = z0 + s * dz;
688 double wx = 0., wy = 0., wz = 0.;
689 electrode.comp->DelayedWeightingField(x, y, z, t, wx, wy, wz, lbl);
690 sum += (wx * vx + wy * vy + wz * vz) * wg[j];
692 id[i] = -q * 0.5 * sum * step;
694 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
700 const std::vector<std::array<double, 3>> &xs,
701 const std::vector<std::array<double, 3>> &vs,
702 const std::vector<double> &ns,
const int navg,
703 const bool useWeightingPotential) {
705 if (ts.size() < 2)
return;
706 if (ts.size() != xs.size() || ts.size() != vs.size()) {
707 std::cerr << m_className <<
"::AddSignal: Mismatch in vector size.\n";
710 const bool aval = ns.size() == ts.size();
711 const size_t nPoints = ts.size();
713 std::cout << m_className <<
"::AddSignal: Adding a " << nPoints
714 <<
"-vector (charge " << q <<
").\n";
717 if (m_nEvents <= 0) m_nEvents = 1;
718 for (
auto &electrode : m_electrodes) {
719 const std::string label = electrode.label;
720 std::vector<double> signal(nPoints, 0.);
721 for (
size_t i = 0; i < nPoints; ++i) {
722 const auto &x = xs[i];
723 const auto &v = vs[i];
724 if (useWeightingPotential) {
725 const double dt = i < nPoints - 1 ? ts[i + 1] - ts[i] : 0.;
727 const double dx = dt * v[0];
728 const double dy = dt * v[1];
729 const double dz = dt * v[2];
731 const double x0 = x[0] - 0.5 * dx;
732 const double y0 = x[1] - 0.5 * dy;
733 const double z0 = x[2] - 0.5 * dz;
735 const double x1 = x[0] + 0.5 * dx;
736 const double y1 = x[1] + 0.5 * dy;
737 const double z1 = x[2] + 0.5 * dz;
739 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, label);
740 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, label);
741 if (w0 > -0.5 && w1 > -0.5) {
742 signal[i] = -q * (w1 - w0) / dt;
743 if (aval) signal[i] *= ns[i];
747 double wx = 0., wy = 0., wz = 0.;
748 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
750 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
751 if (aval) signal[i] *= ns[i];
754 FillSignal(electrode, q, ts, signal, navg);
757 if (!m_delayedSignal)
return;
758 if (m_delayedSignalTimes.empty())
return;
760 constexpr size_t nG = 6;
764 const size_t nd = m_delayedSignalTimes.size();
765 for (
size_t k = 0; k < nPoints - 1; ++k) {
766 const double t0 = ts[k];
767 const double t1 = ts[k + 1];
768 const double dt = t1 - t0;
769 if (dt < Small)
continue;
770 const auto &x0 = xs[k];
771 const auto &x1 = xs[k + 1];
772 const auto &v = vs[k];
773 std::vector<double> td(nd);
774 for (
size_t i = 0; i < nd; ++i) {
775 td[i] = t0 + m_delayedSignalTimes[i];
778 for (
auto &electrode : m_electrodes) {
779 const std::string lbl = electrode.label;
780 std::vector<double> id(nd, 0.);
781 for (
size_t i = 0; i < nd; ++i) {
783 const double step = std::min(m_delayedSignalTimes[i], dt);
784 const double scale = step / dt;
785 const double dx = scale * (x1[0] - x0[0]);
786 const double dy = scale * (x1[1] - x0[1]);
787 const double dz = scale * (x1[2] - x0[2]);
789 for (
size_t j = 0; j < nG; ++j) {
790 const double f = 0.5 * (1. + tg[j]);
791 const double t = m_delayedSignalTimes[i] - f * step;
793 double wx = 0., wy = 0., wz = 0.;
794 electrode.comp->DelayedWeightingField(x0[0] + f * dx, x0[1] + f * dy,
795 x0[2] + f * dz, t, wx, wy, wz,
797 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
799 id[i] = -q * 0.5 * sum * step;
801 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
806void Sensor::FillSignal(Electrode &electrode,
const double q,
807 const std::vector<double> &ts,
808 const std::vector<double> &is,
const int navg,
809 const bool delayed) {
810 const bool electron = q < 0.;
812 constexpr unsigned int k = 1;
813 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
814 const double t0 = m_tStart + i * m_tStep;
815 const double t1 = t0 + m_tStep;
816 if (ts.front() > t1)
continue;
817 if (ts.back() < t0)
break;
819 const double tmin = std::max(ts.front(), t0);
820 const double tmax = std::min(ts.back(), t1);
823 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
825 const double h = 0.5 * (tmax - tmin) / navg;
826 for (
int j = -navg; j <= navg; ++j) {
827 const int jj = j + navg;
828 const double t = t0 + jj * h;
829 if (t < ts.front() || t > ts.back())
continue;
830 if (j == -navg || j == navg) {
831 sum += Interpolate(is, ts, t, k);
832 }
else if (jj == 2 * (jj / 2)) {
833 sum += 2 * Interpolate(is, ts, t, k);
835 sum += 4 * Interpolate(is, ts, t, k);
841 FillBin(electrode, i, sum, electron, delayed);
846 const double z0,
const double x1,
const double y1,
848 if (m_debug) std::cout << m_className <<
"::AddInducedCharge:\n";
849 for (
auto &electrode : m_electrodes) {
851 auto cmp = electrode.comp;
852 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
854 const double w1 = cmp->WeightingPotential(x1, y1, z1, electrode.label);
855 if (w0 > -0.5 && w1 > -0.5) {
856 electrode.charge += q * (w1 - w0);
859 std::cout <<
" Electrode " << electrode.label <<
":\n"
860 <<
" Weighting potential at (" << x0 <<
", " << y0 <<
", "
861 << z0 <<
"): " << w0 <<
"\n"
862 <<
" Weighting potential at (" << x1 <<
", " << y1 <<
", "
863 << z1 <<
"): " << w1 <<
"\n"
864 <<
" Induced charge: " << electrode.charge <<
"\n";
870 const unsigned int nsteps) {
873 std::cerr << m_className <<
"::SetTimeWindow: Start time out of range.\n";
879 std::cerr << m_className <<
"::SetTimeWindow: Invalid number of bins.\n";
881 m_nTimeBins = nsteps;
885 std::cout << m_className <<
"::SetTimeWindow: " << m_tStart
886 <<
" < t [ns] < " << m_tStart + m_nTimeBins * m_tStep <<
"\n"
887 <<
" Step size: " << m_tStep <<
" ns\n";
890 std::cout << m_className <<
"::SetTimeWindow: Resetting all signals.\n";
891 for (
auto &electrode : m_electrodes) {
892 electrode.signal.assign(m_nTimeBins, 0.);
893 electrode.electronSignal.assign(m_nTimeBins, 0.);
894 electrode.ionSignal.assign(m_nTimeBins, 0.);
895 electrode.delayedSignal.assign(m_nTimeBins, 0.);
896 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
897 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
898 electrode.integrated =
false;
903 m_fTransferFFT.clear();
907 const unsigned int bin) {
908 if (m_nEvents == 0)
return 0.;
909 if (bin >= m_nTimeBins)
return 0.;
911 for (
const auto &electrode : m_electrodes) {
912 if (electrode.label == label) sig += electrode.electronSignal[bin];
914 return ElementaryCharge * sig / (m_nEvents * m_tStep);
918 if (m_nEvents == 0)
return 0.;
919 if (bin >= m_nTimeBins)
return 0.;
921 for (
const auto &electrode : m_electrodes) {
922 if (electrode.label == label) sig += electrode.ionSignal[bin];
924 return ElementaryCharge * sig / (m_nEvents * m_tStep);
928 const unsigned int bin) {
929 if (m_nEvents == 0)
return 0.;
930 if (bin >= m_nTimeBins)
return 0.;
932 for (
const auto &electrode : m_electrodes) {
933 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
935 return ElementaryCharge * sig / (m_nEvents * m_tStep);
939 const unsigned int bin) {
940 if (m_nEvents == 0)
return 0.;
941 if (bin >= m_nTimeBins)
return 0.;
943 for (
const auto &electrode : m_electrodes) {
944 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
946 return ElementaryCharge * sig / (m_nEvents * m_tStep);
950 const double signal) {
951 if (bin >= m_nTimeBins)
return;
952 if (m_nEvents == 0) m_nEvents = 1;
953 for (
auto &electrode : m_electrodes) {
954 if (electrode.label == label) {
955 electrode.signal[bin] = m_nEvents * m_tStep * signal / ElementaryCharge;
962 const std::vector<double>& ts,
963 const std::vector<double>& is) {
965 constexpr double q = -1.;
966 constexpr int navg = 0;
967 if (m_nEvents == 0) m_nEvents = 1;
968 for (
auto &electrode : m_electrodes) {
969 if (electrode.label == label) {
970 FillSignal(electrode, q, ts, is, navg,
false);
977 if (m_nEvents == 0)
return 0.;
978 if (bin >= m_nTimeBins)
return 0.;
980 for (
const auto &electrode : m_electrodes) {
981 if (electrode.label == label) sig += electrode.signal[bin];
983 return ElementaryCharge * sig / (m_nEvents * m_tStep);
988 if (m_nEvents == 0)
return 0.;
989 if (bin >= m_nTimeBins)
return 0.;
991 for (
const auto &electrode : m_electrodes) {
992 if (electrode.label == label) {
995 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
999 sig += electrode.delayedSignal[bin];
1003 sig += electrode.signal[bin];
1007 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1011 const unsigned int bin) {
1012 if (m_nEvents == 0)
return 0.;
1013 if (bin >= m_nTimeBins)
return 0.;
1015 for (
const auto &electrode : m_electrodes) {
1016 if (electrode.label == label)
1017 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
1019 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1023 const unsigned int bin) {
1024 if (m_nEvents == 0)
return 0.;
1025 if (bin >= m_nTimeBins)
return 0.;
1027 for (
const auto &electrode : m_electrodes) {
1028 if (electrode.label == label) sig += electrode.delayedSignal[bin];
1030 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1034 if (m_nEvents == 0)
return 0.;
1036 for (
const auto &electrode : m_electrodes) {
1037 if (electrode.label == label) charge += electrode.charge;
1040 return charge / m_nEvents;
1045 std::cerr << m_className <<
"::SetTransferFunction: Empty function.\n";
1050 m_fTransferTab.clear();
1051 m_fTransferSq = -1.;
1052 m_fTransferFFT.clear();
1056 const std::vector<double> &values) {
1057 if (times.empty() || values.empty()) {
1058 std::cerr << m_className <<
"::SetTransferFunction: Empty vector.\n";
1060 }
else if (times.size() != values.size()) {
1061 std::cerr << m_className <<
"::SetTransferFunction:\n"
1062 <<
" Time and value vectors must have same size.\n";
1065 const auto n = times.size();
1066 m_fTransferTab.clear();
1067 for (
unsigned int i = 0; i < n; ++i) {
1068 m_fTransferTab.emplace_back(std::make_pair(times[i], values[i]));
1070 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
1071 m_fTransfer =
nullptr;
1073 m_fTransferSq = -1.;
1074 m_fTransferFFT.clear();
1079 m_fTransfer =
nullptr;
1080 m_fTransferTab.clear();
1081 m_fTransferSq = -1.;
1082 m_fTransferFFT.clear();
1088 std::vector<double> t;
1089 std::vector<double> f;
1090 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1092 t.push_back(i * m_tStep);
1095 if (t.empty())
return;
1096 double fmin = *std::min_element(f.begin(), f.end());
1097 double fmax = *std::max_element(f.begin(), f.end());
1098 double df = fmax - fmin;
1099 if (fabs(df) < 1.e-6) {
1104 if (fmin < 0.) fmin -= 0.1 * df;
1107 TCanvas* cf =
new TCanvas(name.c_str(),
"Transfer Function");
1110 cf->DrawFrame(0., fmin, m_nTimeBins * m_tStep, fmax,
1111 ";time [ns]; transfer function");
1113 graph.SetLineWidth(4);
1114 graph.SetLineColor(kBlue + 2);
1115 graph.DrawGraph(t.size(), t.data(), f.data(),
"l");
1119double Sensor::InterpolateTransferFunctionTable(
const double t)
const {
1120 if (m_fTransferTab.empty())
return 0.;
1122 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
1126 const auto begin = m_fTransferTab.cbegin();
1127 const auto end = m_fTransferTab.cend();
1128 const auto it1 = std::upper_bound(begin, end, std::make_pair(t, 0.));
1129 if (it1 == end)
return 0.;
1130 if (it1 == begin)
return m_fTransferTab.front().second;
1131 const auto it0 = std::prev(it1);
1132 const double t0 = (*it0).first;
1133 const double t1 = (*it1).first;
1134 const double f = t0 == t1 ? 0. : (t - t0) / (t1 - t0);
1136 return (*it0).second * (1. - f) + (*it1).second * f;
1141 return m_fTransfer(t);
1142 }
else if (m_shaper) {
1143 return m_shaper->Shape(t);
1145 return InterpolateTransferFunctionTable(t);
1149 std::cout << m_className <<
"::PrintTransferFunction:\n";
1151 std::cout <<
" User-defined function.";
1152 }
else if (m_shaper) {
1153 std::string shaperType =
"Unknown";
1154 if (m_shaper->IsUnipolar()) {
1155 shaperType =
"Unipolar";
1156 }
else if (m_shaper->IsBipolar()) {
1157 shaperType =
"Bipolar";
1161 m_shaper->GetParameters(n, tp);
1162 std::printf(
" %s shaper with order %u and %5.1f ns peaking time.\n",
1163 shaperType.c_str(), n, tp);
1164 }
else if (!m_fTransferTab.empty()) {
1165 std::cout <<
" Table with " << m_fTransferTab.size() <<
" entries.\n";
1167 std::cout <<
" No transfer function set.\n";
1170 std::cout <<
" Time [ns] Transfer function\n";
1171 const double dt = m_nTimeBins * m_tStep / 10.;
1172 for (
size_t i = 0; i < 10; ++i) {
1173 const double t = m_tStart + (i + 0.5) * dt;
1175 std::printf(
" %10.3f %10.5f\n", t, f);
1180 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1181 std::cerr << m_className <<
"::ConvoluteSignal: "
1182 <<
"Transfer function not set.\n";
1185 if (m_nEvents == 0) {
1186 std::cerr << m_className <<
"::ConvoluteSignal: No signals present.\n";
1190 if (fft)
return ConvoluteSignalFFT(label);
1191 std::vector<double> cnvTab;
1192 MakeTransferFunctionTable(cnvTab);
1194 for (
auto &electrode : m_electrodes) {
1195 if (label != electrode.label)
continue;
1203 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1204 std::cerr << m_className <<
"::ConvoluteSignals: "
1205 <<
"Transfer function not set.\n";
1208 if (m_nEvents == 0) {
1209 std::cerr << m_className <<
"::ConvoluteSignals: No signals present.\n";
1213 if (fft)
return ConvoluteSignalFFT();
1214 std::vector<double> cnvTab;
1215 MakeTransferFunctionTable(cnvTab);
1217 for (
auto &electrode : m_electrodes)
ConvoluteSignal(electrode, cnvTab);
1221void Sensor::MakeTransferFunctionTable(std::vector<double> &cnvTab) {
1223 constexpr double cnvMin = 0.;
1224 constexpr double cnvMax = 1.e10;
1226 cnvTab.assign(2 * m_nTimeBins - 1, 0.);
1227 const unsigned int offset = m_nTimeBins - 1;
1229 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1231 double t = (-int(i)) * m_tStep;
1232 if (t < cnvMin || t > cnvMax) {
1233 cnvTab[offset - i] = 0.;
1237 if (i == 0)
continue;
1240 if (t < cnvMin || t > cnvMax) {
1241 cnvTab[offset + i] = 0.;
1249 const std::vector<double> &tab) {
1251 std::vector<double> tmpSignal(m_nTimeBins, 0.);
1252 std::vector<double> tmpSignalDelayed(m_nTimeBins, 0.);
1253 const unsigned int offset = m_nTimeBins - 1;
1254 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1255 tmpSignal[j] = tmpSignalDelayed[j] = 0.;
1256 for (
unsigned int k = 0; k < m_nTimeBins; ++k) {
1257 tmpSignal[j] += m_tStep * tab[offset + j - k] * electrode.signal[k];
1258 tmpSignalDelayed[j] +=
1259 m_tStep * tab[offset + j - k] * electrode.delayedSignal[k];
1262 electrode.signal.swap(tmpSignal);
1263 electrode.delayedSignal.swap(tmpSignalDelayed);
1264 electrode.integrated =
true;
1267bool Sensor::ConvoluteSignalFFT() {
1269 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1271 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1273 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1274 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1277 FFT(m_fTransferFFT,
false, nn);
1280 for (
auto &electrode : m_electrodes) {
1281 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1286bool Sensor::ConvoluteSignalFFT(
const std::string &label) {
1288 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1290 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1292 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1293 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1296 FFT(m_fTransferFFT,
false, nn);
1299 for (
auto &electrode : m_electrodes) {
1300 if (label != electrode.label)
continue;
1301 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1307void Sensor::ConvoluteSignalFFT(Electrode &electrode,
1308 const std::vector<double> &tab,
1309 const unsigned int nn) {
1310 std::vector<double> g(2 * (nn + 1), 0.);
1311 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1312 g[2 * i + 1] = electrode.signal[i];
1315 for (
unsigned int i = 0; i < nn; ++i) {
1316 const double fr = tab[2 * i + 1];
1317 const double fi = tab[2 * i + 2];
1318 const double gr = g[2 * i + 1];
1319 const double gi = g[2 * i + 2];
1320 g[2 * i + 1] = fr * gr - fi * gi;
1321 g[2 * i + 2] = fr * gi + gr * fi;
1324 const double scale = m_tStep / nn;
1325 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1326 electrode.signal[i] = scale * g[2 * i + 1];
1328 electrode.integrated =
true;
1332 if (m_nEvents == 0) {
1333 std::cerr << m_className <<
"::IntegrateSignals: No signals present.\n";
1342 if (m_nEvents == 0) {
1343 std::cerr << m_className <<
"::IntegrateSignal: No signals present.\n";
1347 for (
auto &electrode : m_electrodes) {
1348 if (label != electrode.label)
continue;
1352 std::cerr << m_className <<
"::IntegrateSignal: Electrode " << label
1358 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1359 electrode.signal[j] *= m_tStep;
1360 electrode.electronSignal[j] *= m_tStep;
1361 electrode.ionSignal[j] *= m_tStep;
1362 electrode.delayedSignal[j] *= m_tStep;
1364 electrode.signal[j] += electrode.signal[j - 1];
1365 electrode.electronSignal[j] += electrode.electronSignal[j - 1];
1366 electrode.ionSignal[j] += electrode.ionSignal[j - 1];
1367 electrode.delayedSignal[j] += electrode.delayedSignal[j - 1];
1370 electrode.integrated =
true;
1374 for (
const auto &electrode : m_electrodes) {
1375 if (electrode.label == label)
return electrode.integrated;
1381 const int offset = int(td / m_tStep);
1382 for (
auto &electrode : m_electrodes) {
1383 std::vector<double> signal1(m_nTimeBins, 0.);
1384 std::vector<double> signal2(m_nTimeBins, 0.);
1385 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1386 signal2[j] = f * electrode.signal[j];
1387 const int bin = j - offset;
1388 if (bin < 0 || bin >= (
int)m_nTimeBins)
continue;
1389 signal1[j] = electrode.signal[bin];
1391 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1392 electrode.signal[j] = signal1[j] - signal2[j];
1400 std::cerr << m_className <<
"::SetNoiseFunction: Null pointer.\n";
1408 std::cerr << m_className <<
"::AddNoise: Noise function not set.\n";
1411 if (m_nEvents == 0) m_nEvents = 1;
1413 for (
auto &electrode : m_electrodes) {
1414 double t = m_tStart + 0.5 * m_tStep;
1415 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1416 const double noise = m_fNoise(t);
1417 if (total) electrode.signal[j] += noise;
1418 if (electron) electrode.electronSignal[j] += noise;
1419 if (ion) electrode.ionSignal[j] += noise;
1426 const bool poisson,
const double q0) {
1427 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1428 std::cerr << m_className <<
"::AddWhiteNoise: Transfer function not set.\n";
1431 if (m_nEvents == 0) m_nEvents = 1;
1433 const double f2 = TransferFunctionSq();
1435 std::cerr << m_className <<
"::AddWhiteNoise:\n"
1436 <<
" Could not calculate transfer function integral.\n";
1442 const double nu = (enc * enc / (q0 * q0)) / f2;
1444 const double avg = nu * m_tStep * m_nTimeBins;
1446 for (
auto &electrode : m_electrodes) {
1447 if (label != electrode.label)
continue;
1449 for (
int j = 0; j < nPulses; ++j) {
1450 const int bin =
static_cast<int>(m_nTimeBins *
RndmUniform());
1451 electrode.signal[bin] += q0;
1453 const double offset = q0 * nu * m_tStep;
1454 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1455 electrode.signal[j] -= offset;
1461 const double sigma = enc * sqrt(m_tStep / f2);
1462 for (
auto &electrode : m_electrodes) {
1463 if (label != electrode.label)
continue;
1464 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1474 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1475 std::cerr << m_className <<
"::AddWhiteNoise: Transfer function not set.\n";
1478 if (m_nEvents == 0) m_nEvents = 1;
1480 const double f2 = TransferFunctionSq();
1482 std::cerr << m_className <<
"::AddWhiteNoise:\n"
1483 <<
" Could not calculate transfer function integral.\n";
1489 const double nu = (enc * enc / (q0 * q0)) / f2;
1491 const double avg = nu * m_tStep * m_nTimeBins;
1493 for (
auto &electrode : m_electrodes) {
1495 for (
int j = 0; j < nPulses; ++j) {
1496 const int bin =
static_cast<int>(m_nTimeBins *
RndmUniform());
1497 electrode.signal[bin] += q0;
1499 const double offset = q0 * nu * m_tStep;
1500 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1501 electrode.signal[j] -= offset;
1506 const double sigma = enc * sqrt(m_tStep / f2);
1507 for (
auto &electrode : m_electrodes) {
1508 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1515double Sensor::TransferFunctionSq() {
1516 if (m_fTransferSq >= 0.)
return m_fTransferSq;
1517 double integral = -1.;
1519 std::function<double(
double)> fsq = [
this](
double x) {
1520 const double y = m_fTransfer(x);
1523 constexpr double epsrel = 1.e-8;
1525 unsigned int stat = 0;
1527 }
else if (m_shaper) {
1528 integral = m_shaper->TransferFuncSq();
1530 integral = Trapezoid2(m_fTransferTab);
1532 if (m_cacheTransferFunction) m_fTransferSq = integral;
1537 const std::string &label,
int &n) {
1539 m_thresholdCrossings.clear();
1540 m_thresholdLevel = thr;
1545 if (m_nEvents == 0) {
1546 std::cerr << m_className <<
"::ComputeThresholdCrossings: "
1547 <<
"No signals present.\n";
1551 std::vector<double> signal(m_nTimeBins, 0.);
1553 bool foundLabel =
false;
1554 for (
const auto &electrode : m_electrodes) {
1555 if (electrode.label != label)
continue;
1557 if (!electrode.integrated) {
1558 std::cerr << m_className <<
"::ComputeThresholdCrossings:\n "
1559 <<
"Warning: signal on electrode " << label
1560 <<
" has not been integrated/convoluted.\n";
1562 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1563 signal[i] += electrode.signal[i];
1567 std::cerr << m_className <<
"::ComputeThresholdCrossings: Electrode "
1568 << label <<
" not found.\n";
1571 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1572 for (
unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1575 const double vMin = *std::min_element(std::begin(signal), std::end(signal));
1576 const double vMax = *std::max_element(std::begin(signal), std::end(signal));
1577 if (m_debug) std::cout << m_className <<
"::ComputeThresholdCrossings:\n";
1578 if (thr < vMin && thr > vMax) {
1580 std::cout <<
" Threshold outside the range [" << vMin <<
", " << vMax
1587 constexpr std::array<int, 2> directions = {1, -1};
1588 for (
const auto dir : directions) {
1589 const bool up = dir > 0;
1592 std::cout <<
" Hunting for rising edges.\n";
1594 std::cout <<
" Hunting for falling edges.\n";
1598 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1599 std::vector<double> vs = {signal[0]};
1601 for (
unsigned int i = 1; i < m_nTimeBins; ++i) {
1603 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1604 const double vNew = signal[i];
1606 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1612 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1613 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1616 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1626 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1627 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1629 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1632 n = m_thresholdCrossings.size();
1635 std::cout <<
" Found " << n <<
" crossings.\n";
1636 if (n > 0) std::cout <<
" Time [ns] Direction\n";
1637 for (
const auto &crossing : m_thresholdCrossings) {
1638 std::cout <<
" " << crossing.first <<
" ";
1639 if (crossing.second) {
1640 std::cout <<
"rising\n";
1642 std::cout <<
"falling\n";
1651 double &level,
bool &rise)
const {
1652 level = m_thresholdLevel;
1654 if (i >= m_thresholdCrossings.size()) {
1655 std::cerr << m_className <<
"::GetThresholdCrossing: Index out of range.\n";
1656 time = m_tStart + m_nTimeBins * m_tStep;
1660 time = m_thresholdCrossings[i].first;
1661 rise = m_thresholdCrossings[i].second;
1665bool Sensor::GetBoundingBox(
double &xmin,
double &ymin,
double &zmin,
1666 double &xmax,
double &ymax,
double &zmax) {
1670 double x0, y0, z0, x1, y1, z1;
1671 for (
const auto &cmp : m_components) {
1672 if (!std::get<1>(cmp))
continue;
1673 if (!std::get<0>(cmp)->GetBoundingBox(x0, y0, z0, x1, y1, z1))
continue;
1675 if (x0 < xmin) xmin = x0;
1676 if (y0 < ymin) ymin = y0;
1677 if (z0 < zmin) zmin = z0;
1678 if (x1 > xmax) xmax = x1;
1679 if (y1 > ymax) ymax = y1;
1680 if (z1 > zmax) zmax = z1;
1694 std::cerr << m_className <<
"::GetBoundingBox:\n"
1695 <<
" Sensor bounding box not known.\n";
1696 xmin = ymin = zmin = 0.;
1697 xmax = ymax = zmax = 0.;
1702 std::cout << m_className <<
"::GetBoundingBox:\n"
1703 <<
" " << xmin <<
" < x [cm] < " << xmax <<
"\n"
1704 <<
" " << ymin <<
" < y [cm] < " << ymax <<
"\n"
1705 <<
" " << zmin <<
" < z [cm] < " << zmax <<
"\n";
1710void Sensor::FFT(std::vector<double> &data,
const bool inverse,
const int nn) {
1716 const int n = 2 * nn;
1719 for (
int i = 1; i < n; i += 2) {
1722 std::swap(data[j], data[i]);
1723 std::swap(data[j + 1], data[i + 1]);
1726 while (m >= 2 && j > m) {
1733 const int isign = inverse ? -1 : 1;
1736 const int step = 2 * mmax;
1737 const double theta = isign * TwoPi / mmax;
1738 double wtemp =
sin(0.5 * theta);
1739 const double wpr = -2. * wtemp * wtemp;
1740 const double wpi =
sin(theta);
1743 for (
int m = 1; m < mmax; m += 2) {
1744 for (
int i = m; i <= n; i += step) {
1746 double tr = wr * data[j] - wi * data[j + 1];
1747 double ti = wr * data[j + 1] + wi * data[j];
1748 data[j] = data[i] - tr;
1749 data[j + 1] = data[i + 1] - ti;
1753 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1754 wi = wi * wpr + wtemp * wpi + wi;
1765 std::string optTotal =
"t";
1766 std::string optPrompt =
"";
1767 std::string optDelayed =
"";
1768 view.
PlotSignal(label, optTotal, optPrompt, optDelayed);
1772 const bool chargeCarriers)
const {
1773 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1774 for (
const auto &electrode : m_electrodes) {
1775 if (electrode.label != label)
continue;
1777 const std::string quantity = electrode.integrated ?
"Charge" :
"Signal";
1778 const std::string unit = electrode.integrated ?
" [fC]" :
" [fC/ns]";
1779 std::ofstream myfile;
1780 std::string filename = name +
".csv";
1781 myfile.open(filename);
1782 if (!electrode.integrated) {
1783 myfile <<
"The cumulative induced charge.\n";
1785 myfile <<
"The induced signal.\n";
1787 myfile <<
"Time [ns]"
1788 <<
",Total Prompt" << unit <<
",Total Delayed" << unit <<
", Total "
1789 << quantity << unit;
1790 if (chargeCarriers) {
1791 myfile <<
",Electron Prompt" << unit <<
",Electron Delayed" << unit
1792 <<
",Electron " << quantity << unit <<
",Ion Prompt" << unit
1793 <<
",Ion Delayed" << unit <<
",Ion " << quantity << unit;
1796 myfile << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
1797 for (
unsigned int i = 0; i < m_nTimeBins; i++) {
1798 myfile << m_tStart + i * m_tStep <<
","
1799 << scale * (electrode.signal[i] - electrode.delayedSignal[i])
1800 <<
"," << scale * electrode.delayedSignal[i] <<
","
1801 << scale * electrode.signal[i];
1802 if (!chargeCarriers) {
1807 << scale * (electrode.electronSignal[i] -
1808 electrode.delayedElectronSignal[i])
1809 <<
"," << scale * electrode.delayedElectronSignal[i] <<
","
1810 << scale * electrode.electronSignal[i] <<
","
1811 << scale * (electrode.ionSignal[i] - electrode.delayedIonSignal[i])
1812 <<
"," << scale * electrode.delayedIonSignal[i] <<
","
1813 << scale * electrode.ionSignal[i] <<
"\n";
1816 std::cout << m_className <<
"::ExportSignal: File '" << name
1817 <<
".csv' exported.\n";
1822 for (
const auto &electrode : m_electrodes) {
1823 if (electrode.label != label)
continue;
1824 if (!electrode.integrated || m_nEvents == 0)
return 0.;
1825 return ElementaryCharge * electrode.signal.back() / (m_nEvents * m_tStep);
Abstract base class for components.
Abstract base class for media.
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Get the magnetic field at (x, y, z).
void EnableMagneticField(const unsigned int i, const bool on)
Activate/deactivate use of the magnetic field of a given component.
double GetElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the electron signal for a given electrode and time bin.
bool ConvoluteSignal(const std::string &label, const bool fft=false)
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at (x, y, z).
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
double GetTotalInducedCharge(const std::string &label)
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)
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.
void PlotSignal(const std::string &label, TPad *pad)
Plot the induced signal.
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 EnableComponent(const unsigned int i, const bool on)
Activate/deactivate a given component.
bool CrossedWire(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 AddElectrode(Component *comp, const std::string &label)
Add an electrode.
bool IntegrateSignal(const std::string &label)
Replace the signal on a given electrode by its integral.
void AddComponent(Component *comp)
Add a component.
void SetTransferFunction(std::function< double(double)>)
Set the function to be used for evaluating the transfer function.
void PrintTransferFunction()
Print some information about the presently set transfer function.
double DelayedWeightingPotential(const double x, const double y, const double z, const double t, const std::string &label)
Get the delayed weighting potential 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).
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.
double GetPromptSignal(const std::string &label, const unsigned int bin)
Retrieve the prompt signal for a given electrode and time bin.
bool IntegrateSignals()
Replace the signals on all electrodes curve by their integrals.
bool HasMagneticField() const
Does the sensor have a non-zero magnetic field?
bool ConvoluteSignals(const bool fft=false)
Convolute all induced currents with the transfer function.
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
bool CrossedPlane(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc)
double GetInducedCharge(const std::string &label)
Calculated using the weighting potentials at the start and end points.
void AddNoise(const bool total=true, const bool electron=false, const bool ion=false)
Add noise to the induced signal.
double IntegrateFluxLine(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Get the weighting potential at (x, y, z).
Component * GetComponent(const unsigned int i)
Retrieve the pointer to a given component.
void AddWhiteNoise(const std::string &label, const double enc, const bool poisson=true, const double q0=1.)
bool ComputeThresholdCrossings(const double thr, const std::string &label, int &n)
void Clear()
Remove all components, electrodes and reset the sensor.
bool SetArea(const bool verbose=false)
Set the user area to the default.
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 GetThresholdCrossing(const unsigned int i, double &time, double &level, bool &rise) const
bool IsInside(const double x, const double y, const double z)
Check if a point is inside an active medium and inside the user area.
double GetDelayedElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed electron signal for a given electrode and time bin.
void ClearSignal()
Reset signals and induced charges of all electrodes.
double GetDelayedSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed signal for a given electrode and time bin.
void ExportSignal(const std::string &label, const std::string &filename, const bool chargeCariers=false) const
Exporting induced signal to a csv file.
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.
void PlotTransferFunction()
Plot the presently set transfer function.
bool InTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
Determine whether a point is in the trap radius of a wire.
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.
bool IsIntegrated(const std::string &label) const
Return whether the signal has been integrated/convoluted.
Class for signal processing.
void SetCanvas(TPad *pad)
Set the canvas to be painted on.
static std::string FindUnusedCanvasName(const std::string &s)
Find an unused canvas name.
Plot the signal computed by a sensor as a ROOT histogram.
void SetSensor(Sensor *s)
Set the sensor from which to retrieve the signal.
void PlotSignal(const std::string &label, const std::string &optTotal="t", const std::string &optPrompt="", const std::string &optDelayed="", const bool same=false)
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)
constexpr std::array< double, 6 > GaussLegendreWeights6()
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
constexpr std::array< double, 6 > GaussLegendreNodes6()
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)