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