Garfield++ 4.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 <algorithm>
2#include <cmath>
3#include <fstream>
4#include <iomanip>
5#include <iostream>
6
10#include "Garfield/Random.hh"
11#include "Garfield/Sensor.hh"
12
13namespace {
14
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.;
18 if (order > 1) {
19 return Garfield::Numerics::Divdif(y, x, x.size(), xx, order);
20 }
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()];
28}
29
30double Trapezoid2(const std::vector<std::pair<double, double> >& f) {
31 const size_t n = f.size();
32 if (n < 2) return -1.;
33 double sum = 0.;
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;
38 if (n == 2) {
39 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
40 } else if (n == 3) {
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;
44 } else {
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;
51 if (n > 4) {
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;
55 }
56 }
57 }
58 return 0.5 * sum;
59}
60
61} // namespace
62
63namespace Garfield {
64
65void Sensor::ElectricField(const double x, const double y, const double z,
66 double& ex, double& ey, double& ez, double& v,
67 Medium*& medium, int& status) {
68 ex = ey = ez = v = 0.;
69 status = -10;
70 medium = nullptr;
71 double fx = 0., fy = 0., fz = 0., p = 0.;
72 Medium* med = nullptr;
73 int stat = 0;
74 // Add up electric field contributions from all components.
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);
78 if (status != 0) {
79 status = stat;
80 medium = med;
81 }
82 if (stat == 0) {
83 ex += fx;
84 ey += fy;
85 ez += fz;
86 v += p;
87 }
88 }
89}
90
91void Sensor::ElectricField(const double x, const double y, const double z,
92 double& ex, double& ey, double& ez, Medium*& medium,
93 int& status) {
94 ex = ey = ez = 0.;
95 status = -10;
96 medium = nullptr;
97 double fx = 0., fy = 0., fz = 0.;
98 Medium* med = nullptr;
99 int stat = 0;
100 // Add up electric field contributions from all components.
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);
104 if (status != 0) {
105 status = stat;
106 medium = med;
107 }
108 if (stat == 0) {
109 ex += fx;
110 ey += fy;
111 ez += fz;
112 }
113 }
114}
115
116void Sensor::MagneticField(const double x, const double y, const double z,
117 double& bx, double& by, double& bz, int& status) {
118 bx = by = bz = 0.;
119 double fx = 0., fy = 0., fz = 0.;
120 // Add up contributions.
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;
125 bx += fx;
126 by += fy;
127 bz += fz;
128 }
129}
130
131void Sensor::WeightingField(const double x, const double y, const double z,
132 double& wx, double& wy, double& wz,
133 const std::string& label) {
134 wx = wy = wz = 0.;
135 // Add up field contributions from all components.
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);
140 wx += fx;
141 wy += fy;
142 wz += fz;
143 }
144 }
145}
146
147double Sensor::WeightingPotential(const double x, const double y,
148 const double z, const std::string& label) {
149 double v = 0.;
150 // Add up contributions from all components.
151 for (const auto& electrode : m_electrodes) {
152 if (electrode.label == label) {
153 v += electrode.comp->WeightingPotential(x, y, z, label);
154 }
155 }
156 return v;
157}
158
159bool Sensor::GetMedium(const double x, const double y, const double z,
160 Medium*& m) {
161 m = nullptr;
162
163 // Make sure there is at least one component.
164 if (m_components.empty()) return false;
165
166 // Check if we are still in the same component as in the previous call.
167 if (m_lastComponent) {
168 m = m_lastComponent->GetMedium(x, y, z);
169 if (m) return true;
170 }
171
172 for (const auto& cmp : m_components) {
173 if (!std::get<1>(cmp)) continue;
174 m = std::get<0>(cmp)->GetMedium(x, y, z);
175 if (m) {
176 m_lastComponent = std::get<0>(cmp);
177 return true;
178 }
179 }
180 return false;
181}
182
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";
188 return false;
189 }
190
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";
197 }
198 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
199 std::cerr << m_className << "::SetArea: Warning. Infinite y-range.\n";
200 }
201 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
202 std::cerr << m_className << "::SetArea: Warning. Infinite z-range.\n";
203 }
204 m_hasUserArea = true;
205 return true;
206}
207
208bool Sensor::SetArea(const double xmin, const double ymin, const double zmin,
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";
214 return false;
215 }
216
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);
223
224 m_hasUserArea = true;
225 return true;
226}
227
228bool Sensor::GetArea(double& xmin, double& ymin, double& zmin, double& xmax,
229 double& ymax, double& zmax) {
230 if (m_hasUserArea) {
231 xmin = m_xMinUser;
232 ymin = m_yMinUser;
233 zmin = m_zMinUser;
234 xmax = m_xMaxUser;
235 ymax = m_yMaxUser;
236 zmax = m_zMaxUser;
237 return true;
238 }
239
240 // User area bounds are not (yet) defined.
241 // Get the bounding box of the sensor.
242 if (!SetArea()) return false;
243
244 xmin = m_xMinUser;
245 ymin = m_yMinUser;
246 zmin = m_zMinUser;
247 xmax = m_xMaxUser;
248 ymax = m_yMaxUser;
249 zmax = m_zMaxUser;
250
251 return true;
252}
253
254bool Sensor::IsInArea(const double x, const double y, const double z) {
255 if (!m_hasUserArea) {
256 if (!SetArea()) {
257 std::cerr << m_className << "::IsInArea: User area could not be set.\n";
258 return false;
259 }
260 m_hasUserArea = true;
261 }
262
263 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
264 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
265 return true;
266 }
267
268 if (m_debug) {
269 std::cout << m_className << "::IsInArea: (" << x << ", " << y << ", " << z
270 << ") "
271 << " is outside.\n";
272 }
273 return false;
274}
275
276bool Sensor::IsWireCrossed(const double x0, const double y0, const double z0,
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,
283 centre, rc)) {
284 return true;
285 }
286 }
287 return false;
288}
289
290bool Sensor::IsInTrapRadius(const double q0, const double x0, const double y0,
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)) {
295 return true;
296 }
297 }
298 return false;
299}
300
301double Sensor::IntegrateFluxLine(const double x0, const double y0,
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,
306 const int isign) {
307 double q = 0.;
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,
311 nI, isign);
312 }
313 return q;
314}
315
317 if (!cmp) {
318 std::cerr << m_className << "::AddComponent: Null pointer.\n";
319 return;
320 }
321
322 m_components.push_back(std::make_tuple(cmp, true, true));
323}
324
325Component* Sensor::GetComponent(const unsigned int i) {
326 if (i >= m_components.size()) {
327 std::cerr << m_className << "::GetComponent: Index out of range.\n";
328 return nullptr;
329 };
330 return std::get<0>(m_components[i]);
331}
332
333void Sensor::EnableComponent(const unsigned int i, const bool on) {
334 if (i >= m_components.size()) {
335 std::cerr << m_className << "::EnableComponent: Index out of range.\n";
336 return;
337 };
338 std::get<1>(m_components[i]) = on;
339}
340
341void Sensor::EnableMagneticField(const unsigned int i, const bool on) {
342 if (i >= m_components.size()) {
343 std::cerr << m_className << "::EnableMagneticField: Index out of range.\n";
344 return;
345 };
346 std::get<2>(m_components[i]) = on;
347}
348
349void Sensor::AddElectrode(Component* cmp, const std::string& label) {
350 if (!cmp) {
351 std::cerr << m_className << "::AddElectrode: Null pointer.\n";
352 return;
353 }
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";
359 break;
360 }
361 }
362
363 Electrode electrode;
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";
376 ClearSignal();
377}
378
380 std::lock_guard<std::mutex> guard(m_mutex);
381 m_components.clear();
382 m_lastComponent = nullptr;
383 m_electrodes.clear();
384 m_nTimeBins = 200;
385 m_tStart = 0.;
386 m_tStep = 10.;
387 m_nEvents = 0;
388 m_hasUserArea = false;
389 m_fTransfer = nullptr;
390 m_shaper = nullptr;
391 m_fTransferTab.clear();
392 m_fTransferSq = -1.;
393 m_fTransferFFT.clear();
394}
395
396bool Sensor::GetVoltageRange(double& vmin, double& vmax) {
397 // We don't know the range yet.
398 bool set = false;
399 // Loop over the components.
400 for (const auto& cmp : m_components) {
401 if (!std::get<1>(cmp)) continue;
402 double umin = 0., umax = 0.;
403 if (!std::get<0>(cmp)->GetVoltageRange(umin, umax)) continue;
404 if (set) {
405 vmin = std::min(umin, vmin);
406 vmax = std::max(umax, vmax);
407 } else {
408 vmin = umin;
409 vmax = umax;
410 set = true;
411 }
412 }
413
414 // Warn if we still don't know the range.
415 if (!set) {
416 std::cerr << m_className << "::GetVoltageRange:\n"
417 << " Sensor voltage range not known.\n";
418 vmin = vmax = 0.;
419 return false;
420 }
421
422 if (m_debug) {
423 std::cout << m_className << "::GetVoltageRange: " << vmin << " < V < "
424 << vmax << ".\n";
425 }
426 return true;
427}
428
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;
439 }
440 m_nEvents = 0;
441}
442
443void Sensor::SetDelayedSignalTimes(const std::vector<double>& ts) {
444 if (!std::is_sorted(ts.begin(), ts.end())) {
445 std::cerr << m_className << "::SetDelayedSignalTimes:\n"
446 << " Times are not in ascending order.\n";
447 return;
448 }
449 m_delayedSignalTimes = ts;
450}
451
452void Sensor::AddSignal(const double q, const double t0, const double t1,
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: ";
458 // Get the time bin.
459 if (t0 < m_tStart) {
460 if (m_debug) std::cout << "Time " << t0 << " out of range.\n";
461 return;
462 }
463 const double dt = t1 - t0;
464 if (dt < Small) {
465 if (m_debug) std::cout << "Time step too small.\n";
466 return;
467 }
468 const double invdt = 1. / dt;
469
470 const int bin = int((t0 - m_tStart) / m_tStep);
471 // Check if the starting time is outside the range
472 if (bin < 0 || bin >= (int)m_nTimeBins) {
473 if (m_debug) std::cout << "Bin " << bin << " out of range.\n";
474 return;
475 }
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;
484 if (m_debug) {
485 std::cout << " Time: " << t0 << "\n"
486 << " Step: " << dt << "\n"
487 << " Charge: " << q << "\n"
488 << " Velocity: (" << vx << ", " << vy << ", " << vz << ")\n";
489 }
490 // Locations and weights for 6-point Gaussian integration
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";
500 // Induced current.
501 double current = 0.;
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;
506 if (m_debug) {
507 std::cout << " Weighting potentials: " << w0 << ", " << w1 << "\n";
508 }
509 } else {
510 double wx = 0., wy = 0., wz = 0.;
511 // Calculate the weighting field for this electrode.
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);
520 wx += wg[j] * fx;
521 wy += wg[j] * fy;
522 wz += wg[j] * fz;
523 }
524 wx *= 0.5;
525 wy *= 0.5;
526 wz *= 0.5;
527 } else {
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);
532 }
533 if (m_debug) {
534 std::cout << " Weighting field: (" << wx << ", " << wy << ", " << wz
535 << ")\n";
536 }
537 // Calculate the induced current.
538 current = -q * (wx * vx + wy * vy + wz * vz);
539 }
540 if (m_debug) std::cout << " Induced charge: " << current * dt << "\n";
541 double delta = m_tStart + (bin + 1) * m_tStep - t0;
542 // Check if the provided timestep extends over more than one time bin
543 if (dt > delta) {
544 FillBin(electrode, bin, current * delta, electron, false);
545 delta = dt - delta;
546 unsigned int j = 1;
547 while (delta > m_tStep && bin + j < m_nTimeBins) {
548 FillBin(electrode, bin + j, current * m_tStep, electron, false);
549 delta -= m_tStep;
550 ++j;
551 }
552 if (bin + j < m_nTimeBins) {
553 FillBin(electrode, bin + j, current * delta, electron, false);
554 }
555 } else {
556 FillBin(electrode, bin, current * dt, electron, false);
557 }
558 }
559 if (!m_delayedSignal) return;
560 if (m_delayedSignalTimes.empty()) return;
561 const size_t nd = m_delayedSignalTimes.size();
562 // Establish the points in time at which we evaluate the delayed signal.
563 std::vector<double> td(nd);
564 for (size_t i = 0; i < nd; ++i) {
565 td[i] = t0 + m_delayedSignalTimes[i];
566 }
567 // Calculate the signals for each electrode.
568 for (auto& electrode : m_electrodes) {
569 const std::string lbl = electrode.label;
570 std::vector<double> id(nd, 0.);
571 if (useWeightingPotential) {
572 // Using the weighting potential.
573 double chargeHolder = 0.;
574 double currentHolder = 0.;
575 int binHolder = 0;
576 // Loop over each time in the given vector of delayed times.
577 for (size_t i = 0; i < nd; ++i) {
578 double delayedtime = m_delayedSignalTimes[i] - t0; // t - t0
579 if (delayedtime < 0) continue;
580 // Find bin that needs to be filled.
581 int bin2 = int((m_delayedSignalTimes[i] - m_tStart) / m_tStep);
582 // Compute induced charge
583 double dp0 = electrode.comp->DelayedWeightingPotential(
584 x0, y0, z0, delayedtime, lbl);
585 double dp1 = electrode.comp->DelayedWeightingPotential(
586 x1, y1, z1, delayedtime, lbl);
587
588 double charge = q * (dp1 - dp0);
589 // In very rare cases the result is infinity. We do not let this
590 // contribute.
591 if (std::isnan(charge)) {
592 charge = 0.;
593 }
594 // Calculate induced current based on the induced charge.
595 if (i > 0) {
596 // Time difference between previous entry.
597 double dtt = m_delayedSignalTimes[i] - m_delayedSignalTimes[i - 1];
598 // Induced current
599 double current2 = m_tStep * (charge - chargeHolder) / dtt;
600 // Fill bins
601 if (std::abs(current2) < 1e-16) current2 = 0.;
602 electrode.delayedSignal[bin2] += current2;
603 electrode.signal[bin2] += current2;
604 // Linear interpolation if the current is calculated from the induced
605 // charge of two non-subsequent bins.
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 +
611 currentHolder;
612 electrode.signal[j] +=
613 (j - binHolder) * (current2 - currentHolder) / diffBin +
614 currentHolder;
615 }
616 }
617 currentHolder = current2;
618 }
619 // Hold information for next current calculation and interpolation.
620 binHolder = bin2;
621 chargeHolder = charge;
622 }
623 } else {
624 // Using the weighting field.
625 for (size_t i = 0; i < nd; ++i) {
626 // Integrate over the drift line segment.
627 const double step = std::min(m_delayedSignalTimes[i], dt);
628 const double scale = step / dt;
629 double sum = 0.;
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;
633 s *= scale;
634 const double x = x0 + s * dx;
635 const double y = y0 + s * dy;
636 const double z = z0 + s * dz;
637 // Calculate the delayed weighting field.
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];
641 }
642 id[i] = -q * 0.5 * sum * step;
643 }
644 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
645 }
646 }
647}
648
649void Sensor::AddSignal(const double q, const std::vector<double>& ts,
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) {
654 // Don't do anything if there are no points on the signal.
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";
658 return;
659 }
660 const bool aval = ns.size() == ts.size();
661 const size_t nPoints = ts.size();
662 if (m_debug) {
663 std::cout << m_className << "::AddSignal: Adding a " << nPoints
664 << "-vector (charge " << q << ").\n";
665 }
666
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.;
676
677 const double dx = dt * v[0];
678 const double dy = dt * v[1];
679 const double dz = dt * v[2];
680
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;
684
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;
688
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;
693
694 signal[i] = -current;
695 if (aval) signal[i] *= ns[i];
696 } else {
697 // Calculate the weighting field at this point.
698 double wx = 0., wy = 0., wz = 0.;
699 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
700 // Calculate the induced current at this point.
701 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
702 if (aval) signal[i] *= ns[i];
703 }
704 }
705 FillSignal(electrode, q, ts, signal, navg);
706 }
707
708 if (!m_delayedSignal) return;
709 if (m_delayedSignalTimes.empty()) return;
710 // Locations and weights for 6-point Gaussian integration
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];
729 }
730 // Calculate the signals for each electrode.
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) {
735 // Integrate over the drift line segment.
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]);
741 double sum = 0.;
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;
745 // Calculate the delayed weighting field.
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,
749 lbl);
750 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
751 }
752 id[i] = -q * 0.5 * sum * step;
753 }
754 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
755 }
756 }
757}
758
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.;
764 // Interpolation order.
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;
771 // Integration over this bin.
772 const double tmin = std::max(ts.front(), t0);
773 const double tmax = std::min(ts.back(), t1);
774 double sum = 0.;
775 if (navg <= 0) {
776 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
777 } else {
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);
787 } else {
788 sum += 4 * Interpolate(is, ts, t, k);
789 }
790 }
791 sum *= h / 3.;
792 }
793 // Add the result to the signal.
794 FillBin(electrode, i, sum, electron, delayed);
795 }
796}
797
798void Sensor::AddInducedCharge(const double q, const double x0, const double y0,
799 const double z0, const double x1, const double y1,
800 const double z1) {
801 if (m_debug) std::cout << m_className << "::AddInducedCharge:\n";
802 for (auto& electrode : m_electrodes) {
803 // Calculate the weighting potential at the starting point.
804 auto cmp = electrode.comp;
805 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
806 // Calculate the weighting potential at the end point.
807 const double w1 = cmp->WeightingPotential(x1, y1, z1, electrode.label);
808 electrode.charge += q * (w1 - w0);
809 if (m_debug) {
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";
816 }
817 }
818}
819
820void Sensor::SetTimeWindow(const double tstart, const double tstep,
821 const unsigned int nsteps) {
822 m_tStart = tstart;
823 if (tstep <= 0.) {
824 std::cerr << m_className << "::SetTimeWindow: Start time out of range.\n";
825 } else {
826 m_tStep = tstep;
827 }
828
829 if (nsteps == 0) {
830 std::cerr << m_className << "::SetTimeWindow: Invalid number of bins.\n";
831 } else {
832 m_nTimeBins = nsteps;
833 }
834
835 if (m_debug) {
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";
839 }
840
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;
850 }
851 m_nEvents = 0;
852 // Reset the cached FFT of the transfer function
853 // because it depends on the number of time bins.
854 m_fTransferFFT.clear();
855}
856
857double Sensor::GetElectronSignal(const std::string& label,
858 const unsigned int bin) {
859 if (m_nEvents == 0) return 0.;
860 if (bin >= m_nTimeBins) return 0.;
861 double sig = 0.;
862 for (const auto& electrode : m_electrodes) {
863 if (electrode.label == label) sig += electrode.electronSignal[bin];
864 }
865 return ElementaryCharge * sig / (m_nEvents * m_tStep);
866}
867
868double Sensor::GetIonSignal(const std::string& label, const unsigned int bin) {
869 if (m_nEvents == 0) return 0.;
870 if (bin >= m_nTimeBins) return 0.;
871 double sig = 0.;
872 for (const auto& electrode : m_electrodes) {
873 if (electrode.label == label) sig += electrode.ionSignal[bin];
874 }
875 return ElementaryCharge * sig / (m_nEvents * m_tStep);
876}
877
878double Sensor::GetDelayedElectronSignal(const std::string& label,
879 const unsigned int bin) {
880 if (m_nEvents == 0) return 0.;
881 if (bin >= m_nTimeBins) return 0.;
882 double sig = 0.;
883 for (const auto& electrode : m_electrodes) {
884 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
885 }
886 return ElementaryCharge * sig / (m_nEvents * m_tStep);
887}
888
889double Sensor::GetDelayedIonSignal(const std::string& label,
890 const unsigned int bin) {
891 if (m_nEvents == 0) return 0.;
892 if (bin >= m_nTimeBins) return 0.;
893 double sig = 0.;
894 for (const auto& electrode : m_electrodes) {
895 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
896 }
897 return ElementaryCharge * sig / (m_nEvents * m_tStep);
898}
899
900void Sensor::SetSignal(const std::string& label, const unsigned int bin,
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;
907 break;
908 }
909 }
910}
911
912double Sensor::GetSignal(const std::string& label, const unsigned int bin) {
913 if (m_nEvents == 0) return 0.;
914 if (bin >= m_nTimeBins) return 0.;
915 double sig = 0.;
916 for (const auto& electrode : m_electrodes) {
917 if (electrode.label == label) sig += electrode.signal[bin];
918 }
919 return ElementaryCharge * sig / (m_nEvents * m_tStep);
920}
921
922double Sensor::GetSignal(const std::string& label, const unsigned int bin,
923 const int comp) {
924 if (m_nEvents == 0) return 0.;
925 if (bin >= m_nTimeBins) return 0.;
926 double sig = 0.;
927 for (const auto& electrode : m_electrodes) {
928 if (electrode.label == label) {
929 switch (comp) {
930 case 1: {
931 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
932 break;
933 }
934 case 2: {
935 sig += electrode.delayedSignal[bin];
936 break;
937 }
938 default:
939 sig += electrode.signal[bin];
940 }
941 }
942 }
943 return ElementaryCharge * sig / (m_nEvents * m_tStep);
944}
945
946double Sensor::GetPromptSignal(const std::string& label,
947 const unsigned int bin) {
948 if (m_nEvents == 0) return 0.;
949 if (bin >= m_nTimeBins) return 0.;
950 double sig = 0.;
951 for (const auto& electrode : m_electrodes) {
952 if (electrode.label == label)
953 sig += electrode.signal[bin] - electrode.delayedSignal[bin];
954 }
955 return ElementaryCharge * sig / (m_nEvents * m_tStep);
956}
957
958double Sensor::GetDelayedSignal(const std::string& label,
959 const unsigned int bin) {
960 if (m_nEvents == 0) return 0.;
961 if (bin >= m_nTimeBins) return 0.;
962 double sig = 0.;
963 for (const auto& electrode : m_electrodes) {
964 if (electrode.label == label) sig += electrode.delayedSignal[bin];
965 }
966 return ElementaryCharge * sig / (m_nEvents * m_tStep);
967}
968
969double Sensor::GetInducedCharge(const std::string& label) {
970 if (m_nEvents == 0) return 0.;
971 double charge = 0.;
972 for (const auto& electrode : m_electrodes) {
973 if (electrode.label == label) charge += electrode.charge;
974 }
975
976 return charge / m_nEvents;
977}
978
979void Sensor::SetTransferFunction(double (*f)(double t)) {
980 if (!f) {
981 std::cerr << m_className << "::SetTransferFunction: Null pointer.\n";
982 return;
983 }
984 m_fTransfer = f;
985 m_shaper = nullptr;
986 m_fTransferTab.clear();
987 m_fTransferSq = -1.;
988 m_fTransferFFT.clear();
989}
990
991void Sensor::SetTransferFunction(const std::vector<double>& times,
992 const std::vector<double>& values) {
993 if (times.empty() || values.empty()) {
994 std::cerr << m_className << "::SetTransferFunction: Empty vector.\n";
995 return;
996 } else if (times.size() != values.size()) {
997 std::cerr << m_className << "::SetTransferFunction:\n"
998 << " Time and value vectors must have same size.\n";
999 return;
1000 }
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]));
1005 }
1006 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
1007 m_fTransfer = nullptr;
1008 m_shaper = nullptr;
1009 m_fTransferSq = -1.;
1010 m_fTransferFFT.clear();
1011}
1012
1014 m_shaper = &shaper;
1015 m_fTransfer = nullptr;
1016 m_fTransferTab.clear();
1017 m_fTransferSq = -1.;
1018 m_fTransferFFT.clear();
1019}
1020
1021double Sensor::InterpolateTransferFunctionTable(const double t) const {
1022 if (m_fTransferTab.empty()) return 0.;
1023 // Don't extrapolate beyond the range defined in the table.
1024 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
1025 return 0.;
1026 }
1027 // Find the proper interval in the table.
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);
1037 // Linear interpolation.
1038 return (*it0).second * (1. - f) + (*it1).second * f;
1039}
1040
1041double Sensor::GetTransferFunction(const double t) {
1042 if (m_fTransfer) {
1043 return m_fTransfer(t);
1044 } else if (m_shaper) {
1045 return m_shaper->Shape(t);
1046 }
1047 return InterpolateTransferFunctionTable(t);
1048}
1049
1051
1052 std::cout << m_className << "::PrintTransferFunction:\n";
1053 if (m_fTransfer) {
1054 std::cout << " User-defined function.";
1055 } else if (m_shaper) {
1056 std::string shaperType = "Unknown";
1057 if (m_shaper->IsUnipolar()) {
1058 shaperType = "Unipolar";
1059 } else if (m_shaper->IsBipolar()) {
1060 shaperType = "Bipolar";
1061 }
1062 unsigned int n = 1;
1063 double tp = 0.;
1064 m_shaper->GetParameters(n, tp);
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";
1069 } else {
1070 std::cout << " No transfer function set.\n";
1071 return;
1072 }
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;
1077 const double f = GetTransferFunction(t);
1078 std::printf(" %10.3f %10.5f\n", t, f);
1079 }
1080}
1081
1082bool Sensor::ConvoluteSignal(const std::string& label, const bool fft) {
1083 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1084 std::cerr << m_className << "::ConvoluteSignal: "
1085 << "Transfer function not set.\n";
1086 return false;
1087 }
1088 if (m_nEvents == 0) {
1089 std::cerr << m_className << "::ConvoluteSignal: No signals present.\n";
1090 return false;
1091 }
1092
1093 if (fft) return ConvoluteSignalFFT(label);
1094 std::vector<double> cnvTab;
1095 MakeTransferFunctionTable(cnvTab);
1096 // Loop over all electrodes.
1097 for (auto& electrode : m_electrodes) {
1098 if (label != electrode.label) continue;
1099 ConvoluteSignal(electrode, cnvTab);
1100 return true;
1101 }
1102 return false;
1103}
1104
1105bool Sensor::ConvoluteSignals(const bool fft) {
1106 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1107 std::cerr << m_className << "::ConvoluteSignals: "
1108 << "Transfer function not set.\n";
1109 return false;
1110 }
1111 if (m_nEvents == 0) {
1112 std::cerr << m_className << "::ConvoluteSignals: No signals present.\n";
1113 return false;
1114 }
1115
1116 if (fft) return ConvoluteSignalFFT();
1117 std::vector<double> cnvTab;
1118 MakeTransferFunctionTable(cnvTab);
1119 // Loop over all electrodes.
1120 for (auto& electrode : m_electrodes) ConvoluteSignal(electrode, cnvTab);
1121 return true;
1122}
1123
1124void Sensor::MakeTransferFunctionTable(std::vector<double>& cnvTab) {
1125 // Set the range where the transfer function is valid.
1126 constexpr double cnvMin = 0.;
1127 constexpr double cnvMax = 1.e10;
1128
1129 cnvTab.assign(2 * m_nTimeBins - 1, 0.);
1130 const unsigned int offset = m_nTimeBins - 1;
1131 // Evaluate the transfer function.
1132 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1133 // Negative time part.
1134 double t = (-int(i)) * m_tStep;
1135 if (t < cnvMin || t > cnvMax) {
1136 cnvTab[offset - i] = 0.;
1137 } else {
1138 cnvTab[offset - i] = GetTransferFunction(t);
1139 }
1140 if (i == 0) continue;
1141 // Positive time part.
1142 t = i * m_tStep;
1143 if (t < cnvMin || t > cnvMax) {
1144 cnvTab[offset + i] = 0.;
1145 } else {
1146 cnvTab[offset + i] = GetTransferFunction(t);
1147 }
1148 }
1149}
1150
1151void Sensor::ConvoluteSignal(Electrode& electrode,
1152 const std::vector<double>& tab) {
1153 // Do the convolution.
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];
1163 }
1164 }
1165 electrode.signal.swap(tmpSignal);
1166 electrode.delayedSignal.swap(tmpSignalDelayed);
1167 electrode.integrated = true;
1168}
1169
1170bool Sensor::ConvoluteSignalFFT() {
1171 // Number of bins must be a power of 2.
1172 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1173
1174 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1175 // (Re-)compute the FFT of the transfer function.
1176 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1177 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1178 m_fTransferFFT[2 * i + 1] = GetTransferFunction(i * m_tStep);
1179 }
1180 FFT(m_fTransferFFT, false, nn);
1181 }
1182
1183 for (auto& electrode : m_electrodes) {
1184 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1185 }
1186 return true;
1187}
1188
1189bool Sensor::ConvoluteSignalFFT(const std::string& label) {
1190 // Number of bins must be a power of 2.
1191 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
1192
1193 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
1194 // (Re-)compute the FFT of the transfer function.
1195 m_fTransferFFT.assign(2 * (nn + 1), 0.);
1196 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1197 m_fTransferFFT[2 * i + 1] = GetTransferFunction(i * m_tStep);
1198 }
1199 FFT(m_fTransferFFT, false, nn);
1200 }
1201
1202 for (auto& electrode : m_electrodes) {
1203 if (label != electrode.label) continue;
1204 ConvoluteSignalFFT(electrode, m_fTransferFFT, nn);
1205 return true;
1206 }
1207 return false;
1208}
1209
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];
1216 }
1217 FFT(g, false, nn);
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;
1225 }
1226 FFT(g, true, nn);
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];
1230 }
1231 electrode.integrated = true;
1232}
1233
1235 if (m_nEvents == 0) {
1236 std::cerr << m_className << "::IntegrateSignals: No signals present.\n";
1237 return false;
1238 }
1239
1240 for (auto& electrode : m_electrodes) IntegrateSignal(electrode);
1241 return true;
1242}
1243
1244bool Sensor::IntegrateSignal(const std::string& label) {
1245 if (m_nEvents == 0) {
1246 std::cerr << m_className << "::IntegrateSignal: No signals present.\n";
1247 return false;
1248 }
1249
1250 for (auto& electrode : m_electrodes) {
1251 if (label != electrode.label) continue;
1252 IntegrateSignal(electrode);
1253 return true;
1254 }
1255 std::cerr << m_className << "::IntegrateSignal: Electrode " << label
1256 << " not found.\n";
1257 return false;
1258}
1259
1260void Sensor::IntegrateSignal(Electrode& electrode) {
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;
1266 if (j > 0) {
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];
1271 }
1272 }
1273 electrode.integrated = true;
1274}
1275
1276bool Sensor::IsIntegrated(const std::string& label) const {
1277 for (const auto& electrode : m_electrodes) {
1278 if (electrode.label == label) return electrode.integrated;
1279 }
1280 return false;
1281}
1282
1283bool Sensor::DelayAndSubtractFraction(const double td, const double f) {
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];
1293 }
1294 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1295 electrode.signal[j] = signal1[j] - signal2[j];
1296 }
1297 }
1298 return true;
1299}
1300
1301void Sensor::SetNoiseFunction(double (*f)(double t)) {
1302 if (!f) {
1303 std::cerr << m_className << "::SetNoiseFunction: Null pointer.\n";
1304 return;
1305 }
1306 m_fNoise = f;
1307}
1308
1309void Sensor::AddNoise(const bool total, const bool electron, const bool ion) {
1310 if (!m_fNoise) {
1311 std::cerr << m_className << "::AddNoise: Noise function not set.\n";
1312 return;
1313 }
1314 if (m_nEvents == 0) m_nEvents = 1;
1315
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;
1323 t += m_tStep;
1324 }
1325 }
1326}
1327
1328void Sensor::AddWhiteNoise(const std::string& label, const double enc,
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";
1332 return;
1333 }
1334 if (m_nEvents == 0) m_nEvents = 1;
1335
1336 const double f2 = TransferFunctionSq();
1337 if (f2 < 0.) {
1338 std::cerr << m_className << "::AddWhiteNoise:\n"
1339 << " Could not calculate transfer function integral.\n";
1340 return;
1341 }
1342
1343 if (poisson) {
1344 // Frequency of random delta pulses to model noise.
1345 const double nu = (enc * enc / (q0 * q0)) / f2;
1346 // Average number of delta pulses.
1347 const double avg = nu * m_tStep * m_nTimeBins;
1348 // Sample the number of pulses.
1349 for (auto& electrode : m_electrodes) {
1350 if (label != electrode.label) continue;
1351 const int nPulses = RndmPoisson(avg);
1352 for (int j = 0; j < nPulses; ++j) {
1353 const int bin = static_cast<int>(m_nTimeBins * RndmUniform());
1354 electrode.signal[bin] += q0;
1355 }
1356 const double offset = q0 * nu * m_tStep;
1357 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1358 electrode.signal[j] -= offset;
1359 }
1360 break;
1361 }
1362 } else {
1363 // Gaussian approximation.
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) {
1368 electrode.signal[j] += RndmGaussian(0., sigma);
1369 }
1370 break;
1371 }
1372 }
1373}
1374
1375void Sensor::AddWhiteNoise(const double enc, const bool poisson,
1376 const double q0) {
1377 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1378 std::cerr << m_className << "::AddWhiteNoise: Transfer function not set.\n";
1379 return;
1380 }
1381 if (m_nEvents == 0) m_nEvents = 1;
1382
1383 const double f2 = TransferFunctionSq();
1384 if (f2 < 0.) {
1385 std::cerr << m_className << "::AddWhiteNoise:\n"
1386 << " Could not calculate transfer function integral.\n";
1387 return;
1388 }
1389
1390 if (poisson) {
1391 // Frequency of random delta pulses to model noise.
1392 const double nu = (enc * enc / (q0 * q0)) / f2;
1393 // Average number of delta pulses.
1394 const double avg = nu * m_tStep * m_nTimeBins;
1395 // Sample the number of pulses.
1396 for (auto& electrode : m_electrodes) {
1397 const int nPulses = RndmPoisson(avg);
1398 for (int j = 0; j < nPulses; ++j) {
1399 const int bin = static_cast<int>(m_nTimeBins * RndmUniform());
1400 electrode.signal[bin] += q0;
1401 }
1402 const double offset = q0 * nu * m_tStep;
1403 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1404 electrode.signal[j] -= offset;
1405 }
1406 }
1407 } else {
1408 // Gaussian approximation.
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) {
1412 electrode.signal[j] += RndmGaussian(0., sigma);
1413 }
1414 }
1415 }
1416}
1417
1418double Sensor::TransferFunctionSq() {
1419 if (m_fTransferSq >= 0.) return m_fTransferSq;
1420 double integral = -1.;
1421 if (m_fTransfer) {
1422 std::function<double(double)> fsq = [this](double x) {
1423 const double y = m_fTransfer(x);
1424 return y * y;
1425 };
1426 constexpr double epsrel = 1.e-8;
1427 double err = 0.;
1428 unsigned int stat = 0;
1429 Numerics::QUADPACK::qagi(fsq, 0., 1, 0., epsrel, integral, err, stat);
1430 } else if (m_shaper) {
1431 integral = m_shaper->TransferFuncSq();
1432 } else {
1433 integral = Trapezoid2(m_fTransferTab);
1434 }
1435 if (m_cacheTransferFunction) m_fTransferSq = integral;
1436 return integral;
1437}
1438
1440 const std::string& label, int& n) {
1441 // Reset the list of threshold crossings.
1442 m_thresholdCrossings.clear();
1443 m_thresholdLevel = thr;
1444
1445 // Set the interpolation order.
1446 int iOrder = 1;
1447
1448 if (m_nEvents == 0) {
1449 std::cerr << m_className << "::ComputeThresholdCrossings: "
1450 << "No signals present.\n";
1451 return false;
1452 }
1453 // Compute the total signal.
1454 std::vector<double> signal(m_nTimeBins, 0.);
1455 // Loop over the electrodes.
1456 bool foundLabel = false;
1457 for (const auto& electrode : m_electrodes) {
1458 if (electrode.label != label) continue;
1459 foundLabel = true;
1460 if (!electrode.integrated) {
1461 std::cerr << m_className << "::ComputeThresholdCrossings:\n "
1462 << "Warning: signal on electrode " << label
1463 << " has not been integrated/convoluted.\n";
1464 }
1465 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1466 signal[i] += electrode.signal[i];
1467 }
1468 }
1469 if (!foundLabel) {
1470 std::cerr << m_className << "::ComputeThresholdCrossings: Electrode "
1471 << label << " not found.\n";
1472 return false;
1473 }
1474 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1475 for (unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1476
1477 // Establish the range.
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) {
1482 if (m_debug) {
1483 std::cout << " Threshold outside the range [" << vMin << ", " << vMax
1484 << "]\n";
1485 }
1486 return true;
1487 }
1488
1489 // Check both rising and falling edges.
1490 constexpr std::array<int, 2> directions = {1, -1};
1491 for (const auto dir : directions) {
1492 const bool up = dir > 0;
1493 if (m_debug) {
1494 if (up) {
1495 std::cout << " Hunting for rising edges.\n";
1496 } else {
1497 std::cout << " Hunting for falling edges.\n";
1498 }
1499 }
1500 // Initialise the vectors.
1501 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1502 std::vector<double> vs = {signal[0]};
1503 // Scan the signal.
1504 for (unsigned int i = 1; i < m_nTimeBins; ++i) {
1505 // Compute the vector element.
1506 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1507 const double vNew = signal[i];
1508 // If still increasing or decreasing, add to the vector.
1509 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1510 ts.push_back(tNew);
1511 vs.push_back(vNew);
1512 continue;
1513 }
1514 // Otherwise see whether we crossed the threshold level.
1515 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1516 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1517 // Compute the crossing time.
1518 double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1519 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1520 ts = {tNew};
1521 vs = {vNew};
1522 } else {
1523 // No crossing, simply reset the vector.
1524 ts = {tNew};
1525 vs = {vNew};
1526 }
1527 }
1528 // Check the final vector.
1529 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1530 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1531 const double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1532 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1533 }
1534 }
1535 n = m_thresholdCrossings.size();
1536
1537 if (m_debug) {
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";
1544 } else {
1545 std::cout << "falling\n";
1546 }
1547 }
1548 }
1549 // Seems to have worked.
1550 return true;
1551}
1552
1553bool Sensor::GetThresholdCrossing(const unsigned int i, double& time,
1554 double& level, bool& rise) const {
1555 level = m_thresholdLevel;
1556
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;
1560 return false;
1561 }
1562
1563 time = m_thresholdCrossings[i].first;
1564 rise = m_thresholdCrossings[i].second;
1565 return true;
1566}
1567
1568bool Sensor::GetBoundingBox(double& xmin, double& ymin, double& zmin,
1569 double& xmax, double& ymax, double& zmax) {
1570 // We don't know the range yet
1571 bool set = false;
1572 // Loop over the fields
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;
1577 if (set) {
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;
1584 } else {
1585 xmin = x0;
1586 ymin = y0;
1587 zmin = z0;
1588 xmax = x1;
1589 ymax = y1;
1590 zmax = z1;
1591 set = true;
1592 }
1593 }
1594
1595 // Warn if we still don't know the range
1596 if (!set) {
1597 std::cerr << m_className << "::GetBoundingBox:\n"
1598 << " Sensor bounding box not known.\n";
1599 xmin = ymin = zmin = 0.;
1600 xmax = ymax = zmax = 0.;
1601 return false;
1602 }
1603
1604 if (m_debug) {
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";
1609 }
1610 return true;
1611}
1612
1613void Sensor::FFT(std::vector<double>& data, const bool inverse, const int nn) {
1614 // Replaces data[1..2*nn] by its discrete fourier transform
1615 // or replaces data[1..2*nn] by nn times its inverse discrete
1616 // fourier transform.
1617 // nn MUST be an integer power of 2 (this is not checked for!).
1618
1619 const int n = 2 * nn;
1620 // Bit reversal.
1621 int j = 1;
1622 for (int i = 1; i < n; i += 2) {
1623 if (j > i) {
1624 // Exchange the two complex numbers.
1625 std::swap(data[j], data[i]);
1626 std::swap(data[j + 1], data[i + 1]);
1627 }
1628 int m = nn;
1629 while (m >= 2 && j > m) {
1630 j -= m;
1631 m >>= 1;
1632 }
1633 j += m;
1634 }
1635
1636 const int isign = inverse ? -1 : 1;
1637 int mmax = 2;
1638 while (n > mmax) {
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);
1644 double wr = 1.;
1645 double wi = 0.;
1646 for (int m = 1; m < mmax; m += 2) {
1647 for (int i = m; i <= n; i += step) {
1648 j = i + mmax;
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;
1653 data[i] += tr;
1654 data[i + 1] += ti;
1655 }
1656 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1657 wi = wi * wpr + wtemp * wpi + wi;
1658 }
1659 mmax = step;
1660 }
1661}
1662
1663void Sensor::ExportSignal(const std::string& label,
1664 const std::string& name) {
1665
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";
1675 return;
1676 }
1677 if (electrode.integrated) {
1678 myfile << "The cumulative induced charge.\n";
1679 myfile << "Time [ns],Prompt [fC],Delayed [fC],Total [fC],\n";
1680 } else {
1681 myfile << "The induced signal.\n";
1682 myfile << "Time [ns],Prompt [fC/ns],Delayed [fC/ns],Total [fC/ns];\n";
1683 }
1684 for (unsigned int i = 0; i < m_nTimeBins; i++) {
1685 myfile << std::setprecision(std::numeric_limits<long double>::digits10 +
1686 1)
1687 << m_tStart + i * m_tStep << ","
1688 << scale * (electrode.signal[i] - electrode.delayedSignal[i])
1689 << ","
1690 << scale * electrode.delayedSignal[i] << ","
1691 << scale * electrode.signal[i] << "\n";
1692 }
1693 myfile.close();
1694 std::cerr << m_className << "::ExportSignal: File '" << filename
1695 << ".csv' exported.\n";
1696 break;
1697 }
1698}
1699
1700double Sensor::GetTotalInducedCharge(const std::string label) {
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);
1705 }
1706 return 0.;
1707}
1708
1709} // namespace Garfield
Abstract base class for components.
Definition: Component.hh:13
virtual Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).
Definition: Component.cc:22
Abstract base class for media.
Definition: Medium.hh:13
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:116
void EnableMagneticField(const unsigned int i, const bool on)
Activate/deactivate use of the magnetic field of a given component.
Definition: Sensor.cc:341
double GetElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the electron signal for a given electrode and time bin.
Definition: Sensor.cc:857
bool ConvoluteSignal(const std::string &label, const bool fft=false)
Definition: Sensor.cc:1082
double GetTotalInducedCharge(const std::string label)
Definition: Sensor.cc:1700
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
Definition: Sensor.cc:396
void SetDelayedSignalTimes(const std::vector< double > &ts)
Set the points in time at which to evaluate the delayed weighting field.
Definition: Sensor.cc:443
void SetTimeWindow(const double tstart, const double tstep, const unsigned int nsteps)
Definition: Sensor.cc:820
void ExportSignal(const std::string &label, const std::string &filename)
Exporting induced signal to a csv file.
Definition: Sensor.cc:1663
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:900
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:889
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
Definition: Sensor.cc:290
bool DelayAndSubtractFraction(const double td, const double f)
Definition: Sensor.cc:1283
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:131
double GetTransferFunction(const double t)
Evaluate the transfer function at a given time.
Definition: Sensor.cc:1041
void EnableComponent(const unsigned int i, const bool on)
Activate/deactivate a given component.
Definition: Sensor.cc:333
void AddElectrode(Component *comp, const std::string &label)
Add an electrode.
Definition: Sensor.cc:349
bool IntegrateSignal(const std::string &label)
Replace the signal on a given electrode by its integral.
Definition: Sensor.cc:1244
void AddComponent(Component *comp)
Add a component.
Definition: Sensor.cc:316
void PrintTransferFunction()
Print some information about the presently set transfer function.
Definition: Sensor.cc:1050
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:65
bool SetArea()
Set the user area to the default.
Definition: Sensor.cc:183
double GetSignal(const std::string &label, const unsigned int bin)
Retrieve the total signal for a given electrode and time bin.
Definition: Sensor.cc:912
void SetNoiseFunction(double(*f)(double t))
Set the function to be used for evaluating the noise component.
Definition: Sensor.cc:1301
double GetPromptSignal(const std::string &label, const unsigned int bin)
Retrieve the prompt signal for a given electrode and time bin.
Definition: Sensor.cc:946
bool IntegrateSignals()
Replace the signals on all electrodes curve by their integrals.
Definition: Sensor.cc:1234
bool ConvoluteSignals(const bool fft=false)
Convolute all induced currents with the transfer function.
Definition: Sensor.cc:1105
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition: Sensor.cc:254
double GetInducedCharge(const std::string &label)
Calculated using the weighting potentials at the start and end points.
Definition: Sensor.cc:969
void AddNoise(const bool total=true, const bool electron=false, const bool ion=false)
Add noise to the induced signal.
Definition: Sensor.cc:1309
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:301
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:147
Component * GetComponent(const unsigned int i)
Retrieve the pointer to a given component.
Definition: Sensor.cc:325
void AddWhiteNoise(const std::string &label, const double enc, const bool poisson=true, const double q0=1.)
Definition: Sensor.cc:1328
bool ComputeThresholdCrossings(const double thr, const std::string &label, int &n)
Definition: Sensor.cc:1439
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:159
void Clear()
Remove all components, electrodes and reset the sensor.
Definition: Sensor.cc:379
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:452
void SetTransferFunction(double(*f)(double t))
Set the function to be used for evaluating the transfer function.
Definition: Sensor.cc:979
bool GetThresholdCrossing(const unsigned int i, double &time, double &level, bool &rise) const
Definition: Sensor.cc:1553
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:878
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)
Definition: Sensor.cc:276
void ClearSignal()
Reset signals and induced charges of all electrodes.
Definition: Sensor.cc:429
double GetDelayedSignal(const std::string &label, const unsigned int bin)
Retrieve the delayed signal for a given electrode and time bin.
Definition: Sensor.cc:958
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:798
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:868
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:228
bool IsIntegrated(const std::string &label) const
Return whether the signal has been integrated/convoluted.
Definition: Sensor.cc:1276
Class for signal processing.
Definition: Shaper.hh:11
bool IsUnipolar() const
Is it a unipolar shaper?
Definition: Shaper.hh:34
double TransferFuncSq() const
Return the integral of the transfer function squared.
Definition: Shaper.hh:31
void GetParameters(unsigned int &n, double &tp)
Retrieve the parameters.
Definition: Shaper.hh:38
double Shape(const double t) const
Evaluate the transfer function.
Definition: Shaper.cc:50
bool IsBipolar() const
Is it a bipolar shaper?
Definition: Shaper.hh:36
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:144
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
Definition: Numerics.cc:1206
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