ATLAS Offline Software
HGTD_TimingResolution.cxx
Go to the documentation of this file.
1 
11 #include "HGTD_TimingResolution.h"
12 
13 #include <cmath>
14 #include <iostream>
15 
16 #include "CLHEP/Random/RandGaussZiggurat.h"
17 #include "TMath.h"
18 
19 namespace {
20 inline float remainder_1(float a, int b) {
21  float remainderValue = std::fmod(a, b);
22  if (remainderValue != 0. || a == 0.) {
23  return remainderValue;
24  } else {
25  return static_cast<float>(b);
26  }
27 }
28 
29 inline float exp16(float x) {
30  x = 1.0 + x / 16.0;
31  return std::pow(x, 16);
32 }
33 } // namespace
34 
36  const std::string &name,
37  const IInterface *parent)
39  m_version("HGTD Timing three-ring layout"),
40 
41  // resolution in ns
42  m_electronicJitter(std::sqrt((0.025 * 0.025) - (0.010 * 0.010))),
43 
44  // Contain also the peripheral electronics region, i.e. (R>640 mm)
45  m_radii{120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230,
46  240, 250, 260, 270, 280, 290, 300, 310, 320, 330, 340, 350,
47  360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470,
48  480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590,
49  600, 610, 620, 630, 640, 650, 660, 670, 680, 690},
50 
51  m_doseInner1000{1.995, 1.953, 1.787, 1.687, 1.608, 1.518,
52  1.448, 1.425, 1.354, 1.328, 1.275},
53 
54  m_doseMiddle2000{2.44, 2.355, 2.26, 2.204, 2.137, 2.081, 2.015, 1.96,
55  1.878, 1.87, 1.809, 1.732, 1.722, 1.63, 1.588, 1.57,
56  1.509, 1.464, 1.44, 1.38, 1.333, 1.321, 1.284, 1.271},
57 
58  m_doseOuter4000{2.458, 2.4, 2.382, 2.362, 2.266, 2.207, 2.122, 2.067,
59  2.046, 1.973, 1.94, 1.9, 1.869, 1.83, 1.711, 1.701,
60  1.685, 1.615, 1.626, 1.565, 1.53, 1.499, 1.429},
61 
62  m_doseResolution{{0.01, 0.025}, {0.1, 0.031}, {0.3, 0.035}, {0.6, 0.040},
63  {1.0, 0.046}, {3.0, 0.065}, {6.0, 0.065}},
64 
65  m_doseGain{{0.01, 39}, {0.1, 23}, {0.3, 21}, {0.6, 19},
66  {1.0, 10}, {3.0, 5}, {6.0, 4}} {}
67 
69 
70  if (m_radii.size() != (m_doseInner1000.size() + m_doseMiddle2000.size() +
71  m_doseOuter4000.size())) {
73  "ERROR while initializing the HGTD timing resolution. Vector of "
74  "different size.");
75  return StatusCode::FAILURE;
76  }
77 
79 
80  return StatusCode::SUCCESS;
81 }
82 
83 // Calculate dose, resolution, and gain based on the configured integrated
84 // luminosity
86 
87  m_dose.clear();
88  m_resolution.clear();
89  m_gain.clear();
90  computeDose();
92  radToGain();
93 
94  return StatusCode::SUCCESS;
95 }
96 
97 // get the sensor resolution (including the sensor intrinsic resolution and the
98 // time walk effect) for a hit at a radius R (unit: mm)
100  float sigmaT = 99999.;
101  if (!radiusInRange(radius))
102  return sigmaT;
103  if (m_integratedLumi == 0)
104  sigmaT = m_resolution[0];
105  else
106  sigmaT = resolution(std::fabs(radius));
107  return sigmaT;
108 }
109 
110 // get the timing resolution for a hit at a radius R (unit: mm)
112  float sigmaT = sensorResolution(radius);
113  // include the effect of electronic jitter and add in quadrature
114  float sigmaT2 = sigmaT * sigmaT + m_electronicJitter * m_electronicJitter;
115  return std::sqrt(sigmaT2);
116 }
117 
118 // returns the per Hit resolution for the final performance
120  float resolution = 9999.;
121 
122  if (radius < m_radii[0]) {
124  return resolution;
125  }
126 
127  for (unsigned int i = 1; i < m_radii.size(); i++) {
128 
129  if (radius < m_radii[i]) {
130  resolution =
131  m_resolution[i - 1] +
132  (((radius - m_radii[i - 1]) / (m_radii[i] - m_radii[i - 1])) *
133  (m_resolution[i] - m_resolution[i - 1]));
134  return resolution;
135  }
136  }
137 
138  resolution = m_resolution[m_resolution.size() - 1];
139  return resolution;
140 }
141 
143  float gain = 1.;
144 
145  if (radius < m_radii[0]) {
146  gain = m_gain[0];
147  return gain;
148  }
149 
150  for (unsigned int i = 1; i < m_radii.size(); i++) {
151 
152  if (radius < m_radii[i]) {
153  gain = m_gain[i - 1] +
154  (((radius - m_radii[i - 1]) / (m_radii[i] - m_radii[i - 1])) *
155  (m_gain[i] - m_gain[i - 1]));
156  return gain;
157  }
158  }
159 
160  gain = m_gain[m_gain.size() - 1];
161  return gain;
162 }
163 
165  return m_electronicJitter;
166 }
167 
168 // check if the request is in range:
170  return (std::fabs(radius) >= m_Rmin && std::fabs(radius) <= m_Rmax);
171 }
172 
173 // check if the request is in range:
175  float radius = translateEta2R(eta);
176  return radiusInRange(radius);
177 }
178 
179 // translate radius to eta
181  return std::fabs(
182  -std::log(std::tan(std::atan(std::fabs(radius) / m_z) / 2.)));
183 }
184 
185 // translate eta to radius
187  return std::fabs(std::tan(std::atan(std::exp(-eta)) * 2.) * m_z);
188 }
189 
191  for (unsigned int i = 0; i < m_doseInner1000.size(); ++i) {
192  m_dose.push_back(remainder_1(m_integratedLumi, 1000) / 1000 *
193  m_doseInner1000[i]);
194  }
195  for (unsigned int i = 0; i < m_doseMiddle2000.size(); ++i) {
196  m_dose.push_back(remainder_1(m_integratedLumi, 2000) / 2000 *
198  }
199  for (unsigned int i = 0; i < m_doseOuter4000.size(); ++i) {
200  m_dose.push_back(m_integratedLumi / 4000 * m_doseOuter4000[i]);
201  }
202 }
203 
205 
206  for (unsigned int i = 0; i < m_dose.size(); i++) {
207  if (m_dose[i] < m_doseResolution[0].first) {
208  m_resolution.push_back(m_doseResolution[0].second);
209  continue;
210  }
211  for (unsigned int j = 1; j < m_doseResolution.size(); j++) {
212  if (m_dose[i] < m_doseResolution[j].first) {
213  float res =
214  m_doseResolution[j - 1].second +
215  (((m_dose[i] - m_doseResolution[j - 1].first) /
218  m_resolution.push_back(res);
219  break;
220  }
221 
222  else if (j == m_doseResolution.size() - 1) {
223  m_resolution.push_back(
224  m_doseResolution[m_doseResolution.size() - 1].second);
225  }
226  }
227  }
228 }
229 
231  for (unsigned int i = 0; i < m_dose.size(); i++) {
232  if (m_dose[i] < m_doseGain[0].first) {
233  m_gain.push_back(m_doseGain[0].second);
234  continue;
235  }
236  for (unsigned int j = 1; j < m_doseGain.size(); j++) {
237  if (m_dose[i] < m_doseGain[j].first) {
238  float gain = m_doseGain[j - 1].second +
239  (((m_dose[i] - m_doseGain[j - 1].first) /
240  (m_doseGain[j].first - m_doseGain[j - 1].first)) *
241  (m_doseGain[j].second - m_doseGain[j - 1].second));
242  m_gain.push_back(gain);
243  break;
244  }
245 
246  else if (j == m_doseGain.size() - 1) {
247  m_gain.push_back(m_doseGain[m_doseGain.size() - 1].second);
248  }
249  }
250  }
251 }
252 
254  ATH_MSG_VERBOSE("HGTD_TimingResolution version: " << m_version);
255  ATH_MSG_VERBOSE("Integrated Lumi: " << m_integratedLumi);
256  ATH_MSG_VERBOSE("Rmin = " << m_Rmin);
257  ATH_MSG_VERBOSE("Rmax = " << m_Rmax);
258  ATH_MSG_VERBOSE("Z = " << m_z);
259 
260  ATH_MSG_VERBOSE("");
261  for (unsigned int i = 0; i < m_resolution.size(); i++) {
262  ATH_MSG_VERBOSE("Resolution r = " << m_radii[i] << " mm "
264  << " ps");
265  ATH_MSG_VERBOSE("Gain r = " << m_radii[i] << " mm " << gain(m_radii[i]));
266  }
267 }
268 
270  CLHEP::HepRandomEngine *rndm_engine) const {
271 
272  PulseWaveform pulseWaveform{};
273  float pulseParameter[4] = {0};
274  pulseParameter[0] = 0.036; // Width (scale) parameter of Landau density
275  pulseParameter[1] =
276  5.754; // Most Probable (MP, location) parameter of Landau density
277  pulseParameter[3] = CLHEP::RandGaussZiggurat::shoot(
278  rndm_engine, 0.318,
280  0.1 * m_sensorNoiseFactor); // Width (sigma) of convoluted Gaussian
281  // function
282  pulseParameter[2] =
283  0.4785 + 1.4196 * pulseParameter[3]; // Total area (integral -inf to inf,
284  // normalization constant)
285 
286  // Convoluted Landau and Gaussian Fitting Function
287  // Adapted to simulate a pulse in an HGTD pad
288 
289  // Numeric constants
290  float invsq2pi = 1 / sqrt(2 * M_PI); // 0.3989422804014; // (2 pi)^(-1/2)
291  float mpshift = -0.22278298; // Landau maximum location
292 
293  // Variables
294  float mpc = pulseParameter[1] - mpshift * pulseParameter[0];
295  float step = 0.03;
296  float step_par = step / (sqrt(2) * pulseParameter[3]);
297  float f_weight = pulseParameter[2] * step * invsq2pi / pulseParameter[3];
298  float fland;
299  float p_time;
300 
301  // Convolution integral of Landau and Gaussian by sum
302  for (int i = 412; i < 527; i++) {
303 
304  fland = TMath::Landau(3.5 + (i * 0.005), mpc, pulseParameter[0]) /
305  pulseParameter[0];
306  p_time = (1.5 + (i % 6 - i) * 0.005) / (sqrt(2) * pulseParameter[3]);
307 
308  for (size_t point = i % 6; point < pulseWaveform.size(); point += 6) {
309  p_time += step_par;
310  pulseWaveform[point] += fland * exp16(-p_time * p_time);
311  }
312  }
313  for (size_t point = 0; point < pulseWaveform.size(); point++) {
314  pulseWaveform[point] = f_weight * pulseWaveform[point];
315  }
316 
317  return pulseWaveform;
318 }
319 
321  const PulseWaveform &pulseWaveform,
322  std::map<int, std::pair<float, float>> &pulsebin, const float t,
323  const float E, float *max, CLHEP::HepRandomEngine *rndm_engine) const {
324 
325  int timebin = 0;
326  float energy = 0;
327  float time = 0;
328 
329  for (size_t point = 0; point < pulseWaveform.size(); point++) {
330 
331  energy = pulseWaveform[point] * E +
332  CLHEP::RandGaussZiggurat::shoot(rndm_engine, 0, 0.015) * E;
333  time = t + point * 0.005;
334  timebin = (int)(t / 0.005) + point;
335 
336  auto pulse = pulsebin.find(timebin);
337  if (pulse == pulsebin.end()) {
338  pulsebin.insert({timebin, {time * energy, energy}});
339  pulse = pulsebin.find(timebin);
340  } else {
341  (*pulse).second = {(*pulse).second.first + time * energy,
342  (*pulse).second.second + energy};
343  }
344  if (max[0] < (*pulse).second.second) {
345  max[0] = (*pulse).second.second;
346  max[1] = (*pulse).second.first / max[0];
347  }
348  }
349  return;
350 }
351 
353  const float t, const float E, const float r,
354  CLHEP::HepRandomEngine *rndm_engine) const {
355  std::map<int, std::pair<float, float>> pulseBins;
356  float max_hit[4] = {0};
357  const PulseWaveform pulse = simulatePulse(rndm_engine);
358  calculatePulse(pulse, pulseBins, t, E, max_hit, rndm_engine);
359  for (auto &pulse : pulseBins) {
360  if (pulse.second.second == 0) {
362  "HGTD_TimingResolution::calculateTime -> Energy goes zero, please "
363  "have a check, energy * time = "
364  << pulse.second.first << " while energy = " << pulse.second.second);
365  // This bin will have both time and energy equal to 0 and won't affect
366  // the peak finding step afterwards
367  pulse.second.first = 0;
368  } else {
369  pulse.second.first = pulse.second.first / pulse.second.second;
370  }
371  // We look the the time when E=Emax/2 to get the time
372  if ((max_hit[1] > pulse.second.first) &&
373  (max_hit[0] * m_cfdThreshold < pulse.second.second) &&
374  (max_hit[3] > pulse.second.first || max_hit[3] == 0)) {
375  max_hit[2] = pulse.second.second; // Energy when E=Emax*m_cfd
376  max_hit[3] = pulse.second.first; // Time when E=Emax*m_cfd
377  }
378  }
379 
380  return max_hit[3] +
381  CLHEP::RandGaussZiggurat::shoot(rndm_engine, 0, sensorResolution(r));
382 }
HGTD_TimingResolution::etaInRange
bool etaInRange(float) const
Definition: HGTD_TimingResolution.cxx:174
HGTD_TimingResolution::PulseWaveform
std::array< float, 400 > PulseWaveform
Definition: HGTD_TimingResolution.h:44
beamspotman.r
def r
Definition: beamspotman.py:676
HGTD_TimingResolution::radToGain
void radToGain()
Definition: HGTD_TimingResolution.cxx:230
HGTD_TimingResolution::radiusInRange
bool radiusInRange(float) const
Definition: HGTD_TimingResolution.cxx:169
python.SystemOfUnits.second
int second
Definition: SystemOfUnits.py:120
HGTD_TimingResolution::sensorResolution
float sensorResolution(float radius) const
Definition: HGTD_TimingResolution.cxx:99
HGTD_TimingResolution::m_dose
std::vector< float > m_dose
Definition: HGTD_TimingResolution.h:95
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
HGTD_TimingResolution::print
void print()
Definition: HGTD_TimingResolution.cxx:253
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
HGTD_TimingResolution::resolution
float resolution(float) const
Definition: HGTD_TimingResolution.cxx:119
eta
Scalar eta() const
pseudorapidity method
Definition: AmgMatrixBasePlugin.h:83
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
HGTD_TimingResolution::m_doseInner1000
std::vector< float > m_doseInner1000
Definition: HGTD_TimingResolution.h:92
HGTD_TimingResolution::m_cfdThreshold
const float m_cfdThreshold
Definition: HGTD_TimingResolution.h:105
HGTD_TimingResolution::propagateDamage
StatusCode propagateDamage()
Definition: HGTD_TimingResolution.cxx:85
M_PI
#define M_PI
Definition: ActiveFraction.h:11
HGTD_TimingResolution::m_gain
std::vector< float > m_gain
Definition: HGTD_TimingResolution.h:97
HGTD_TimingResolution::computeDose
void computeDose()
Definition: HGTD_TimingResolution.cxx:190
read_hist_ntuple.t
t
Definition: read_hist_ntuple.py:5
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
drawFromPickle.exp
exp
Definition: drawFromPickle.py:36
x
#define x
drawFromPickle.atan
atan
Definition: drawFromPickle.py:36
HGTD_TimingResolution::m_Rmax
float m_Rmax
Definition: HGTD_TimingResolution.h:79
HGTD_TimingResolution::m_doseOuter4000
std::vector< float > m_doseOuter4000
Definition: HGTD_TimingResolution.h:94
HGTD_TimingResolution::hitTimingResolution
float hitTimingResolution(float radius) const
Definition: HGTD_TimingResolution.cxx:111
HGTD_TimingResolution::m_electronicJitter
float m_electronicJitter
Definition: HGTD_TimingResolution.h:86
HGTD_TimingResolution::m_doseMiddle2000
std::vector< float > m_doseMiddle2000
Definition: HGTD_TimingResolution.h:93
HGTD_TimingResolution::calculatePulse
void calculatePulse(const PulseWaveform &pulseWaveform, std::map< int, std::pair< float, float >> &pulsebin, float t, float E, float *max, CLHEP::HepRandomEngine *rndm_engine) const
Calculate the pulse as a vector of float (400 points)
Definition: HGTD_TimingResolution.cxx:320
HGTD_TimingResolution::gain
float gain(float) const
Definition: HGTD_TimingResolution.cxx:142
HGTD_TimingResolution::m_Rmin
float m_Rmin
Definition: HGTD_TimingResolution.h:78
HGTD_TimingResolution::simulatePulse
PulseWaveform simulatePulse(CLHEP::HepRandomEngine *rndm_engine) const
Simulate a new pulse that can be acess using the PulseShape method.
Definition: HGTD_TimingResolution.cxx:269
ParticleGun_FastCalo_ChargeFlip_Config.energy
energy
Definition: ParticleGun_FastCalo_ChargeFlip_Config.py:78
lumiFormat.i
int i
Definition: lumiFormat.py:85
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
HGTD_TimingResolution::m_integratedLumi
FloatProperty m_integratedLumi
Definition: HGTD_TimingResolution.h:82
HGTD_TimingResolution::m_version
std::string m_version
Definition: HGTD_TimingResolution.h:76
res
std::pair< std::vector< unsigned int >, bool > res
Definition: JetGroupProductTest.cxx:14
test_pyathena.parent
parent
Definition: test_pyathena.py:15
HGTD_TimingResolution::radToResolution
void radToResolution()
Definition: HGTD_TimingResolution.cxx:204
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
HGTD_TimingResolution::initialize
virtual StatusCode initialize() override final
AlgTool initialize.
Definition: HGTD_TimingResolution.cxx:68
drawFromPickle.tan
tan
Definition: drawFromPickle.py:36
HGTD_TimingResolution.h
Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration.
HGTD_TimingResolution::m_doseGain
std::vector< std::pair< float, float > > m_doseGain
Definition: HGTD_TimingResolution.h:100
HGTD_TimingResolution::translateR2Eta
float translateR2Eta(float) const
Definition: HGTD_TimingResolution.cxx:180
HGTD_TimingResolution::m_sensorNoiseFactor
const float m_sensorNoiseFactor
Definition: HGTD_TimingResolution.h:102
HGTD_TimingResolution::translateEta2R
float translateEta2R(float) const
Definition: HGTD_TimingResolution.cxx:186
HGTD_TimingResolution::HGTD_TimingResolution
HGTD_TimingResolution(const std::string &type, const std::string &name, const IInterface *parent)
Definition: HGTD_TimingResolution.cxx:35
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:228
VP1PartSpect::E
@ E
Definition: VP1PartSpectFlags.h:21
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
HGTD_TimingResolution::m_resolution
std::vector< float > m_resolution
Definition: HGTD_TimingResolution.h:96
HGTD_TimingResolution::m_doseResolution
std::vector< std::pair< float, float > > m_doseResolution
Definition: HGTD_TimingResolution.h:99
ParticleGun_SamplingFraction.radius
radius
Definition: ParticleGun_SamplingFraction.py:96
a
TList * a
Definition: liststreamerinfos.cxx:10
CaloSwCorrections.time
def time(flags, cells_name, *args, **kw)
Definition: CaloSwCorrections.py:242
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
DeMoScan.first
bool first
Definition: DeMoScan.py:536
python.CaloCondTools.log
log
Definition: CaloCondTools.py:20
LArCellBinning.step
step
Definition: LArCellBinning.py:158
HGTD_TimingResolution::m_radii
std::vector< int > m_radii
Definition: HGTD_TimingResolution.h:88
AthAlgTool
Definition: AthAlgTool.h:26
HGTD_TimingResolution::m_z
float m_z
Definition: HGTD_TimingResolution.h:80
HGTD_TimingResolution::calculateTime
float calculateTime(const float t, const float E, float r, CLHEP::HepRandomEngine *rndm_engine) const
Return simulated CFD time.
Definition: HGTD_TimingResolution.cxx:352
pow
constexpr int pow(int base, int exp) noexcept
Definition: ap_fixedTest.cxx:15
HGTD_TimingResolution::electronicJitter
float electronicJitter() const
Definition: HGTD_TimingResolution.cxx:164