Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Sensor.cc
Go to the documentation of this file.
1#include "Garfield/Sensor.hh"
2
3#include <algorithm>
4#include <cmath>
5#include <fstream>
6#include <iomanip>
7#include <iostream>
8
9#include <TGraph.h>
10
13#include "Garfield/Numerics.hh"
14#include "Garfield/Random.hh"
15#include "Garfield/ViewBase.hh"
17
18namespace {
19
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.;
23 if (order > 1) {
24 return Garfield::Numerics::Divdif(y, x, x.size(), xx, order);
25 }
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()];
33}
34
35double Trapezoid2(const std::vector<std::pair<double, double>> &f) {
36 const size_t n = f.size();
37 if (n < 2) return -1.;
38 double sum = 0.;
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;
43 if (n == 2) {
44 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
45 } else if (n == 3) {
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;
49 } else {
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;
56 if (n > 4) {
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;
60 }
61 }
62 }
63 return 0.5 * sum;
64}
65
66} // namespace
67
68namespace Garfield {
69
70void Sensor::ElectricField(const double x, const double y, const double z,
71 double &ex, double &ey, double &ez, double &v,
72 Medium *&medium, int &status) {
73 ex = ey = ez = v = 0.;
74 status = -10;
75 medium = nullptr;
76 double fx = 0., fy = 0., fz = 0., p = 0.;
77 Medium *med = nullptr;
78 int stat = 0;
79 // Add up electric field contributions from all components.
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);
83 if (status != 0) {
84 status = stat;
85 medium = med;
86 }
87 if (stat == 0) {
88 ex += fx;
89 ey += fy;
90 ez += fz;
91 v += p;
92 }
93 }
94}
95
96void Sensor::ElectricField(const double x, const double y, const double z,
97 double &ex, double &ey, double &ez, Medium *&medium,
98 int &status) {
99 ex = ey = ez = 0.;
100 status = -10;
101 medium = nullptr;
102 double fx = 0., fy = 0., fz = 0.;
103 Medium *med = nullptr;
104 int stat = 0;
105 // Add up electric field contributions from all components.
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);
109 if (status != 0) {
110 status = stat;
111 medium = med;
112 }
113 if (stat == 0) {
114 ex += fx;
115 ey += fy;
116 ez += fz;
117 }
118 }
119}
120
121void Sensor::MagneticField(const double x, const double y, const double z,
122 double &bx, double &by, double &bz, int &status) {
123 bx = by = bz = 0.;
124 double fx = 0., fy = 0., fz = 0.;
125 // Add up contributions.
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;
130 bx += fx;
131 by += fy;
132 bz += fz;
133 }
134}
135
136void Sensor::WeightingField(const double x, const double y, const double z,
137 double &wx, double &wy, double &wz,
138 const std::string &label) {
139 wx = wy = wz = 0.;
140 // Add up field contributions from all components.
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);
145 wx += fx;
146 wy += fy;
147 wz += fz;
148 }
149 }
150}
151
152double Sensor::WeightingPotential(const double x, const double y,
153 const double z, const std::string &label) {
154 double v = 0.;
155 // Add up contributions from all components.
156 for (const auto &electrode : m_electrodes) {
157 if (electrode.label == label) {
158 v += std::max(electrode.comp->WeightingPotential(x, y, z, label), 0.);
159 }
160 }
161 return v;
162}
163
164double Sensor::DelayedWeightingPotential(const double x, const double y,
165 const double z, const double t,
166 const std::string &label) {
167 double v = 0.;
168 // Add up contributions from all components.
169 for (const auto &electrode : m_electrodes) {
170 if (electrode.label == label) {
171 v += std::max(
172 electrode.comp->DelayedWeightingPotential(x, y, z, t, label), 0.);
173 }
174 }
175 return v;
176}
177
178Medium *Sensor::GetMedium(const double x, const double y, const double z) {
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;
183 }
184 return nullptr;
185}
186
187bool Sensor::SetArea(const bool verbose) {
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";
192 return false;
193 }
194
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";
200 }
201 if (std::isinf(m_xMinUser) || std::isinf(m_xMaxUser)) {
202 std::cerr << m_className << "::SetArea: Warning. Infinite x-range.\n";
203 }
204 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
205 std::cerr << m_className << "::SetArea: Warning. Infinite y-range.\n";
206 }
207 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
208 std::cerr << m_className << "::SetArea: Warning. Infinite z-range.\n";
209 }
210 m_hasUserArea = true;
211 return true;
212}
213
214bool Sensor::SetArea(const double xmin, const double ymin, const double zmin,
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";
220 return false;
221 }
222
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);
229
230 m_hasUserArea = true;
231 return true;
232}
233
234bool Sensor::GetArea(double &xmin, double &ymin, double &zmin, double &xmax,
235 double &ymax, double &zmax) {
236 if (m_hasUserArea) {
237 xmin = m_xMinUser;
238 ymin = m_yMinUser;
239 zmin = m_zMinUser;
240 xmax = m_xMaxUser;
241 ymax = m_yMaxUser;
242 zmax = m_zMaxUser;
243 return true;
244 }
245
246 // User area bounds are not (yet) defined.
247 // Get the bounding box of the sensor.
248 if (!SetArea()) return false;
249
250 xmin = m_xMinUser;
251 ymin = m_yMinUser;
252 zmin = m_zMinUser;
253 xmax = m_xMaxUser;
254 ymax = m_yMaxUser;
255 zmax = m_zMaxUser;
256
257 return true;
258}
259
260bool Sensor::IsInArea(const double x, const double y, const double z) {
261 if (!m_hasUserArea) {
262 if (!SetArea()) {
263 std::cerr << m_className << "::IsInArea: User area could not be set.\n";
264 return false;
265 }
266 m_hasUserArea = true;
267 }
268
269 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
270 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
271 return true;
272 }
273
274 if (m_debug) {
275 std::cout << m_className << "::IsInArea: (" << x << ", " << y << ", " << z
276 << ") "
277 << " is outside.\n";
278 }
279 return false;
280}
281
282bool Sensor::IsInside(const double x, const double y, const double z) {
283 double ex = 0., ey = 0., ez = 0.;
284 Medium *medium = nullptr;
285 int status = 0;
286 ElectricField(x, y, z, ex, ey, ez, medium, status);
287 if (status != 0 || !medium) return false;
288
289 return true;
290}
291
292bool Sensor::CrossedWire(const double x0, const double y0, const double z0,
293 const double x1, const double y1, const double z1,
294 double &xc, double &yc, double &zc, const bool centre,
295 double &rc) {
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,
299 centre, rc)) {
300 return true;
301 }
302 }
303 return false;
304}
305
306bool Sensor::InTrapRadius(const double q0, const double x0, const double y0,
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)) {
311 return true;
312 }
313 }
314 return false;
315}
316
317bool Sensor::CrossedPlane(const double x0, const double y0, const double z0,
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)) {
323 return true;
324 }
325 }
326 return false;
327}
328
330
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);
336 }
337 return dmin < std::numeric_limits<double>::max() ? dmin : -1.;
338}
339
340double Sensor::IntegrateFluxLine(const double x0, const double y0,
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,
345 const int isign) {
346 double q = 0.;
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,
350 nI, isign);
351 }
352 return q;
353}
354
356 if (!cmp) {
357 std::cerr << m_className << "::AddComponent: Null pointer.\n";
358 return;
359 }
360
361 m_components.push_back(std::make_tuple(cmp, true, true));
362}
363
364Component *Sensor::GetComponent(const unsigned int i) {
365 if (i >= m_components.size()) {
366 std::cerr << m_className << "::GetComponent: Index out of range.\n";
367 return nullptr;
368 };
369 return std::get<0>(m_components[i]);
370}
371
372void Sensor::EnableComponent(const unsigned int i, const bool on) {
373 if (i >= m_components.size()) {
374 std::cerr << m_className << "::EnableComponent: Index out of range.\n";
375 return;
376 };
377 std::get<1>(m_components[i]) = on;
378}
379
380void Sensor::EnableMagneticField(const unsigned int i, const bool on) {
381 if (i >= m_components.size()) {
382 std::cerr << m_className << "::EnableMagneticField: Index out of range.\n";
383 return;
384 };
385 std::get<2>(m_components[i]) = on;
386}
387
389 for (const auto &cmp : m_components) {
390 if (!std::get<1>(cmp) || !std::get<2>(cmp)) continue;
391 if (std::get<0>(cmp)->HasMagneticField()) return true;
392 }
393 return false;
394}
395
396void Sensor::AddElectrode(Component *cmp, const std::string &label) {
397 if (!cmp) {
398 std::cerr << m_className << "::AddElectrode: Null pointer.\n";
399 return;
400 }
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";
406 break;
407 }
408 }
409
410 Electrode electrode;
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";
423 ClearSignal();
424}
425
427 std::lock_guard<std::mutex> guard(m_mutex);
428 m_components.clear();
429 m_electrodes.clear();
430 m_nTimeBins = 200;
431 m_tStart = 0.;
432 m_tStep = 10.;
433 m_nEvents = 0;
434 m_hasUserArea = false;
435 m_fTransfer = nullptr;
436 m_shaper = nullptr;
437 m_fTransferTab.clear();
438 m_fTransferSq = -1.;
439 m_fTransferFFT.clear();
440}
441
442bool Sensor::GetVoltageRange(double &vmin, double &vmax) {
443 // We don't know the range yet.
444 bool set = false;
445 // Loop over the components.
446 for (const auto &cmp : m_components) {
447 if (!std::get<1>(cmp)) continue;
448 double umin = 0., umax = 0.;
449 if (!std::get<0>(cmp)->GetVoltageRange(umin, umax)) continue;
450 if (set) {
451 vmin = std::min(umin, vmin);
452 vmax = std::max(umax, vmax);
453 } else {
454 vmin = umin;
455 vmax = umax;
456 set = true;
457 }
458 }
459
460 // Warn if we still don't know the range.
461 if (!set) {
462 std::cerr << m_className << "::GetVoltageRange:\n"
463 << " Sensor voltage range not known.\n";
464 vmin = vmax = 0.;
465 return false;
466 }
467
468 if (m_debug) {
469 std::cout << m_className << "::GetVoltageRange: " << vmin << " < V < "
470 << vmax << ".\n";
471 }
472 return true;
473}
474
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;
485 }
486 m_nEvents = 0;
487}
488
489void Sensor::SetDelayedSignalTimes(const std::vector<double> &ts) {
490 if (!std::is_sorted(ts.begin(), ts.end())) {
491 std::cerr << m_className << "::SetDelayedSignalTimes:\n"
492 << " Times are not in ascending order.\n";
493 return;
494 }
495 m_delayedSignalTimes = ts;
496}
497
498void Sensor::AddSignal(const double q, const double t0, const double t1,
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: ";
504 // Get the time bin.
505 if (t0 < m_tStart) {
506 if (m_debug) std::cout << "Time " << t0 << " out of range.\n";
507 return;
508 }
509 const double dt = t1 - t0;
510 if (dt < Small) {
511 if (m_debug) std::cout << "Time step too small.\n";
512 return;
513 }
514 const double invdt = 1. / dt;
515
516 const int bin = int((t0 - m_tStart) / m_tStep);
517 // Check if the starting time is outside the range
518 if (bin < 0 || bin >= (int)m_nTimeBins) {
519 if (m_debug) std::cout << "Bin " << bin << " out of range.\n";
520 return;
521 }
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;
530 if (m_debug) {
531 std::cout << " Time: " << t0 << "\n"
532 << " Step: " << dt << "\n"
533 << " Charge: " << q << "\n"
534 << " Velocity: (" << vx << ", " << vy << ", " << vz << ")\n";
535 }
536 // Locations and weights for 6-point Gaussian integration
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";
543 // Induced current.
544 double current = 0.;
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);
548 if (m_debug) {
549 std::cout << " Weighting potentials: " << w0 << ", " << w1 << "\n";
550 }
551 if (w0 > -0.5 && w1 > -0.5) current = q * (w1 - w0) * invdt;
552 } else {
553 double wx = 0., wy = 0., wz = 0.;
554 // Calculate the weighting field for this electrode.
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);
563 wx += wg[j] * fx;
564 wy += wg[j] * fy;
565 wz += wg[j] * fz;
566 }
567 wx *= 0.5;
568 wy *= 0.5;
569 wz *= 0.5;
570 } else {
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);
575 }
576 if (m_debug) {
577 std::cout << " Weighting field: (" << wx << ", " << wy << ", " << wz
578 << ")\n";
579 }
580 // Calculate the induced current.
581 current = -q * (wx * vx + wy * vy + wz * vz);
582 }
583 if (m_debug) std::cout << " Induced charge: " << current * dt << "\n";
584 double delta = m_tStart + (bin + 1) * m_tStep - t0;
585 // Check if the provided timestep extends over more than one time bin
586 if (dt > delta) {
587 FillBin(electrode, bin, current * delta, electron, false);
588 delta = dt - delta;
589 unsigned int j = 1;
590 while (delta > m_tStep && bin + j < m_nTimeBins) {
591 FillBin(electrode, bin + j, current * m_tStep, electron, false);
592 delta -= m_tStep;
593 ++j;
594 }
595 if (bin + j < m_nTimeBins) {
596 FillBin(electrode, bin + j, current * delta, electron, false);
597 }
598 } else {
599 FillBin(electrode, bin, current * dt, electron, false);
600 }
601 }
602 if (!m_delayedSignal) return;
603 if (m_delayedSignalTimes.empty()) return;
604 const size_t nd = m_delayedSignalTimes.size();
605 // Establish the points in time at which we evaluate the delayed signal.
606 std::vector<double> td(nd);
607 for (size_t i = 0; i < nd; ++i) {
608 td[i] = t0 + m_delayedSignalTimes[i];
609 }
610 // Calculate the signals for each electrode.
611 for (auto &electrode : m_electrodes) {
612 const std::string lbl = electrode.label;
613 std::vector<double> id(nd, 0.);
614 if (useWeightingPotential) {
615 // Using the weighting potential.
616 double chargeHolder = 0.;
617 double currentHolder = 0.;
618 int binHolder = 0;
619 // Loop over each time in the given vector of delayed times.
620 for (size_t i = 0; i < nd; ++i) {
621 double delayedtime = m_delayedSignalTimes[i] - t0; // t - t0
622 if (delayedtime < 0) continue;
623 // Find bin that needs to be filled.
624 int bin2 = int((m_delayedSignalTimes[i] - m_tStart) / m_tStep);
625 // Compute induced charge
626 double dp0 = electrode.comp->DelayedWeightingPotential(
627 x0, y0, z0, delayedtime, lbl);
628 double dp1 = electrode.comp->DelayedWeightingPotential(
629 x1, y1, z1, delayedtime, lbl);
630
631 double charge = q * (dp1 - dp0);
632 // In very rare cases the result is infinity. We do not let this
633 // contribute.
634 if (std::isnan(charge)) {
635 charge = 0.;
636 }
637 // Calculate induced current based on the induced charge.
638 if (i > 0) {
639 // Time difference between previous entry.
640 double dtt = m_delayedSignalTimes[i] - m_delayedSignalTimes[i - 1];
641 // Induced current
642 double current2 = m_tStep * (charge - chargeHolder) / dtt;
643 // Fill bins
644 if (std::abs(current2) < 1e-16) current2 = 0.;
645 electrode.delayedSignal[bin2] += current2;
646 electrode.signal[bin2] += current2;
647 if (q < 0) {
648 electrode.electronSignal[bin2] += current2;
649 electrode.delayedElectronSignal[bin2] += current2;
650 } else {
651 electrode.ionSignal[bin2] += current2;
652 electrode.delayedIonSignal[bin2] += current2;
653 }
654 // Linear interpolation if the current is calculated from the induced
655 // charge of two non-subsequent bins.
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 +
661 currentHolder;
662 electrode.signal[j] +=
663 (j - binHolder) * (current2 - currentHolder) / diffBin +
664 currentHolder;
665 }
666 }
667 currentHolder = current2;
668 }
669 // Hold information for next current calculation and interpolation.
670 binHolder = bin2;
671 chargeHolder = charge;
672 }
673 } else {
674 // Using the weighting field.
675 for (size_t i = 0; i < nd; ++i) {
676 // Integrate over the drift line segment.
677 const double step = std::min(m_delayedSignalTimes[i], dt);
678 const double scale = step / dt;
679 double sum = 0.;
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;
683 s *= scale;
684 const double x = x0 + s * dx;
685 const double y = y0 + s * dy;
686 const double z = z0 + s * dz;
687 // Calculate the delayed weighting field.
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];
691 }
692 id[i] = -q * 0.5 * sum * step;
693 }
694 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
695 }
696 }
697}
698
699void Sensor::AddSignal(const double q, const std::vector<double> &ts,
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) {
704 // Don't do anything if there are no points on the signal.
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";
708 return;
709 }
710 const bool aval = ns.size() == ts.size();
711 const size_t nPoints = ts.size();
712 if (m_debug) {
713 std::cout << m_className << "::AddSignal: Adding a " << nPoints
714 << "-vector (charge " << q << ").\n";
715 }
716
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.;
726
727 const double dx = dt * v[0];
728 const double dy = dt * v[1];
729 const double dz = dt * v[2];
730
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;
734
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;
738
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];
744 }
745 } else {
746 // Calculate the weighting field at this point.
747 double wx = 0., wy = 0., wz = 0.;
748 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
749 // Calculate the induced current at this point.
750 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
751 if (aval) signal[i] *= ns[i];
752 }
753 }
754 FillSignal(electrode, q, ts, signal, navg);
755 }
756
757 if (!m_delayedSignal) return;
758 if (m_delayedSignalTimes.empty()) return;
759 // Locations and weights for 6-point Gaussian integration
760 constexpr size_t nG = 6;
763
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];
776 }
777 // Calculate the signals for each electrode.
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) {
782 // Integrate over the drift line segment.
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]);
788 double sum = 0.;
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;
792 // Calculate the delayed weighting field.
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,
796 lbl);
797 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
798 }
799 id[i] = -q * 0.5 * sum * step;
800 }
801 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
802 }
803 }
804}
805
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.;
811 // Interpolation order.
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;
818 // Integration over this bin.
819 const double tmin = std::max(ts.front(), t0);
820 const double tmax = std::min(ts.back(), t1);
821 double sum = 0.;
822 if (navg <= 0) {
823 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
824 } else {
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);
834 } else {
835 sum += 4 * Interpolate(is, ts, t, k);
836 }
837 }
838 sum *= h / 3.;
839 }
840 // Add the result to the signal.
841 FillBin(electrode, i, sum, electron, delayed);
842 }
843}
844
845void Sensor::AddInducedCharge(const double q, const double x0, const double y0,
846 const double z0, const double x1, const double y1,
847 const double z1) {
848 if (m_debug) std::cout << m_className << "::AddInducedCharge:\n";
849 for (auto &electrode : m_electrodes) {
850 // Calculate the weighting potential at the starting point.
851 auto cmp = electrode.comp;
852 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
853 // Calculate the weighting potential at the end point.
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);
857 }
858 if (m_debug) {
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";
865 }
866 }
867}
868
869void Sensor::SetTimeWindow(const double tstart, const double tstep,
870 const unsigned int nsteps) {
871 m_tStart = tstart;
872 if (tstep <= 0.) {
873 std::cerr << m_className << "::SetTimeWindow: Start time out of range.\n";
874 } else {
875 m_tStep = tstep;
876 }
877
878 if (nsteps == 0) {
879 std::cerr << m_className << "::SetTimeWindow: Invalid number of bins.\n";
880 } else {
881 m_nTimeBins = nsteps;
882 }
883
884 if (m_debug) {
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";
888 }
889
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;
899 }
900 m_nEvents = 0;
901 // Reset the cached FFT of the transfer function
902 // because it depends on the number of time bins.
903 m_fTransferFFT.clear();
904}
905
906double Sensor::GetElectronSignal(const std::string &label,
907 const unsigned int bin) {
908 if (m_nEvents == 0) return 0.;
909 if (bin >= m_nTimeBins) return 0.;
910 double sig = 0.;
911 for (const auto &electrode : m_electrodes) {
912 if (electrode.label == label) sig += electrode.electronSignal[bin];
913 }
914 return ElementaryCharge * sig / (m_nEvents * m_tStep);
915}
916
917double Sensor::GetIonSignal(const std::string &label, const unsigned int bin) {
918 if (m_nEvents == 0) return 0.;
919 if (bin >= m_nTimeBins) return 0.;
920 double sig = 0.;
921 for (const auto &electrode : m_electrodes) {
922 if (electrode.label == label) sig += electrode.ionSignal[bin];
923 }
924 return ElementaryCharge * sig / (m_nEvents * m_tStep);
925}
926
927double Sensor::GetDelayedElectronSignal(const std::string &label,
928 const unsigned int bin) {
929 if (m_nEvents == 0) return 0.;
930 if (bin >= m_nTimeBins) return 0.;
931 double sig = 0.;
932 for (const auto &electrode : m_electrodes) {
933 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
934 }
935 return ElementaryCharge * sig / (m_nEvents * m_tStep);
936}
937
938double Sensor::GetDelayedIonSignal(const std::string &label,
939 const unsigned int bin) {
940 if (m_nEvents == 0) return 0.;
941 if (bin >= m_nTimeBins) return 0.;
942 double sig = 0.;
943 for (const auto &electrode : m_electrodes) {
944 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
945 }
946 return ElementaryCharge * sig / (m_nEvents * m_tStep);
947}
948
949void Sensor::SetSignal(const std::string &label, const unsigned int bin,
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;
956 break;
957 }
958 }
959}
960
961void Sensor::SetSignal(const std::string& label,
962 const std::vector<double>& ts,
963 const std::vector<double>& is) {
964
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);
971 break;
972 }
973 }
974}
975
976double Sensor::GetSignal(const std::string &label, const unsigned int bin) {
977 if (m_nEvents == 0) return 0.;
978 if (bin >= m_nTimeBins) return 0.;
979 double sig = 0.;
980 for (const auto &electrode : m_electrodes) {
981 if (electrode.label == label) sig += electrode.signal[bin];
982 }
983 return ElementaryCharge * sig / (m_nEvents * m_tStep);
984}
985
986double Sensor::GetSignal(const std::string &label, const unsigned int bin,
987 const int comp) {
988 if (m_nEvents == 0) return 0.;
989 if (bin >= m_nTimeBins) return 0.;
990 double sig = 0.;
991 for (const auto &electrode : m_electrodes) {
992 if (electrode.label == label) {
993 switch (comp) {
994 case 1: {
995 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
996 break;
997 }
998 case 2: {
999 sig += electrode.delayedSignal[bin];
1000 break;
1001 }
1002 default:
1003 sig += electrode.signal[bin];
1004 }
1005 }
1006 }
1007 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1008}
1009
1010double Sensor::GetPromptSignal(const std::string &label,
1011 const unsigned int bin) {
1012 if (m_nEvents == 0) return 0.;
1013 if (bin >= m_nTimeBins) return 0.;
1014 double sig = 0.;
1015 for (const auto &electrode : m_electrodes) {
1016 if (electrode.label == label)
1017 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
1018 }
1019 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1020}
1021
1022double Sensor::GetDelayedSignal(const std::string &label,
1023 const unsigned int bin) {
1024 if (m_nEvents == 0) return 0.;
1025 if (bin >= m_nTimeBins) return 0.;
1026 double sig = 0.;
1027 for (const auto &electrode : m_electrodes) {
1028 if (electrode.label == label) sig += electrode.delayedSignal[bin];
1029 }
1030 return ElementaryCharge * sig / (m_nEvents * m_tStep);
1031}
1032
1033double Sensor::GetInducedCharge(const std::string &label) {
1034 if (m_nEvents == 0) return 0.;
1035 double charge = 0.;
1036 for (const auto &electrode : m_electrodes) {
1037 if (electrode.label == label) charge += electrode.charge;
1038 }
1039
1040 return charge / m_nEvents;
1041}
1042
1043void Sensor::SetTransferFunction(std::function<double(double)> f) {
1044 if (!f) {
1045 std::cerr << m_className << "::SetTransferFunction: Empty function.\n";
1046 return;
1047 }
1048 m_fTransfer = f;
1049 m_shaper = nullptr;
1050 m_fTransferTab.clear();
1051 m_fTransferSq = -1.;
1052 m_fTransferFFT.clear();
1053}
1054
1055void Sensor::SetTransferFunction(const std::vector<double> &times,
1056 const std::vector<double> &values) {
1057 if (times.empty() || values.empty()) {
1058 std::cerr << m_className << "::SetTransferFunction: Empty vector.\n";
1059 return;
1060 } else if (times.size() != values.size()) {
1061 std::cerr << m_className << "::SetTransferFunction:\n"
1062 << " Time and value vectors must have same size.\n";
1063 return;
1064 }
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]));
1069 }
1070 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
1071 m_fTransfer = nullptr;
1072 m_shaper = nullptr;
1073 m_fTransferSq = -1.;
1074 m_fTransferFFT.clear();
1075}
1076
1078 m_shaper = &shaper;
1079 m_fTransfer = nullptr;
1080 m_fTransferTab.clear();
1081 m_fTransferSq = -1.;
1082 m_fTransferFFT.clear();
1083}
1084
1086
1087 const std::string name = ViewBase::FindUnusedCanvasName("cTransferFunction");
1088 std::vector<double> t;
1089 std::vector<double> f;
1090 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1091 // Positive time part.
1092 t.push_back(i * m_tStep);
1093 f.push_back(GetTransferFunction(i * m_tStep));
1094 }
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) {
1100 fmin -= 1.;
1101 fmax += 1.;
1102 } else {
1103 fmax += 0.1 * df;
1104 if (fmin < 0.) fmin -= 0.1 * df;
1105 }
1106
1107 TCanvas* cf = new TCanvas(name.c_str(), "Transfer Function");
1108 cf->SetGridx();
1109 cf->SetGridy();
1110 cf->DrawFrame(0., fmin, m_nTimeBins * m_tStep, fmax,
1111 ";time [ns]; transfer function");
1112 TGraph graph;
1113 graph.SetLineWidth(4);
1114 graph.SetLineColor(kBlue + 2);
1115 graph.DrawGraph(t.size(), t.data(), f.data(), "l");
1116 gPad->Update();
1117}
1118
1119double Sensor::InterpolateTransferFunctionTable(const double t) const {
1120 if (m_fTransferTab.empty()) return 0.;
1121 // Don't extrapolate beyond the range defined in the table.
1122 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
1123 return 0.;
1124 }
1125 // Find the proper interval in the table.
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);
1135 // Linear interpolation.
1136 return (*it0).second * (1. - f) + (*it1).second * f;
1137}
1138
1139double Sensor::GetTransferFunction(const double t) {
1140 if (m_fTransfer) {
1141 return m_fTransfer(t);
1142 } else if (m_shaper) {
1143 return m_shaper->Shape(t);
1144 }
1145 return InterpolateTransferFunctionTable(t);
1146}
1147
1149 std::cout << m_className << "::PrintTransferFunction:\n";
1150 if (m_fTransfer) {
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";
1158 }
1159 unsigned int n = 1;
1160 double tp = 0.;
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";
1166 } else {
1167 std::cout << " No transfer function set.\n";
1168 return;
1169 }
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;
1174 const double f = GetTransferFunction(t);
1175 std::printf(" %10.3f %10.5f\n", t, f);
1176 }
1177}
1178
1179bool Sensor::ConvoluteSignal(const std::string &label, const bool fft) {
1180 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1181 std::cerr << m_className << "::ConvoluteSignal: "
1182 << "Transfer function not set.\n";
1183 return false;
1184 }
1185 if (m_nEvents == 0) {
1186 std::cerr << m_className << "::ConvoluteSignal: No signals present.\n";
1187 return false;
1188 }
1189
1190 if (fft) return ConvoluteSignalFFT(label);
1191 std::vector<double> cnvTab;
1192 MakeTransferFunctionTable(cnvTab);
1193 // Loop over all electrodes.
1194 for (auto &electrode : m_electrodes) {
1195 if (label != electrode.label) continue;
1196 ConvoluteSignal(electrode, cnvTab);
1197 return true;
1198 }
1199 return false;
1200}
1201
1202bool Sensor::ConvoluteSignals(const bool fft) {
1203 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1204 std::cerr << m_className << "::ConvoluteSignals: "
1205 << "Transfer function not set.\n";
1206 return false;
1207 }
1208 if (m_nEvents == 0) {
1209 std::cerr << m_className << "::ConvoluteSignals: No signals present.\n";
1210 return false;
1211 }
1212
1213 if (fft) return ConvoluteSignalFFT();
1214 std::vector<double> cnvTab;
1215 MakeTransferFunctionTable(cnvTab);
1216 // Loop over all electrodes.
1217 for (auto &electrode : m_electrodes) ConvoluteSignal(electrode, cnvTab);
1218 return true;
1219}
1220
1221void Sensor::MakeTransferFunctionTable(std::vector<double> &cnvTab) {
1222 // Set the range where the transfer function is valid.
1223 constexpr double cnvMin = 0.;
1224 constexpr double cnvMax = 1.e10;
1225
1226 cnvTab.assign(2 * m_nTimeBins - 1, 0.);
1227 const unsigned int offset = m_nTimeBins - 1;
1228 // Evaluate the transfer function.
1229 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1230 // Negative time part.
1231 double t = (-int(i)) * m_tStep;
1232 if (t < cnvMin || t > cnvMax) {
1233 cnvTab[offset - i] = 0.;
1234 } else {
1235 cnvTab[offset - i] = GetTransferFunction(t);
1236 }
1237 if (i == 0) continue;
1238 // Positive time part.
1239 t = i * m_tStep;
1240 if (t < cnvMin || t > cnvMax) {
1241 cnvTab[offset + i] = 0.;
1242 } else {
1243 cnvTab[offset + i] = GetTransferFunction(t);
1244 }
1245 }
1246}
1247
1248void Sensor::ConvoluteSignal(Electrode &electrode,
1249 const std::vector<double> &tab) {
1250 // Do the convolution.
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];
1260 }
1261 }
1262 electrode.signal.swap(tmpSignal);
1263 electrode.delayedSignal.swap(tmpSignalDelayed);
1264 electrode.integrated = true;
1265}
1266
1267bool Sensor::ConvoluteSignalFFT() {
1268 // Number of bins must be a power of 2.
1269 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1270
1271 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1272 // (Re-)compute the FFT of the transfer function.
1273 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1274 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1275 m_fTransferFFT[2 * i + 1] = GetTransferFunction(i * m_tStep);
1276 }
1277 FFT(m_fTransferFFT, false, nn);
1278 }
1279
1280 for (auto &electrode : m_electrodes) {
1281 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1282 }
1283 return true;
1284}
1285
1286bool Sensor::ConvoluteSignalFFT(const std::string &label) {
1287 // Number of bins must be a power of 2.
1288 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1289
1290 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1291 // (Re-)compute the FFT of the transfer function.
1292 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1293 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1294 m_fTransferFFT[2 * i + 1] = GetTransferFunction(i * m_tStep);
1295 }
1296 FFT(m_fTransferFFT, false, nn);
1297 }
1298
1299 for (auto &electrode : m_electrodes) {
1300 if (label != electrode.label) continue;
1301 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1302 return true;
1303 }
1304 return false;
1305}
1306
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];
1313 }
1314 FFT(g, false, nn);
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;
1322 }
1323 FFT(g, true, nn);
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];
1327 }
1328 electrode.integrated = true;
1329}
1330
1332 if (m_nEvents == 0) {
1333 std::cerr << m_className << "::IntegrateSignals: No signals present.\n";
1334 return false;
1335 }
1336
1337 for (auto &electrode : m_electrodes) IntegrateSignal(electrode);
1338 return true;
1339}
1340
1341bool Sensor::IntegrateSignal(const std::string &label) {
1342 if (m_nEvents == 0) {
1343 std::cerr << m_className << "::IntegrateSignal: No signals present.\n";
1344 return false;
1345 }
1346
1347 for (auto &electrode : m_electrodes) {
1348 if (label != electrode.label) continue;
1349 IntegrateSignal(electrode);
1350 return true;
1351 }
1352 std::cerr << m_className << "::IntegrateSignal: Electrode " << label
1353 << " not found.\n";
1354 return false;
1355}
1356
1357void Sensor::IntegrateSignal(Electrode &electrode) {
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;
1363 if (j > 0) {
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];
1368 }
1369 }
1370 electrode.integrated = true;
1371}
1372
1373bool Sensor::IsIntegrated(const std::string &label) const {
1374 for (const auto &electrode : m_electrodes) {
1375 if (electrode.label == label) return electrode.integrated;
1376 }
1377 return false;
1378}
1379
1380bool Sensor::DelayAndSubtractFraction(const double td, const double f) {
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];
1390 }
1391 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1392 electrode.signal[j] = signal1[j] - signal2[j];
1393 }
1394 }
1395 return true;
1396}
1397
1398void Sensor::SetNoiseFunction(double (*f)(double t)) {
1399 if (!f) {
1400 std::cerr << m_className << "::SetNoiseFunction: Null pointer.\n";
1401 return;
1402 }
1403 m_fNoise = f;
1404}
1405
1406void Sensor::AddNoise(const bool total, const bool electron, const bool ion) {
1407 if (!m_fNoise) {
1408 std::cerr << m_className << "::AddNoise: Noise function not set.\n";
1409 return;
1410 }
1411 if (m_nEvents == 0) m_nEvents = 1;
1412
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;
1420 t += m_tStep;
1421 }
1422 }
1423}
1424
1425void Sensor::AddWhiteNoise(const std::string &label, const double enc,
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";
1429 return;
1430 }
1431 if (m_nEvents == 0) m_nEvents = 1;
1432
1433 const double f2 = TransferFunctionSq();
1434 if (f2 < 0.) {
1435 std::cerr << m_className << "::AddWhiteNoise:\n"
1436 << " Could not calculate transfer function integral.\n";
1437 return;
1438 }
1439
1440 if (poisson) {
1441 // Frequency of random delta pulses to model noise.
1442 const double nu = (enc * enc / (q0 * q0)) / f2;
1443 // Average number of delta pulses.
1444 const double avg = nu * m_tStep * m_nTimeBins;
1445 // Sample the number of pulses.
1446 for (auto &electrode : m_electrodes) {
1447 if (label != electrode.label) continue;
1448 const int nPulses = RndmPoisson(avg);
1449 for (int j = 0; j < nPulses; ++j) {
1450 const int bin = static_cast<int>(m_nTimeBins * RndmUniform());
1451 electrode.signal[bin] += q0;
1452 }
1453 const double offset = q0 * nu * m_tStep;
1454 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1455 electrode.signal[j] -= offset;
1456 }
1457 break;
1458 }
1459 } else {
1460 // Gaussian approximation.
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) {
1465 electrode.signal[j] += RndmGaussian(0., sigma);
1466 }
1467 break;
1468 }
1469 }
1470}
1471
1472void Sensor::AddWhiteNoise(const double enc, const bool poisson,
1473 const double q0) {
1474 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1475 std::cerr << m_className << "::AddWhiteNoise: Transfer function not set.\n";
1476 return;
1477 }
1478 if (m_nEvents == 0) m_nEvents = 1;
1479
1480 const double f2 = TransferFunctionSq();
1481 if (f2 < 0.) {
1482 std::cerr << m_className << "::AddWhiteNoise:\n"
1483 << " Could not calculate transfer function integral.\n";
1484 return;
1485 }
1486
1487 if (poisson) {
1488 // Frequency of random delta pulses to model noise.
1489 const double nu = (enc * enc / (q0 * q0)) / f2;
1490 // Average number of delta pulses.
1491 const double avg = nu * m_tStep * m_nTimeBins;
1492 // Sample the number of pulses.
1493 for (auto &electrode : m_electrodes) {
1494 const int nPulses = RndmPoisson(avg);
1495 for (int j = 0; j < nPulses; ++j) {
1496 const int bin = static_cast<int>(m_nTimeBins * RndmUniform());
1497 electrode.signal[bin] += q0;
1498 }
1499 const double offset = q0 * nu * m_tStep;
1500 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1501 electrode.signal[j] -= offset;
1502 }
1503 }
1504 } else {
1505 // Gaussian approximation.
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) {
1509 electrode.signal[j] += RndmGaussian(0., sigma);
1510 }
1511 }
1512 }
1513}
1514
1515double Sensor::TransferFunctionSq() {
1516 if (m_fTransferSq >= 0.) return m_fTransferSq;
1517 double integral = -1.;
1518 if (m_fTransfer) {
1519 std::function<double(double)> fsq = [this](double x) {
1520 const double y = m_fTransfer(x);
1521 return y * y;
1522 };
1523 constexpr double epsrel = 1.e-8;
1524 double err = 0.;
1525 unsigned int stat = 0;
1526 Numerics::QUADPACK::qagi(fsq, 0., 1, 0., epsrel, integral, err, stat);
1527 } else if (m_shaper) {
1528 integral = m_shaper->TransferFuncSq();
1529 } else {
1530 integral = Trapezoid2(m_fTransferTab);
1531 }
1532 if (m_cacheTransferFunction) m_fTransferSq = integral;
1533 return integral;
1534}
1535
1537 const std::string &label, int &n) {
1538 // Reset the list of threshold crossings.
1539 m_thresholdCrossings.clear();
1540 m_thresholdLevel = thr;
1541
1542 // Set the interpolation order.
1543 int iOrder = 1;
1544
1545 if (m_nEvents == 0) {
1546 std::cerr << m_className << "::ComputeThresholdCrossings: "
1547 << "No signals present.\n";
1548 return false;
1549 }
1550 // Compute the total signal.
1551 std::vector<double> signal(m_nTimeBins, 0.);
1552 // Loop over the electrodes.
1553 bool foundLabel = false;
1554 for (const auto &electrode : m_electrodes) {
1555 if (electrode.label != label) continue;
1556 foundLabel = true;
1557 if (!electrode.integrated) {
1558 std::cerr << m_className << "::ComputeThresholdCrossings:\n "
1559 << "Warning: signal on electrode " << label
1560 << " has not been integrated/convoluted.\n";
1561 }
1562 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1563 signal[i] += electrode.signal[i];
1564 }
1565 }
1566 if (!foundLabel) {
1567 std::cerr << m_className << "::ComputeThresholdCrossings: Electrode "
1568 << label << " not found.\n";
1569 return false;
1570 }
1571 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1572 for (unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1573
1574 // Establish the range.
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) {
1579 if (m_debug) {
1580 std::cout << " Threshold outside the range [" << vMin << ", " << vMax
1581 << "]\n";
1582 }
1583 return true;
1584 }
1585
1586 // Check both rising and falling edges.
1587 constexpr std::array<int, 2> directions = {1, -1};
1588 for (const auto dir : directions) {
1589 const bool up = dir > 0;
1590 if (m_debug) {
1591 if (up) {
1592 std::cout << " Hunting for rising edges.\n";
1593 } else {
1594 std::cout << " Hunting for falling edges.\n";
1595 }
1596 }
1597 // Initialise the vectors.
1598 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1599 std::vector<double> vs = {signal[0]};
1600 // Scan the signal.
1601 for (unsigned int i = 1; i < m_nTimeBins; ++i) {
1602 // Compute the vector element.
1603 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1604 const double vNew = signal[i];
1605 // If still increasing or decreasing, add to the vector.
1606 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1607 ts.push_back(tNew);
1608 vs.push_back(vNew);
1609 continue;
1610 }
1611 // Otherwise see whether we crossed the threshold level.
1612 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1613 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1614 // Compute the crossing time.
1615 double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1616 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1617 ts = {tNew};
1618 vs = {vNew};
1619 } else {
1620 // No crossing, simply reset the vector.
1621 ts = {tNew};
1622 vs = {vNew};
1623 }
1624 }
1625 // Check the final vector.
1626 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1627 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1628 const double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1629 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1630 }
1631 }
1632 n = m_thresholdCrossings.size();
1633
1634 if (m_debug) {
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";
1641 } else {
1642 std::cout << "falling\n";
1643 }
1644 }
1645 }
1646 // Seems to have worked.
1647 return true;
1648}
1649
1650bool Sensor::GetThresholdCrossing(const unsigned int i, double &time,
1651 double &level, bool &rise) const {
1652 level = m_thresholdLevel;
1653
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;
1657 return false;
1658 }
1659
1660 time = m_thresholdCrossings[i].first;
1661 rise = m_thresholdCrossings[i].second;
1662 return true;
1663}
1664
1665bool Sensor::GetBoundingBox(double &xmin, double &ymin, double &zmin,
1666 double &xmax, double &ymax, double &zmax) {
1667 // We don't know the range yet
1668 bool set = false;
1669 // Loop over the fields
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;
1674 if (set) {
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;
1681 } else {
1682 xmin = x0;
1683 ymin = y0;
1684 zmin = z0;
1685 xmax = x1;
1686 ymax = y1;
1687 zmax = z1;
1688 set = true;
1689 }
1690 }
1691
1692 // Warn if we still don't know the range
1693 if (!set) {
1694 std::cerr << m_className << "::GetBoundingBox:\n"
1695 << " Sensor bounding box not known.\n";
1696 xmin = ymin = zmin = 0.;
1697 xmax = ymax = zmax = 0.;
1698 return false;
1699 }
1700
1701 if (m_debug) {
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";
1706 }
1707 return true;
1708}
1709
1710void Sensor::FFT(std::vector<double> &data, const bool inverse, const int nn) {
1711 // Replaces data[1..2*nn] by its discrete fourier transform
1712 // or replaces data[1..2*nn] by nn times its inverse discrete
1713 // fourier transform.
1714 // nn MUST be an integer power of 2 (this is not checked for!).
1715
1716 const int n = 2 * nn;
1717 // Bit reversal.
1718 int j = 1;
1719 for (int i = 1; i < n; i += 2) {
1720 if (j > i) {
1721 // Exchange the two complex numbers.
1722 std::swap(data[j], data[i]);
1723 std::swap(data[j + 1], data[i + 1]);
1724 }
1725 int m = nn;
1726 while (m >= 2 && j > m) {
1727 j -= m;
1728 m >>= 1;
1729 }
1730 j += m;
1731 }
1732
1733 const int isign = inverse ? -1 : 1;
1734 int mmax = 2;
1735 while (n > mmax) {
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);
1741 double wr = 1.;
1742 double wi = 0.;
1743 for (int m = 1; m < mmax; m += 2) {
1744 for (int i = m; i <= n; i += step) {
1745 j = i + mmax;
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;
1750 data[i] += tr;
1751 data[i + 1] += ti;
1752 }
1753 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1754 wi = wi * wpr + wtemp * wpi + wi;
1755 }
1756 mmax = step;
1757 }
1758}
1759
1760void Sensor::PlotSignal(const std::string& label, TPad* pad) {
1761
1762 ViewSignal view;
1763 view.SetSensor(this);
1764 if (pad) view.SetCanvas(pad);
1765 std::string optTotal = "t";
1766 std::string optPrompt = "";
1767 std::string optDelayed = "";
1768 view.PlotSignal(label, optTotal, optPrompt, optDelayed);
1769}
1770
1771void Sensor::ExportSignal(const std::string &label, const std::string &name,
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;
1776
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";
1784 } else {
1785 myfile << "The induced signal.\n";
1786 }
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;
1794 }
1795 myfile << "\n";
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) {
1803 myfile << "\n";
1804 continue;
1805 }
1806 myfile << ","
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";
1814 }
1815 myfile.close();
1816 std::cout << m_className << "::ExportSignal: File '" << name
1817 << ".csv' exported.\n";
1818 }
1819}
1820
1821double Sensor::GetTotalInducedCharge(const std::string &label) {
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);
1826 }
1827 return 0.;
1828}
1829
1830} // namespace Garfield
Abstract base class for components.
Definition Component.hh:13
Abstract base class for media.
Definition Medium.hh:16
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).
Definition Sensor.cc:121
void EnableMagneticField(const unsigned int i, const bool on)
Activate/deactivate use of the magnetic field of a given component.
Definition Sensor.cc:380
double GetElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the electron signal for a given electrode and time bin.
Definition Sensor.cc:906
bool ConvoluteSignal(const std::string &label, const bool fft=false)
Definition Sensor.cc:1179
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at (x, y, z).
Definition Sensor.cc:178
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
Definition Sensor.cc:442
double GetTotalInducedCharge(const std::string &label)
Definition Sensor.cc:1821
double StepSizeHint()
Definition Sensor.cc:329
void SetDelayedSignalTimes(const std::vector< double > &ts)
Set the points in time at which to evaluate the delayed weighting field.
Definition Sensor.cc:489
void SetTimeWindow(const double tstart, const double tstep, const unsigned int nsteps)
Definition Sensor.cc:869
void SetSignal(const std::string &label, const unsigned int bin, const double signal)
Set/override the signal in a given time bin explicitly.
Definition Sensor.cc:949
double GetDelayedIonSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed ion/hole signal for a given electrode and time bin.
Definition Sensor.cc:938
void PlotSignal(const std::string &label, TPad *pad)
Plot the induced signal.
Definition Sensor.cc:1760
bool DelayAndSubtractFraction(const double td, const double f)
Definition Sensor.cc:1380
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).
Definition Sensor.cc:136
double GetTransferFunction(const double t)
Evaluate the transfer function at a given time.
Definition Sensor.cc:1139
void EnableComponent(const unsigned int i, const bool on)
Activate/deactivate a given component.
Definition Sensor.cc:372
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)
Definition Sensor.cc:292
void AddElectrode(Component *comp, const std::string &label)
Add an electrode.
Definition Sensor.cc:396
bool IntegrateSignal(const std::string &label)
Replace the signal on a given electrode by its integral.
Definition Sensor.cc:1341
void AddComponent(Component *comp)
Add a component.
Definition Sensor.cc:355
void SetTransferFunction(std::function< double(double)>)
Set the function to be used for evaluating the transfer function.
Definition Sensor.cc:1043
void PrintTransferFunction()
Print some information about the presently set transfer function.
Definition Sensor.cc:1148
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).
Definition Sensor.cc:164
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).
Definition Sensor.cc:70
double GetSignal(const std::string &label, const unsigned int bin)
Retrieve the total signal for a given electrode and time bin.
Definition Sensor.cc:976
void SetNoiseFunction(double(*f)(double t))
Set the function to be used for evaluating the noise component.
Definition Sensor.cc:1398
double GetPromptSignal(const std::string &label, const unsigned int bin)
Retrieve the prompt signal for a given electrode and time bin.
Definition Sensor.cc:1010
bool IntegrateSignals()
Replace the signals on all electrodes curve by their integrals.
Definition Sensor.cc:1331
bool HasMagneticField() const
Does the sensor have a non-zero magnetic field?
Definition Sensor.cc:388
bool ConvoluteSignals(const bool fft=false)
Convolute all induced currents with the transfer function.
Definition Sensor.cc:1202
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition Sensor.cc:260
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)
Definition Sensor.cc:317
double GetInducedCharge(const std::string &label)
Calculated using the weighting potentials at the start and end points.
Definition Sensor.cc:1033
void AddNoise(const bool total=true, const bool electron=false, const bool ion=false)
Add noise to the induced signal.
Definition Sensor.cc:1406
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)
Definition Sensor.cc:340
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Get the weighting potential at (x, y, z).
Definition Sensor.cc:152
Component * GetComponent(const unsigned int i)
Retrieve the pointer to a given component.
Definition Sensor.cc:364
void AddWhiteNoise(const std::string &label, const double enc, const bool poisson=true, const double q0=1.)
Definition Sensor.cc:1425
bool ComputeThresholdCrossings(const double thr, const std::string &label, int &n)
Definition Sensor.cc:1536
void Clear()
Remove all components, electrodes and reset the sensor.
Definition Sensor.cc:426
bool SetArea(const bool verbose=false)
Set the user area to the default.
Definition Sensor.cc:187
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.
Definition Sensor.cc:498
bool GetThresholdCrossing(const unsigned int i, double &time, double &level, bool &rise) const
Definition Sensor.cc:1650
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.
Definition Sensor.cc:282
double GetDelayedElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed electron signal for a given electrode and time bin.
Definition Sensor.cc:927
void ClearSignal()
Reset signals and induced charges of all electrodes.
Definition Sensor.cc:475
double GetDelayedSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed signal for a given electrode and time bin.
Definition Sensor.cc:1022
void ExportSignal(const std::string &label, const std::string &filename, const bool chargeCariers=false) const
Exporting induced signal to a csv file.
Definition Sensor.cc:1771
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.
Definition Sensor.cc:845
void PlotTransferFunction()
Plot the presently set transfer function.
Definition Sensor.cc:1085
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.
Definition Sensor.cc:306
double GetIonSignal(const std::string &label, const unsigned int bin)
Retrieve the ion or hole signal for a given electrode and time bin.
Definition Sensor.cc:917
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition Sensor.cc:234
bool IsIntegrated(const std::string &label) const
Return whether the signal has been integrated/convoluted.
Definition Sensor.cc:1373
Class for signal processing.
Definition Shaper.hh:11
void SetCanvas(TPad *pad)
Set the canvas to be painted on.
Definition ViewBase.hh:28
static std::string FindUnusedCanvasName(const std::string &s)
Find an unused canvas name.
Definition ViewBase.cc:337
Plot the signal computed by a sensor as a ROOT histogram.
Definition ViewSignal.hh:19
void SetSensor(Sensor *s)
Set the sensor from which to retrieve the signal.
Definition ViewSignal.cc:18
void PlotSignal(const std::string &label, const std::string &optTotal="t", const std::string &optPrompt="", const std::string &optDelayed="", const bool same=false)
Definition ViewSignal.cc:61
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)
Definition Numerics.cc:145
constexpr std::array< double, 6 > GaussLegendreWeights6()
Definition Numerics.hh:49
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
Definition Numerics.cc:1255
constexpr std::array< double, 6 > GaussLegendreNodes6()
Definition Numerics.hh:44
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition Random.hh:14
double RndmGaussian()
Draw a Gaussian random variate with mean zero and standard deviation one.
Definition Random.hh:24
int RndmPoisson(const double mean)
Draw a random number from a Poisson distribution.
Definition Random.cc:664
DoubleAc sin(const DoubleAc &f)
Definition DoubleAc.cpp:384