15double Interpolate(
const std::vector<double>& y,
const std::vector<double>& x,
16 const double xx,
const unsigned int order) {
17 if (xx <
x.front() || xx >
x.back())
return 0.;
21 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
22 if (it1 ==
x.cend())
return y.back();
23 const auto it0 = std::prev(it1);
24 const double dx = (*it1 - *it0);
25 if (dx < Garfield::Small)
return y[it0 -
x.cbegin()];
26 const double f = (xx - *it0) / dx;
27 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
30double Trapezoid2(
const std::vector<std::pair<double, double> >& f) {
31 const size_t n = f.size();
32 if (n < 2)
return -1.;
34 const double x0 = f[0].first;
35 const double y0 = f[0].second;
36 const double x1 = f[1].first;
37 const double y1 = f[1].second;
39 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
41 const double x2 = f[2].first;
42 const double y2 = f[2].second;
43 sum = (x1 - x0) * y0 * y0 + (x2 - x0) * y1 * y1 + (x2 - x1) * y2 * y2;
45 sum = (x1 - x0) * y0 * y0 + (f[2].first - x0) * y1 * y1;
46 const double xm = f[n - 2].first;
47 const double ym = f[n - 2].second;
48 const double xn = f[n - 1].first;
49 const double yn = f[n - 1].second;
50 sum += (xn - f[n - 3].first) * ym * ym + (xn - xm) * yn * yn;
52 for (
size_t k = 2; k < n - 2; ++k) {
53 const double y = f[k].second;
54 sum += (f[k + 1].first - f[k - 1].first) * y * y;
66 double& ex,
double& ey,
double& ez,
double& v,
67 Medium*& medium,
int& status) {
68 ex = ey = ez = v = 0.;
71 double fx = 0., fy = 0., fz = 0., p = 0.;
75 for (
const auto& cmp : m_components) {
76 if (!std::get<1>(cmp))
continue;
77 std::get<0>(cmp)->ElectricField(x, y, z, fx, fy, fz, p, med, stat);
92 double& ex,
double& ey,
double& ez,
Medium*& medium,
97 double fx = 0., fy = 0., fz = 0.;
101 for (
const auto& cmp : m_components) {
102 if (!std::get<1>(cmp))
continue;
103 std::get<0>(cmp)->ElectricField(x, y, z, fx, fy, fz, med, stat);
117 double& bx,
double& by,
double& bz,
int& status) {
119 double fx = 0., fy = 0., fz = 0.;
121 for (
const auto& cmp : m_components) {
122 if (!std::get<2>(cmp))
continue;
123 std::get<0>(cmp)->MagneticField(x, y, z, fx, fy, fz, status);
124 if (status != 0)
continue;
132 double& wx,
double& wy,
double& wz,
133 const std::string& label) {
136 for (
const auto& electrode : m_electrodes) {
137 if (electrode.label == label) {
138 double fx = 0., fy = 0., fz = 0.;
139 electrode.comp->WeightingField(x, y, z, fx, fy, fz, label);
148 const double z,
const std::string& label) {
151 for (
const auto& electrode : m_electrodes) {
152 if (electrode.label == label) {
153 v += electrode.comp->WeightingPotential(x, y, z, label);
164 if (m_components.empty())
return false;
167 if (m_lastComponent) {
172 for (
const auto& cmp : m_components) {
173 if (!std::get<1>(cmp))
continue;
174 m = std::get<0>(cmp)->GetMedium(x, y, z);
176 m_lastComponent = std::get<0>(cmp);
184 std::lock_guard<std::mutex> guard(m_mutex);
185 if (!GetBoundingBox(m_xMinUser, m_yMinUser, m_zMinUser, m_xMaxUser,
186 m_yMaxUser, m_zMaxUser)) {
187 std::cerr << m_className <<
"::SetArea: Bounding box is not known.\n";
191 std::cout << m_className <<
"::SetArea:\n"
192 <<
" " << m_xMinUser <<
" < x [cm] < " << m_xMaxUser <<
"\n"
193 <<
" " << m_yMinUser <<
" < y [cm] < " << m_yMaxUser <<
"\n"
194 <<
" " << m_zMinUser <<
" < z [cm] < " << m_zMaxUser <<
"\n";
195 if (std::isinf(m_xMinUser) || std::isinf(m_xMaxUser)) {
196 std::cerr << m_className <<
"::SetArea: Warning. Infinite x-range.\n";
198 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
199 std::cerr << m_className <<
"::SetArea: Warning. Infinite y-range.\n";
201 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
202 std::cerr << m_className <<
"::SetArea: Warning. Infinite z-range.\n";
204 m_hasUserArea =
true;
209 const double xmax,
const double ymax,
const double zmax) {
210 std::lock_guard<std::mutex> guard(m_mutex);
211 if (fabs(xmax - xmin) < Small || fabs(ymax - ymin) < Small ||
212 fabs(zmax - zmin) < Small) {
213 std::cerr << m_className <<
"::SetArea: Invalid range.\n";
217 m_xMinUser = std::min(xmin, xmax);
218 m_yMinUser = std::min(ymin, ymax);
219 m_zMinUser = std::min(zmin, zmax);
220 m_xMaxUser = std::max(xmax, xmin);
221 m_yMaxUser = std::max(ymax, ymin);
222 m_zMaxUser = std::max(zmax, zmin);
224 m_hasUserArea =
true;
229 double& ymax,
double& zmax) {
255 if (!m_hasUserArea) {
257 std::cerr << m_className <<
"::IsInArea: User area could not be set.\n";
260 m_hasUserArea =
true;
263 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
264 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
269 std::cout << m_className <<
"::IsInArea: (" << x <<
", " << y <<
", " << z
277 const double x1,
const double y1,
const double z1,
278 double& xc,
double& yc,
double& zc,
279 const bool centre,
double& rc) {
280 for (
const auto& cmp : m_components) {
281 if (!std::get<1>(cmp))
continue;
282 if (std::get<0>(cmp)->
IsWireCrossed(x0, y0, z0, x1, y1, z1, xc, yc, zc,
291 double z0,
double& xw,
double& yw,
double& rw) {
292 for (
const auto& cmp : m_components) {
293 if (!std::get<1>(cmp))
continue;
294 if (std::get<0>(cmp)->
IsInTrapRadius(q0, x0, y0, z0, xw, yw, rw)) {
302 const double z0,
const double x1,
303 const double y1,
const double z1,
304 const double xp,
const double yp,
305 const double zp,
const unsigned int nI,
308 for (
const auto& cmp : m_components) {
309 if (!std::get<1>(cmp))
continue;
310 q += std::get<0>(cmp)->IntegrateFluxLine(x0, y0, z0, x1, y1, z1, xp, yp, zp,
318 std::cerr << m_className <<
"::AddComponent: Null pointer.\n";
322 m_components.push_back(std::make_tuple(cmp,
true,
true));
326 if (i >= m_components.size()) {
327 std::cerr << m_className <<
"::GetComponent: Index out of range.\n";
330 return std::get<0>(m_components[i]);
334 if (i >= m_components.size()) {
335 std::cerr << m_className <<
"::EnableComponent: Index out of range.\n";
338 std::get<1>(m_components[i]) = on;
342 if (i >= m_components.size()) {
343 std::cerr << m_className <<
"::EnableMagneticField: Index out of range.\n";
346 std::get<2>(m_components[i]) = on;
351 std::cerr << m_className <<
"::AddElectrode: Null pointer.\n";
354 for (
const auto& electrode : m_electrodes) {
355 if (electrode.label == label) {
356 std::cout << m_className <<
"::AddElectrode:\n"
357 <<
" Warning: An electrode with label \"" << label
358 <<
"\" exists already. Weighting fields will be summed up.\n";
364 electrode.comp = cmp;
365 electrode.label = label;
366 electrode.signal.assign(m_nTimeBins, 0.);
367 electrode.electronSignal.assign(m_nTimeBins, 0.);
368 electrode.ionSignal.assign(m_nTimeBins, 0.);
369 electrode.delayedSignal.assign(m_nTimeBins, 0.);
370 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
371 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
372 m_electrodes.push_back(std::move(electrode));
373 std::cout << m_className <<
"::AddElectrode:\n"
374 <<
" Added readout electrode \"" << label <<
"\".\n"
375 <<
" All signals are reset.\n";
380 std::lock_guard<std::mutex> guard(m_mutex);
381 m_components.clear();
382 m_lastComponent =
nullptr;
383 m_electrodes.clear();
388 m_hasUserArea =
false;
389 m_fTransfer =
nullptr;
391 m_fTransferTab.clear();
393 m_fTransferFFT.clear();
400 for (
const auto& cmp : m_components) {
401 if (!std::get<1>(cmp))
continue;
402 double umin = 0., umax = 0.;
405 vmin = std::min(umin, vmin);
406 vmax = std::max(umax, vmax);
416 std::cerr << m_className <<
"::GetVoltageRange:\n"
417 <<
" Sensor voltage range not known.\n";
423 std::cout << m_className <<
"::GetVoltageRange: " << vmin <<
" < V < "
430 for (
auto& electrode : m_electrodes) {
431 electrode.charge = 0.;
432 electrode.signal.assign(m_nTimeBins, 0.);
433 electrode.delayedSignal.assign(m_nTimeBins, 0.);
434 electrode.electronSignal.assign(m_nTimeBins, 0.);
435 electrode.ionSignal.assign(m_nTimeBins, 0.);
436 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
437 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
438 electrode.integrated =
false;
444 if (!std::is_sorted(ts.begin(), ts.end())) {
445 std::cerr << m_className <<
"::SetDelayedSignalTimes:\n"
446 <<
" Times are not in ascending order.\n";
449 m_delayedSignalTimes = ts;
453 const double x0,
const double y0,
const double z0,
454 const double x1,
const double y1,
const double z1,
455 const bool integrateWeightingField,
456 const bool useWeightingPotential) {
457 if (m_debug) std::cout << m_className <<
"::AddSignal: ";
460 if (m_debug) std::cout <<
"Time " << t0 <<
" out of range.\n";
463 const double dt = t1 - t0;
465 if (m_debug) std::cout <<
"Time step too small.\n";
468 const double invdt = 1. / dt;
470 const int bin = int((t0 - m_tStart) / m_tStep);
472 if (bin < 0 || bin >= (
int)m_nTimeBins) {
473 if (m_debug) std::cout <<
"Bin " << bin <<
" out of range.\n";
476 if (m_nEvents <= 0) m_nEvents = 1;
477 const bool electron = q < 0;
478 const double dx = x1 - x0;
479 const double dy = y1 - y0;
480 const double dz = z1 - z0;
481 const double vx = dx * invdt;
482 const double vy = dy * invdt;
483 const double vz = dz * invdt;
485 std::cout <<
" Time: " << t0 <<
"\n"
486 <<
" Step: " << dt <<
"\n"
487 <<
" Charge: " << q <<
"\n"
488 <<
" Velocity: (" << vx <<
", " << vy <<
", " << vz <<
")\n";
491 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
492 -0.238619186083196909, 0.238619186083196909,
493 0.661209386466264514, 0.932469514203152028};
494 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
495 0.467913934572691047, 0.467913934572691047,
496 0.360761573048138608, 0.171324492379170345};
497 for (
auto& electrode : m_electrodes) {
498 const std::string lbl = electrode.label;
499 if (m_debug) std::cout <<
" Electrode " << electrode.label <<
":\n";
502 if (useWeightingPotential) {
503 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, lbl);
504 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, lbl);
505 current = q * (w1 - w0) * invdt;
507 std::cout <<
" Weighting potentials: " << w0 <<
", " << w1 <<
"\n";
510 double wx = 0., wy = 0., wz = 0.;
512 if (integrateWeightingField) {
513 for (
size_t j = 0; j < 6; ++j) {
514 const double s = 0.5 * (1. + tg[j]);
515 const double x = x0 + s * dx;
516 const double y = y0 + s * dy;
517 const double z = z0 + s * dz;
518 double fx = 0., fy = 0., fz = 0.;
519 electrode.comp->WeightingField(x, y, z, fx, fy, fz, lbl);
528 const double x = x0 + 0.5 * dx;
529 const double y = y0 + 0.5 * dy;
530 const double z = z0 + 0.5 * dz;
531 electrode.comp->WeightingField(x, y, z, wx, wy, wz, lbl);
534 std::cout <<
" Weighting field: (" << wx <<
", " << wy <<
", " << wz
538 current = -q * (wx * vx + wy * vy + wz * vz);
540 if (m_debug) std::cout <<
" Induced charge: " << current * dt <<
"\n";
541 double delta = m_tStart + (bin + 1) * m_tStep - t0;
544 FillBin(electrode, bin, current * delta, electron,
false);
547 while (delta > m_tStep && bin + j < m_nTimeBins) {
548 FillBin(electrode, bin + j, current * m_tStep, electron,
false);
552 if (bin + j < m_nTimeBins) {
553 FillBin(electrode, bin + j, current * delta, electron,
false);
556 FillBin(electrode, bin, current * dt, electron,
false);
559 if (!m_delayedSignal)
return;
560 if (m_delayedSignalTimes.empty())
return;
561 const size_t nd = m_delayedSignalTimes.size();
563 std::vector<double> td(nd);
564 for (
size_t i = 0; i < nd; ++i) {
565 td[i] = t0 + m_delayedSignalTimes[i];
568 for (
auto& electrode : m_electrodes) {
569 const std::string lbl = electrode.label;
570 std::vector<double> id(nd, 0.);
571 if (useWeightingPotential) {
573 double chargeHolder = 0.;
574 double currentHolder = 0.;
577 for (
size_t i = 0; i < nd; ++i) {
578 double delayedtime = m_delayedSignalTimes[i] - t0;
579 if (delayedtime < 0)
continue;
581 int bin2 = int((m_delayedSignalTimes[i] - m_tStart) / m_tStep);
583 double dp0 = electrode.comp->DelayedWeightingPotential(
584 x0, y0, z0, delayedtime, lbl);
585 double dp1 = electrode.comp->DelayedWeightingPotential(
586 x1, y1, z1, delayedtime, lbl);
588 double charge = q * (dp1 - dp0);
591 if (std::isnan(charge)) {
597 double dtt = m_delayedSignalTimes[i] - m_delayedSignalTimes[i - 1];
599 double current2 = m_tStep * (charge - chargeHolder) / dtt;
601 if (std::abs(current2) < 1e-16) current2 = 0.;
602 electrode.delayedSignal[bin2] += current2;
603 electrode.signal[bin2] += current2;
606 if (binHolder > 0 && binHolder + 1 < bin2) {
607 const int diffBin = bin2 - binHolder;
608 for (
int j = binHolder + 1; j < bin2; j++) {
609 electrode.delayedSignal[j] +=
610 (j - binHolder) * (current2 - currentHolder) / diffBin +
612 electrode.signal[j] +=
613 (j - binHolder) * (current2 - currentHolder) / diffBin +
617 currentHolder = current2;
621 chargeHolder = charge;
625 for (
size_t i = 0; i < nd; ++i) {
627 const double step = std::min(m_delayedSignalTimes[i], dt);
628 const double scale = step / dt;
630 for (
size_t j = 0; j < 6; ++j) {
631 double s = 0.5 * (1. + tg[j]);
632 const double t = m_delayedSignalTimes[i] - s * step;
634 const double x = x0 + s * dx;
635 const double y = y0 + s * dy;
636 const double z = z0 + s * dz;
638 double wx = 0., wy = 0., wz = 0.;
639 electrode.comp->DelayedWeightingField(x, y, z, t, wx, wy, wz, lbl);
640 sum += (wx * vx + wy * vy + wz * vz) * wg[j];
642 id[i] = -q * 0.5 * sum * step;
644 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
650 const std::vector<std::array<double, 3> >& xs,
651 const std::vector<std::array<double, 3> >& vs,
652 const std::vector<double>& ns,
const int navg,
653 const bool useWeightingPotential) {
655 if (ts.size() < 2)
return;
656 if (ts.size() != xs.size() || ts.size() != vs.size()) {
657 std::cerr << m_className <<
"::AddSignal: Mismatch in vector size.\n";
660 const bool aval = ns.size() == ts.size();
661 const size_t nPoints = ts.size();
663 std::cout << m_className <<
"::AddSignal: Adding a " << nPoints
664 <<
"-vector (charge " << q <<
").\n";
667 if (m_nEvents <= 0) m_nEvents = 1;
668 for (
auto& electrode : m_electrodes) {
669 const std::string label = electrode.label;
670 std::vector<double> signal(nPoints, 0.);
671 for (
size_t i = 0; i < nPoints; ++i) {
672 const auto& x = xs[i];
673 const auto& v = vs[i];
674 if (useWeightingPotential) {
675 const double dt = i < nPoints - 1 ? ts[i + 1] - ts[i] : 0.;
677 const double dx = dt * v[0];
678 const double dy = dt * v[1];
679 const double dz = dt * v[2];
681 const double x0 = x[0] - 0.5 * dx;
682 const double y0 = x[1] - 0.5 * dy;
683 const double z0 = x[2] - 0.5 * dz;
685 const double x1 = x[0] + 0.5 * dx;
686 const double y1 = x[1] + 0.5 * dy;
687 const double z1 = x[2] + 0.5 * dz;
689 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, label);
690 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, label);
691 const double charge = q * (w1 - w0);
692 const double current = charge / dt;
694 signal[i] = -current;
695 if (aval) signal[i] *= ns[i];
698 double wx = 0., wy = 0., wz = 0.;
699 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
701 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
702 if (aval) signal[i] *= ns[i];
705 FillSignal(electrode, q, ts, signal, navg);
708 if (!m_delayedSignal)
return;
709 if (m_delayedSignalTimes.empty())
return;
711 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
712 -0.238619186083196909, 0.238619186083196909,
713 0.661209386466264514, 0.932469514203152028};
714 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
715 0.467913934572691047, 0.467913934572691047,
716 0.360761573048138608, 0.171324492379170345};
717 const size_t nd = m_delayedSignalTimes.size();
718 for (
size_t k = 0; k < nPoints - 1; ++k) {
719 const double t0 = ts[k];
720 const double t1 = ts[k + 1];
721 const double dt = t1 - t0;
722 if (dt < Small)
continue;
723 const auto& x0 = xs[k];
724 const auto& x1 = xs[k + 1];
725 const auto& v = vs[k];
726 std::vector<double> td(nd);
727 for (
size_t i = 0; i < nd; ++i) {
728 td[i] = t0 + m_delayedSignalTimes[i];
731 for (
auto& electrode : m_electrodes) {
732 const std::string lbl = electrode.label;
733 std::vector<double> id(nd, 0.);
734 for (
size_t i = 0; i < nd; ++i) {
736 const double step = std::min(m_delayedSignalTimes[i], dt);
737 const double scale = step / dt;
738 const double dx = scale * (x1[0] - x0[0]);
739 const double dy = scale * (x1[1] - x0[1]);
740 const double dz = scale * (x1[2] - x0[2]);
742 for (
size_t j = 0; j < 6; ++j) {
743 const double f = 0.5 * (1. + tg[j]);
744 const double t = m_delayedSignalTimes[i] - f * step;
746 double wx = 0., wy = 0., wz = 0.;
747 electrode.comp->DelayedWeightingField(x0[0] + f * dx, x0[1] + f * dy,
748 x0[2] + f * dz, t, wx, wy, wz,
750 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
752 id[i] = -q * 0.5 * sum * step;
754 FillSignal(electrode, q, td,
id, m_nAvgDelayedSignal,
true);
759void Sensor::FillSignal(Electrode& electrode,
const double q,
760 const std::vector<double>& ts,
761 const std::vector<double>& is,
const int navg,
762 const bool delayed) {
763 const bool electron = q < 0.;
765 constexpr unsigned int k = 1;
766 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
767 const double t0 = m_tStart + i * m_tStep;
768 const double t1 = t0 + m_tStep;
769 if (ts.front() > t1)
continue;
770 if (ts.back() < t0)
break;
772 const double tmin = std::max(ts.front(), t0);
773 const double tmax = std::min(ts.back(), t1);
776 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
778 const double h = 0.5 * (tmax - tmin) / navg;
779 for (
int j = -navg; j <= navg; ++j) {
780 const int jj = j + navg;
781 const double t = t0 + jj * h;
782 if (t < ts.front() || t > ts.back())
continue;
783 if (j == -navg || j == navg) {
784 sum += Interpolate(is, ts, t, k);
785 }
else if (jj == 2 * (jj / 2)) {
786 sum += 2 * Interpolate(is, ts, t, k);
788 sum += 4 * Interpolate(is, ts, t, k);
794 FillBin(electrode, i, sum, electron, delayed);
799 const double z0,
const double x1,
const double y1,
801 if (m_debug) std::cout << m_className <<
"::AddInducedCharge:\n";
802 for (
auto& electrode : m_electrodes) {
804 auto cmp = electrode.comp;
805 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
807 const double w1 = cmp->WeightingPotential(x1, y1, z1, electrode.label);
808 electrode.charge += q * (w1 - w0);
810 std::cout <<
" Electrode " << electrode.label <<
":\n"
811 <<
" Weighting potential at (" << x0 <<
", " << y0 <<
", "
812 << z0 <<
"): " << w0 <<
"\n"
813 <<
" Weighting potential at (" << x1 <<
", " << y1 <<
", "
814 << z1 <<
"): " << w1 <<
"\n"
815 <<
" Induced charge: " << electrode.charge <<
"\n";
821 const unsigned int nsteps) {
824 std::cerr << m_className <<
"::SetTimeWindow: Start time out of range.\n";
830 std::cerr << m_className <<
"::SetTimeWindow: Invalid number of bins.\n";
832 m_nTimeBins = nsteps;
836 std::cout << m_className <<
"::SetTimeWindow: " << m_tStart
837 <<
" < t [ns] < " << m_tStart + m_nTimeBins * m_tStep <<
"\n"
838 <<
" Step size: " << m_tStep <<
" ns\n";
841 std::cout << m_className <<
"::SetTimeWindow: Resetting all signals.\n";
842 for (
auto& electrode : m_electrodes) {
843 electrode.signal.assign(m_nTimeBins, 0.);
844 electrode.electronSignal.assign(m_nTimeBins, 0.);
845 electrode.ionSignal.assign(m_nTimeBins, 0.);
846 electrode.delayedSignal.assign(m_nTimeBins, 0.);
847 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
848 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
849 electrode.integrated =
false;
854 m_fTransferFFT.clear();
858 const unsigned int bin) {
859 if (m_nEvents == 0)
return 0.;
860 if (bin >= m_nTimeBins)
return 0.;
862 for (
const auto& electrode : m_electrodes) {
863 if (electrode.label == label) sig += electrode.electronSignal[bin];
865 return ElementaryCharge * sig / (m_nEvents * m_tStep);
869 if (m_nEvents == 0)
return 0.;
870 if (bin >= m_nTimeBins)
return 0.;
872 for (
const auto& electrode : m_electrodes) {
873 if (electrode.label == label) sig += electrode.ionSignal[bin];
875 return ElementaryCharge * sig / (m_nEvents * m_tStep);
879 const unsigned int bin) {
880 if (m_nEvents == 0)
return 0.;
881 if (bin >= m_nTimeBins)
return 0.;
883 for (
const auto& electrode : m_electrodes) {
884 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
886 return ElementaryCharge * sig / (m_nEvents * m_tStep);
890 const unsigned int bin) {
891 if (m_nEvents == 0)
return 0.;
892 if (bin >= m_nTimeBins)
return 0.;
894 for (
const auto& electrode : m_electrodes) {
895 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
897 return ElementaryCharge * sig / (m_nEvents * m_tStep);
901 const double signal) {
902 if (bin >= m_nTimeBins)
return;
903 if (m_nEvents == 0) m_nEvents = 1;
904 for (
auto& electrode : m_electrodes) {
905 if (electrode.label == label) {
906 electrode.signal[bin] = m_nEvents * m_tStep * signal / ElementaryCharge;
913 if (m_nEvents == 0)
return 0.;
914 if (bin >= m_nTimeBins)
return 0.;
916 for (
const auto& electrode : m_electrodes) {
917 if (electrode.label == label) sig += electrode.signal[bin];
919 return ElementaryCharge * sig / (m_nEvents * m_tStep);
924 if (m_nEvents == 0)
return 0.;
925 if (bin >= m_nTimeBins)
return 0.;
927 for (
const auto& electrode : m_electrodes) {
928 if (electrode.label == label) {
931 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
935 sig += electrode.delayedSignal[bin];
939 sig += electrode.signal[bin];
943 return ElementaryCharge * sig / (m_nEvents * m_tStep);
947 const unsigned int bin) {
948 if (m_nEvents == 0)
return 0.;
949 if (bin >= m_nTimeBins)
return 0.;
951 for (
const auto& electrode : m_electrodes) {
952 if (electrode.label == label)
953 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
955 return ElementaryCharge * sig / (m_nEvents * m_tStep);
959 const unsigned int bin) {
960 if (m_nEvents == 0)
return 0.;
961 if (bin >= m_nTimeBins)
return 0.;
963 for (
const auto& electrode : m_electrodes) {
964 if (electrode.label == label) sig += electrode.delayedSignal[bin];
966 return ElementaryCharge * sig / (m_nEvents * m_tStep);
970 if (m_nEvents == 0)
return 0.;
972 for (
const auto& electrode : m_electrodes) {
973 if (electrode.label == label) charge += electrode.charge;
976 return charge / m_nEvents;
981 std::cerr << m_className <<
"::SetTransferFunction: Null pointer.\n";
986 m_fTransferTab.clear();
988 m_fTransferFFT.clear();
992 const std::vector<double>& values) {
993 if (times.empty() || values.empty()) {
994 std::cerr << m_className <<
"::SetTransferFunction: Empty vector.\n";
996 }
else if (times.size() != values.size()) {
997 std::cerr << m_className <<
"::SetTransferFunction:\n"
998 <<
" Time and value vectors must have same size.\n";
1001 const auto n = times.size();
1002 m_fTransferTab.clear();
1003 for (
unsigned int i = 0; i < n; ++i) {
1004 m_fTransferTab.emplace_back(std::make_pair(times[i], values[i]));
1006 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
1007 m_fTransfer =
nullptr;
1009 m_fTransferSq = -1.;
1010 m_fTransferFFT.clear();
1015 m_fTransfer =
nullptr;
1016 m_fTransferTab.clear();
1017 m_fTransferSq = -1.;
1018 m_fTransferFFT.clear();
1021double Sensor::InterpolateTransferFunctionTable(
const double t)
const {
1022 if (m_fTransferTab.empty())
return 0.;
1024 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
1028 const auto begin = m_fTransferTab.cbegin();
1029 const auto end = m_fTransferTab.cend();
1030 const auto it1 = std::upper_bound(begin, end, std::make_pair(t, 0.));
1031 if (it1 == end)
return 0.;
1032 if (it1 == begin)
return m_fTransferTab.front().second;
1033 const auto it0 = std::prev(it1);
1034 const double t0 = (*it0).first;
1035 const double t1 = (*it1).first;
1036 const double f = t0 == t1 ? 0. : (t - t0) / (t1 - t0);
1038 return (*it0).second * (1. - f) + (*it1).second * f;
1043 return m_fTransfer(t);
1044 }
else if (m_shaper) {
1045 return m_shaper->
Shape(t);
1047 return InterpolateTransferFunctionTable(t);
1052 std::cout << m_className <<
"::PrintTransferFunction:\n";
1054 std::cout <<
" User-defined function.";
1055 }
else if (m_shaper) {
1056 std::string shaperType =
"Unknown";
1058 shaperType =
"Unipolar";
1060 shaperType =
"Bipolar";
1065 std::printf(
" %s shaper with order %u and %5.1f ns peaking time.\n",
1066 shaperType.c_str(), n, tp);
1067 }
else if (!m_fTransferTab.empty()) {
1068 std::cout <<
" Table with " << m_fTransferTab.size() <<
" entries.\n";
1070 std::cout <<
" No transfer function set.\n";
1073 std::cout <<
" Time [ns] Transfer function\n";
1074 const double dt = m_nTimeBins * m_tStep / 10.;
1075 for (
size_t i = 0; i < 10; ++i) {
1076 const double t = m_tStart + (i + 0.5) * dt;
1078 std::printf(
" %10.3f %10.5f\n", t, f);
1083 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1084 std::cerr << m_className <<
"::ConvoluteSignal: "
1085 <<
"Transfer function not set.\n";
1088 if (m_nEvents == 0) {
1089 std::cerr << m_className <<
"::ConvoluteSignal: No signals present.\n";
1093 if (fft)
return ConvoluteSignalFFT(label);
1094 std::vector<double> cnvTab;
1095 MakeTransferFunctionTable(cnvTab);
1097 for (
auto& electrode : m_electrodes) {
1098 if (label != electrode.label)
continue;
1106 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1107 std::cerr << m_className <<
"::ConvoluteSignals: "
1108 <<
"Transfer function not set.\n";
1111 if (m_nEvents == 0) {
1112 std::cerr << m_className <<
"::ConvoluteSignals: No signals present.\n";
1116 if (fft)
return ConvoluteSignalFFT();
1117 std::vector<double> cnvTab;
1118 MakeTransferFunctionTable(cnvTab);
1120 for (
auto& electrode : m_electrodes)
ConvoluteSignal(electrode, cnvTab);
1124void Sensor::MakeTransferFunctionTable(std::vector<double>& cnvTab) {
1126 constexpr double cnvMin = 0.;
1127 constexpr double cnvMax = 1.e10;
1129 cnvTab.assign(2 * m_nTimeBins - 1, 0.);
1130 const unsigned int offset = m_nTimeBins - 1;
1132 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1134 double t = (-int(i)) * m_tStep;
1135 if (t < cnvMin || t > cnvMax) {
1136 cnvTab[offset - i] = 0.;
1140 if (i == 0)
continue;
1143 if (t < cnvMin || t > cnvMax) {
1144 cnvTab[offset + i] = 0.;
1152 const std::vector<double>& tab) {
1154 std::vector<double> tmpSignal(m_nTimeBins, 0.);
1155 std::vector<double> tmpSignalDelayed(m_nTimeBins, 0.);
1156 const unsigned int offset = m_nTimeBins - 1;
1157 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1158 tmpSignal[j] = tmpSignalDelayed[j] = 0.;
1159 for (
unsigned int k = 0; k < m_nTimeBins; ++k) {
1160 tmpSignal[j] += m_tStep * tab[offset + j - k] * electrode.signal[k];
1161 tmpSignalDelayed[j] +=
1162 m_tStep * tab[offset + j - k] * electrode.delayedSignal[k];
1165 electrode.signal.swap(tmpSignal);
1166 electrode.delayedSignal.swap(tmpSignalDelayed);
1167 electrode.integrated =
true;
1170bool Sensor::ConvoluteSignalFFT() {
1172 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1174 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1176 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1177 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1180 FFT(m_fTransferFFT,
false, nn);
1183 for (
auto& electrode : m_electrodes) {
1184 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1189bool Sensor::ConvoluteSignalFFT(
const std::string& label) {
1191 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1193 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1195 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1196 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1199 FFT(m_fTransferFFT,
false, nn);
1202 for (
auto& electrode : m_electrodes) {
1203 if (label != electrode.label)
continue;
1204 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1210void Sensor::ConvoluteSignalFFT(Electrode& electrode,
1211 const std::vector<double>& tab,
1212 const unsigned int nn) {
1213 std::vector<double> g(2 * (nn + 1), 0.);
1214 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1215 g[2 * i + 1] = electrode.signal[i];
1218 for (
unsigned int i = 0; i < nn; ++i) {
1219 const double fr = tab[2 * i + 1];
1220 const double fi = tab[2 * i + 2];
1221 const double gr = g[2 * i + 1];
1222 const double gi = g[2 * i + 2];
1223 g[2 * i + 1] = fr * gr - fi * gi;
1224 g[2 * i + 2] = fr * gi + gr * fi;
1227 const double scale = m_tStep / nn;
1228 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1229 electrode.signal[i] = scale * g[2 * i + 1];
1231 electrode.integrated =
true;
1235 if (m_nEvents == 0) {
1236 std::cerr << m_className <<
"::IntegrateSignals: No signals present.\n";
1245 if (m_nEvents == 0) {
1246 std::cerr << m_className <<
"::IntegrateSignal: No signals present.\n";
1250 for (
auto& electrode : m_electrodes) {
1251 if (label != electrode.label)
continue;
1255 std::cerr << m_className <<
"::IntegrateSignal: Electrode " << label
1261 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1262 electrode.signal[j] *= m_tStep;
1263 electrode.electronSignal[j] *= m_tStep;
1264 electrode.ionSignal[j] *= m_tStep;
1265 electrode.delayedSignal[j] *= m_tStep;
1267 electrode.signal[j] += electrode.signal[j - 1];
1268 electrode.electronSignal[j] += electrode.electronSignal[j - 1];
1269 electrode.ionSignal[j] += electrode.ionSignal[j - 1];
1270 electrode.delayedSignal[j] += electrode.delayedSignal[j - 1];
1273 electrode.integrated =
true;
1277 for (
const auto& electrode : m_electrodes) {
1278 if (electrode.label == label)
return electrode.integrated;
1284 const int offset = int(td / m_tStep);
1285 for (
auto& electrode : m_electrodes) {
1286 std::vector<double> signal1(m_nTimeBins, 0.);
1287 std::vector<double> signal2(m_nTimeBins, 0.);
1288 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1289 signal2[j] = f * electrode.signal[j];
1290 const int bin = j - offset;
1291 if (bin < 0 || bin >= (
int)m_nTimeBins)
continue;
1292 signal1[j] = electrode.signal[bin];
1294 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1295 electrode.signal[j] = signal1[j] - signal2[j];
1303 std::cerr << m_className <<
"::SetNoiseFunction: Null pointer.\n";
1311 std::cerr << m_className <<
"::AddNoise: Noise function not set.\n";
1314 if (m_nEvents == 0) m_nEvents = 1;
1316 for (
auto& electrode : m_electrodes) {
1317 double t = m_tStart + 0.5 * m_tStep;
1318 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1319 const double noise = m_fNoise(t);
1320 if (total) electrode.signal[j] += noise;
1321 if (electron) electrode.electronSignal[j] += noise;
1322 if (ion) electrode.ionSignal[j] += noise;
1329 const bool poisson,
const double q0) {
1330 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1331 std::cerr << m_className <<
"::AddWhiteNoise: Transfer function not set.\n";
1334 if (m_nEvents == 0) m_nEvents = 1;
1336 const double f2 = TransferFunctionSq();
1338 std::cerr << m_className <<
"::AddWhiteNoise:\n"
1339 <<
" Could not calculate transfer function integral.\n";
1345 const double nu = (enc * enc / (q0 * q0)) / f2;
1347 const double avg = nu * m_tStep * m_nTimeBins;
1349 for (
auto& electrode : m_electrodes) {
1350 if (label != electrode.label)
continue;
1352 for (
int j = 0; j < nPulses; ++j) {
1353 const int bin =
static_cast<int>(m_nTimeBins *
RndmUniform());
1354 electrode.signal[bin] += q0;
1356 const double offset = q0 * nu * m_tStep;
1357 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1358 electrode.signal[j] -= offset;
1364 const double sigma = enc * sqrt(m_tStep / f2);
1365 for (
auto& electrode : m_electrodes) {
1366 if (label != electrode.label)
continue;
1367 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1377 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1378 std::cerr << m_className <<
"::AddWhiteNoise: Transfer function not set.\n";
1381 if (m_nEvents == 0) m_nEvents = 1;
1383 const double f2 = TransferFunctionSq();
1385 std::cerr << m_className <<
"::AddWhiteNoise:\n"
1386 <<
" Could not calculate transfer function integral.\n";
1392 const double nu = (enc * enc / (q0 * q0)) / f2;
1394 const double avg = nu * m_tStep * m_nTimeBins;
1396 for (
auto& electrode : m_electrodes) {
1398 for (
int j = 0; j < nPulses; ++j) {
1399 const int bin =
static_cast<int>(m_nTimeBins *
RndmUniform());
1400 electrode.signal[bin] += q0;
1402 const double offset = q0 * nu * m_tStep;
1403 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1404 electrode.signal[j] -= offset;
1409 const double sigma = enc * sqrt(m_tStep / f2);
1410 for (
auto& electrode : m_electrodes) {
1411 for (
unsigned int j = 0; j < m_nTimeBins; ++j) {
1418double Sensor::TransferFunctionSq() {
1419 if (m_fTransferSq >= 0.)
return m_fTransferSq;
1420 double integral = -1.;
1422 std::function<double(
double)> fsq = [
this](
double x) {
1423 const double y = m_fTransfer(x);
1426 constexpr double epsrel = 1.e-8;
1428 unsigned int stat = 0;
1430 }
else if (m_shaper) {
1433 integral = Trapezoid2(m_fTransferTab);
1435 if (m_cacheTransferFunction) m_fTransferSq = integral;
1440 const std::string& label,
int& n) {
1442 m_thresholdCrossings.clear();
1443 m_thresholdLevel = thr;
1448 if (m_nEvents == 0) {
1449 std::cerr << m_className <<
"::ComputeThresholdCrossings: "
1450 <<
"No signals present.\n";
1454 std::vector<double> signal(m_nTimeBins, 0.);
1456 bool foundLabel =
false;
1457 for (
const auto& electrode : m_electrodes) {
1458 if (electrode.label != label)
continue;
1460 if (!electrode.integrated) {
1461 std::cerr << m_className <<
"::ComputeThresholdCrossings:\n "
1462 <<
"Warning: signal on electrode " << label
1463 <<
" has not been integrated/convoluted.\n";
1465 for (
unsigned int i = 0; i < m_nTimeBins; ++i) {
1466 signal[i] += electrode.signal[i];
1470 std::cerr << m_className <<
"::ComputeThresholdCrossings: Electrode "
1471 << label <<
" not found.\n";
1474 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1475 for (
unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1478 const double vMin = *std::min_element(std::begin(signal), std::end(signal));
1479 const double vMax = *std::max_element(std::begin(signal), std::end(signal));
1480 if (m_debug) std::cout << m_className <<
"::ComputeThresholdCrossings:\n";
1481 if (thr < vMin && thr > vMax) {
1483 std::cout <<
" Threshold outside the range [" << vMin <<
", " << vMax
1490 constexpr std::array<int, 2> directions = {1, -1};
1491 for (
const auto dir : directions) {
1492 const bool up = dir > 0;
1495 std::cout <<
" Hunting for rising edges.\n";
1497 std::cout <<
" Hunting for falling edges.\n";
1501 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1502 std::vector<double> vs = {signal[0]};
1504 for (
unsigned int i = 1; i < m_nTimeBins; ++i) {
1506 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1507 const double vNew = signal[i];
1509 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1515 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1516 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1519 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1529 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1530 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1532 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1535 n = m_thresholdCrossings.size();
1538 std::cout <<
" Found " << n <<
" crossings.\n";
1539 if (n > 0) std::cout <<
" Time [ns] Direction\n";
1540 for (
const auto& crossing : m_thresholdCrossings) {
1541 std::cout <<
" " << crossing.first <<
" ";
1542 if (crossing.second) {
1543 std::cout <<
"rising\n";
1545 std::cout <<
"falling\n";
1554 double& level,
bool& rise)
const {
1555 level = m_thresholdLevel;
1557 if (i >= m_thresholdCrossings.size()) {
1558 std::cerr << m_className <<
"::GetThresholdCrossing: Index out of range.\n";
1559 time = m_tStart + m_nTimeBins * m_tStep;
1563 time = m_thresholdCrossings[i].first;
1564 rise = m_thresholdCrossings[i].second;
1568bool Sensor::GetBoundingBox(
double& xmin,
double& ymin,
double& zmin,
1569 double& xmax,
double& ymax,
double& zmax) {
1573 double x0, y0, z0, x1, y1, z1;
1574 for (
const auto& cmp : m_components) {
1575 if (!std::get<1>(cmp))
continue;
1576 if (!std::get<0>(cmp)->GetBoundingBox(x0, y0, z0, x1, y1, z1))
continue;
1578 if (x0 < xmin) xmin = x0;
1579 if (y0 < ymin) ymin = y0;
1580 if (z0 < zmin) zmin = z0;
1581 if (x1 > xmax) xmax = x1;
1582 if (y1 > ymax) ymax = y1;
1583 if (z1 > zmax) zmax = z1;
1597 std::cerr << m_className <<
"::GetBoundingBox:\n"
1598 <<
" Sensor bounding box not known.\n";
1599 xmin = ymin = zmin = 0.;
1600 xmax = ymax = zmax = 0.;
1605 std::cout << m_className <<
"::GetBoundingBox:\n"
1606 <<
" " << xmin <<
" < x [cm] < " << xmax <<
"\n"
1607 <<
" " << ymin <<
" < y [cm] < " << ymax <<
"\n"
1608 <<
" " << zmin <<
" < z [cm] < " << zmax <<
"\n";
1613void Sensor::FFT(std::vector<double>& data,
const bool inverse,
const int nn) {
1619 const int n = 2 * nn;
1622 for (
int i = 1; i < n; i += 2) {
1625 std::swap(data[j], data[i]);
1626 std::swap(data[j + 1], data[i + 1]);
1629 while (m >= 2 && j > m) {
1636 const int isign = inverse ? -1 : 1;
1639 const int step = 2 * mmax;
1640 const double theta = isign * TwoPi / mmax;
1641 double wtemp =
sin(0.5 * theta);
1642 const double wpr = -2. * wtemp * wtemp;
1643 const double wpi =
sin(theta);
1646 for (
int m = 1; m < mmax; m += 2) {
1647 for (
int i = m; i <= n; i += step) {
1649 double tr = wr * data[j] - wi * data[j + 1];
1650 double ti = wr * data[j + 1] + wi * data[j];
1651 data[j] = data[i] - tr;
1652 data[j + 1] = data[i + 1] - ti;
1656 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1657 wi = wi * wpr + wtemp * wpi + wi;
1664 const std::string& name) {
1666 const double scale = m_nEvents > 0 ? ElementaryCharge / (m_nEvents * m_tStep) : 0.;
1667 for (
const auto& electrode : m_electrodes) {
1668 if (electrode.label != label)
continue;
1669 std::ofstream myfile;
1670 std::string filename = name +
".csv";
1671 myfile.open(filename);
1672 if (myfile.fail()) {
1673 std::cerr << m_className <<
"::ExportSignal:\n"
1674 <<
" Could not open file " << filename <<
".\n";
1677 if (electrode.integrated) {
1678 myfile <<
"The cumulative induced charge.\n";
1679 myfile <<
"Time [ns],Prompt [fC],Delayed [fC],Total [fC],\n";
1681 myfile <<
"The induced signal.\n";
1682 myfile <<
"Time [ns],Prompt [fC/ns],Delayed [fC/ns],Total [fC/ns];\n";
1684 for (
unsigned int i = 0; i < m_nTimeBins; i++) {
1685 myfile << std::setprecision(std::numeric_limits<long double>::digits10 +
1687 << m_tStart + i * m_tStep <<
","
1688 << scale * (electrode.signal[i] - electrode.delayedSignal[i])
1690 << scale * electrode.delayedSignal[i] <<
","
1691 << scale * electrode.signal[i] <<
"\n";
1694 std::cerr << m_className <<
"::ExportSignal: File '" << filename
1695 <<
".csv' exported.\n";
1701 for (
const auto& electrode : m_electrodes) {
1702 if (electrode.label != label)
continue;
1703 if (!electrode.integrated || m_nEvents == 0)
return 0.;
1704 return ElementaryCharge * electrode.signal.back() / (m_nEvents * m_tStep);
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).
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)
double GetTotalInducedCharge(const std::string label)
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
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 ExportSignal(const std::string &label, const std::string &filename)
Exporting induced signal to a csv file.
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 EnableComponent(const unsigned int i, const bool on)
Activate/deactivate a given component.
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 PrintTransferFunction()
Print some information about the presently set transfer function.
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.
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 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.
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)
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.
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
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.
double GetDelayedSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed signal for a given electrode and time bin.
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.
bool IsIntegrated(const std::string &label) const
Return whether the signal has been integrated/convoluted.
Class for signal processing.
bool IsUnipolar() const
Is it a unipolar shaper?
double TransferFuncSq() const
Return the integral of the transfer function squared.
void GetParameters(unsigned int &n, double &tp)
Retrieve the parameters.
double Shape(const double t) const
Evaluate the transfer function.
bool IsBipolar() const
Is it a bipolar shaper?
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)