Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Garfield::Sensor Class Reference

Sensor More...

#include <Sensor.hh>

Public Member Functions

 Sensor ()=default
 Constructor.
 
 ~Sensor ()
 Destructor.
 
void AddComponent (ComponentBase *comp)
 Add a component.
 
unsigned int GetNumberOfComponents () const
 Get the number of components attached to the sensor.
 
ComponentBaseGetComponent (const unsigned int i)
 Retrieve the pointer to a given component.
 
void AddElectrode (ComponentBase *comp, const std::string &label)
 Add an electrode.
 
unsigned int GetNumberOfElectrodes () const
 Get the number of electrodes attached to the sensor.
 
void Clear ()
 Remove all components, electrodes and reset the sensor.
 
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).
 
void ElectricField (const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&medium, int &status)
 Get the drift field at (x, y, z).
 
void MagneticField (const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
 Get the magnetic field at (x, y, z).
 
void WeightingField (const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
 Get the weighting field at (x, y, z).
 
double WeightingPotential (const double x, const double y, const double z, const std::string &label)
 Get the weighting potential at (x, y, z).
 
bool GetMedium (const double x, const double y, const double z, Medium *&medium)
 Get the medium at (x, y, z).
 
void EnableDebugging (const bool on=true)
 Switch debugging messages on/off.
 
bool SetArea ()
 Set the user area to the default.
 
bool SetArea (const double xmin, const double ymin, const double zmin, const double xmax, const double ymax, const double zmax)
 Set the user area explicitly.
 
bool GetArea (double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
 Return the current user area.
 
bool IsInArea (const double x, const double y, const double z)
 Check if a point is inside the user area.
 
bool GetVoltageRange (double &vmin, double &vmax)
 Return the voltage range.
 
void NewSignal ()
 Start a new event, when computing the average signal over multiple events.
 
void ClearSignal ()
 Reset signals and induced charges of all electrodes.
 
void SetTimeWindow (const double tstart, const double tstep, const unsigned int nsteps)
 
void GetTimeWindow (double &tstart, double &tstep, unsigned int &nsteps) const
 Retrieve the time window and binning.
 
void EnableDelayedSignal (const bool on=true)
 Compute the component of the signal due to the delayed weighting field.
 
void SetDelayedSignalTimes (const std::vector< double > &ts)
 Set the points in time at which to evaluate the delayed weighting field.
 
void SetDelayedSignalAveragingOrder (const unsigned int navg)
 
void SetSignal (const std::string &label, const unsigned int bin, const double signal)
 Set/override the signal in a given time bin explicitly.
 
double GetSignal (const std::string &label, const unsigned int bin)
 Retrieve the total signal for a given electrode and time bin.
 
double GetElectronSignal (const std::string &label, const unsigned int bin)
 Retrieve the electron signal for a given electrode and time bin.
 
double GetIonSignal (const std::string &label, const unsigned int bin)
 Retrieve the ion or hole signal for a given electrode and time bin.
 
double GetDelayedElectronSignal (const std::string &label, const unsigned int bin)
 Retrieve the delayed electron signal for a given electrode and time bin.
 
double GetDelayedIonSignal (const std::string &label, const unsigned int bin)
 Retrieve the delayed ion/hole signal for a given electrode and time bin.
 
double GetInducedCharge (const std::string &label)
 
void SetTransferFunction (double(*f)(double t))
 Set the function to be used for evaluating the transfer function.
 
void SetTransferFunction (const std::vector< double > &times, const std::vector< double > &values)
 Set the points to be used for interpolating the transfer function.
 
void SetTransferFunction (Shaper &shaper)
 Set the transfer function using a Shaper object.
 
double GetTransferFunction (const double t)
 Evaluate the transfer function at a given time.
 
void EnableTransferFunctionCache (const bool on=true)
 
bool ConvoluteSignal (const bool fft=false)
 Convolute the induced current with the transfer function.
 
bool IntegrateSignal ()
 Replace the current signal curve by its integral.
 
bool IsIntegrated () const
 Return whether the signal has been integrated/convoluted.
 
bool DelayAndSubtractFraction (const double td, const double f)
 
void SetNoiseFunction (double(*f)(double t))
 Set the function to be used for evaluating the noise component.
 
void AddNoise (const bool total=true, const bool electron=false, const bool ion=false)
 Add noise to the induced signal.
 
void AddWhiteNoise (const double enc, const bool poisson=true, const double q0=1.)
 
bool ComputeThresholdCrossings (const double thr, const std::string &label, int &n)
 
unsigned int GetNumberOfThresholdCrossings () const
 
bool GetThresholdCrossing (const unsigned int i, double &time, double &level, bool &rise) const
 
void AddSignal (const double q, const double t0, const double t1, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const bool integrateWeightingField, const bool useWeightingPotential=false)
 Add the signal from a charge-carrier step.
 
void AddSignal (const double q, const std::vector< double > &ts, const std::vector< std::array< double, 3 > > &xs, const std::vector< std::array< double, 3 > > &vs, const std::vector< double > &ns, const int navg)
 Add the signal from a drift line.
 
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.
 
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)
 
bool IsInTrapRadius (const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
 

Detailed Description

Sensor

Definition at line 14 of file Sensor.hh.

Constructor & Destructor Documentation

◆ Sensor()

Garfield::Sensor::Sensor ( )
default

Constructor.

◆ ~Sensor()

Garfield::Sensor::~Sensor ( )
inline

Destructor.

Definition at line 19 of file Sensor.hh.

19{}

Member Function Documentation

◆ AddComponent()

void Garfield::Sensor::AddComponent ( ComponentBase comp)

Add a component.

Definition at line 301 of file Sensor.cc.

301 {
302 if (!comp) {
303 std::cerr << m_className << "::AddComponent: Null pointer.\n";
304 return;
305 }
306
307 m_components.push_back(comp);
308}

Referenced by GarfieldPhysics::CreateGeometry(), and main().

◆ AddElectrode()

void Garfield::Sensor::AddElectrode ( ComponentBase comp,
const std::string &  label 
)

Add an electrode.

Definition at line 310 of file Sensor.cc.

310 {
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}
void ClearSignal()
Reset signals and induced charges of all electrodes.
Definition: Sensor.cc:387

◆ AddInducedCharge()

void Garfield::Sensor::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 at line 678 of file Sensor.cc.

680 {
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}

◆ AddNoise()

void Garfield::Sensor::AddNoise ( const bool  total = true,
const bool  electron = false,
const bool  ion = false 
)

Add noise to the induced signal.

Definition at line 1017 of file Sensor.cc.

1017 {
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}

◆ AddSignal() [1/2]

void Garfield::Sensor::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 at line 409 of file Sensor.cc.

413 {
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}

◆ AddSignal() [2/2]

void Garfield::Sensor::AddSignal ( const double  q,
const std::vector< double > &  ts,
const std::vector< std::array< double, 3 > > &  xs,
const std::vector< std::array< double, 3 > > &  vs,
const std::vector< double > &  ns,
const int  navg 
)

Add the signal from a drift line.

Definition at line 550 of file Sensor.cc.

553 {
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}

◆ AddWhiteNoise()

void Garfield::Sensor::AddWhiteNoise ( const double  enc,
const bool  poisson = true,
const double  q0 = 1. 
)

Add white noise to the induced signal, given a desired output ENC.

Parameters
encEquivalent Noise Charge, in electrons.
poissonflag to sample the number of noise pulses from a Poisson distribution. Otherwise the noise charge in each bin is sampled from a Gaussian distribution.
q0amplitude of the noise delta pulses (in electrons).

Definition at line 1036 of file Sensor.cc.

1037 {
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}
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 sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314

◆ Clear()

void Garfield::Sensor::Clear ( )

Remove all components, electrodes and reset the sensor.

Definition at line 339 of file Sensor.cc.

339 {
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}

◆ ClearSignal()

void Garfield::Sensor::ClearSignal ( )

Reset signals and induced charges of all electrodes.

Definition at line 387 of file Sensor.cc.

387 {
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}

Referenced by AddElectrode().

◆ ComputeThresholdCrossings()

bool Garfield::Sensor::ComputeThresholdCrossings ( const double  thr,
const std::string &  label,
int &  n 
)

Determine the threshold crossings of the current signal curve.

Parameters
thrthreshold value
labelelectrode for which to compute the threshold crossings
nnumber of threshold crossings

Definition at line 1102 of file Sensor.cc.

1103 {
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}
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
Definition: Numerics.cc:1206

◆ ConvoluteSignal()

bool Garfield::Sensor::ConvoluteSignal ( const bool  fft = false)

Convolute the induced current with the transfer function.

Definition at line 877 of file Sensor.cc.

877 {
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}
double GetTransferFunction(const double t)
Evaluate the transfer function at a given time.
Definition: Sensor.cc:868

◆ DelayAndSubtractFraction()

bool Garfield::Sensor::DelayAndSubtractFraction ( const double  td,
const double  f 
)

Delay the signal and subtract an attenuated copy (modelling a constant fraction discriminator).

\[
  v_{out} = v_{in}\left(t - t_d\right) - f v_{in}.
\]

Definition at line 990 of file Sensor.cc.

990 {
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}

◆ ElectricField() [1/2]

void Garfield::Sensor::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 at line 75 of file Sensor.cc.

77 {
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}

Referenced by Garfield::ViewField::Evaluate2D(), Garfield::ViewField::EvaluateProfile(), and Heed::HeedFieldMap::field_map().

◆ ElectricField() [2/2]

void Garfield::Sensor::ElectricField ( const double  x,
const double  y,
const double  z,
double &  ex,
double &  ey,
double &  ez,
Medium *&  medium,
int &  status 
)

Get the drift field at (x, y, z).

Definition at line 100 of file Sensor.cc.

102 {
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}

◆ EnableDebugging()

void Garfield::Sensor::EnableDebugging ( const bool  on = true)
inline

Switch debugging messages on/off.

Definition at line 60 of file Sensor.hh.

60{ m_debug = on; }

◆ EnableDelayedSignal()

void Garfield::Sensor::EnableDelayedSignal ( const bool  on = true)
inline

Compute the component of the signal due to the delayed weighting field.

Definition at line 97 of file Sensor.hh.

97{ m_delayedSignal = on; }

◆ EnableTransferFunctionCache()

void Garfield::Sensor::EnableTransferFunctionCache ( const bool  on = true)
inline

Cache integral and FFT of the transfer function instead of recomputing it at every call (default: on).

Definition at line 135 of file Sensor.hh.

135 {
136 m_cacheTransferFunction = on;
137 }

◆ GetArea()

bool Garfield::Sensor::GetArea ( double &  xmin,
double &  ymin,
double &  zmin,
double &  xmax,
double &  ymax,
double &  zmax 
)

Return the current user area.

Definition at line 232 of file Sensor.cc.

233 {
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}
bool SetArea()
Set the user area to the default.
Definition: Sensor.cc:189

Referenced by Garfield::TrackSrim::NewTrack().

◆ GetComponent()

ComponentBase * Garfield::Sensor::GetComponent ( const unsigned int  i)

Retrieve the pointer to a given component.

Definition at line 67 of file Sensor.cc.

67 {
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}

◆ GetDelayedElectronSignal()

double Garfield::Sensor::GetDelayedElectronSignal ( const std::string &  label,
const unsigned int  bin 
)

Retrieve the delayed electron signal for a given electrode and time bin.

Definition at line 754 of file Sensor.cc.

755 {
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}

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetDelayedIonSignal()

double Garfield::Sensor::GetDelayedIonSignal ( const std::string &  label,
const unsigned int  bin 
)

Retrieve the delayed ion/hole signal for a given electrode and time bin.

Definition at line 765 of file Sensor.cc.

766 {
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}

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetElectronSignal()

double Garfield::Sensor::GetElectronSignal ( const std::string &  label,
const unsigned int  bin 
)

Retrieve the electron signal for a given electrode and time bin.

Definition at line 733 of file Sensor.cc.

734 {
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}

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetInducedCharge()

double Garfield::Sensor::GetInducedCharge ( const std::string &  label)

Retrieve the total induced charge for a given electrode, calculated using the weighting potentials at the start and end points.

Definition at line 798 of file Sensor.cc.

798 {
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}

◆ GetIonSignal()

double Garfield::Sensor::GetIonSignal ( const std::string &  label,
const unsigned int  bin 
)

Retrieve the ion or hole signal for a given electrode and time bin.

Definition at line 744 of file Sensor.cc.

744 {
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}

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetMedium()

bool Garfield::Sensor::GetMedium ( const double  x,
const double  y,
const double  z,
Medium *&  medium 
)

Get the medium at (x, y, z).

Definition at line 166 of file Sensor.cc.

167 {
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}
virtual Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).

Referenced by Garfield::TrackBichsel::GetCluster(), Garfield::TrackSimple::GetCluster(), Garfield::TrackElectron::GetCluster(), Garfield::TrackPAI::GetCluster(), Heed::HeedFieldMap::inside(), Garfield::TrackBichsel::NewTrack(), Garfield::TrackElectron::NewTrack(), Garfield::TrackPAI::NewTrack(), Garfield::TrackSimple::NewTrack(), Garfield::TrackHeed::NewTrack(), Garfield::TrackSrim::NewTrack(), Garfield::TrackHeed::TransportDeltaElectron(), and Garfield::TrackHeed::TransportPhoton().

◆ GetNumberOfComponents()

unsigned int Garfield::Sensor::GetNumberOfComponents ( ) const
inline

Get the number of components attached to the sensor.

Definition at line 24 of file Sensor.hh.

24{ return m_components.size(); }

◆ GetNumberOfElectrodes()

unsigned int Garfield::Sensor::GetNumberOfElectrodes ( ) const
inline

Get the number of electrodes attached to the sensor.

Definition at line 31 of file Sensor.hh.

31{ return m_electrodes.size(); }

◆ GetNumberOfThresholdCrossings()

unsigned int Garfield::Sensor::GetNumberOfThresholdCrossings ( ) const
inline

Get the number of threshold crossings (after having called ComputeThresholdCrossings).

Definition at line 174 of file Sensor.hh.

174 {
175 return m_thresholdCrossings.size();
176 }

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetSignal()

double Garfield::Sensor::GetSignal ( const std::string &  label,
const unsigned int  bin 
)

Retrieve the total signal for a given electrode and time bin.

Definition at line 788 of file Sensor.cc.

788 {
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}

Referenced by main(), and Garfield::ViewSignal::PlotSignal().

◆ GetThresholdCrossing()

bool Garfield::Sensor::GetThresholdCrossing ( const unsigned int  i,
double &  time,
double &  level,
bool &  rise 
) const

Retrieve the time and type of a given threshold crossing (after having called ComputeThresholdCrossings.

Parameters
iindex
timethreshold crossing time [ns]
levelthreshold (should correspond to the value requested).
riseflag whether the crossing is on a rising or falling slope.

Definition at line 1216 of file Sensor.cc.

1217 {
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}

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetTimeWindow()

void Garfield::Sensor::GetTimeWindow ( double &  tstart,
double &  tstep,
unsigned int &  nsteps 
) const
inline

Retrieve the time window and binning.

Definition at line 89 of file Sensor.hh.

90 {
91 tstart = m_tStart;
92 tstep = m_tStep;
93 nsteps = m_nTimeBins;
94 }

Referenced by Garfield::ViewSignal::PlotSignal().

◆ GetTransferFunction()

double Garfield::Sensor::GetTransferFunction ( const double  t)

Evaluate the transfer function at a given time.

Definition at line 868 of file Sensor.cc.

868 {
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}
double Shape(const double t) const
Evaluate the transfer function.
Definition: Shaper.cc:50

Referenced by ConvoluteSignal().

◆ GetVoltageRange()

bool Garfield::Sensor::GetVoltageRange ( double &  vmin,
double &  vmax 
)

Return the voltage range.

Definition at line 355 of file Sensor.cc.

355 {
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}

◆ IntegrateSignal()

bool Garfield::Sensor::IntegrateSignal ( )

Replace the current signal curve by its integral.

Definition at line 968 of file Sensor.cc.

968 {
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}

◆ IsInArea()

bool Garfield::Sensor::IsInArea ( const double  x,
const double  y,
const double  z 
)

Check if a point is inside the user area.

Definition at line 258 of file Sensor.cc.

258 {
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}

Referenced by Garfield::TrackElectron::GetCluster(), Garfield::TrackPAI::GetCluster(), Heed::HeedFieldMap::inside(), and Garfield::TrackSrim::NewTrack().

◆ IsIntegrated()

bool Garfield::Sensor::IsIntegrated ( ) const
inline

Return whether the signal has been integrated/convoluted.

Definition at line 143 of file Sensor.hh.

143{ return m_integrated; }

Referenced by Garfield::ViewSignal::PlotSignal().

◆ IsInTrapRadius()

bool Garfield::Sensor::IsInTrapRadius ( const double  q0,
const double  x0,
const double  y0,
const double  z0,
double &  xw,
double &  yw,
double &  rw 
)

Definition at line 293 of file Sensor.cc.

294 {
295 for (auto component : m_components) {
296 if (component->IsInTrapRadius(q0, x0, y0, z0, xw, yw, rw)) return true;
297 }
298 return false;
299}

◆ IsWireCrossed()

bool Garfield::Sensor::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 
)

Determine whether a line between two points has crossed a wire, calls ComponentBase::IsWireCrossed.

Definition at line 280 of file Sensor.cc.

283 {
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}

◆ MagneticField()

void Garfield::Sensor::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 at line 124 of file Sensor.cc.

125 {
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}

Referenced by Heed::HeedFieldMap::field_map().

◆ NewSignal()

void Garfield::Sensor::NewSignal ( )
inline

Start a new event, when computing the average signal over multiple events.

Definition at line 77 of file Sensor.hh.

77{ ++m_nEvents; }

◆ SetArea() [1/2]

bool Garfield::Sensor::SetArea ( )

Set the user area to the default.

Definition at line 189 of file Sensor.cc.

189 {
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}

Referenced by GetArea(), IsInArea(), and main().

◆ SetArea() [2/2]

bool Garfield::Sensor::SetArea ( const double  xmin,
const double  ymin,
const double  zmin,
const double  xmax,
const double  ymax,
const double  zmax 
)

Set the user area explicitly.

Definition at line 213 of file Sensor.cc.

214 {
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}
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615

◆ SetDelayedSignalAveragingOrder()

void Garfield::Sensor::SetDelayedSignalAveragingOrder ( const unsigned int  navg)
inline

Set the number of points to be used when averaging the delayed signal vector over a time bin (default: 0). The averaging is done with a $2\times navg + 1$ point Newton-Raphson integration.

Definition at line 104 of file Sensor.hh.

104 {
105 m_nAvgDelayedSignal = navg;
106 }

◆ SetDelayedSignalTimes()

void Garfield::Sensor::SetDelayedSignalTimes ( const std::vector< double > &  ts)

Set the points in time at which to evaluate the delayed weighting field.

Definition at line 400 of file Sensor.cc.

400 {
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}

◆ SetNoiseFunction()

void Garfield::Sensor::SetNoiseFunction ( double(*)(double t)  f)

Set the function to be used for evaluating the noise component.

Definition at line 1009 of file Sensor.cc.

1009 {
1010 if (!f) {
1011 std::cerr << m_className << "::SetNoiseFunction: Null pointer.\n";
1012 return;
1013 }
1014 m_fNoise = f;
1015}

◆ SetSignal()

void Garfield::Sensor::SetSignal ( const std::string &  label,
const unsigned int  bin,
const double  signal 
)

Set/override the signal in a given time bin explicitly.

Definition at line 776 of file Sensor.cc.

777 {
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}

◆ SetTimeWindow()

void Garfield::Sensor::SetTimeWindow ( const double  tstart,
const double  tstep,
const unsigned int  nsteps 
)

Set the time window and binning for the signal calculation.

Parameters
tstartstart time [ns]
tstepbin width [ns]
nstepsnumber of bins

Definition at line 700 of file Sensor.cc.

701 {
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}

◆ SetTransferFunction() [1/3]

void Garfield::Sensor::SetTransferFunction ( const std::vector< double > &  times,
const std::vector< double > &  values 
)

Set the points to be used for interpolating the transfer function.

Definition at line 820 of file Sensor.cc.

821 {
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}

◆ SetTransferFunction() [2/3]

void Garfield::Sensor::SetTransferFunction ( double(*)(double t)  f)

Set the function to be used for evaluating the transfer function.

Definition at line 808 of file Sensor.cc.

808 {
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}

◆ SetTransferFunction() [3/3]

void Garfield::Sensor::SetTransferFunction ( Shaper shaper)

Set the transfer function using a Shaper object.

Definition at line 842 of file Sensor.cc.

842 {
843 m_shaper = &shaper;
844 m_fTransfer = nullptr;
845 m_fTransferTab.clear();
846 m_fTransferSq = -1.;
847 m_fTransferFFT.clear();
848}

◆ WeightingField()

void Garfield::Sensor::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 at line 138 of file Sensor.cc.

140 {
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}

Referenced by Garfield::ViewField::Evaluate2D(), and Garfield::ViewField::EvaluateProfile().

◆ WeightingPotential()

double Garfield::Sensor::WeightingPotential ( const double  x,
const double  y,
const double  z,
const std::string &  label 
)

Get the weighting potential at (x, y, z).

Definition at line 154 of file Sensor.cc.

155 {
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}

Referenced by Garfield::ViewField::Evaluate2D(), and Garfield::ViewField::EvaluateProfile().


The documentation for this class was generated from the following files: