Garfield++ 3.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 <iostream>
5
8#include "Garfield/Random.hh"
10#include "Garfield/Sensor.hh"
11
12namespace {
13
14double Interpolate(const std::vector<double>& y,
15 const std::vector<double>& x,
16 const double xx, const unsigned int order) {
17
18 if (xx < x.front() || xx > x.back()) return 0.;
19 if (order > 1) {
20 return Garfield::Numerics::Divdif(y, x, x.size(), xx, order);
21 }
22 const auto it1 = std::upper_bound(x.cbegin(), x.cend(), xx);
23 if (it1 == x.cend()) return y.back();
24 const auto it0 = std::prev(it1);
25 const double dx = (*it1 - *it0);
26 if (dx < Garfield::Small) return y[it0 - x.cbegin()];
27 const double f = (xx - *it0) / dx;
28 return y[it0 - x.cbegin()] * (1. - f) + f * y[it1 - x.cbegin()];
29}
30
31double Trapezoid2(const std::vector<std::pair<double, double> >& f) {
32
33 const unsigned int n = f.size();
34 if (n < 2) return -1.;
35 double sum = 0.;
36 const double x0 = f[0].first;
37 const double y0 = f[0].second;
38 const double x1 = f[1].first;
39 const double y1 = f[1].second;
40 if (n == 2) {
41 sum = (x1 - x0) * (y0 * y0 + y1 * y1);
42 } else if (n == 3) {
43 const double x2 = f[2].first;
44 const double y2 = f[2].second;
45 sum = (x1 - x0) * y0 * y0 + (x2 - x0) * y1 * y1 + (x2 - x1) * y2 * y2;
46 } else {
47 sum = (x1 - x0) * y0 * y0 + (f[2].first - x0) * y1 * y1;
48 const double xm = f[n - 2].first;
49 const double ym = f[n - 2].second;
50 const double xn = f[n - 1].first;
51 const double yn = f[n - 1].second;
52 sum += (xn - f[n - 3].first) * ym * ym + (xn - xm) * yn * yn;
53 if (n > 4) {
54 for (unsigned int k = 2; k < n - 2; ++k) {
55 const double y = f[k].second;
56 sum += (f[k + 1].first - f[k - 1].first) * y * y;
57 }
58 }
59 }
60 return 0.5 * sum;
61}
62
63}
64
65namespace Garfield {
66
67ComponentBase* Sensor::GetComponent(const unsigned int i) {
68 if (i >= m_components.size()) {
69 std::cerr << m_className << "::GetComponent: Index out of range.\n";
70 return nullptr;
71 };
72 return m_components[i];
73}
74
75void Sensor::ElectricField(const double x, const double y, const double z,
76 double& ex, double& ey, double& ez, double& v,
77 Medium*& medium, int& status) {
78 ex = ey = ez = v = 0.;
79 status = -10;
80 medium = nullptr;
81 double fx = 0., fy = 0., fz = 0., p = 0.;
82 Medium* med = nullptr;
83 int stat = 0;
84 // Add up electric field contributions from all components.
85 for (auto component : m_components) {
86 component->ElectricField(x, y, z, fx, fy, fz, p, med, stat);
87 if (status != 0) {
88 status = stat;
89 medium = med;
90 }
91 if (stat == 0) {
92 ex += fx;
93 ey += fy;
94 ez += fz;
95 v += p;
96 }
97 }
98}
99
100void Sensor::ElectricField(const double x, const double y, const double z,
101 double& ex, double& ey, double& ez, Medium*& medium,
102 int& status) {
103 ex = ey = ez = 0.;
104 status = -10;
105 medium = nullptr;
106 double fx = 0., fy = 0., fz = 0.;
107 Medium* med = nullptr;
108 int stat = 0;
109 // Add up electric field contributions from all components.
110 for (auto component : m_components) {
111 component->ElectricField(x, y, z, fx, fy, fz, med, stat);
112 if (status != 0) {
113 status = stat;
114 medium = med;
115 }
116 if (stat == 0) {
117 ex += fx;
118 ey += fy;
119 ez += fz;
120 }
121 }
122}
123
124void Sensor::MagneticField(const double x, const double y, const double z,
125 double& bx, double& by, double& bz, int& status) {
126 bx = by = bz = 0.;
127 double fx = 0., fy = 0., fz = 0.;
128 // Add up contributions.
129 for (auto component : m_components) {
130 component->MagneticField(x, y, z, fx, fy, fz, status);
131 if (status != 0) continue;
132 bx += fx;
133 by += fy;
134 bz += fz;
135 }
136}
137
138void Sensor::WeightingField(const double x, const double y, const double z,
139 double& wx, double& wy, double& wz,
140 const std::string& label) {
141 wx = wy = wz = 0.;
142 // Add up field contributions from all components.
143 for (const auto& electrode : m_electrodes) {
144 if (electrode.label == label) {
145 double fx = 0., fy = 0., fz = 0.;
146 electrode.comp->WeightingField(x, y, z, fx, fy, fz, label);
147 wx += fx;
148 wy += fy;
149 wz += fz;
150 }
151 }
152}
153
154double Sensor::WeightingPotential(const double x, const double y,
155 const double z, const std::string& label) {
156 double v = 0.;
157 // Add up contributions from all components.
158 for (const auto& electrode : m_electrodes) {
159 if (electrode.label == label) {
160 v += electrode.comp->WeightingPotential(x, y, z, label);
161 }
162 }
163 return v;
164}
165
166bool Sensor::GetMedium(const double x, const double y, const double z,
167 Medium*& m) {
168 m = nullptr;
169
170 // Make sure there is at least one component.
171 if (m_components.empty()) return false;
172
173 // Check if we are still in the same component as in the previous call.
174 if (m_lastComponent) {
175 m = m_lastComponent->GetMedium(x, y, z);
176 if (m) return true;
177 }
178
179 for (auto component : m_components) {
180 m = component->GetMedium(x, y, z);
181 if (m) {
182 m_lastComponent = component;
183 return true;
184 }
185 }
186 return false;
187}
188
190 if (!GetBoundingBox(m_xMinUser, m_yMinUser, m_zMinUser, m_xMaxUser,
191 m_yMaxUser, m_zMaxUser)) {
192 std::cerr << m_className << "::SetArea: Bounding box is not known.\n";
193 return false;
194 }
195
196 std::cout << m_className << "::SetArea:\n"
197 << " " << m_xMinUser << " < x [cm] < " << m_xMaxUser << "\n"
198 << " " << m_yMinUser << " < y [cm] < " << m_yMaxUser << "\n"
199 << " " << m_zMinUser << " < z [cm] < " << m_zMaxUser << "\n";
200 if (std::isinf(m_xMinUser) || std::isinf(m_xMaxUser)) {
201 std::cerr << m_className << "::SetArea: Warning. Infinite x-range.\n";
202 }
203 if (std::isinf(m_yMinUser) || std::isinf(m_yMaxUser)) {
204 std::cerr << m_className << "::SetArea: Warning. Infinite y-range.\n";
205 }
206 if (std::isinf(m_zMinUser) || std::isinf(m_zMaxUser)) {
207 std::cerr << m_className << "::SetArea: Warning. Infinite z-range.\n";
208 }
209 m_hasUserArea = true;
210 return true;
211}
212
213bool Sensor::SetArea(const double xmin, const double ymin, const double zmin,
214 const double xmax, const double ymax, const double zmax) {
215 if (fabs(xmax - xmin) < Small || fabs(ymax - ymin) < Small ||
216 fabs(zmax - zmin) < Small) {
217 std::cerr << m_className << "::SetArea: Invalid range.\n";
218 return false;
219 }
220
221 m_xMinUser = std::min(xmin, xmax);
222 m_yMinUser = std::min(ymin, ymax);
223 m_zMinUser = std::min(zmin, zmax);
224 m_xMaxUser = std::max(xmax, xmin);
225 m_yMaxUser = std::max(ymax, ymin);
226 m_zMaxUser = std::max(zmax, zmin);
227
228 m_hasUserArea = true;
229 return true;
230}
231
232bool Sensor::GetArea(double& xmin, double& ymin, double& zmin, double& xmax,
233 double& ymax, double& zmax) {
234 if (m_hasUserArea) {
235 xmin = m_xMinUser;
236 ymin = m_yMinUser;
237 zmin = m_zMinUser;
238 xmax = m_xMaxUser;
239 ymax = m_yMaxUser;
240 zmax = m_zMaxUser;
241 return true;
242 }
243
244 // User area bounds are not (yet) defined.
245 // Get the bounding box of the sensor.
246 if (!SetArea()) return false;
247
248 xmin = m_xMinUser;
249 ymin = m_yMinUser;
250 zmin = m_zMinUser;
251 xmax = m_xMaxUser;
252 ymax = m_yMaxUser;
253 zmax = m_zMaxUser;
254
255 return true;
256}
257
258bool Sensor::IsInArea(const double x, const double y, const double z) {
259 if (!m_hasUserArea) {
260 if (!SetArea()) {
261 std::cerr << m_className << "::IsInArea: User area could not be set.\n";
262 return false;
263 }
264 m_hasUserArea = true;
265 }
266
267 if (x >= m_xMinUser && x <= m_xMaxUser && y >= m_yMinUser &&
268 y <= m_yMaxUser && z >= m_zMinUser && z <= m_zMaxUser) {
269 return true;
270 }
271
272 if (m_debug) {
273 std::cout << m_className << "::IsInArea: (" << x << ", " << y << ", " << z
274 << ") "
275 << " is outside.\n";
276 }
277 return false;
278}
279
280bool Sensor::IsWireCrossed(const double x0, const double y0, const double z0,
281 const double x1, const double y1, const double z1,
282 double& xc, double& yc, double& zc,
283 const bool centre, double& rc) {
284 for (auto component : m_components) {
285 if (component->IsWireCrossed(x0, y0, z0, x1, y1, z1,
286 xc, yc, zc, centre, rc)) {
287 return true;
288 }
289 }
290 return false;
291}
292
293bool Sensor::IsInTrapRadius(const double q0, const double x0, const double y0,
294 double z0, double& xw, double& yw, double& rw) {
295 for (auto component : m_components) {
296 if (component->IsInTrapRadius(q0, x0, y0, z0, xw, yw, rw)) return true;
297 }
298 return false;
299}
300
302 if (!comp) {
303 std::cerr << m_className << "::AddComponent: Null pointer.\n";
304 return;
305 }
306
307 m_components.push_back(comp);
308}
309
310void Sensor::AddElectrode(ComponentBase* comp, const std::string& label) {
311 if (!comp) {
312 std::cerr << m_className << "::AddElectrode: Null pointer.\n";
313 return;
314 }
315 for (const auto& electrode : m_electrodes) {
316 if (electrode.label == label) {
317 std::cout << m_className << "::AddElectrode:\n"
318 << " Warning: An electrode with label \"" << label
319 << "\" exists already. Weighting fields will be summed up.\n";
320 break;
321 }
322 }
323
324 Electrode electrode;
325 electrode.comp = comp;
326 electrode.label = label;
327 electrode.signal.assign(m_nTimeBins, 0.);
328 electrode.electronsignal.assign(m_nTimeBins, 0.);
329 electrode.ionsignal.assign(m_nTimeBins, 0.);
330 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
331 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
332 m_electrodes.push_back(std::move(electrode));
333 std::cout << m_className << "::AddElectrode:\n"
334 << " Added readout electrode \"" << label << "\".\n"
335 << " All signals are reset.\n";
336 ClearSignal();
337}
338
340 m_components.clear();
341 m_lastComponent = nullptr;
342 m_electrodes.clear();
343 m_nTimeBins = 200;
344 m_tStart = 0.;
345 m_tStep = 10.;
346 m_nEvents = 0;
347 m_hasUserArea = false;
348 m_fTransfer = nullptr;
349 m_shaper = nullptr;
350 m_fTransferTab.clear();
351 m_fTransferSq = -1.;
352 m_fTransferFFT.clear();
353}
354
355bool Sensor::GetVoltageRange(double& vmin, double& vmax) {
356 // We don't know the range yet.
357 bool set = false;
358 // Loop over the components.
359 for (auto component : m_components) {
360 double umin = 0., umax = 0.;
361 if (!component->GetVoltageRange(umin, umax)) continue;
362 if (set) {
363 vmin = std::min(umin, vmin);
364 vmax = std::max(umax, vmax);
365 } else {
366 vmin = umin;
367 vmax = umax;
368 set = true;
369 }
370 }
371
372 // Warn if we still don't know the range.
373 if (!set) {
374 std::cerr << m_className << "::GetVoltageRange:\n"
375 << " Sensor voltage range not known.\n";
376 vmin = vmax = 0.;
377 return false;
378 }
379
380 if (m_debug) {
381 std::cout << m_className << "::GetVoltageRange: " << vmin << " < V < "
382 << vmax << ".\n";
383 }
384 return true;
385}
386
388 for (auto& electrode : m_electrodes) {
389 electrode.charge = 0.;
390 electrode.signal.assign(m_nTimeBins, 0.);
391 electrode.electronsignal.assign(m_nTimeBins, 0.);
392 electrode.ionsignal.assign(m_nTimeBins, 0.);
393 electrode.delayedElectronSignal.assign(m_nTimeBins, 0.);
394 electrode.delayedIonSignal.assign(m_nTimeBins, 0.);
395 }
396 m_nEvents = 0;
397 m_integrated = false;
398}
399
400void Sensor::SetDelayedSignalTimes(const std::vector<double>& ts) {
401 if (!std::is_sorted(ts.begin(), ts.end())) {
402 std::cerr << m_className << "::SetDelayedSignalTimes:\n"
403 << " Times are not in ascending order.\n";
404 return;
405 }
406 m_delayedSignalTimes = ts;
407}
408
409void Sensor::AddSignal(const double q, const double t0, const double t1,
410 const double x0, const double y0, const double z0,
411 const double x1, const double y1, const double z1,
412 const bool integrateWeightingField,
413 const bool useWeightingPotential) {
414 if (m_debug) std::cout << m_className << "::AddSignal: ";
415 // Get the time bin.
416 if (t0 < m_tStart) {
417 if (m_debug) std::cout << "Time " << t0 << " out of range.\n";
418 return;
419 }
420 const double dt = t1 - t0;
421 if (dt < Small) {
422 if (m_debug) std::cout << "Time step too small.\n";
423 return;
424 }
425 const int bin = int((t0 - m_tStart) / m_tStep);
426 // Check if the starting time is outside the range
427 if (bin < 0 || bin >= (int)m_nTimeBins) {
428 if (m_debug) std::cout << "Bin " << bin << " out of range.\n";
429 return;
430 }
431 if (m_nEvents <= 0) m_nEvents = 1;
432
433 const bool electron = q < 0;
434 const double dx = x1 - x0;
435 const double dy = y1 - y0;
436 const double dz = z1 - z0;
437 const double vx = dx / dt;
438 const double vy = dy / dt;
439 const double vz = dz / dt;
440 if (m_debug) {
441 std::cout << " Time: " << t0 << "\n"
442 << " Step: " << dt << "\n"
443 << " Charge: " << q << "\n"
444 << " Velocity: (" << vx << ", " << vy << ", " << vz << ")\n";
445 }
446
447 // Locations and weights for 6-point Gaussian integration
448 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
449 -0.238619186083196909, 0.238619186083196909,
450 0.661209386466264514, 0.932469514203152028};
451 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
452 0.467913934572691047, 0.467913934572691047,
453 0.360761573048138608, 0.171324492379170345};
454 for (auto& electrode : m_electrodes) {
455 const std::string lbl = electrode.label;
456 if (m_debug) std::cout << " Electrode " << electrode.label << ":\n";
457 // Induced current.
458 double current = 0.;
459 if (useWeightingPotential) {
460 const double w0 = electrode.comp->WeightingPotential(x0, y0, z0, lbl);
461 const double w1 = electrode.comp->WeightingPotential(x1, y1, z1, lbl);
462 current = q * (w1 - w0) / dt;
463 } else {
464 double wx = 0., wy = 0., wz = 0.;
465 // Calculate the weighting field for this electrode.
466 if (integrateWeightingField) {
467 for (unsigned int j = 0; j < 6; ++j) {
468 const double s = 0.5 * (1. + tg[j]);
469 const double x = x0 + s * dx;
470 const double y = y0 + s * dy;
471 const double z = z0 + s * dz;
472 double fx = 0., fy = 0., fz = 0.;
473 electrode.comp->WeightingField(x, y, z, fx, fy, fz, lbl);
474 wx += wg[j] * fx;
475 wy += wg[j] * fy;
476 wz += wg[j] * fz;
477 }
478 wx *= 0.5;
479 wy *= 0.5;
480 wz *= 0.5;
481 } else {
482 const double x = x0 + 0.5 * dx;
483 const double y = y0 + 0.5 * dy;
484 const double z = z0 + 0.5 * dz;
485 electrode.comp->WeightingField(x, y, z, wx, wy, wz, lbl);
486 }
487 if (m_debug) {
488 std::cout << " Weighting field: (" << wx << ", " << wy << ", "
489 << wz << ")\n";
490 }
491 // Calculate the induced current.
492 current = -q * (wx * vx + wy * vy + wz * vz);
493
494 }
495 if (m_debug) std::cout << " Induced charge: " << current * dt << "\n";
496 double delta = m_tStart + (bin + 1) * m_tStep - t0;
497 // Check if the provided timestep extends over more than one time bin
498 if (dt > delta) {
499 FillBin(electrode, bin, current * delta, electron, false);
500 delta = dt - delta;
501 unsigned int j = 1;
502 while (delta > m_tStep && bin + j < m_nTimeBins) {
503 FillBin(electrode, bin + j, current * m_tStep, electron, false);
504 delta -= m_tStep;
505 ++j;
506 }
507 if (bin + j < m_nTimeBins) {
508 FillBin(electrode, bin + j, current * delta, electron, false);
509 }
510 } else {
511 FillBin(electrode, bin, current * dt, electron, false);
512 }
513 }
514 if (!m_delayedSignal) return;
515 if (m_delayedSignalTimes.empty()) return;
516 const unsigned int nd = m_delayedSignalTimes.size();
517 // Establish the points in time at which we evaluate the delayed signal.
518 std::vector<double> td(nd);
519 for (unsigned int i = 0; i < nd; ++i) {
520 td[i] = t0 + m_delayedSignalTimes[i];
521 }
522 // Calculate the signals for each electrode.
523 for (auto& electrode : m_electrodes) {
524 const std::string lbl = electrode.label;
525 std::vector<double> id(nd, 0.);
526 for (unsigned int i = 0; i < nd; ++i) {
527 // Integrate over the drift line segment.
528 const double step = std::min(m_delayedSignalTimes[i], dt);
529 const double scale = step / dt;
530 double sum = 0.;
531 for (unsigned int j = 0; j < 6; ++j) {
532 double s = 0.5 * (1. + tg[j]);
533 const double t = m_delayedSignalTimes[i] - s * step;
534 s *= scale;
535 const double x = x0 + s * dx;
536 const double y = y0 + s * dy;
537 const double z = z0 + s * dz;
538 // Calculate the delayed weighting field.
539 double wx = 0., wy = 0., wz = 0.;
540 electrode.comp->DelayedWeightingField(x, y, z, t, wx, wy, wz, lbl);
541 sum += (wx * vx + wy * vy + wz * vz) * wg[j];
542 }
543 id[i] = -q * 0.5 * sum * step;
544 }
545 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
546 }
547
548}
549
550void Sensor::AddSignal(const double q, const std::vector<double>& ts,
551 const std::vector<std::array<double, 3> >& xs,
552 const std::vector<std::array<double, 3> >& vs,
553 const std::vector<double>& ns, const int navg) {
554
555 // Don't do anything if there are no points on the signal.
556 if (ts.size() < 2) return;
557 if (ts.size() != xs.size() || ts.size() != vs.size()) {
558 std::cerr << m_className << "::AddSignal: Mismatch in vector size.\n";
559 return;
560 }
561 const bool aval = ns.size() == ts.size();
562 const unsigned int nPoints = ts.size();
563 if (m_debug) {
564 std::cout << m_className << "::AddSignal: Adding a " << nPoints
565 << "-vector (charge " << q << ").\n";
566 }
567
568 if (m_nEvents <= 0) m_nEvents = 1;
569 for (auto& electrode : m_electrodes) {
570 const std::string label = electrode.label;
571 std::vector<double> signal(nPoints, 0.);
572 for (unsigned int i = 0; i < nPoints; ++i) {
573 // Calculate the weighting field at this point.
574 const auto& x = xs[i];
575 double wx = 0., wy = 0., wz = 0.;
576 electrode.comp->WeightingField(x[0], x[1], x[2], wx, wy, wz, label);
577 // Calculate the induced current at this point.
578 const auto& v = vs[i];
579 signal[i] = -q * (v[0] * wx + v[1] * wy + v[2] * wz);
580 if (aval) signal[i] *= ns[i];
581 }
582 FillSignal(electrode, q, ts, signal, navg);
583 }
584
585 if (!m_delayedSignal) return;
586 if (m_delayedSignalTimes.empty()) return;
587 // Locations and weights for 6-point Gaussian integration
588 constexpr double tg[6] = {-0.932469514203152028, -0.661209386466264514,
589 -0.238619186083196909, 0.238619186083196909,
590 0.661209386466264514, 0.932469514203152028};
591 constexpr double wg[6] = {0.171324492379170345, 0.360761573048138608,
592 0.467913934572691047, 0.467913934572691047,
593 0.360761573048138608, 0.171324492379170345};
594 const unsigned int nd = m_delayedSignalTimes.size();
595 for (unsigned int k = 0; k < nPoints - 1; ++k) {
596 const double t0 = ts[k];
597 const double t1 = ts[k + 1];
598 const double dt = t1 - t0;
599 if (dt < Small) continue;
600 const auto& x0 = xs[k];
601 const auto& x1 = xs[k + 1];
602 const auto& v = vs[k];
603 std::vector<double> td(nd);
604 for (unsigned int i = 0; i < nd; ++i) {
605 td[i] = t0 + m_delayedSignalTimes[i];
606 }
607 // Calculate the signals for each electrode.
608 for (auto& electrode : m_electrodes) {
609 const std::string lbl = electrode.label;
610 std::vector<double> id(nd, 0.);
611 for (unsigned int i = 0; i < nd; ++i) {
612 // Integrate over the drift line segment.
613 const double step = std::min(m_delayedSignalTimes[i], dt);
614 const double scale = step / dt;
615 const double dx = scale * (x1[0] - x0[0]);
616 const double dy = scale * (x1[1] - x0[1]);
617 const double dz = scale * (x1[2] - x0[2]);
618 double sum = 0.;
619 for (unsigned int j = 0; j < 6; ++j) {
620 const double f = 0.5 * (1. + tg[j]);
621 const double t = m_delayedSignalTimes[i] - f * step;
622 // Calculate the delayed weighting field.
623 double wx = 0., wy = 0., wz = 0.;
624 electrode.comp->DelayedWeightingField(x0[0] + f * dx,
625 x0[1] + f * dy,
626 x0[2] + f * dz,
627 t, wx, wy, wz, lbl);
628 sum += (wx * v[0] + wy * v[1] + wz * v[2]) * wg[j];
629 }
630 id[i] = -q * 0.5 * sum * step;
631 }
632 FillSignal(electrode, q, td, id, m_nAvgDelayedSignal, true);
633 }
634 }
635
636}
637
638void Sensor::FillSignal(Electrode& electrode, const double q,
639 const std::vector<double>& ts,
640 const std::vector<double>& is, const int navg,
641 const bool delayed) {
642
643 const bool electron = q < 0.;
644 // Interpolation order.
645 constexpr unsigned int k = 1;
646 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
647 const double t0 = m_tStart + i * m_tStep;
648 const double t1 = t0 + m_tStep;
649 if (ts.front() > t1) continue;
650 if (ts.back() < t0) break;
651 // Integration over this bin.
652 const double tmin = std::max(ts.front(), t0);
653 const double tmax = std::min(ts.back(), t1);
654 double sum = 0.;
655 if (navg <= 0) {
656 sum += (tmax - tmin) * Interpolate(is, ts, 0.5 * (tmin + tmax), k);
657 } else {
658 const double h = 0.5 * (tmax - tmin) / navg;
659 for (int j = -navg; j <= navg; ++j) {
660 const int jj = j + navg;
661 const double t = t0 + jj * h;
662 if (t < ts.front() || t > ts.back()) continue;
663 if (j == -navg || j == navg) {
664 sum += Interpolate(is, ts, t, k);
665 } else if (jj == 2 * (jj /2)) {
666 sum += 2 * Interpolate(is, ts, t, k);
667 } else {
668 sum += 4 * Interpolate(is, ts, t, k);
669 }
670 }
671 sum *= h / 3.;
672 }
673 // Add the result to the signal.
674 FillBin(electrode, i, sum, electron, delayed);
675 }
676}
677
678void Sensor::AddInducedCharge(const double q, const double x0, const double y0,
679 const double z0, const double x1, const double y1,
680 const double z1) {
681 if (m_debug) std::cout << m_className << "::AddInducedCharge:\n";
682 for (auto& electrode : m_electrodes) {
683 // Calculate the weighting potential at the starting point.
684 auto cmp = electrode.comp;
685 const double w0 = cmp->WeightingPotential(x0, y0, z0, electrode.label);
686 // Calculate the weighting potential at the end point.
687 const double w1 = cmp->WeightingPotential(x1, y1, z1, electrode.label);
688 electrode.charge += q * (w1 - w0);
689 if (m_debug) {
690 std::cout << " Electrode " << electrode.label << ":\n"
691 << " Weighting potential at (" << x0 << ", " << y0 << ", "
692 << z0 << "): " << w0 << "\n"
693 << " Weighting potential at (" << x1 << ", " << y1 << ", "
694 << z1 << "): " << w1 << "\n"
695 << " Induced charge: " << electrode.charge << "\n";
696 }
697 }
698}
699
700void Sensor::SetTimeWindow(const double tstart, const double tstep,
701 const unsigned int nsteps) {
702 m_tStart = tstart;
703 if (tstep <= 0.) {
704 std::cerr << m_className << "::SetTimeWindow: Start time out of range.\n";
705 } else {
706 m_tStep = tstep;
707 }
708
709 if (nsteps == 0) {
710 std::cerr << m_className << "::SetTimeWindow: Invalid number of bins.\n";
711 } else {
712 m_nTimeBins = nsteps;
713 }
714
715 if (m_debug) {
716 std::cout << m_className << "::SetTimeWindow: " << m_tStart
717 << " < t [ns] < " << m_tStart + m_nTimeBins * m_tStep << "\n"
718 << " Step size: " << m_tStep << " ns\n";
719 }
720
721 std::cout << m_className << "::SetTimeWindow: Resetting all signals.\n";
722 for (auto& electrode : m_electrodes) {
723 electrode.signal.assign(m_nTimeBins, 0.);
724 electrode.electronsignal.assign(m_nTimeBins, 0.);
725 electrode.ionsignal.assign(m_nTimeBins, 0.);
726 }
727 m_nEvents = 0;
728 // Reset the cached FFT of the transfer function
729 // because it depends on the number of time bins.
730 m_fTransferFFT.clear();
731}
732
733double Sensor::GetElectronSignal(const std::string& label,
734 const unsigned int bin) {
735 if (m_nEvents == 0) return 0.;
736 if (bin >= m_nTimeBins) return 0.;
737 double sig = 0.;
738 for (const auto& electrode : m_electrodes) {
739 if (electrode.label == label) sig += electrode.electronsignal[bin];
740 }
741 return ElementaryCharge * sig / (m_nEvents * m_tStep);
742}
743
744double Sensor::GetIonSignal(const std::string& label, const unsigned int bin) {
745 if (m_nEvents == 0) return 0.;
746 if (bin >= m_nTimeBins) return 0.;
747 double sig = 0.;
748 for (const auto& electrode : m_electrodes) {
749 if (electrode.label == label) sig += electrode.ionsignal[bin];
750 }
751 return ElementaryCharge * sig / (m_nEvents * m_tStep);
752}
753
754double Sensor::GetDelayedElectronSignal(const std::string& label,
755 const unsigned int bin) {
756 if (m_nEvents == 0) return 0.;
757 if (bin >= m_nTimeBins) return 0.;
758 double sig = 0.;
759 for (const auto& electrode : m_electrodes) {
760 if (electrode.label == label) sig += electrode.delayedElectronSignal[bin];
761 }
762 return ElementaryCharge * sig / (m_nEvents * m_tStep);
763}
764
765double Sensor::GetDelayedIonSignal(const std::string& label,
766 const unsigned int bin) {
767 if (m_nEvents == 0) return 0.;
768 if (bin >= m_nTimeBins) return 0.;
769 double sig = 0.;
770 for (const auto& electrode : m_electrodes) {
771 if (electrode.label == label) sig += electrode.delayedIonSignal[bin];
772 }
773 return ElementaryCharge * sig / (m_nEvents * m_tStep);
774}
775
776void Sensor::SetSignal(const std::string& label, const unsigned int bin,
777 const double signal) {
778 if (bin >= m_nTimeBins) return;
779 if (m_nEvents == 0) m_nEvents = 1;
780 for (auto& electrode : m_electrodes) {
781 if (electrode.label == label) {
782 electrode.signal[bin] = m_nEvents * m_tStep * signal / ElementaryCharge;
783 break;
784 }
785 }
786}
787
788double Sensor::GetSignal(const std::string& label, const unsigned int bin) {
789 if (m_nEvents == 0) return 0.;
790 if (bin >= m_nTimeBins) return 0.;
791 double sig = 0.;
792 for (const auto& electrode : m_electrodes) {
793 if (electrode.label == label) sig += electrode.signal[bin];
794 }
795 return ElementaryCharge * sig / (m_nEvents * m_tStep);
796}
797
798double Sensor::GetInducedCharge(const std::string& label) {
799 if (m_nEvents == 0) return 0.;
800 double charge = 0.;
801 for (const auto& electrode : m_electrodes) {
802 if (electrode.label == label) charge += electrode.charge;
803 }
804
805 return charge / m_nEvents;
806}
807
808void Sensor::SetTransferFunction(double (*f)(double t)) {
809 if (!f) {
810 std::cerr << m_className << "::SetTransferFunction: Null pointer.\n";
811 return;
812 }
813 m_fTransfer = f;
814 m_shaper = nullptr;
815 m_fTransferTab.clear();
816 m_fTransferSq = -1.;
817 m_fTransferFFT.clear();
818}
819
820void Sensor::SetTransferFunction(const std::vector<double>& times,
821 const std::vector<double>& values) {
822 if (times.empty() || values.empty()) {
823 std::cerr << m_className << "::SetTransferFunction: Empty vector.\n";
824 return;
825 } else if (times.size() != values.size()) {
826 std::cerr << m_className << "::SetTransferFunction:\n"
827 << " Time and value vectors must have same size.\n";
828 return;
829 }
830 const auto n = times.size();
831 m_fTransferTab.clear();
832 for (unsigned int i = 0; i < n; ++i) {
833 m_fTransferTab.emplace_back(std::make_pair(times[i], values[i]));
834 }
835 std::sort(m_fTransferTab.begin(), m_fTransferTab.end());
836 m_fTransfer = nullptr;
837 m_shaper = nullptr;
838 m_fTransferSq = -1.;
839 m_fTransferFFT.clear();
840}
841
843 m_shaper = &shaper;
844 m_fTransfer = nullptr;
845 m_fTransferTab.clear();
846 m_fTransferSq = -1.;
847 m_fTransferFFT.clear();
848}
849
850double Sensor::InterpolateTransferFunctionTable(const double t) const {
851 if (m_fTransferTab.empty()) return 0.;
852 // Don't extrapolate beyond the range defined in the table.
853 if (t < m_fTransferTab.front().first || t > m_fTransferTab.back().first) {
854 return 0.;
855 }
856 // Find the proper interval in the table.
857 const auto begin = m_fTransferTab.cbegin();
858 const auto it1 = std::upper_bound(begin, m_fTransferTab.cend(), std::make_pair(t, 0.));
859 if (it1 == begin) return m_fTransferTab.front().second;
860 const auto it0 = std::prev(it1);
861 const double t0 = (*it0).first;
862 const double t1 = (*it1).first;
863 const double f = t - t0 / (t1 - t0);
864 // Linear interpolation.
865 return (*it0).second * (1. - f) + (*it1).second * f;
866}
867
868double Sensor::GetTransferFunction(const double t) {
869 if (m_fTransfer) {
870 return m_fTransfer(t);
871 } else if (m_shaper) {
872 return m_shaper->Shape(t);
873 }
874 return InterpolateTransferFunctionTable(t);
875}
876
877bool Sensor::ConvoluteSignal(const bool fft) {
878 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
879 std::cerr << m_className << "::ConvoluteSignal: "
880 << "Transfer function not set.\n";
881 return false;
882 }
883 if (m_nEvents == 0) {
884 std::cerr << m_className << "::ConvoluteSignal: No signals present.\n";
885 return false;
886 }
887
888 if (fft) return ConvoluteSignalFFT();
889
890 // Set the range where the transfer function is valid.
891 constexpr double cnvMin = 0.;
892 constexpr double cnvMax = 1.e10;
893
894 std::vector<double> cnvTab(2 * m_nTimeBins - 1, 0.);
895 const unsigned int offset = m_nTimeBins - 1;
896 // Evaluate the transfer function.
897 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
898 // Negative time part.
899 double t = (-int(i)) * m_tStep;
900 if (t < cnvMin || t > cnvMax) {
901 cnvTab[offset - i] = 0.;
902 } else {
903 cnvTab[offset - i] = GetTransferFunction(t);
904 }
905 if (i == 0) continue;
906 // Positive time part.
907 t = i * m_tStep;
908 if (t < cnvMin || t > cnvMax) {
909 cnvTab[offset + i] = 0.;
910 } else {
911 cnvTab[offset + i] = GetTransferFunction(t);
912 }
913 }
914 std::vector<double> tmpSignal(m_nTimeBins, 0.);
915 // Loop over all electrodes.
916 for (auto& electrode : m_electrodes) {
917 // Do the convolution.
918 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
919 tmpSignal[j] = 0.;
920 for (unsigned int k = 0; k < m_nTimeBins; ++k) {
921 tmpSignal[j] += m_tStep * cnvTab[offset + j - k] * electrode.signal[k];
922 }
923 }
924 electrode.signal.swap(tmpSignal);
925 }
926 m_integrated = true;
927 return true;
928}
929
930bool Sensor::ConvoluteSignalFFT() {
931
932 // Number of bins must be a power of 2.
933 const unsigned int nn = exp2(ceil(log2(m_nTimeBins)));
934
935 if (!m_cacheTransferFunction || m_fTransferFFT.size() != 2 * (nn + 1)) {
936 // (Re-)compute the FFT of the transfer function.
937 m_fTransferFFT.assign(2 * (nn + 1), 0.);
938 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
939 m_fTransferFFT[2 * i + 1] = GetTransferFunction(i * m_tStep);
940 }
941 FFT(m_fTransferFFT, false, nn);
942 }
943
944 const double scale = m_tStep / nn;
945 for (auto& electrode : m_electrodes) {
946 std::vector<double> g(2 * (nn + 1), 0.);
947 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
948 g[2 * i + 1] = electrode.signal[i];
949 }
950 FFT(g, false, nn);
951 for (unsigned int i = 0; i < nn; ++i) {
952 const double fr = m_fTransferFFT[2 * i + 1];
953 const double fi = m_fTransferFFT[2 * i + 2];
954 const double gr = g[2 * i + 1];
955 const double gi = g[2 * i + 2];
956 g[2 * i + 1] = fr * gr - fi * gi;
957 g[2 * i + 2] = fr * gi + gr * fi;
958 }
959 FFT(g, true, nn);
960 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
961 electrode.signal[i] = scale * g[2 * i + 1];
962 }
963 }
964 m_integrated = true;
965 return true;
966}
967
969 if (m_nEvents == 0) {
970 std::cerr << m_className << "::IntegrateSignal: No signals present.\n";
971 return false;
972 }
973
974 for (auto& electrode : m_electrodes) {
975 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
976 electrode.signal[j] *= m_tStep;
977 electrode.electronsignal[j] *= m_tStep;
978 electrode.ionsignal[j] *= m_tStep;
979 if (j > 0) {
980 electrode.signal[j] += electrode.signal[j - 1];
981 electrode.electronsignal[j] += electrode.electronsignal[j - 1];
982 electrode.ionsignal[j] += electrode.ionsignal[j - 1];
983 }
984 }
985 }
986 m_integrated = true;
987 return true;
988}
989
990bool Sensor::DelayAndSubtractFraction(const double td, const double f) {
991
992 const int offset = int(td / m_tStep);
993 for (auto& electrode : m_electrodes) {
994 std::vector<double> signal1(m_nTimeBins, 0.);
995 std::vector<double> signal2(m_nTimeBins, 0.);
996 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
997 signal2[j] = f * electrode.signal[j];
998 const int bin = j - offset;
999 if (bin < 0 || bin >= (int)m_nTimeBins) continue;
1000 signal1[j] = electrode.signal[bin];
1001 }
1002 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1003 electrode.signal[j] = signal1[j] - signal2[j];
1004 }
1005 }
1006 return true;
1007}
1008
1009void Sensor::SetNoiseFunction(double (*f)(double t)) {
1010 if (!f) {
1011 std::cerr << m_className << "::SetNoiseFunction: Null pointer.\n";
1012 return;
1013 }
1014 m_fNoise = f;
1015}
1016
1017void Sensor::AddNoise(const bool total, const bool electron, const bool ion) {
1018 if (!m_fNoise) {
1019 std::cerr << m_className << "::AddNoise: Noise function not set.\n";
1020 return;
1021 }
1022 if (m_nEvents == 0) m_nEvents = 1;
1023
1024 for (auto& electrode : m_electrodes) {
1025 double t = m_tStart + 0.5 * m_tStep;
1026 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1027 const double noise = m_fNoise(t);
1028 if (total) electrode.signal[j] += noise;
1029 if (electron) electrode.electronsignal[j] += noise;
1030 if (ion) electrode.ionsignal[j] += noise;
1031 t += m_tStep;
1032 }
1033 }
1034}
1035
1036void Sensor::AddWhiteNoise(const double enc, const bool poisson,
1037 const double q0) {
1038
1039 if (!m_fTransfer && !m_shaper && m_fTransferTab.empty()) {
1040 std::cerr << m_className << "::AddWhiteNoise: Transfer function not set.\n";
1041 return;
1042 }
1043 if (m_nEvents == 0) m_nEvents = 1;
1044
1045 const double f2 = TransferFunctionSq();
1046 if (f2 < 0.) {
1047 std::cerr << m_className << "::AddWhiteNoise:\n"
1048 << " Could not calculate transfer function integral.\n";
1049 return;
1050 }
1051
1052 if (poisson) {
1053 // Frequency of random delta pulses to model noise.
1054 const double nu = (enc * enc / (q0 * q0)) / f2;
1055 // Average number of delta pulses.
1056 const double avg = nu * m_tStep * m_nTimeBins;
1057 // Sample the number of pulses.
1058 for (auto& electrode : m_electrodes) {
1059 const int nPulses = RndmPoisson(avg);
1060 for (int j = 0; j < nPulses; ++j) {
1061 const int bin = static_cast<int>(m_nTimeBins * RndmUniform());
1062 electrode.signal[bin] += q0;
1063 }
1064 const double offset = q0 * nu * m_tStep;
1065 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1066 electrode.signal[j] -= offset;
1067 }
1068 }
1069 } else {
1070 // Gaussian approximation.
1071 const double sigma = enc * sqrt(m_tStep / f2);
1072 for (auto& electrode : m_electrodes) {
1073 for (unsigned int j = 0; j < m_nTimeBins; ++j) {
1074 electrode.signal[j] += RndmGaussian(0., sigma);
1075 }
1076 }
1077 }
1078}
1079
1080double Sensor::TransferFunctionSq() {
1081
1082 if (m_fTransferSq >= 0.) return m_fTransferSq;
1083 double integral = -1.;
1084 if (m_fTransfer) {
1085 std::function<double(double)> fsq = [this](double x) {
1086 const double y = m_fTransfer(x);
1087 return y * y;
1088 };
1089 constexpr double epsrel = 1.e-8;
1090 double err = 0.;
1091 unsigned int stat = 0;
1092 Numerics::QUADPACK::qagi(fsq, 0., 1, 0., epsrel, integral, err, stat);
1093 } else if (m_shaper) {
1094 integral = m_shaper->TransferFuncSq();
1095 } else {
1096 integral = Trapezoid2(m_fTransferTab);
1097 }
1098 if (m_cacheTransferFunction) m_fTransferSq = integral;
1099 return integral;
1100}
1101
1103 const std::string& label, int& n) {
1104 // Reset the list of threshold crossings.
1105 m_thresholdCrossings.clear();
1106 m_thresholdLevel = thr;
1107
1108 // Set the interpolation order.
1109 int iOrder = 1;
1110
1111 if (m_nEvents == 0) {
1112 std::cerr << m_className << "::ComputeThresholdCrossings: "
1113 << "No signals present.\n";
1114 return false;
1115 }
1116 if (!m_integrated) {
1117 std::cerr << m_className << "::ComputeThresholdCrossings:\n "
1118 << "Warning: signal has not been integrated/convoluted.\n";
1119 }
1120 // Compute the total signal.
1121 std::vector<double> signal(m_nTimeBins, 0.);
1122 // Loop over the electrodes.
1123 bool foundLabel = false;
1124 for (const auto& electrode : m_electrodes) {
1125 if (electrode.label == label) {
1126 foundLabel = true;
1127 for (unsigned int i = 0; i < m_nTimeBins; ++i) {
1128 signal[i] += electrode.signal[i];
1129 }
1130 }
1131 }
1132 if (!foundLabel) {
1133 std::cerr << m_className << "::ComputeThresholdCrossings: Electrode "
1134 << label << " not found.\n";
1135 return false;
1136 }
1137 const double scale = ElementaryCharge / (m_nEvents * m_tStep);
1138 for (unsigned int i = 0; i < m_nTimeBins; ++i) signal[i] *= scale;
1139
1140 // Establish the range.
1141 const double vMin = *std::min_element(std::begin(signal), std::end(signal));
1142 const double vMax = *std::max_element(std::begin(signal), std::end(signal));
1143 if (m_debug) std::cout << m_className << "::ComputeThresholdCrossings:\n";
1144 if (thr < vMin && thr > vMax) {
1145 if (m_debug) {
1146 std::cout << " Threshold outside the range [" << vMin << ", "
1147 << vMax << "]\n";
1148 }
1149 return true;
1150 }
1151
1152 // Check both rising and falling edges.
1153 constexpr std::array<int, 2> directions = {1, -1};
1154 for (const auto dir : directions) {
1155 const bool up = dir > 0;
1156 if (m_debug) {
1157 if (up) {
1158 std::cout << " Hunting for rising edges.\n";
1159 } else {
1160 std::cout << " Hunting for falling edges.\n";
1161 }
1162 }
1163 // Initialise the vectors.
1164 std::vector<double> ts = {m_tStart + 0.5 * m_tStep};
1165 std::vector<double> vs = {signal[0]};
1166 // Scan the signal.
1167 for (unsigned int i = 1; i < m_nTimeBins; ++i) {
1168 // Compute the vector element.
1169 const double tNew = m_tStart + (i + 0.5) * m_tStep;
1170 const double vNew = signal[i];
1171 // If still increasing or decreasing, add to the vector.
1172 if ((up && vNew > vs.back()) || (!up && vNew < vs.back())) {
1173 ts.push_back(tNew);
1174 vs.push_back(vNew);
1175 continue;
1176 }
1177 // Otherwise see whether we crossed the threshold level.
1178 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1179 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1180 // Compute the crossing time.
1181 double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1182 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1183 ts = {tNew};
1184 vs = {vNew};
1185 } else {
1186 // No crossing, simply reset the vector.
1187 ts = {tNew};
1188 vs = {vNew};
1189 }
1190 }
1191 // Check the final vector.
1192 if ((vs[0] - thr) * (thr - vs.back()) >= 0. && ts.size() > 1 &&
1193 ((up && vs.back() > vs[0]) || (!up && vs.back() < vs[0]))) {
1194 const double tcr = Numerics::Divdif(ts, vs, ts.size(), thr, iOrder);
1195 m_thresholdCrossings.emplace_back(std::make_pair(tcr, up));
1196 }
1197 }
1198 n = m_thresholdCrossings.size();
1199
1200 if (m_debug) {
1201 std::cout << " Found " << n << " crossings.\n";
1202 if (n > 0) std::cout << " Time [ns] Direction\n";
1203 for (const auto& crossing : m_thresholdCrossings) {
1204 std::cout << " " << crossing.first << " ";
1205 if (crossing.second) {
1206 std::cout << "rising\n";
1207 } else {
1208 std::cout << "falling\n";
1209 }
1210 }
1211 }
1212 // Seems to have worked.
1213 return true;
1214}
1215
1216bool Sensor::GetThresholdCrossing(const unsigned int i, double& time,
1217 double& level, bool& rise) const {
1218 level = m_thresholdLevel;
1219
1220 if (i >= m_thresholdCrossings.size()) {
1221 std::cerr << m_className << "::GetThresholdCrossing: Index out of range.\n";
1222 time = m_tStart + m_nTimeBins * m_tStep;
1223 return false;
1224 }
1225
1226 time = m_thresholdCrossings[i].first;
1227 rise = m_thresholdCrossings[i].second;
1228 return true;
1229}
1230
1231bool Sensor::GetBoundingBox(double& xmin, double& ymin, double& zmin,
1232 double& xmax, double& ymax, double& zmax) {
1233 // We don't know the range yet
1234 bool set = false;
1235 // Loop over the fields
1236 double x0, y0, z0, x1, y1, z1;
1237 for (auto component : m_components) {
1238 if (!component->GetBoundingBox(x0, y0, z0, x1, y1, z1)) continue;
1239 if (set) {
1240 if (x0 < xmin) xmin = x0;
1241 if (y0 < ymin) ymin = y0;
1242 if (z0 < zmin) zmin = z0;
1243 if (x1 > xmax) xmax = x1;
1244 if (y1 > ymax) ymax = y1;
1245 if (z1 > zmax) zmax = z1;
1246 } else {
1247 xmin = x0;
1248 ymin = y0;
1249 zmin = z0;
1250 xmax = x1;
1251 ymax = y1;
1252 zmax = z1;
1253 set = true;
1254 }
1255 }
1256
1257 // Warn if we still don't know the range
1258 if (!set) {
1259 std::cerr << m_className << "::GetBoundingBox:\n"
1260 << " Sensor bounding box not known.\n";
1261 xmin = ymin = zmin = 0.;
1262 xmax = ymax = zmax = 0.;
1263 return false;
1264 }
1265
1266 if (m_debug) {
1267 std::cout << m_className << "::GetBoundingBox:\n"
1268 << " " << xmin << " < x [cm] < " << xmax << "\n"
1269 << " " << ymin << " < y [cm] < " << ymax << "\n"
1270 << " " << zmin << " < z [cm] < " << zmax << "\n";
1271 }
1272 return true;
1273}
1274
1275void Sensor::FFT(std::vector<double>& data, const bool inverse,
1276 const int nn) {
1277
1278 // Replaces data[1..2*nn] by its discrete fourier transform
1279 // or replaces data[1..2*nn] by nn times its inverse discrete
1280 // fourier transform.
1281 // nn MUST be an integer power of 2 (this is not checked for!).
1282
1283 const int n = 2 * nn;
1284 // Bit reversal.
1285 int j = 1;
1286 for (int i = 1; i < n; i += 2) {
1287 if (j > i) {
1288 // Exchange the two complex numbers.
1289 std::swap(data[j],data[i]);
1290 std::swap(data[j+1],data[i+1]);
1291 }
1292 int m = nn;
1293 while (m >= 2 && j > m) {
1294 j -= m;
1295 m >>= 1;
1296 }
1297 j += m;
1298 }
1299
1300 const int isign = inverse ? -1 : 1;
1301 int mmax = 2;
1302 while (n > mmax) {
1303 const int step = 2 * mmax;
1304 const double theta = isign * TwoPi / mmax;
1305 double wtemp = sin(0.5 * theta);
1306 const double wpr = -2. * wtemp * wtemp;
1307 const double wpi = sin(theta);
1308 double wr = 1.;
1309 double wi = 0.;
1310 for (int m = 1; m < mmax; m += 2) {
1311 for (int i = m; i <= n;i += step) {
1312 j = i + mmax;
1313 double tr = wr * data[j] - wi * data[j + 1];
1314 double ti = wr * data[j + 1] + wi * data[j];
1315 data[j] = data[i] - tr;
1316 data[j + 1] = data[i + 1] - ti;
1317 data[i] += tr;
1318 data[i + 1] += ti;
1319 }
1320 wr = (wtemp = wr) * wpr - wi * wpi + wr;
1321 wi = wi * wpr + wtemp * wpi + wi;
1322 }
1323 mmax = step;
1324 }
1325}
1326
1327}
Abstract base class for components.
virtual Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).
Abstract base class for media.
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:124
double GetElectronSignal(const std::string &label, const unsigned int bin)
Retrieve the electron signal for a given electrode and time bin.
Definition: Sensor.cc:733
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
Definition: Sensor.cc:355
void AddWhiteNoise(const double enc, const bool poisson=true, const double q0=1.)
Definition: Sensor.cc:1036
void SetDelayedSignalTimes(const std::vector< double > &ts)
Set the points in time at which to evaluate the delayed weighting field.
Definition: Sensor.cc:400
void SetTimeWindow(const double tstart, const double tstep, const unsigned int nsteps)
Definition: Sensor.cc:700
bool IntegrateSignal()
Replace the current signal curve by its integral.
Definition: Sensor.cc:968
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:776
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:765
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
Definition: Sensor.cc:293
bool DelayAndSubtractFraction(const double td, const double f)
Definition: Sensor.cc:990
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:138
double GetTransferFunction(const double t)
Evaluate the transfer function at a given time.
Definition: Sensor.cc:868
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:75
bool SetArea()
Set the user area to the default.
Definition: Sensor.cc:189
double GetSignal(const std::string &label, const unsigned int bin)
Retrieve the total signal for a given electrode and time bin.
Definition: Sensor.cc:788
void SetNoiseFunction(double(*f)(double t))
Set the function to be used for evaluating the noise component.
Definition: Sensor.cc:1009
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition: Sensor.cc:258
void AddElectrode(ComponentBase *comp, const std::string &label)
Add an electrode.
Definition: Sensor.cc:310
double GetInducedCharge(const std::string &label)
Definition: Sensor.cc:798
void AddNoise(const bool total=true, const bool electron=false, const bool ion=false)
Add noise to the induced signal.
Definition: Sensor.cc:1017
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:154
bool ComputeThresholdCrossings(const double thr, const std::string &label, int &n)
Definition: Sensor.cc:1102
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:166
void Clear()
Remove all components, electrodes and reset the sensor.
Definition: Sensor.cc:339
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:409
bool ConvoluteSignal(const bool fft=false)
Convolute the induced current with the transfer function.
Definition: Sensor.cc:877
void SetTransferFunction(double(*f)(double t))
Set the function to be used for evaluating the transfer function.
Definition: Sensor.cc:808
bool GetThresholdCrossing(const unsigned int i, double &time, double &level, bool &rise) const
Definition: Sensor.cc:1216
void AddComponent(ComponentBase *comp)
Add a component.
Definition: Sensor.cc:301
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:754
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:280
void ClearSignal()
Reset signals and induced charges of all electrodes.
Definition: Sensor.cc:387
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:678
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:744
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:232
ComponentBase * GetComponent(const unsigned int i)
Retrieve the pointer to a given component.
Definition: Sensor.cc:67
Class for signal processing.
Definition: Shaper.hh:11
double TransferFuncSq() const
Return the integral of the transfer function squared.
Definition: Shaper.hh:31
double Shape(const double t) const
Evaluate the transfer function.
Definition: Shaper.cc:50
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