ATLAS Offline Software
sTgcDigitMaker.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3 */
4 
6 
13 #include "TrkSurfaces/Surface.h"
14 #include "GaudiKernel/MsgStream.h"
16 #include "CLHEP/Units/SystemOfUnits.h"
17 #include "CLHEP/Random/RandomEngine.h"
18 #include "CLHEP/Random/RandFlat.h"
19 #include "CLHEP/Random/RandGaussZiggurat.h"
20 #include "CLHEP/Random/RandGamma.h"
21 #include "CLHEP/Vector/ThreeVector.h"
23 
24 #include "TF1.h"
25 #include <cmath>
26 #include <iostream>
27 #include <fstream>
28 
29 //---------------------------------------------------
30 // Constructor and Destructor
31 //---------------------------------------------------
32 
33 //----- Constructor
35  const int channelTypes,
36  double meanGasGain,
37  bool doPadChargeSharing,
38  double stripChargeScale)
39  : AthMessaging ("sTgcDigitMaker"),
40  m_idHelperSvc{idHelperSvc},
41  m_channelTypes{channelTypes},
42  m_meanGasGain{meanGasGain},
43  m_doPadSharing{doPadChargeSharing},
44  m_stripChargeScale{stripChargeScale} {}
45 //----- Destructor
47 //------------------------------------------------------
48 // Initialize
49 //------------------------------------------------------
51 
52  // Read share/sTGC_Digitization_timeArrivale.dat, containing the digit time of arrival
54 
55  // Read share/sTGC_Digitization_timeOffsetStrip.dat if the the strip time correction is enable
56  if (m_doTimeOffsetStrip) {
58  }
59  // check the digitization channel type
60  if(m_channelTypes!=1 && m_channelTypes!=2 && m_channelTypes!=3) {
61  ATH_MSG_ERROR("Invalid ChannelTypes : " << m_channelTypes << " (valid values are : 1 --> strips ; 2 --> strips / wires ; 3 --> strips / wires / pads)");
62  return StatusCode::FAILURE;
63  }
64  return StatusCode::SUCCESS;
65 }
66 
67 //---------------------------------------------------
68 // Execute Digitization
69 //---------------------------------------------------
71  const sTGCSimHit& hit) const {
72  // SimHits without energy loss are not recorded.
73  double energyDeposit = hit.depositEnergy(); // Energy deposit in MeV
74  if(energyDeposit==0.) return {};
75 
77  const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
78  sTgcSimIdToOfflineId simToOffline{&idHelper};
79  const Identifier offlineId = simToOffline.convert(hit.sTGCId());
80  const Identifier layid = idHelper.channelID(offlineId,
81  idHelper.multilayer(offlineId),
82  idHelper.gasGap(offlineId),
83  sTgcIdHelper::sTgcChannelTypes::Wire, 1);
84 
85  ATH_MSG_VERBOSE("sTgc hit: time " << hit.globalTime()
86  << " position " << Amg::toString(hit.globalPosition(), 2)
87  << " mclink " << hit.particleLink() << " PDG ID " << hit.particleEncoding() );
88 
89 
90  ATH_MSG_DEBUG("Retrieving detector element for: "<< m_idHelperSvc->toStringDetEl(layid) << " energyDeposit "<<energyDeposit );
91 
92  const MuonGM::sTgcReadoutElement* detEl = condContainers.detMgr->getsTgcReadoutElement(layid);
93  if( !detEl ){ return {}; }
94 
95 
96  // DO THE DIGITIZATTION HERE ////////
97 
98  // Required precision on length in mm
99  constexpr double length_tolerance = 0.01;
100 
101  // Retrieve the wire surface
102  int surfHash_wire = detEl->surfaceHash(layid);
103  const Trk::PlaneSurface& SURF_WIRE = detEl->surface(surfHash_wire); // get the wire surface
104 
105  // Hit post-Step global position
106  const Amg::Vector3D& GPOS{hit.globalPosition()};
107  // Hit pre-Step global position
108  const Amg::Vector3D& pre_pos{hit.globalPrePosition()};
109  // Hit global direction
110  const Amg::Vector3D& GLODIRE{hit.globalDirection()};
111 
112  // Hit position in the wire surface's coordinate frame
113  Amg::Vector3D hitOnSurface_wire{SURF_WIRE.transform().inverse() * GPOS};
114  Amg::Vector3D pre_pos_wire_surf{SURF_WIRE.transform().inverse() * pre_pos};
115  Amg::Vector2D posOnSurf_wire{0.5 * (hitOnSurface_wire + pre_pos_wire_surf).block<2,1>(0,0)};
116 
117  /* Determine the closest wire and the distance of closest approach
118  * Since most particles pass through the the wire plane between two wires,
119  * the nearest wire should be one of these two wire. Otherwise, the particle's
120  * trajectory is uncommon, and such rare case is not supported yet.
121  *
122  * Finding that nearest wire follows the following steps:
123  * - Compute the distance to the wire at the center of the current wire pitch
124  * - Compute the distance to the other adjacent wire
125  */
126 
127  // Wire number of the current wire pitch
128  int wire_number = detEl->getDesign(layid)->wireNumber(posOnSurf_wire);
129 
130  // If wire number is invalid, verify if hit is near the edge of the chamber.
131  const int number_wires = detEl->numberOfWires(layid);
132  if ((wire_number < 1) || (wire_number > number_wires)) {
133  // Compute the wire number using either the pos-step position or
134  // the pre-step position, whichever yields a valid wire number.
135  wire_number = detEl->getDesign(layid)->wireNumber(hitOnSurface_wire.block<2,1>(0,0));
136  if ((wire_number < 1) || (wire_number > number_wires)) {
137  wire_number = detEl->getDesign(layid)->wireNumber(pre_pos_wire_surf.block<2,1>(0,0));
138  }
139  }
140 
141  // Compute the position of the ionization and its distance relative to a given sTGC wire.
142  Ionization ionization = pointClosestApproach(detEl, layid, wire_number, pre_pos_wire_surf, hitOnSurface_wire);
143  double dist_wire = ionization.distance;
144  if (dist_wire > 0.) {
145  // Determine the other adjacent wire, which is
146  // -1 if particle crosses the wire surface between wire_number-1 and wire_number
147  // +1 if particle crosses the wire surface between wire_number and wire_number+1
148  int adjacent = 1;
149  if (ionization.posOnSegment.x() < ionization.posOnWire.x()) {adjacent = -1;}
150 
151  // Find the position of the ionization with respect to the adjacent wire
152  Ionization ion_adj = pointClosestApproach(detEl, layid, wire_number+adjacent, pre_pos_wire_surf, hitOnSurface_wire);
153  // Keep the closest point
154  double dist_wire_adj = ion_adj.distance;
155  if ((dist_wire_adj > 0.) && (dist_wire_adj < dist_wire)) {
156  dist_wire = dist_wire_adj;
157  wire_number += adjacent;
158  ionization = std::move(ion_adj);
159  }
160  } else {
161  ATH_MSG_DEBUG("Failed to get the distance between the wire number = " << wire_number
162  << " and hit at " <<Amg::toString(hitOnSurface_wire, 2)
163  << ". Number of wires = " << number_wires
164  << ", "<<m_idHelperSvc->toStringGasGap(layid));
165  return {};
166  }
167 
168  // Update the position of ionization on the wire surface
169  posOnSurf_wire = ionization.posOnWire.block<2,1>(0,0);
170  // Position of the ionization in the global coordinate frame
171  const Amg::Vector3D glob_ionization_pos = SURF_WIRE.transform() * ionization.posOnWire;
172 
173  ATH_MSG_VERBOSE("Ionization_info: distance: " << ionization.distance
174  << " posOnTrack: " <<Amg::toString(ionization.posOnSegment, 3)
175  << " posOnWire: " << Amg::toString(ionization.posOnWire, 3)
176  << " hitGPos: " << Amg::toString(GPOS,3)
177  << " hitPrePos: " <<Amg::toString(pre_pos, 3)
178  << " EDep: " << hit.depositEnergy() << " EKin: " << hit.kineticEnergy()
179  << " pdgId: " << hit.particleEncoding()
180  <<" "<<m_idHelperSvc->toStringGasGap(layid));
181 
182  // Distance should be in the range [0, 0.9] mm, excepting
183  // - particles pass through the wire plane near the edges
184  // - secondary particles created inside the gas gap that go through the gas gap partially.
185  // Most of such particles are not muons and have low kinetic energy.
186  // - particle with trajectory parallel to the sTGC wire plane
187  const double wire_pitch = detEl->wirePitch();
188  if ((dist_wire > 0.) && (std::abs(hit.particleEncoding()) == 13) && (dist_wire > (wire_pitch/2))) {
189  ATH_MSG_DEBUG("Distance to the nearest wire (" << dist_wire << ") is greater than expected.");
190  ATH_MSG_DEBUG("Hit globalPos: "<<Amg::toString(GPOS,2)
191  << " globalPrePos: " << Amg::toString(pre_pos, 2)
192  << " EDeposited: " << hit.depositEnergy()
193  << " EKinetic: " << hit.kineticEnergy()
194  << " pdgID: " << hit.particleEncoding()
195  << " "<<m_idHelperSvc->toStringGasGap(layid));
196  }
197 
198  // Do not digitize hits that are too far from the nearest wire
199  if (dist_wire > wire_pitch) {
200  return {};
201  }
202 
203  // Get the gamma pdf parameters associated with the distance of closest approach.
204  const GammaParameter gamParam = getGammaParameter(dist_wire);
205  const double par_kappa = gamParam.kParameter;
206  const double par_theta = gamParam.thetaParameter;
207  const double most_prob_time = getMostProbableArrivalTime(dist_wire);
208  // Compute the most probable value of the gamma pdf
209  double gamma_mpv = (par_kappa - 1) * par_theta;
210  // If the most probable value is less than zero, then set it to zero
211  if (gamma_mpv < 0.) {gamma_mpv = 0.;}
212  const double t0_par = most_prob_time - gamma_mpv;
213 
214  // Digit time follows a gamma distribution, so a value val is
215  // chosen using a gamma random generator then is shifted by t0
216  // to account for drift time.
217  // Note: CLHEP::RandGamma takes the parameters k and lambda,
218  // where lambda = 1 / theta.
219  double digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.rndmEngine, par_kappa, 1/par_theta);
220 
221  // Sometimes, digit_time is negative because t0_par can be negative.
222  // In such case, discard the negative value and shoot RandGamma for another value.
223  // However, if that has already been done many times then set digit_time to zero
224  // in order to avoid runaway loop.
225  constexpr int shoot_limit = 4;
226  int shoot_counter = 0;
227  while (digit_time < 0.) {
228  if (shoot_counter > shoot_limit) {
229  digit_time = 0.;
230  break;
231  }
232  digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.rndmEngine, par_kappa, 1/par_theta);
233  ++shoot_counter;
234  }
235 
236  ATH_MSG_DEBUG("sTgcDigitMaker distance = " << dist_wire
237  << ", time = " << digit_time
238  << ", k parameter = " << par_kappa
239  << ", theta parameter = " << par_theta
240  << ", most probable time = " << most_prob_time);
241 
242  bool isValid = false;
244  if (condContainers.efficiencies) {
245  const double efficiency = condContainers.efficiencies->getEfficiency(layid);
246  // Lose Hits to match HV efficiency
247  if (CLHEP::RandFlat::shoot(condContainers.rndmEngine,0.0,1.0) > efficiency) return {};
248  }
249 
250 
251  sTgcDigitVec digits{};
252 
253  //const double stripPropagationTime = 3.3*CLHEP::ns/CLHEP::m * detEl->distanceToReadout(posOnSurf_strip, elemId); // 8.5*ns/m was used until MC10.
254  const double stripPropagationTime = 0.; // 8.5*ns/m was used until MC10.
255 
256  double sDigitTimeWire = digit_time;
257  double sDigitTimePad = sDigitTimeWire;
258  double sDigitTimeStrip = sDigitTimeWire + stripPropagationTime;
259 
260  uint16_t bctag = 0;
261 
262  //##################################################################################
263  //######################################### strip readout ##########################
264  //##################################################################################
265  ATH_MSG_DEBUG("sTgcDigitMaker::strip response ");
266  int channelType = sTgcIdHelper::sTgcChannelTypes::Strip;
267 
268  Identifier newId = idHelper.channelID(layid,idHelper.multilayer(layid),
269  idHelper.gasGap(layid), channelType, 1, isValid);
270 
271  // get strip surface
272  int surfHash_strip = detEl->surfaceHash(newId);
273  const Trk::PlaneSurface& SURF_STRIP = detEl->surface(surfHash_strip); // get the strip surface
274 
275  const Amg::Vector3D hitOnSurface_strip = SURF_STRIP.transform().inverse()*glob_ionization_pos;
276 
277  const Amg::Vector2D posOnSurf_strip(hitOnSurface_strip.x(),hitOnSurface_strip.y());
278  bool insideBounds = SURF_STRIP.insideBounds(posOnSurf_strip);
279  if(!insideBounds) {
280  ATH_MSG_DEBUG("Outside of the strip surface boundary : " << m_idHelperSvc->toString(newId) << "; local position " <<posOnSurf_strip );
281  return {};
282  }
283 
284  //************************************ find the nearest readout element **************************************
285 
286  int stripNumber = detEl->stripNumber(posOnSurf_strip, newId);
287  if( stripNumber == -1 ){
288  // Verify if the energy deposit is at the boundary
289  const double new_posX = (posOnSurf_strip.x() > 0.0)? posOnSurf_strip.x() - length_tolerance
290  : posOnSurf_strip.x() + length_tolerance;
291  const Amg::Vector2D new_position(new_posX, posOnSurf_strip.y());
292  stripNumber = detEl->stripNumber(new_position, newId);
293  // Skip hit if still unable to obtain strip number
294  if (stripNumber < 1) {
295  ATH_MSG_WARNING("Failed to obtain strip number " << m_idHelperSvc->toString(newId) );
296  ATH_MSG_WARNING("Position on strip surface = (" << posOnSurf_strip.x() << ", " << posOnSurf_strip.y() << ")");
297  return {};
298  }
299  }
300  newId = idHelper.channelID(layid, idHelper.multilayer(layid),
301  idHelper.gasGap(layid), channelType, stripNumber, isValid);
302  if(!isValid && stripNumber != -1) {
303  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(newId) );
304  return {};
305  }
306 
307  const int NumberOfStrips = detEl->numberOfStrips(newId);
308  const double stripHalfPitch = detEl->channelPitch(newId)*0.5; // 3.2/2 = 1.6 mm
309 
310  //************************************ conversion of energy to charge **************************************
311 
312  // Typical ionized charge in pC per keV deposited. The constant is determined from ionization
313  // study with Garfield program. A note titled "Charge Energy Relation" which outlines
314  // conversion can be found here:
315  // https://cernbox.cern.ch/index.php/apps/files/?dir=/__myshares/Documents (id:274113) // link is dead
316  const double ionized_charge = (5.65E-6)*energyDeposit/CLHEP::keV;
317 
318  // To get avalanche gain, polya function is taken from Blum paper https://inspirehep.net/literature/807304
319  // m_polyaFunction = new TF1("m_polyaFunction","(1.0/[1])*(TMath::Power([0]+1,[0]+1)/TMath::Gamma([0]+1))*TMath::Power(x/[1],[0])*TMath::Exp(-([0]+1)*x/[1])",0,3000000);
320 
321  // Mean value for total gain due to E field;
322  // To calculate this gain from polya distibution, we replace in gamma PDF:
323  // alpha = 1+theta and
324  // beta = 1+theta/mean
325  // With these substitutions, gamma PDF gives the same sampling values as those from polya PDF.
326  const double gain = CLHEP::RandGamma::shoot(condContainers.rndmEngine, 1. + m_theta, (1. + m_theta)/m_meanGasGain);
327 
328  // total charge after avalanche
329  const double total_charge = gain*ionized_charge;
330 
331  //************************************ spread charge among readout element **************************************
332 
333  // Charge Spread including tan(theta) resolution term.
334  const double tan_theta = GLODIRE.perp()/GLODIRE.z();
335  // The angle dependance on strip resolution goes as tan^2(angle)
336  const double angle_dependency = std::hypot(m_posResIncident, m_posResAngular * tan_theta);
337 
338  const double cluster_posX = posOnSurf_strip.x();
339  double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.rndmEngine, cluster_posX, m_StripResolution*angle_dependency);
340 
341  // Each readout plane reads about half the total charge produced on the wire,
342  // including a tan(theta) term to describe the increase of charge with incident angle
343  const double norm = 0.5 * total_charge * m_stripChargeScale * std::hypot(1, m_chargeAngularFactor * tan_theta);
344  // Strip cluster charge profile described by a double Gaussian.
345  // The independent parameter x is in the units of strip channel, one strip channel = 3.2 mm,
346  // so convert position from mm to strip channel if it is not already.
347  std::unique_ptr<TF1> clusterProfile = std::make_unique<TF1>("fgaus",
348  "[0]*exp(-0.5*(x/[1])^2)+[2]*exp(-0.5*(x/[3])^2)",
349  -300., 300.);
350  clusterProfile->SetParameters(norm * m_clusterProfile[0], // normalization of 1st Gaussian
351  m_clusterProfile[1], // sigma of 1st Gaussian
352  norm * m_clusterProfile[2], // normalization of 2nd Gaussian
353  m_clusterProfile[3]); // sigma of 2nd Gaussian
354 
355  // Lower limit on strip charge (arbitrary limit), in pC, which has the same units as the parameter ionized_charge.
356  constexpr double tolerance_charge = 0.0005;
357  // Set a maximum number of neighbour strips to avoid very long loop. This is an arbitrary number.
358  constexpr unsigned int max_neighbor = 10;
359 
360  // Spread charge on the strips that are on the upper half of the strip cluster
361  for (unsigned int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
362  int currentStrip = stripNumber + iStrip;
363  if (currentStrip > NumberOfStrips) break;
364 
365  // Get the strip identifier and create the digit
366  newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
367  channelType, currentStrip, isValid);
368  if (isValid) {
370  if (!detEl->stripPosition(newId, locpos)) {
371  ATH_MSG_WARNING("Failed to obtain local position for identifier " << m_idHelperSvc->toString(newId) );
372  }
373 
374  // Estimate the digit charge
375  // Position with respect to the peak of the charge curve
376  double x_relative = locpos.x() - peak_position;
377  // In clusterProfile curve, position should be in the units of strip channel
378  double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
379  // If charge is too small, stop creating neighbor strip
380  if (charge < tolerance_charge) break;
381 
382  // Estimate digit time
383  double strip_time = sDigitTimeStrip;
384  // Strip time response can be delayed due to the resistive layer.
385  // A correction would be required if the actual VMM front-end doesn't re-align the strip timing.
386  if (m_doTimeOffsetStrip) {
387  // Determine how far the current strip is from the middle strip
388  int indexFromMiddleStrip = iStrip;
389  if ((iStrip > 0) && ((x_relative + (0.75 - iStrip * 2) * stripHalfPitch) < 0))
390  indexFromMiddleStrip = iStrip - 1;
391  // Add time delay due to resistive layer
392  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
393  }
394 
395  addDigit(digits, newId, bctag, strip_time, charge);
396 
397  ATH_MSG_VERBOSE("Created a strip digit: strip number = " << currentStrip << ", charge = " << charge
398  << ", time = " << strip_time << ", time offset = " << strip_time-sDigitTimeStrip
399  << ", neighbor index = " << iStrip
400  << ", strip position = "<<Amg::toString(locpos, 2));
401  }
402  }
403 
404  // The lower half of the strip cluster
405  for (unsigned int iStrip = 1; iStrip <= max_neighbor; ++iStrip) {
406  int currentStrip = stripNumber - iStrip;
407  if (currentStrip < 1) break;
408 
409  newId = idHelper.channelID(layid,idHelper.multilayer(layid),
410  idHelper.gasGap(layid), channelType, currentStrip, isValid);
411  if (isValid) {
413  if (!detEl->stripPosition(newId, locpos)) {
414  ATH_MSG_WARNING("Failed to obtain local position for identifier " << m_idHelperSvc->toString(newId) );
415  }
416 
417  // Estimate the digit charge
418  double x_relative = locpos.x() - peak_position;
419  double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
420  if (charge < tolerance_charge) break;
421 
422  // Estimate digit time
423  double strip_time = sDigitTimeStrip;
424  // Time delay due to resistive layer
425  if (m_doTimeOffsetStrip) {
426  int indexFromMiddleStrip = ((x_relative + (iStrip * 2 - 0.75) * stripHalfPitch) > 0)? iStrip - 1 : iStrip;
427  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
428  }
429 
430  addDigit(digits, newId, bctag, strip_time, charge);
431 
432  ATH_MSG_VERBOSE("Created a strip digit: strip number = " << currentStrip << ", charge = " << charge
433  << ", time = " << strip_time << ", time offset = " << strip_time-sDigitTimeStrip
434  << ", neighbor index = " << iStrip
435  << ", strip position = " << Amg::toString(locpos, 2));
436  }
437  }
438  // end of strip digitization
439 
440  if(m_channelTypes==1) {
441  ATH_MSG_WARNING("Only digitize strip response !");
442  return digits;
443  }
444 
445  //##################################################################################
446  //######################################### pad readout ##########################
447  //##################################################################################
448  ATH_MSG_DEBUG("sTgcDigitMaker::pad response ");
449  channelType = sTgcIdHelper::sTgcChannelTypes::Pad;
450 
451  Identifier PAD_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
452  idHelper.gasGap(layid), channelType, 1);// find the a pad id
453  //************************************ find the nearest readout element **************************************
454  int surfHash_pad = detEl->surfaceHash(PAD_ID);
455  const Trk::PlaneSurface& SURF_PAD = detEl->surface(surfHash_pad); // get the pad surface
456 
457  const Amg::Vector3D hitOnSurface_pad = SURF_PAD.transform().inverse()*glob_ionization_pos;
458  const Amg::Vector2D posOnSurf_pad(hitOnSurface_pad.x(), hitOnSurface_pad.y());
459 
460 
461  insideBounds = SURF_PAD.insideBounds(posOnSurf_pad);
462 
463  if(insideBounds) {
464  int padNumber = detEl->stripNumber(posOnSurf_pad, PAD_ID);
465  if( padNumber == -1 ){
466  // Verify if the energy deposit is at the boundary
467  const double new_posX = (posOnSurf_pad.x()>0.0)? posOnSurf_pad.x()-length_tolerance
468  : posOnSurf_pad.x()+length_tolerance;
469  const double new_posY = (posOnSurf_pad.y()>0.0)? posOnSurf_pad.y()-length_tolerance
470  : posOnSurf_pad.y()+length_tolerance;
471  const Amg::Vector2D new_position(new_posX, new_posY);
472  padNumber = detEl->stripNumber(new_position, PAD_ID);
473  // Skip hit if still unable to obtain pad number
474  if (padNumber < 1) {
475  ATH_MSG_WARNING("Failed to obtain pad number " << m_idHelperSvc->toString(PAD_ID) );
476  ATH_MSG_WARNING("Position on pad surface = (" << posOnSurf_pad.x() << ", " << posOnSurf_pad.y() << ")");
477  return digits;
478  }
479  }
480  newId = idHelper.channelID(layid, idHelper.multilayer(layid),
481  idHelper.gasGap(layid), channelType, padNumber, isValid);
482  if(isValid) {
483  // Find centre position of pad
485  if (!detEl->stripPosition(newId, padPos)) {
486  ATH_MSG_ERROR("Failed to obtain local position for neighbor pad with identifier " << m_idHelperSvc->toString(newId) );
487  return digits;
488  }
489  // Pad sharing needs to look at position on hit vs pad boundaries
490  const Amg::Vector2D diff = posOnSurf_pad - padPos;
491  double halfPadWidthX = 0.5*detEl->getPadDesign(newId)->channelWidth(posOnSurf_pad, true, true);
492  double halfPadWidthY = 0.5*detEl->getPadDesign(newId)->channelWidth(posOnSurf_pad, false);
493 
494  // Charge sharing happens within 4mm window for pads
495  // i.e. the charge is spread over a 2D gaussian of total radius about 4mm
496  // This value depends on actual width of the distribution, but we don't have the
497  // width of the charge distribution for pads. So lets consider 2.5*sigma, where
498  // sigma is the width of the charge distribution for strips.
499  double deltaX = halfPadWidthX - std::abs(diff.x());
500  double deltaY= halfPadWidthY - std::abs(diff.y());
501  bool isNeighX = deltaX < 2.5*m_clusterProfile[3];
502  bool isNeighY = deltaY < 2.5*m_clusterProfile[3];
503  // Pad width can be calculated to be very slightly larger than it should due to rounding errors
504  // So if a hit falls on a given pad but is "outside" the width, just define it to be on the boundary of 2 pads.
505  if (deltaX < 0.) deltaX = 0.1;
506  if (deltaY < 0.) deltaY = 0.1;
507 
508  if (m_doPadSharing && (isNeighX || isNeighY)){
509  std::pair<int, int> padEtaPhi(detEl->getPadDesign(newId)->channelNumber(padPos));
510  // Phi == 1 at the right in the local geometry
511  // In local coordinates, the pad to the right has phi' = phi - 1
512  int newPhi = diff.x() > 0 ? padEtaPhi.second-1 : padEtaPhi.second+1;
513  int newEta = diff.y() > 0 ? padEtaPhi.first+1 : padEtaPhi.first-1;
514  bool validEta = newEta > 0 && newEta < detEl->getPadDesign(newId)->nPadH+1;
515  bool validPhi = newPhi > 0 && newPhi < detEl->getPadDesign(newId)->nPadColumns+1;
516 
517  if (isNeighX && isNeighY && validEta && validPhi){
518  // 4 pads total; makes life a bit harder. Corner of 4 valid pads
519  Identifier neigh_ID_X = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
520  channelType, padEtaPhi.first, newPhi, isValid);
521  Identifier neigh_ID_Y = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
522  channelType, newEta, padEtaPhi.second, isValid);
523  Identifier neigh_ID_XY = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
524  channelType, newEta, newPhi, isValid);
525  double xQfraction = getPadChargeFraction(deltaX);
526  double yQfraction = getPadChargeFraction(deltaY);
527 
528  // Main pad gets 1 - Qfraction of total
529  addDigit(digits, neigh_ID_X, bctag, sDigitTimePad, xQfraction*(1.-yQfraction)*0.5*total_charge);
530  addDigit(digits, neigh_ID_Y, bctag, sDigitTimePad, yQfraction*(1.-xQfraction)*0.5*total_charge);
531  addDigit(digits, neigh_ID_XY, bctag, sDigitTimePad, xQfraction*yQfraction*0.5*total_charge);
532  addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*total_charge);
533  } else if (isNeighX && validPhi){
534  // There is only 1 neighbor, immediately to the left or right.
535  Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
536  channelType, padEtaPhi.first, newPhi, isValid);
537 
538  // NeighborPad gets Qfraction of the total pad charge: 0.5*total_charge
539  // Main pad gets 1 - Qfraction of total
540  double xQfraction = getPadChargeFraction(deltaX);
541  addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction)*0.5*total_charge);
542  addDigit(digits, neigh_ID, bctag, sDigitTimePad, xQfraction*0.5*total_charge);
543  }
544  else if (isNeighY && validEta){
545  // There is only 1 neighbor, immediately above or below
546  Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
547  channelType, newEta, padEtaPhi.second, isValid);
548 
549  // NeighborPad gets Qfraction of the total pad charge: 0.5*total_charge
550  // Main pad gets 1 - Qfraction of total
551  double yQfraction = getPadChargeFraction(deltaX);
552  addDigit(digits, newId, bctag, sDigitTimePad, (1.-yQfraction)*0.5*total_charge);
553  addDigit(digits, neigh_ID, bctag, sDigitTimePad, yQfraction*0.5*total_charge);
554  }
555 
556  }
557  else{
558  // No charge sharing: hit is nicely isolated within pad
559  addDigit(digits, newId, bctag, sDigitTimePad, 0.5*total_charge);
560  }
561 
562  }
563  else if(padNumber != -1) {
564  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(newId) );
565  }
566  }
567  else {
568  ATH_MSG_DEBUG("Outside of the pad surface boundary :" << m_idHelperSvc->toString(PAD_ID)<< " local position " <<posOnSurf_pad );
569  }
570 
571  if(m_channelTypes==2) {
572  ATH_MSG_WARNING("Only digitize strip/pad response !");
573  return digits;
574  }
575 
576 
577  //##################################################################################
578  //######################################### wire readout ##########################
579  //##################################################################################
580  ATH_MSG_DEBUG("sTgcDigitMaker::wire response ");
581  channelType = sTgcIdHelper::sTgcChannelTypes::Wire;
582 
583  // Find the ID of the first wiregroup
584  Identifier WIREGP_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
585  idHelper.gasGap(layid), channelType, 1);
586 
587  //************************************ find the nearest readout element **************************************
588  insideBounds = SURF_WIRE.insideBounds(posOnSurf_wire);
589 
590  if(insideBounds) {
591  // Determine the wire number
592 
593  int wiregroupNumber = detEl->stripNumber(posOnSurf_wire, WIREGP_ID);
594  if( wiregroupNumber == -1 ){
595  // Verify if the energy deposit is at the boundary
596  const double new_posX = (posOnSurf_wire.x() > 0.0)? posOnSurf_wire.x() - length_tolerance
597  : posOnSurf_wire.x() + length_tolerance;
598  const Amg::Vector2D new_position(new_posX, posOnSurf_wire.y());
599  wiregroupNumber = detEl->stripNumber(new_position, WIREGP_ID);
600  // Skip hit if still unable to obtain pad number
601  if (wiregroupNumber < 1) {
602  ATH_MSG_WARNING("Failed to obtain wire number " << m_idHelperSvc->toString(WIREGP_ID) );
603  ATH_MSG_WARNING("Position on wire surface = (" << posOnSurf_wire.x() << ", " << posOnSurf_wire.y() << ")");
604  return digits;
605  }
606  }
607 
608  // Find ID of the actual wiregroup
609  newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
610  channelType, wiregroupNumber, isValid);
611 
612  if(isValid) {
613  int NumberOfWiregroups = detEl->numberOfStrips(newId);
614  if(wiregroupNumber>=1&&wiregroupNumber<=NumberOfWiregroups) {
615  addDigit(digits, newId, bctag, sDigitTimeWire, total_charge);
616  }
617  } // end of if(isValid)
618  else if (wiregroupNumber != -1){
619  ATH_MSG_ERROR("Failed to obtain wiregroup identifier " << m_idHelperSvc->toString(newId) );
620  }
621  }
622  else {
623  ATH_MSG_DEBUG("Outside of the wire surface boundary :" << m_idHelperSvc->toString(WIREGP_ID)<< " local position " <<posOnSurf_wire );
624  }
625  // end of wire digitization
626 
627  return digits;
628 }
629 
630 //+++++++++++++++++++++++++++++++++++++++++++++++
632  const Identifier& id,
633  int wireNumber,
634  const Amg::Vector3D& preStepPos,
635  const Amg::Vector3D& postStepPos) const {
636  // tolerance in mm
637  constexpr double tolerance = 0.001;
638  // minimum segment length
639  constexpr double min_length = 0.1;
640 
641  // Position of the ionization
642  Ionization ionization;
643 
644  // Wire number should be one or greater
645  if (wireNumber < 1) return ionization;
646 
647  // Wire number too large
648  if (wireNumber > detEl->numberOfWires(id)) return ionization;
649 
650  // Finding smallest distance and the points at the smallest distance.
651  // The smallest distance between two lines is perpendicular to both lines.
652  // We can construct two lines in the wire surface local coordinate frame:
653  // - one for the hit segment with equation h0 + t * v_h, where h0 is a point
654  // and v_h is the unit vector of the hit segment
655  // - another for the wire with similar equation w0 + s * v_w, where w0 is a
656  // point and v_w is the unit vector of the wire line
657  // Then it is possible to determine the factors s and t by requiring the
658  // dot product to be zero:
659  // 1. (h0 - w0) \dot v_h = 0
660  // 2. (h0 - w0) \dot v_w = 0
661 
662  // Wire pitch
663  const double wire_pitch = detEl->wirePitch();
664  // Wire local position on the wire plane, the y-coordinate is arbitrary and z-coordinate is zero
665  const double wire_posX = detEl->positionFirstWire(id) + (wireNumber - 1) * wire_pitch;
666  const Amg::Vector3D wire_position(wire_posX, postStepPos.y(), 0.);
667  // The wires are parallel to Y in the wire plane's coordinate frame
668  const Amg::Vector3D wire_direction{Amg::Vector3D::UnitY()};
669 
670  // particle trajectory
671  Amg::Vector3D hit_direction{postStepPos - preStepPos};
672  const double seg_length = hit_direction.mag();
673  if (seg_length > tolerance) hit_direction /= seg_length;
674 
675  // Find the point on the track segment that is closest to the wire
676  if (seg_length < min_length) {
677  ionization.posOnSegment = postStepPos;
678  ionization.posOnWire = wire_position;
679  ionization.distance = std::hypot(postStepPos.x() - wire_posX, postStepPos.z());
680  return ionization;
681  }
682 
683  // Dot product between the wire and hit direction
684  const double cos_theta = wire_direction.dot(hit_direction);
685  // distance between the hit and wire
686  const Amg::Vector3D dist_wire_hit = postStepPos - wire_position;
687 
688  // Verifier the special case where the two lines are parallel
689  if (std::abs(cos_theta - 1.0) < tolerance) {
690  ATH_MSG_DEBUG("The track segment is parallel to the wire, position of digit is undefined");
691  ionization.posOnSegment = 0.5 * (postStepPos + preStepPos);
692  ionization.posOnWire = Amg::Vector3D(wire_posX, ionization.posOnSegment.y(), 0.0);
693  ionization.distance = std::hypot(ionization.posOnSegment.x() - wire_posX,
694  ionization.posOnSegment.z());
695  return ionization;
696  }
697 
698  // Perpendicular component squared
699  const double sin_theta_2 = 1.0 - cos_theta * cos_theta;
700 
701  //* Point on the hit segment
702  const double factor_hit = (-dist_wire_hit.dot(hit_direction)
703  + dist_wire_hit.dot(wire_direction) * cos_theta) / sin_theta_2;
704  Amg::Vector3D ionization_pos = postStepPos + factor_hit * hit_direction;
705 
706  // If the point is on the track segment, then compute the other point on the wire.
707  // Otherwise, set the ionization at the pre-step position and compute where it
708  // should be on the wire.
709  Amg::Vector3D pos_on_wire{Amg::Vector3D::Zero()};
710  if (factor_hit < seg_length) {
711  //* Point on the wire line
712  const double factor_wire = (dist_wire_hit.dot(wire_direction)
713  - dist_wire_hit.dot(hit_direction) * cos_theta) / sin_theta_2;
714  pos_on_wire = wire_position + factor_wire * wire_direction;
715  } else {
716  ionization_pos = preStepPos;
717  pos_on_wire = wire_position - (seg_length * cos_theta) * wire_direction;
718  }
719 
720  // Save the distance and ionization position
721  ionization.distance = (ionization_pos - pos_on_wire).mag();
722  ionization.posOnSegment = ionization_pos;
723  ionization.posOnWire = pos_on_wire;
724 
725  return ionization;
726 }
727 
728 //+++++++++++++++++++++++++++++++++++++++++++++++
730  const Identifier& id,
731  const uint16_t bctag,
732  const double digittime,
733  const double charge) {
734 
735  constexpr double tolerance = 0.1;
736  if (std::find_if(digits.begin(),digits.end(), [&](std::unique_ptr<sTgcDigit>& known) {
737  return known->identify() == id && std::abs(digittime - known->time()) < tolerance;
738  }) == digits.end()) {
739  digits.push_back(std::make_unique<sTgcDigit>(id, bctag, digittime, charge, 0, 0));
740  }
741 }
742 
743 //+++++++++++++++++++++++++++++++++++++++++++++++
745  // Verify the file sTGC_Digitization_timeArrival.dat exists
746  const std::string file_name = "sTGC_Digitization_timeArrival.dat";
747  std::string file_path = PathResolver::find_file(file_name, "DATAPATH");
748  if(file_path.empty()) {
749  ATH_MSG_FATAL("readFileOfTimeWindowOffset(): Could not find file " << file_name );
750  return StatusCode::FAILURE;
751  }
752 
753  // Open the sTGC_Digitization_timeArrival.dat file
754  std::ifstream ifs{file_path, std::ios::in};
755  if(ifs.bad()) {
756  ATH_MSG_FATAL("sTgcDigitMaker: Failed to open time of arrival file " << file_name );
757  return StatusCode::FAILURE;
758  }
759 
760  // Read the sTGC_Digitization_timeWindowOffset.dat file
761  std::string line;
762  GammaParameter param{};
763 
764  while (std::getline(ifs, line)) {
765  std::string key;
766  std::istringstream iss(line);
767  iss >> key;
768  if (key.compare("bin") == 0) {
769  iss >> param.lowEdge >> param.kParameter >> param.thetaParameter;
770  m_gammaParameter.push_back(param);
771  } else if (key.compare("mpv") == 0) {
772  double mpt;
773  int idx{0};
774  while (iss >> mpt) {m_mostProbableArrivalTime[idx++] = mpt;}
775  }
776  }
777 
778  // Close the file
779  ifs.close();
780  return StatusCode::SUCCESS;
781 }
782 
783 //+++++++++++++++++++++++++++++++++++++++++++++++
785 
786  const double d = std::abs(distance);
787  // Find the parameters assuming the container is sorted in ascending order of 'lowEdge'
788  int index{-1};
789  for (const auto& par: m_gammaParameter) {
790  if (d < par.lowEdge) {
791  break;
792  }
793  ++index;
794  }
795  return m_gammaParameter.at(index);
796 }
797 
798 //+++++++++++++++++++++++++++++++++++++++++++++++
800 
801  const double d{std::abs(distance)};
802  double mpt{0};
803  for (size_t t = 0 ; t < m_mostProbableArrivalTime.size(); ++t){
804  mpt += m_mostProbableArrivalTime[t] * std::pow(d, t);
805  }
806  return mpt;
807 }
808 
809 //+++++++++++++++++++++++++++++++++++++++++++++++
811  // Verify the file sTGC_Digitization_timeOffsetStrip.dat exists
812  const std::string file_name = "sTGC_Digitization_timeOffsetStrip.dat";
813  std::string file_path = PathResolver::find_file(file_name, "DATAPATH");
814  if(file_path.empty()) {
815  ATH_MSG_FATAL("readFileOfTimeWindowOffset(): Could not find file " << file_name );
816  return StatusCode::FAILURE;
817  }
818 
819  // Open the sTGC_Digitization_timeOffsetStrip.dat file
820  std::ifstream ifs{file_path, std::ios::in};
821  if(ifs.bad()) {
822  ATH_MSG_FATAL("Failed to open time of arrival file " << file_name );
823  return StatusCode::FAILURE;
824  }
825 
826  // Initialize the container to store the time offset.
827  // The number of parameters, 6, corresponds to the number of lines to be read
828  // from sTGC_Digitization_timeOffsetStrip.dat.
829  // Setting the default offset to 0 ns.
830 
831  // Read the input file
832  std::string line;
833  size_t index{0};
834  double value{0.0};
835  while (std::getline(ifs, line)) {
836  std::string key;
837  std::istringstream iss(line);
838  iss >> key;
839  if (key.compare("strip") == 0) {
840  iss >> index >> value;
841  if (index >= m_timeOffsetStrip.size()) continue;
843  }
844  }
845  return StatusCode::SUCCESS;
846 }
847 
848 //+++++++++++++++++++++++++++++++++++++++++++++++
849 double sTgcDigitMaker::getTimeOffsetStrip(size_t neighbor_index) const {
850  return m_timeOffsetStrip.at(std::min(neighbor_index, m_timeOffsetStrip.size() -1));
851 }
852 
853 //+++++++++++++++++++++++++++++++++++++++++++++++
855  // The charge fraction that is found past a distance x away from the
856  // centre of a 2D gaussian distribution of width of cluster profile is
857  // described by a modified error function.
858 
859  // The modified error function perfectly describes
860  // the pad charge sharing distribution figure 16 of the sTGC
861  // testbeam paper https://arxiv.org/pdf/1509.06329.pdf
862 
863  return 0.5 * (1.0 - std::erf( distance / (std::sqrt(2) * m_clusterProfile[3])));
864 }
PlotCalibFromCool.norm
norm
Definition: PlotCalibFromCool.py:100
MuonGM::MuonPadDesign::channelNumber
std::pair< int, int > channelNumber(const Amg::Vector2D &pos) const
calculate local channel number, range 1=nstrips like identifiers.
Definition: MuonPadDesign.cxx:39
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
Muon::IMuonIdHelperSvc::stgcIdHelper
virtual const sTgcIdHelper & stgcIdHelper() const =0
access to TgcIdHelper
checkFileSG.line
line
Definition: checkFileSG.py:75
sTgcSimIdToOfflineId
Definition: sTgcSimIdToOfflineId.h:12
sTgcDigitMaker::m_posResIncident
double m_posResIncident
Definition: sTgcDigitMaker.h:157
AthCheckMacros.h
MuonGM::sTgcReadoutElement::numberOfStrips
virtual int numberOfStrips(const Identifier &layerId) const override final
number of strips per layer
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:410
sTGCSimHit::particleLink
const HepMcParticleLink & particleLink() const
Definition: sTGCSimHit.h:96
PathResolver::find_file
static std::string find_file(const std::string &logical_file_name, const std::string &search_path, SearchType search_type=LocalSearch)
Definition: PathResolver.cxx:251
Surface.h
makeComparison.deltaY
int deltaY
Definition: makeComparison.py:44
sTgcDigitMaker::getTimeOffsetStrip
double getTimeOffsetStrip(size_t neighbor_index) const
Get digit time offset of a strip depending on its relative position to the strip at the centre of the...
Definition: sTgcDigitMaker.cxx:849
sTgcReadoutElement.h
Amg::Vector2D
Eigen::Matrix< double, 2, 1 > Vector2D
Definition: GeoPrimitives.h:48
index
Definition: index.py:1
sTgcDigitMaker::readFileOfTimeOffsetStrip
StatusCode readFileOfTimeOffsetStrip()
Read share/sTGC_Digitization_timeOffsetStrip.dat.
Definition: sTgcDigitMaker.cxx:810
hist_file_dump.d
d
Definition: hist_file_dump.py:137
sTgcDigitMaker::Ionization
Ionization object with distance, position on the hit segment and position on the wire.
Definition: sTgcDigitMaker.h:83
conifer::pow
constexpr int pow(int x)
Definition: conifer.h:20
CaloCondBlobAlgs_fillNoiseFromASCII.gain
gain
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:110
sTgcDigitMaker::getMostProbableArrivalTime
double getMostProbableArrivalTime(double distance) const
Get the most probable time of arrival.
Definition: sTgcDigitMaker.cxx:799
sTgcDigitMaker::m_chargeAngularFactor
double m_chargeAngularFactor
Definition: sTgcDigitMaker.h:163
InDet::adjacent
bool adjacent(unsigned int strip1, unsigned int strip2)
Definition: SCT_ClusteringTool.cxx:45
MuonGM::sTgcReadoutElement::getDesign
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:281
mc.diff
diff
Definition: mc.SFGenPy8_MuMu_DD.py:14
sTgcDigitMaker::executeDigi
sTgcDigitVec executeDigi(const DigiConditions &condContainers, const sTGCSimHit &hit) const
Definition: sTgcDigitMaker.cxx:70
athena.value
value
Definition: athena.py:122
Muon::IMuonIdHelperSvc::toStringDetEl
virtual std::string toStringDetEl(const Identifier &id) const =0
print all fields up to detector element to string
MuonGM::sTgcReadoutElement::numberOfWires
int numberOfWires(const Identifier &id) const
Get the total number of wires (single wires) of a chamber.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:684
sTgcDigitMaker::readFileOfTimeArrival
StatusCode readFileOfTimeArrival()
Read share/sTGC_Digitization_timeArrival.dat.
Definition: sTgcDigitMaker.cxx:744
sTgcDigitMaker::DigiConditions
Digitize a given hit, determining the time and charge spread on wires, pads and strips.
Definition: sTgcDigitMaker.h:57
read_hist_ntuple.t
t
Definition: read_hist_ntuple.py:5
sTgcDigitMaker::GammaParameter
Parameters of a gamma probability distribution function, required for estimating wire digit's time of...
Definition: sTgcDigitMaker.h:74
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
isValid
bool isValid(const T &p)
Definition: AtlasPID.h:214
sTgcDigitMaker::~sTgcDigitMaker
virtual ~sTgcDigitMaker()
MuonGM::MuonClusterReadoutElement::surface
virtual const Trk::PlaneSurface & surface() const override
access to chamber surface (phi orientation), uses the first gas gap
Definition: MuonClusterReadoutElement.h:123
sTgcDigitMaker::GammaParameter::thetaParameter
double thetaParameter
Definition: sTgcDigitMaker.h:77
Trk::energyDeposit
@ energyDeposit
Definition: MeasurementType.h:32
MuonGM::sTgcReadoutElement::stripPosition
virtual bool stripPosition(const Identifier &id, Amg::Vector2D &pos) const override final
strip position - should be renamed to channel position If the strip number is outside the range of va...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:323
MuonGM::MuonPadDesign::nPadH
int nPadH
Definition: MuonPadDesign.h:68
python.SystemOfUnits.keV
int keV
Definition: SystemOfUnits.py:156
sTgcDigitMaker::pointClosestApproach
Ionization pointClosestApproach(const MuonGM::sTgcReadoutElement *readoutEle, const Identifier &id, int wireNumber, const Amg::Vector3D &preStepPos, const Amg::Vector3D &postStepPos) const
Determine the points where the distance between two segments is smallest.
Definition: sTgcDigitMaker.cxx:631
sTgcDigitMaker::m_gammaParameter
std::vector< GammaParameter > m_gammaParameter
Definition: sTgcDigitMaker.h:131
physics_parameters.file_name
string file_name
Definition: physics_parameters.py:32
Trk::PlaneSurface::insideBounds
virtual bool insideBounds(const Amg::Vector2D &locpos, double tol1=0., double tol2=0.) const override
This method calls the inside() method of the Bounds.
efficiency
void efficiency(std::vector< double > &bins, std::vector< double > &values, const std::vector< std::string > &files, const std::string &histname, const std::string &tplotname, const std::string &label="")
Definition: dependence.cxx:128
Amg::toString
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Definition: GeoPrimitivesToStringConverter.h:40
sTgcDigitMaker::m_posResAngular
double m_posResAngular
Definition: sTgcDigitMaker.h:158
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
xAOD::uint16_t
setWord1 uint16_t
Definition: eFexEMRoI_v1.cxx:88
sTgcDigitMaker::m_channelTypes
int m_channelTypes
define offsets and widths of time windows for signals from wiregangs and strips.
Definition: sTgcDigitMaker.h:147
sTgcDigitMaker::getGammaParameter
GammaParameter getGammaParameter(double distance) const
Find the gamma pdf parameters of a given distance.
Definition: sTgcDigitMaker.cxx:784
Identifier
Definition: DetectorDescription/Identifier/Identifier/Identifier.h:32
MuonGM::sTgcReadoutElement::surfaceHash
virtual int surfaceHash(const Identifier &id) const override final
returns the hash to be used to look up the surface and transform in the MuonClusterReadoutElement tra...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:259
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
MuonGM::sTgcReadoutElement
An sTgcReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station c...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:28
Muon::DigitEffiData::getEfficiency
double getEfficiency(const Identifier &channelId, bool isInnerQ1=false) const
Returns the signal generation efficiency of the sTgc channel.
Definition: DigitEffiData.cxx:36
Muon::IMuonIdHelperSvc::toStringGasGap
virtual std::string toStringGasGap(const Identifier &id) const =0
print all fields up to gas gap to string
athena.file_path
file_path
Definition: athena.py:92
AthMessaging
Class to provide easy MsgStream access and capabilities.
Definition: AthMessaging.h:55
sTGCSimHit::sTGCId
HitID sTGCId() const
Definition: sTGCSimHit.h:51
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
sTgcSimIdToOfflineId.h
sTgcDigitMaker::GammaParameter::kParameter
double kParameter
Definition: sTgcDigitMaker.h:76
sTgcDigitMaker::addDigit
static void addDigit(sTgcDigitVec &digits, const Identifier &id, const uint16_t bctag, const double digittime, const double charge)
Definition: sTgcDigitMaker.cxx:729
compareGeometries.deltaX
float deltaX
Definition: compareGeometries.py:32
MuonGM::MuonPadDesign::channelWidth
double channelWidth(const Amg::Vector2D &pos, bool measPhi, bool preciseMeas=false) const
calculate local channel width
Definition: MuonPadDesign.h:142
MuonGM::sTgcReadoutElement::channelPitch
double channelPitch(const Identifier &id) const
Channel pitch.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:582
sTgcDigitMaker::Ionization::posOnWire
Amg::Vector3D posOnWire
Definition: sTgcDigitMaker.h:86
sTgcDigitMaker::Ionization::posOnSegment
Amg::Vector3D posOnSegment
Definition: sTgcDigitMaker.h:85
min
#define min(a, b)
Definition: cfImp.cxx:40
tolerance
Definition: suep_shower.h:17
sTGCSimHit::globalDirection
const Amg::Vector3D & globalDirection() const
Definition: sTGCSimHit.h:46
sTgcDigitMaker::m_doTimeOffsetStrip
bool m_doTimeOffsetStrip
Definition: sTgcDigitMaker.h:154
PathResolver.h
sTGCSimHit::kineticEnergy
double kineticEnergy() const
Definition: sTGCSimHit.h:48
sTGCSimHit::globalTime
double globalTime() const
Definition: sTGCSimHit.h:41
createCoolChannelIdFile.par
par
Definition: createCoolChannelIdFile.py:29
sTgcDigitMaker::m_doPadSharing
bool m_doPadSharing
Definition: sTgcDigitMaker.h:150
charge
double charge(const T &p)
Definition: AtlasPID.h:494
sTgcIdHelper
Definition: sTgcIdHelper.h:55
sTgcDigitMaker::getPadChargeFraction
static double getPadChargeFraction(double distance)
Definition: sTgcDigitMaker.cxx:854
MuonGM::MuonPadDesign::nPadColumns
int nPadColumns
Definition: MuonPadDesign.h:69
MuonGM::MuonChannelDesign::wireNumber
int wireNumber(const Amg::Vector2D &pos) const
calculate the sTGC wire number. The method can return a value outside the range [1,...
Definition: MuonChannelDesign.h:259
sTgcDigitMaker::Ionization::distance
double distance
Definition: sTgcDigitMaker.h:84
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
MuonDetectorManager.h
sTGCSimHit::particleEncoding
int particleEncoding() const
Definition: sTGCSimHit.h:45
sTgcDigitMaker::DigiConditions::detMgr
const MuonGM::MuonDetectorManager * detMgr
Definition: sTgcDigitMaker.h:58
sTGCSimHit
Definition: sTGCSimHit.h:15
sTgcDigitMaker::m_clusterProfile
static constexpr std::array< double, 4 > m_clusterProfile
Definition: sTgcDigitMaker.h:161
MuonGM::sTgcReadoutElement::stripNumber
virtual int stripNumber(const Amg::Vector2D &pos, const Identifier &id) const override final
strip number corresponding to local position.
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:311
DeMoScan.index
string index
Definition: DeMoScan.py:362
Muon::IMuonIdHelperSvc::toString
virtual std::string toString(const Identifier &id) const =0
print all fields to string
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
Trk::PlaneSurface
Definition: PlaneSurface.h:64
sTgcDigitMaker::m_timeOffsetStrip
std::array< double, 6 > m_timeOffsetStrip
Definition: sTgcDigitMaker.h:136
sTgcDigitMaker::m_stripChargeScale
double m_stripChargeScale
Definition: sTgcDigitMaker.h:165
known
Definition: TrigBStoxAODTool.cxx:107
sTgcDigitMaker::sTgcDigitMaker
sTgcDigitMaker(const Muon::IMuonIdHelperSvc *idHelperSvc, const int channelTypes, double meanGasGain, bool doPadChargeSharing, double stripChargeScale)
Definition: sTgcDigitMaker.cxx:34
sTgcDigitMaker::m_theta
double m_theta
Definition: sTgcDigitMaker.h:148
MuonGM::sTgcReadoutElement::positionFirstWire
double positionFirstWire(const Identifier &id) const
Get the local position of the first wire of the chamber corresponding to the identifier.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:667
GeoPrimitivesToStringConverter.h
LArNewCalib_DelayDump_OFC_Cali.idx
idx
Definition: LArNewCalib_DelayDump_OFC_Cali.py:69
Muon::IMuonIdHelperSvc
Interface for Helper service that creates muon Identifiers and can be used to print Identifiers.
Definition: IMuonIdHelperSvc.h:26
sTgcDigitCollection.h
sTgcDigitMaker::m_mostProbableArrivalTime
std::array< double, 5 > m_mostProbableArrivalTime
Definition: sTgcDigitMaker.h:133
sTGCSimHit::depositEnergy
double depositEnergy() const
Definition: sTGCSimHit.h:47
sTgcDigitMaker::initialize
StatusCode initialize()
Initializes sTgcHitIdHelper and sTgcIdHelper, and call the functions to read files containing digitiz...
Definition: sTgcDigitMaker.cxx:50
sTgcDigitMaker::m_StripResolution
double m_StripResolution
Definition: sTgcDigitMaker.h:156
sTgcDigitMaker::sTgcDigitVec
std::vector< std::unique_ptr< sTgcDigit > > sTgcDigitVec
Definition: sTgcDigitMaker.h:64
LocalDirection.h
sTgcDigitMaker.h
MuonGM::sTgcReadoutElement::getPadDesign
const MuonPadDesign * getPadDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:287
MuonGM::sTgcReadoutElement::wirePitch
double wirePitch(int gas_gap=1) const
single wire pitch.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:657
sTgcDigitMaker::DigiConditions::rndmEngine
CLHEP::HepRandomEngine * rndmEngine
Definition: sTgcDigitMaker.h:61
MuonGM::MuonDetectorManager::getsTgcReadoutElement
const sTgcReadoutElement * getsTgcReadoutElement(const Identifier &id) const
access via extended identifier (requires unpacking)
Definition: MuonDetDescr/MuonReadoutGeometry/src/MuonDetectorManager.cxx:259
sTGCSimHit::globalPrePosition
const Amg::Vector3D & globalPrePosition() const
Definition: sTGCSimHit.h:49
sTgcDigitMaker::m_meanGasGain
double m_meanGasGain
Definition: sTgcDigitMaker.h:149
Amg::distance
float distance(const Amg::Vector3D &p1, const Amg::Vector3D &p2)
calculates the distance between two point in 3D space
Definition: GeoPrimitivesHelpers.h:54
Trk::Surface::transform
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
sTGCSimHit::globalPosition
const Amg::Vector3D & globalPosition() const
Definition: sTGCSimHit.h:44
mag
Scalar mag() const
mag method
Definition: AmgMatrixBasePlugin.h:25
sTgcDigitMaker::m_idHelperSvc
const Muon::IMuonIdHelperSvc * m_idHelperSvc
Definition: sTgcDigitMaker.h:138
generate::Zero
void Zero(TH1D *hin)
Definition: generate.cxx:32
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
sTgcDigitMaker::DigiConditions::efficiencies
const Muon::DigitEffiData * efficiencies
Definition: sTgcDigitMaker.h:59