ATLAS Offline Software
Classes | Public Types | Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes | Static Private Attributes | List of all members
sTgcDigitMaker Class Reference

Handles the digitization of sTGC hits, converting simulated hits into sTGC digits. More...

#include <sTgcDigitMaker.h>

Inheritance diagram for sTgcDigitMaker:
Collaboration diagram for sTgcDigitMaker:

Classes

struct  DigiConditions
 Digitize a given hit, determining the time and charge spread on wires, pads and strips. More...
 
struct  GammaParameter
 Parameters of a gamma probability distribution function, required for estimating wire digit's time of arrival. More...
 
struct  Ionization
 Ionization object with distance, position on the hit segment and position on the wire. More...
 

Public Types

enum  digitMode { StripsOnly = 1, StripsAndPads = 2, AllChType = 3 }
 Constructor initializing digitization parameters. More...
 
using sTgcDigitVec = std::vector< std::unique_ptr< sTgcDigit > >
 
using TimedHit = TimedHitPtr< xAOD::MuonSimHit >
 
using ReadoutChannelType = sTgcIdHelper::sTgcChannelTypes
 
using sTgcDigitVec = std::vector< std::unique_ptr< sTgcDigit > >
 Digitize a given hit. More...
 

Public Member Functions

 sTgcDigitMaker (const Muon::IMuonIdHelperSvc *idHelperSvc, const int channelTypes, double meanGasGain, bool doPadChargeSharing, double stripChargeScale, bool applyAsBuiltBLines)
 
virtual ~sTgcDigitMaker ()
 
StatusCode initialize ()
 Initializes sTgcHitIdHelper and sTgcIdHelper, and call the functions to read files containing digitization parameters. More...
 
sTgcDigitVec executeDigi (const DigiConditions &condContainers, const sTGCSimHit &hit) const
 
 sTgcDigitMaker (const Muon::IMuonIdHelperSvc *idHelperSvc, digitMode mode, double meanGasGain, bool doPadChargeSharing)
 
virtual ~sTgcDigitMaker ()
 Destructor. More...
 
StatusCode initialize ()
 Initialize digitization parameters, including reading necessary data files. More...
 
sTgcDigitVec executeDigi (const DigiConditions &condContainers, const TimedHit &hit) const
 
bool msgLvl (const MSG::Level lvl) const
 Test the output level. More...
 
MsgStream & msg () const
 The standard message stream. More...
 
MsgStream & msg (const MSG::Level lvl) const
 The standard message stream. More...
 
void setLevel (MSG::Level lvl)
 Change the current logging level. More...
 

Private Member Functions

StatusCode readFileOfTimeArrival ()
 Read share/sTGC_Digitization_timeArrival.dat. More...
 
StatusCode readFileOfTimeOffsetStrip ()
 Read share/sTGC_Digitization_timeOffsetStrip.dat. More...
 
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. More...
 
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 cluster. More...
 
GammaParameter getGammaParameter (double distance) const
 Find the gamma pdf parameters of a given distance. More...
 
double getMostProbableArrivalTime (double distance) const
 Get the most probable time of arrival. More...
 
StatusCode readFileOfTimeArrival ()
 Reads time arrival data file. More...
 
StatusCode readFileOfTimeOffsetStrip ()
 Reads strip time offset data file. More...
 
Ionization pointClosestApproach (const MuonGMR4::WireGroupDesign &wireDesign, int wireNumber, const Amg::Vector3D &locHitPos, const Amg::Vector3D &locHitDir, const double stepLength) const
 Computes the closest approach between a trajectory and a wire segment. More...
 
double getTimeOffsetStrip (size_t neighbor_index) const
 Gets the time offset for a strip cluster. More...
 
GammaParameter getGammaParameter (double distance) const
 Retrieves gamma distribution parameters based on distance. More...
 
double getMostProbableArrivalTime (double distance) const
 Computes the most probable arrival time based on the distance of closest approach. More...
 
double chargeIntegral (double N, double M) const
 
void initMessaging () const
 Initialize our message level and MessageSvc. More...
 

Static Private Member Functions

static void addDigit (sTgcDigitVec &digits, const Identifier &id, const uint16_t bctag, const double digittime, const double charge)
 
static double getPadChargeFraction (double distance)
 
static void addDigit (sTgcDigitVec &digits, const Identifier &id, uint16_t bctag, double digittime, double charge)
 Adds a digit to the appropriate cache. More...
 
static double getPadChargeFraction (double distance)
 Computes charge fraction shared among pads. More...
 

Private Attributes

std::vector< GammaParameterm_gammaParameter
 
std::array< double, 5 > m_mostProbableArrivalTime {make_array<double, 5>(0.)}
 
std::array< double, 6 > m_timeOffsetStrip {make_array<double, 6>(0.)}
 
const Muon::IMuonIdHelperSvcm_idHelperSvc {nullptr}
 
int m_channelTypes {3}
 define offsets and widths of time windows for signals from wiregangs and strips. More...
 
double m_theta {0.8}
 
double m_meanGasGain {5.e4}
 
bool m_doPadSharing {false}
 
bool m_doTimeOffsetStrip {false}
 
double m_StripResolution {0.0949}
 
double m_posResIncident {1.}
 
double m_posResAngular {0.305/m_StripResolution}
 
double m_chargeAngularFactor {4.0}
 
double m_stripChargeScale {0.4}
 
bool m_applyAsBuiltBLines {false}
 
digitMode m_digitMode = AllChType
 define offsets and widths of time windows for signals from wiregroups and strips. More...
 
std::string m_nm
 Message source name. More...
 
boost::thread_specific_ptr< MsgStream > m_msg_tls
 MsgStream instance (a std::cout like with print-out levels) More...
 
std::atomic< IMessageSvc * > m_imsg { nullptr }
 MessageSvc pointer. More...
 
std::atomic< MSG::Level > m_lvl { MSG::NIL }
 Current logging level. More...
 
std::atomic_flag m_initialized ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
 Messaging initialized (initMessaging) More...
 

Static Private Attributes

static constexpr std::array< double, 4 > m_clusterProfile {0.350, 0.573, 0.186, 1.092}
 
static constexpr std::array< double, 2 > m_clusterParams {0.573, 1.092}
 

Detailed Description

Handles the digitization of sTGC hits, converting simulated hits into sTGC digits.

for SCT_ChargeTrappingTool

All functionality of sTGC digitization is implemented to this class.

Definition at line 36 of file MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h.

Member Typedef Documentation

◆ ReadoutChannelType

◆ sTgcDigitVec [1/2]

using sTgcDigitMaker::sTgcDigitVec = std::vector<std::unique_ptr<sTgcDigit> >

◆ sTgcDigitVec [2/2]

using sTgcDigitMaker::sTgcDigitVec = std::vector<std::unique_ptr<sTgcDigit> >

Digitize a given hit.

Parameters
condContainersConditions required for digitization.
hitThe simulated hit to be digitized.

Definition at line 79 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h.

◆ TimedHit

Member Enumeration Documentation

◆ digitMode

Constructor initializing digitization parameters.

Enumerator
StripsOnly 
StripsAndPads 
AllChType 

Definition at line 40 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h.

40  {
41  StripsOnly = 1,
42  StripsAndPads = 2,
43  AllChType = 3,
44  };

Constructor & Destructor Documentation

◆ sTgcDigitMaker() [1/2]

sTgcDigitMaker::sTgcDigitMaker ( const Muon::IMuonIdHelperSvc idHelperSvc,
const int  channelTypes,
double  meanGasGain,
bool  doPadChargeSharing,
double  stripChargeScale,
bool  applyAsBuiltBLines 
)

Definition at line 34 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

40  : AthMessaging ("sTgcDigitMaker"),
41  m_idHelperSvc{idHelperSvc},
42  m_channelTypes{channelTypes},
43  m_meanGasGain{meanGasGain},
44  m_doPadSharing{doPadChargeSharing},
45  m_stripChargeScale{stripChargeScale},
46  m_applyAsBuiltBLines(applyAsBuiltBLines) {}

◆ ~sTgcDigitMaker() [1/2]

sTgcDigitMaker::~sTgcDigitMaker ( )
virtualdefault

◆ sTgcDigitMaker() [2/2]

sTgcDigitMaker::sTgcDigitMaker ( const Muon::IMuonIdHelperSvc idHelperSvc,
digitMode  mode,
double  meanGasGain,
bool  doPadChargeSharing 
)

Definition at line 23 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/src/sTgcDigitMaker.cxx.

27  : AthMessaging("sTgcDigitMaker"),
28  m_idHelperSvc{idHelperSvc},
30  m_meanGasGain{meanGasGain},
31  m_doPadSharing{doPadChargeSharing}{}

◆ ~sTgcDigitMaker() [2/2]

virtual sTgcDigitMaker::~sTgcDigitMaker ( )
virtual

Destructor.

Member Function Documentation

◆ addDigit() [1/2]

void sTgcDigitMaker::addDigit ( sTgcDigitVec digits,
const Identifier id,
const uint16_t  bctag,
const double  digittime,
const double  charge 
)
staticprivate

Definition at line 740 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

744  {
745 
746  constexpr double tolerance = 0.1;
747  if (std::find_if(digits.begin(),digits.end(), [&](std::unique_ptr<sTgcDigit>& known) {
748  return known->identify() == id && std::abs(digittime - known->time()) < tolerance;
749  }) == digits.end()) {
750  digits.push_back(std::make_unique<sTgcDigit>(id, bctag, digittime, charge, 0, 0));
751  }
752 }

◆ addDigit() [2/2]

static void sTgcDigitMaker::addDigit ( sTgcDigitVec digits,
const Identifier id,
uint16_t  bctag,
double  digittime,
double  charge 
)
staticprivate

Adds a digit to the appropriate cache.

◆ chargeIntegral()

double sTgcDigitMaker::chargeIntegral ( double  N,
double  M 
) const
private

Definition at line 763 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/src/sTgcDigitMaker.cxx.

763  {
764 
765  double term1 = 0.25 * std::erf( M / (std::sqrt(2) * m_clusterParams[0]));
766  double term2 = 0.25 * std::erf( N / (std::sqrt(2) * m_clusterParams[0]));
767  double term3 = 0.25 * std::erf( M / (std::sqrt(2) * m_clusterParams[1]));
768  double term4 = 0.25 * std::erf( N / (std::sqrt(2) * m_clusterParams[1]));
769 
770  return (term1 - term2 + term3 - term4);
771 }

◆ executeDigi() [1/2]

sTgcDigitMaker::sTgcDigitVec sTgcDigitMaker::executeDigi ( const DigiConditions condContainers,
const sTGCSimHit hit 
) const

Definition at line 72 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

73  {
74  // SimHits without energy loss are not recorded.
75  double energyDeposit = hit.depositEnergy(); // Energy deposit in MeV
76  if(energyDeposit==0.) return {};
77 
79  const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
80  sTgcSimIdToOfflineId simToOffline{&idHelper};
81  const Identifier offlineId = simToOffline.convert(hit.sTGCId());
82  const Identifier layid = idHelper.channelID(offlineId,
83  idHelper.multilayer(offlineId),
84  idHelper.gasGap(offlineId),
85  sTgcIdHelper::sTgcChannelTypes::Wire, 1);
86 
87  ATH_MSG_VERBOSE("sTgc hit: time " << hit.globalTime()
88  << " position " << Amg::toString(hit.globalPosition(), 2)
89  << " mclink " << hit.particleLink() << " PDG ID " << hit.particleEncoding() );
90 
91 
92  ATH_MSG_DEBUG("Retrieving detector element for: "<< m_idHelperSvc->toStringDetEl(layid) << " energyDeposit "<<energyDeposit );
93 
94  const MuonGM::sTgcReadoutElement* detEl = condContainers.detMgr->getsTgcReadoutElement(layid);
95  if( !detEl ){ return {}; }
96 
97 
98  // DO THE DIGITIZATTION HERE ////////
99 
100  // Required precision on length in mm
101  constexpr double length_tolerance = 0.01;
102 
103  // Retrieve the wire surface
104  int surfHash_wire = detEl->surfaceHash(layid);
105  const Trk::PlaneSurface& SURF_WIRE = detEl->surface(surfHash_wire); // get the wire surface
106 
107  // Hit post-Step global position
108  const Amg::Vector3D& GPOS{hit.globalPosition()};
109  // Hit pre-Step global position
110  const Amg::Vector3D& pre_pos{hit.globalPrePosition()};
111  // Hit global direction
112  const Amg::Vector3D& GLODIRE{hit.globalDirection()};
113 
114  // Hit position in the wire surface's coordinate frame
115  Amg::Vector3D hitOnSurface_wire{SURF_WIRE.transform().inverse() * GPOS};
116  Amg::Vector3D pre_pos_wire_surf{SURF_WIRE.transform().inverse() * pre_pos};
117  Amg::Vector2D posOnSurf_wire{0.5 * (hitOnSurface_wire + pre_pos_wire_surf).block<2,1>(0,0)};
118 
119  /* Determine the closest wire and the distance of closest approach
120  * Since most particles pass through the the wire plane between two wires,
121  * the nearest wire should be one of these two wire. Otherwise, the particle's
122  * trajectory is uncommon, and such rare case is not supported yet.
123  *
124  * Finding that nearest wire follows the following steps:
125  * - Compute the distance to the wire at the center of the current wire pitch
126  * - Compute the distance to the other adjacent wire
127  */
128 
129  // Wire number of the current wire pitch
130  int wire_number = detEl->getDesign(layid)->wireNumber(posOnSurf_wire);
131 
132  // If wire number is invalid, verify if hit is near the edge of the chamber.
133  const int number_wires = detEl->numberOfWires(layid);
134  if ((wire_number < 1) || (wire_number > number_wires)) {
135  // Compute the wire number using either the pos-step position or
136  // the pre-step position, whichever yields a valid wire number.
137  wire_number = detEl->getDesign(layid)->wireNumber(hitOnSurface_wire.block<2,1>(0,0));
138  if ((wire_number < 1) || (wire_number > number_wires)) {
139  wire_number = detEl->getDesign(layid)->wireNumber(pre_pos_wire_surf.block<2,1>(0,0));
140  }
141  }
142 
143  // Compute the position of the ionization and its distance relative to a given sTGC wire.
144  Ionization ionization = pointClosestApproach(detEl, layid, wire_number, pre_pos_wire_surf, hitOnSurface_wire);
145  double dist_wire = ionization.distance;
146  if (dist_wire > 0.) {
147  // Determine the other adjacent wire, which is
148  // -1 if particle crosses the wire surface between wire_number-1 and wire_number
149  // +1 if particle crosses the wire surface between wire_number and wire_number+1
150  int adjacent = 1;
151  if (ionization.posOnSegment.x() < ionization.posOnWire.x()) {adjacent = -1;}
152 
153  // Find the position of the ionization with respect to the adjacent wire
154  Ionization ion_adj = pointClosestApproach(detEl, layid, wire_number+adjacent, pre_pos_wire_surf, hitOnSurface_wire);
155  // Keep the closest point
156  double dist_wire_adj = ion_adj.distance;
157  if ((dist_wire_adj > 0.) && (dist_wire_adj < dist_wire)) {
158  dist_wire = dist_wire_adj;
159  wire_number += adjacent;
160  ionization = std::move(ion_adj);
161  }
162  } else {
163  ATH_MSG_DEBUG("Failed to get the distance between the wire number = " << wire_number
164  << " and hit at " <<Amg::toString(hitOnSurface_wire, 2)
165  << ". Number of wires = " << number_wires
166  << ", "<<m_idHelperSvc->toStringGasGap(layid));
167  return {};
168  }
169 
170  // Update the position of ionization on the wire surface
171  posOnSurf_wire = ionization.posOnWire.block<2,1>(0,0);
172  // Position of the ionization in the global coordinate frame
173  const Amg::Vector3D glob_ionization_pos = SURF_WIRE.transform() * ionization.posOnWire;
174 
175  ATH_MSG_VERBOSE("Ionization_info: distance: " << ionization.distance
176  << " posOnTrack: " <<Amg::toString(ionization.posOnSegment, 3)
177  << " posOnWire: " << Amg::toString(ionization.posOnWire, 3)
178  << " hitGPos: " << Amg::toString(GPOS,3)
179  << " hitPrePos: " <<Amg::toString(pre_pos, 3)
180  << " EDep: " << hit.depositEnergy() << " EKin: " << hit.kineticEnergy()
181  << " pdgId: " << hit.particleEncoding()
182  <<" "<<m_idHelperSvc->toStringGasGap(layid));
183 
184  // Distance should be in the range [0, 0.9] mm, excepting
185  // - particles pass through the wire plane near the edges
186  // - secondary particles created inside the gas gap that go through the gas gap partially.
187  // Most of such particles are not muons and have low kinetic energy.
188  // - particle with trajectory parallel to the sTGC wire plane
189  const double wire_pitch = detEl->wirePitch();
190  if ((dist_wire > 0.) && (std::abs(hit.particleEncoding()) == 13) && (dist_wire > (wire_pitch/2))) {
191  ATH_MSG_DEBUG("Distance to the nearest wire (" << dist_wire << ") is greater than expected.");
192  ATH_MSG_DEBUG("Hit globalPos: "<<Amg::toString(GPOS,2)
193  << " globalPrePos: " << Amg::toString(pre_pos, 2)
194  << " EDeposited: " << hit.depositEnergy()
195  << " EKinetic: " << hit.kineticEnergy()
196  << " pdgID: " << hit.particleEncoding()
197  << " "<<m_idHelperSvc->toStringGasGap(layid));
198  }
199 
200  // Do not digitize hits that are too far from the nearest wire
201  if (dist_wire > wire_pitch) {
202  return {};
203  }
204 
205  // Get the gamma pdf parameters associated with the distance of closest approach.
206  const GammaParameter gamParam = getGammaParameter(dist_wire);
207  const double par_kappa = gamParam.kParameter;
208  const double par_theta = gamParam.thetaParameter;
209  const double most_prob_time = getMostProbableArrivalTime(dist_wire);
210  // Compute the most probable value of the gamma pdf
211  double gamma_mpv = (par_kappa - 1) * par_theta;
212  // If the most probable value is less than zero, then set it to zero
213  if (gamma_mpv < 0.) {gamma_mpv = 0.;}
214  const double t0_par = most_prob_time - gamma_mpv;
215 
216  // Digit time follows a gamma distribution, so a value val is
217  // chosen using a gamma random generator then is shifted by t0
218  // to account for drift time.
219  // Note: CLHEP::RandGamma takes the parameters k and lambda,
220  // where lambda = 1 / theta.
221  double digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.rndmEngine, par_kappa, 1/par_theta);
222 
223  // Sometimes, digit_time is negative because t0_par can be negative.
224  // In such case, discard the negative value and shoot RandGamma for another value.
225  // However, if that has already been done many times then set digit_time to zero
226  // in order to avoid runaway loop.
227  constexpr int shoot_limit = 4;
228  int shoot_counter = 0;
229  while (digit_time < 0.) {
230  if (shoot_counter > shoot_limit) {
231  digit_time = 0.;
232  break;
233  }
234  digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.rndmEngine, par_kappa, 1/par_theta);
235  ++shoot_counter;
236  }
237 
238  ATH_MSG_DEBUG("sTgcDigitMaker distance = " << dist_wire
239  << ", time = " << digit_time
240  << ", k parameter = " << par_kappa
241  << ", theta parameter = " << par_theta
242  << ", most probable time = " << most_prob_time);
243 
244  bool isValid = false;
246  if (condContainers.efficiencies) {
247  const double efficiency = condContainers.efficiencies->getEfficiency(layid);
248  // Lose Hits to match HV efficiency
249  if (CLHEP::RandFlat::shoot(condContainers.rndmEngine,0.0,1.0) > efficiency) return {};
250  }
251 
252 
253  sTgcDigitVec digits{};
254 
255  //const double stripPropagationTime = 3.3*CLHEP::ns/CLHEP::m * detEl->distanceToReadout(posOnSurf_strip, elemId); // 8.5*ns/m was used until MC10.
256  const double stripPropagationTime = 0.; // 8.5*ns/m was used until MC10.
257 
258  double sDigitTimeWire = digit_time;
259  double sDigitTimePad = sDigitTimeWire;
260  double sDigitTimeStrip = sDigitTimeWire + stripPropagationTime;
261 
262  uint16_t bctag = 0;
263 
264  //##################################################################################
265  //######################################### strip readout ##########################
266  //##################################################################################
267  ATH_MSG_DEBUG("sTgcDigitMaker::strip response ");
268  int channelType = sTgcIdHelper::sTgcChannelTypes::Strip;
269 
270  Identifier newId = idHelper.channelID(layid,idHelper.multilayer(layid),
271  idHelper.gasGap(layid), channelType, 1, isValid);
272 
273  // get strip surface
274  int surfHash_strip = detEl->surfaceHash(newId);
275  const Trk::PlaneSurface& SURF_STRIP = detEl->surface(surfHash_strip); // get the strip surface
276 
277  const Amg::Vector3D hitOnSurface_strip = SURF_STRIP.transform().inverse()*glob_ionization_pos;
278  Amg::Vector2D posOnSurf_strip {Amg::Vector2D::Zero()};
279 
281  //This block is used to apply As-Built and BLine corrections for dedicated studies.
282  Amg::Vector3D posAfterAsBuilt {Amg::Vector3D::Zero()};
283  detEl->spacePointPosition(newId, hitOnSurface_strip.x(), hitOnSurface_strip.y(), posAfterAsBuilt);
284  posOnSurf_strip = posAfterAsBuilt.block<2,1>(0,0);
285  } else {
286  posOnSurf_strip = hitOnSurface_strip.block<2,1>(0,0);
287  }
288 
289  bool insideBounds = SURF_STRIP.insideBounds(posOnSurf_strip);
290  if(!insideBounds) {
291  ATH_MSG_DEBUG("Outside of the strip surface boundary : " << m_idHelperSvc->toString(newId) << "; local position " <<posOnSurf_strip );
292  return {};
293  }
294 
295  //************************************ find the nearest readout element **************************************
296 
297  int stripNumber = detEl->stripNumber(posOnSurf_strip, newId);
298  if( stripNumber == -1 ){
299  // Verify if the energy deposit is at the boundary
300  const double new_posX = (posOnSurf_strip.x() > 0.0)? posOnSurf_strip.x() - length_tolerance
301  : posOnSurf_strip.x() + length_tolerance;
302  const Amg::Vector2D new_position(new_posX, posOnSurf_strip.y());
303  stripNumber = detEl->stripNumber(new_position, newId);
304  // Skip hit if still unable to obtain strip number
305  if (stripNumber < 1) {
306  ATH_MSG_WARNING("Failed to obtain strip number " << m_idHelperSvc->toString(newId) );
307  ATH_MSG_WARNING("Position on strip surface = (" << posOnSurf_strip.x() << ", " << posOnSurf_strip.y() << ")");
308  return {};
309  }
310  }
311  newId = idHelper.channelID(layid, idHelper.multilayer(layid),
312  idHelper.gasGap(layid), channelType, stripNumber, isValid);
313  if(!isValid && stripNumber != -1) {
314  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(newId) );
315  return {};
316  }
317 
318  const int NumberOfStrips = detEl->numberOfStrips(newId);
319  const double stripHalfPitch = detEl->channelPitch(newId)*0.5; // 3.2/2 = 1.6 mm
320 
321  //************************************ conversion of energy to charge **************************************
322 
323  // Typical ionized charge in pC per keV deposited. The constant is determined from ionization
324  // study with Garfield program. A note titled "Charge Energy Relation" which outlines
325  // conversion can be found here:
326  // https://cernbox.cern.ch/index.php/apps/files/?dir=/__myshares/Documents (id:274113) // link is dead
327  const double ionized_charge = (5.65E-6)*energyDeposit/CLHEP::keV;
328 
329  // To get avalanche gain, polya function is taken from Blum paper https://inspirehep.net/literature/807304
330  // 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);
331 
332  // Mean value for total gain due to E field;
333  // To calculate this gain from polya distibution, we replace in gamma PDF:
334  // alpha = 1+theta and
335  // beta = 1+theta/mean
336  // With these substitutions, gamma PDF gives the same sampling values as those from polya PDF.
337  const double gain = CLHEP::RandGamma::shoot(condContainers.rndmEngine, 1. + m_theta, (1. + m_theta)/m_meanGasGain);
338 
339  // total charge after avalanche
340  const double total_charge = gain*ionized_charge;
341 
342  //************************************ spread charge among readout element **************************************
343 
344  // Charge Spread including tan(theta) resolution term.
345  const double tan_theta = GLODIRE.perp()/GLODIRE.z();
346  // The angle dependance on strip resolution goes as tan^2(angle)
347  const double angle_dependency = std::hypot(m_posResIncident, m_posResAngular * tan_theta);
348 
349  const double cluster_posX = posOnSurf_strip.x();
350  double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.rndmEngine, cluster_posX, m_StripResolution*angle_dependency);
351 
352  // Each readout plane reads about half the total charge produced on the wire,
353  // including a tan(theta) term to describe the increase of charge with incident angle
354  const double norm = 0.5 * total_charge * m_stripChargeScale * std::hypot(1, m_chargeAngularFactor * tan_theta);
355  // Strip cluster charge profile described by a double Gaussian.
356  // The independent parameter x is in the units of strip channel, one strip channel = 3.2 mm,
357  // so convert position from mm to strip channel if it is not already.
358  std::unique_ptr<TF1> clusterProfile = std::make_unique<TF1>("fgaus",
359  "[0]*exp(-0.5*(x/[1])^2)+[2]*exp(-0.5*(x/[3])^2)",
360  -300., 300.);
361  clusterProfile->SetParameters(norm * m_clusterProfile[0], // normalization of 1st Gaussian
362  m_clusterProfile[1], // sigma of 1st Gaussian
363  norm * m_clusterProfile[2], // normalization of 2nd Gaussian
364  m_clusterProfile[3]); // sigma of 2nd Gaussian
365 
366  // Lower limit on strip charge (arbitrary limit), in pC, which has the same units as the parameter ionized_charge.
367  constexpr double tolerance_charge = 0.0005;
368  // Set a maximum number of neighbour strips to avoid very long loop. This is an arbitrary number.
369  constexpr unsigned int max_neighbor = 10;
370 
371  // Spread charge on the strips that are on the upper half of the strip cluster
372  for (unsigned int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
373  int currentStrip = stripNumber + iStrip;
374  if (currentStrip > NumberOfStrips) break;
375 
376  // Get the strip identifier and create the digit
377  newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
378  channelType, currentStrip, isValid);
379  if (isValid) {
381  if (!detEl->stripPosition(newId, locpos)) {
382  ATH_MSG_WARNING("Failed to obtain local position for identifier " << m_idHelperSvc->toString(newId) );
383  }
384 
385  // Estimate the digit charge
386  // Position with respect to the peak of the charge curve
387  double x_relative = locpos.x() - peak_position;
388  // In clusterProfile curve, position should be in the units of strip channel
389  double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
390  // If charge is too small, stop creating neighbor strip
391  if (charge < tolerance_charge) break;
392 
393  // Estimate digit time
394  double strip_time = sDigitTimeStrip;
395  // Strip time response can be delayed due to the resistive layer.
396  // A correction would be required if the actual VMM front-end doesn't re-align the strip timing.
397  if (m_doTimeOffsetStrip) {
398  // Determine how far the current strip is from the middle strip
399  int indexFromMiddleStrip = iStrip;
400  if ((iStrip > 0) && ((x_relative + (0.75 - iStrip * 2) * stripHalfPitch) < 0))
401  indexFromMiddleStrip = iStrip - 1;
402  // Add time delay due to resistive layer
403  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
404  }
405 
406  addDigit(digits, newId, bctag, strip_time, charge);
407 
408  ATH_MSG_VERBOSE("Created a strip digit: strip number = " << currentStrip << ", charge = " << charge
409  << ", time = " << strip_time << ", time offset = " << strip_time-sDigitTimeStrip
410  << ", neighbor index = " << iStrip
411  << ", strip position = "<<Amg::toString(locpos, 2));
412  }
413  }
414 
415  // The lower half of the strip cluster
416  for (unsigned int iStrip = 1; iStrip <= max_neighbor; ++iStrip) {
417  int currentStrip = stripNumber - iStrip;
418  if (currentStrip < 1) break;
419 
420  newId = idHelper.channelID(layid,idHelper.multilayer(layid),
421  idHelper.gasGap(layid), channelType, currentStrip, isValid);
422  if (isValid) {
424  if (!detEl->stripPosition(newId, locpos)) {
425  ATH_MSG_WARNING("Failed to obtain local position for identifier " << m_idHelperSvc->toString(newId) );
426  }
427 
428  // Estimate the digit charge
429  double x_relative = locpos.x() - peak_position;
430  double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
431  if (charge < tolerance_charge) break;
432 
433  // Estimate digit time
434  double strip_time = sDigitTimeStrip;
435  // Time delay due to resistive layer
436  if (m_doTimeOffsetStrip) {
437  int indexFromMiddleStrip = ((x_relative + (iStrip * 2 - 0.75) * stripHalfPitch) > 0)? iStrip - 1 : iStrip;
438  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
439  }
440 
441  addDigit(digits, newId, bctag, strip_time, charge);
442 
443  ATH_MSG_VERBOSE("Created a strip digit: strip number = " << currentStrip << ", charge = " << charge
444  << ", time = " << strip_time << ", time offset = " << strip_time-sDigitTimeStrip
445  << ", neighbor index = " << iStrip
446  << ", strip position = " << Amg::toString(locpos, 2));
447  }
448  }
449  // end of strip digitization
450 
451  if(m_channelTypes==1) {
452  ATH_MSG_WARNING("Only digitize strip response !");
453  return digits;
454  }
455 
456  //##################################################################################
457  //######################################### pad readout ##########################
458  //##################################################################################
459  ATH_MSG_DEBUG("sTgcDigitMaker::pad response ");
460  channelType = sTgcIdHelper::sTgcChannelTypes::Pad;
461 
462  Identifier PAD_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
463  idHelper.gasGap(layid), channelType, 1);// find the a pad id
464  //************************************ find the nearest readout element **************************************
465  int surfHash_pad = detEl->surfaceHash(PAD_ID);
466  const Trk::PlaneSurface& SURF_PAD = detEl->surface(surfHash_pad); // get the pad surface
467 
468  const Amg::Vector3D hitOnSurface_pad = SURF_PAD.transform().inverse()*glob_ionization_pos;
469  const Amg::Vector2D posOnSurf_pad(hitOnSurface_pad.x(), hitOnSurface_pad.y());
470 
471 
472  insideBounds = SURF_PAD.insideBounds(posOnSurf_pad);
473 
474  if(insideBounds) {
475  int padNumber = detEl->stripNumber(posOnSurf_pad, PAD_ID);
476  if( padNumber == -1 ){
477  // Verify if the energy deposit is at the boundary
478  const double new_posX = (posOnSurf_pad.x()>0.0)? posOnSurf_pad.x()-length_tolerance
479  : posOnSurf_pad.x()+length_tolerance;
480  const double new_posY = (posOnSurf_pad.y()>0.0)? posOnSurf_pad.y()-length_tolerance
481  : posOnSurf_pad.y()+length_tolerance;
482  const Amg::Vector2D new_position(new_posX, new_posY);
483  padNumber = detEl->stripNumber(new_position, PAD_ID);
484  // Skip hit if still unable to obtain pad number
485  if (padNumber < 1) {
486  ATH_MSG_WARNING("Failed to obtain pad number " << m_idHelperSvc->toString(PAD_ID) );
487  ATH_MSG_WARNING("Position on pad surface = (" << posOnSurf_pad.x() << ", " << posOnSurf_pad.y() << ")");
488  return digits;
489  }
490  }
491  newId = idHelper.channelID(layid, idHelper.multilayer(layid),
492  idHelper.gasGap(layid), channelType, padNumber, isValid);
493  if(isValid) {
494  // Find centre position of pad
496  if (!detEl->stripPosition(newId, padPos)) {
497  ATH_MSG_ERROR("Failed to obtain local position for neighbor pad with identifier " << m_idHelperSvc->toString(newId) );
498  return digits;
499  }
500  // Pad sharing needs to look at position on hit vs pad boundaries
501  const Amg::Vector2D diff = posOnSurf_pad - padPos;
502  double halfPadWidthX = 0.5*detEl->getPadDesign(newId)->channelWidth(posOnSurf_pad, true, true);
503  double halfPadWidthY = 0.5*detEl->getPadDesign(newId)->channelWidth(posOnSurf_pad, false);
504 
505  // Charge sharing happens within 4mm window for pads
506  // i.e. the charge is spread over a 2D gaussian of total radius about 4mm
507  // This value depends on actual width of the distribution, but we don't have the
508  // width of the charge distribution for pads. So lets consider 2.5*sigma, where
509  // sigma is the width of the charge distribution for strips.
510  double deltaX = halfPadWidthX - std::abs(diff.x());
511  double deltaY= halfPadWidthY - std::abs(diff.y());
512  bool isNeighX = deltaX < 2.5*m_clusterProfile[3];
513  bool isNeighY = deltaY < 2.5*m_clusterProfile[3];
514  // Pad width can be calculated to be very slightly larger than it should due to rounding errors
515  // 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.
516  if (deltaX < 0.) deltaX = 0.1;
517  if (deltaY < 0.) deltaY = 0.1;
518 
519  if (m_doPadSharing && (isNeighX || isNeighY)){
520  std::pair<int, int> padEtaPhi(detEl->getPadDesign(newId)->channelNumber(padPos));
521  // Phi == 1 at the right in the local geometry
522  // In local coordinates, the pad to the right has phi' = phi - 1
523  int newPhi = diff.x() > 0 ? padEtaPhi.second-1 : padEtaPhi.second+1;
524  int newEta = diff.y() > 0 ? padEtaPhi.first+1 : padEtaPhi.first-1;
525  bool validEta = newEta > 0 && newEta < detEl->getPadDesign(newId)->nPadH+1;
526  bool validPhi = newPhi > 0 && newPhi < detEl->getPadDesign(newId)->nPadColumns+1;
527 
528  if (isNeighX && isNeighY && validEta && validPhi){
529  // 4 pads total; makes life a bit harder. Corner of 4 valid pads
530  Identifier neigh_ID_X = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
531  channelType, padEtaPhi.first, newPhi, isValid);
532  Identifier neigh_ID_Y = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
533  channelType, newEta, padEtaPhi.second, isValid);
534  Identifier neigh_ID_XY = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
535  channelType, newEta, newPhi, isValid);
536  double xQfraction = getPadChargeFraction(deltaX);
537  double yQfraction = getPadChargeFraction(deltaY);
538 
539  // Main pad gets 1 - Qfraction of total
540  addDigit(digits, neigh_ID_X, bctag, sDigitTimePad, xQfraction*(1.-yQfraction)*0.5*total_charge);
541  addDigit(digits, neigh_ID_Y, bctag, sDigitTimePad, yQfraction*(1.-xQfraction)*0.5*total_charge);
542  addDigit(digits, neigh_ID_XY, bctag, sDigitTimePad, xQfraction*yQfraction*0.5*total_charge);
543  addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*total_charge);
544  } else if (isNeighX && validPhi){
545  // There is only 1 neighbor, immediately to the left or right.
546  Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
547  channelType, padEtaPhi.first, newPhi, 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 xQfraction = getPadChargeFraction(deltaX);
552  addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction)*0.5*total_charge);
553  addDigit(digits, neigh_ID, bctag, sDigitTimePad, xQfraction*0.5*total_charge);
554  }
555  else if (isNeighY && validEta){
556  // There is only 1 neighbor, immediately above or below
557  Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
558  channelType, newEta, padEtaPhi.second, isValid);
559 
560  // NeighborPad gets Qfraction of the total pad charge: 0.5*total_charge
561  // Main pad gets 1 - Qfraction of total
562  double yQfraction = getPadChargeFraction(deltaX);
563  addDigit(digits, newId, bctag, sDigitTimePad, (1.-yQfraction)*0.5*total_charge);
564  addDigit(digits, neigh_ID, bctag, sDigitTimePad, yQfraction*0.5*total_charge);
565  }
566 
567  }
568  else{
569  // No charge sharing: hit is nicely isolated within pad
570  addDigit(digits, newId, bctag, sDigitTimePad, 0.5*total_charge);
571  }
572 
573  }
574  else if(padNumber != -1) {
575  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(newId) );
576  }
577  }
578  else {
579  ATH_MSG_DEBUG("Outside of the pad surface boundary :" << m_idHelperSvc->toString(PAD_ID)<< " local position " <<posOnSurf_pad );
580  }
581 
582  if(m_channelTypes==2) {
583  ATH_MSG_WARNING("Only digitize strip/pad response !");
584  return digits;
585  }
586 
587 
588  //##################################################################################
589  //######################################### wire readout ##########################
590  //##################################################################################
591  ATH_MSG_DEBUG("sTgcDigitMaker::wire response ");
592  channelType = sTgcIdHelper::sTgcChannelTypes::Wire;
593 
594  // Find the ID of the first wiregroup
595  Identifier WIREGP_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
596  idHelper.gasGap(layid), channelType, 1);
597 
598  //************************************ find the nearest readout element **************************************
599  insideBounds = SURF_WIRE.insideBounds(posOnSurf_wire);
600 
601  if(insideBounds) {
602  // Determine the wire number
603 
604  int wiregroupNumber = detEl->stripNumber(posOnSurf_wire, WIREGP_ID);
605  if( wiregroupNumber == -1 ){
606  // Verify if the energy deposit is at the boundary
607  const double new_posX = (posOnSurf_wire.x() > 0.0)? posOnSurf_wire.x() - length_tolerance
608  : posOnSurf_wire.x() + length_tolerance;
609  const Amg::Vector2D new_position(new_posX, posOnSurf_wire.y());
610  wiregroupNumber = detEl->stripNumber(new_position, WIREGP_ID);
611  // Skip hit if still unable to obtain pad number
612  if (wiregroupNumber < 1) {
613  ATH_MSG_WARNING("Failed to obtain wire number " << m_idHelperSvc->toString(WIREGP_ID) );
614  ATH_MSG_WARNING("Position on wire surface = (" << posOnSurf_wire.x() << ", " << posOnSurf_wire.y() << ")");
615  return digits;
616  }
617  }
618 
619  // Find ID of the actual wiregroup
620  newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
621  channelType, wiregroupNumber, isValid);
622 
623  if(isValid) {
624  int NumberOfWiregroups = detEl->numberOfStrips(newId);
625  if(wiregroupNumber>=1&&wiregroupNumber<=NumberOfWiregroups) {
626  addDigit(digits, newId, bctag, sDigitTimeWire, total_charge);
627  }
628  } // end of if(isValid)
629  else if (wiregroupNumber != -1){
630  ATH_MSG_ERROR("Failed to obtain wiregroup identifier " << m_idHelperSvc->toString(newId) );
631  }
632  }
633  else {
634  ATH_MSG_DEBUG("Outside of the wire surface boundary :" << m_idHelperSvc->toString(WIREGP_ID)<< " local position " <<posOnSurf_wire );
635  }
636  // end of wire digitization
637 
638  return digits;
639 }

◆ executeDigi() [2/2]

sTgcDigitMaker::sTgcDigitVec sTgcDigitMaker::executeDigi ( const DigiConditions condContainers,
const TimedHit hit 
) const

Definition at line 53 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/src/sTgcDigitMaker.cxx.

53  {
54  // Extract energy deposit from the hit
55  double energyDeposit = hit->energyDeposit();
56  if (energyDeposit == 0.) return {}; // Ignore hits with no energy deposit
57 
58  // Retrieve the detector element for the given hit
59  const MuonGMR4::MuonDetectorManager* detMgr = condContainers.detMgr;
60 
61  // Convert hit to identifier and retrieve sTgc readout element
62  const Identifier hitId = hit->identify();
63  ATH_MSG_DEBUG("Retrieving detector element for: "<< m_idHelperSvc->toStringDetEl(hitId) << " energyDeposit "<< energyDeposit );
64  const MuonGMR4::sTgcReadoutElement* readoutElement = detMgr->getsTgcReadoutElement(hitId);
65 
66  // Projecting the hit position on wire surface given the hit position
67  // and direction w.r.t. the wire plane
68  Amg::Vector3D locHitDir = xAOD::toEigen(hit->localDirection());
69  Amg::Vector3D locHitPos = xAOD::toEigen(hit->localPosition());
70  ATH_MSG_VERBOSE("sTgc hit: time " << hit->globalTime()
71  << " position " << Amg::toString(locHitPos, 2)
72  << " direction" << Amg::toString(locHitDir, 2)
73  << " mclink " << hit->genParticleLink() << " PDG ID " << hit->pdgId() );
74  ATH_MSG_VERBOSE("Projecting hit to Wire Surface" );
75  const double scale = Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0.).value_or(0);
76  Amg::Vector3D hitOnWireSurf = locHitPos + scale * locHitDir;
77  ATH_MSG_VERBOSE("Hit on Wire Surface: " << Amg::toString(hitOnWireSurf, 2));
78 
79  /* Determine the closest wire and the distance of closest approach
80  * Since most particles pass through the the wire plane between two wires,
81  * the nearest wire should be one of these two wire. Otherwise, the particle's
82  * trajectory is uncommon, and such rare case is not supported yet.
83  *
84  * Finding that nearest wire follows the following steps:
85  * - Compute the distance to the wire at the center of the current wire pitch
86  * - Compute the distance to the other adjacent wire
87  */
88  const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
89  const Identifier wireLayId = idHelper.channelID(hitId,
90  idHelper.multilayer(hitId),
91  idHelper.gasGap(hitId),
92  ReadoutChannelType::Wire,
93  1);
94 
95  const MuonGMR4::WireGroupDesign& wireDesign{readoutElement->wireDesign(wireLayId)};
96  Amg::Vector2D hitOnWire2D = hitOnWireSurf.block<2,1>(0,0);
97  if(!wireDesign.insideTrapezoid(hitOnWire2D)) {
98  return {};
99  }
100  std::pair<int, int> wireGrpWireNum = wireDesign.wireNumber(hitOnWire2D);
101  int wireNumber = -1;
102  wireNumber = wireDesign.numPitchesToGroup(wireGrpWireNum.first) + wireGrpWireNum.second;
103  // If wire number is invalid, verify if hit is near the edge of the chamber.
104  // Try to get the wire number from the x and y coordinates of the hit position rather than the hit on wire surface.
105  const int numWires = wireDesign.nAllWires();
106  if((wireNumber < 1) || (wireNumber > numWires)) {
107  wireGrpWireNum = wireDesign.wireNumber(locHitPos.block<2,1>(0,0));
108  wireNumber = wireDesign.numPitchesToGroup(wireGrpWireNum.first) + wireGrpWireNum.second;
109  if((wireNumber < 1) || (wireNumber > numWires)) {
110  ATH_MSG_WARNING("Unable to retrieve the wire number, skipping the hit: " << m_idHelperSvc->toString(hitId));
111  return {};
112  }
113  }
114  // Compute the position of the ionization and its distance to the closest wire.
115  Ionization ionization = pointClosestApproach(wireDesign, wireNumber, locHitPos, locHitDir, hit->stepLength());
116  double distToWire = ionization.distance;
117  if(distToWire > 0.) {
118  // Determine on which side of the wire does the particle cross
119  // -1 if particle crosses the wire surface between wireNumber-1 and wireNumber
120  // +1 if particle crosses the wire surface between wireNumber and wireNumber+1
121  int adjacent = 1;
122  if (ionization.posOnSegment.x() < ionization.posOnWire.x()) {adjacent = -1;}
123  // Find the position of the ionization with respect to the adjacent wire
124  Ionization ionizationAdj = pointClosestApproach(wireDesign, wireNumber+adjacent, locHitPos, locHitDir, hit->stepLength());
125  // Determine if the adjacent wire is closer to the hit on wire surface
126  double distToWireAdj = ionizationAdj.distance;
127  if ((distToWireAdj > 0.) && (distToWireAdj < distToWire)) {
128  distToWire = distToWireAdj;
129  wireNumber += adjacent;
130  ionization = std::move(ionizationAdj);
131  }
132  } else {
133  ATH_MSG_DEBUG("Failed to get the distance between the wire number = " << wireNumber
134  << " and hit at " <<Amg::toString(hitOnWireSurf, 2)
135  << ". Number of wires = " << numWires
136  << ", "<<m_idHelperSvc->toStringGasGap(wireLayId));
137  return {};
138  }
139 
140  // Update the position of ionization on the wire surface
141  hitOnWireSurf = ionization.posOnWire;
142  ATH_MSG_VERBOSE("Ionization_info: distance: " << ionization.distance
143  << " posOnTrack: " <<Amg::toString(ionization.posOnSegment, 3)
144  << " posOnWire: " << Amg::toString(ionization.posOnWire, 3)
145  << " hit local Pos: " << Amg::toString(locHitPos,2)
146  << " hit local Dir: " <<Amg::toString(locHitDir, 2)
147  << " EDep: " << hit->energyDeposit() << " EKin: " << hit->kineticEnergy()
148  << " pdgId: " << hit->pdgId()
149  << "gasGap: "<<m_idHelperSvc->toStringGasGap(wireLayId));
150 
151  // Distance should be in the range [0, 0.9] mm, excepting
152  // - particles pass through the wire plane near the edges
153  // - secondary particles created inside the gas gap that go through the gas gap partially.
154  // Most of such particles are not muons and have low kinetic energy.
155  // - particle with trajectory parallel to the sTGC wire plane
156  const double wirePitch = wireDesign.stripPitch();
157  if ((distToWire > 0.) && (std::abs(hit->pdgId()) == 13) && (distToWire > (0.5 * wirePitch))) {
158  ATH_MSG_DEBUG("Distance to the nearest wire (" << distToWire << ") is greater than expected.");
159  ATH_MSG_DEBUG("Hit local Pos: "<<Amg::toString(locHitPos, 2)
160  << " hit local Dir: " <<Amg::toString(locHitDir, 2)
161  << " EDeposited: " << hit->energyDeposit()
162  << " EKinetic: " << hit->kineticEnergy()
163  << " pdgID: " << hit->pdgId()
164  << " gasGap: "<<m_idHelperSvc->toStringGasGap(wireLayId));
165  }
166  // Do not digitize hits that are too far from the nearest wire
167  if (distToWire > wirePitch) {
168  return {};
169  }
170 
171  // Get the gamma pdf parameters associated with the distance of closest approach.
172  const GammaParameter gamParam = getGammaParameter(distToWire);
173  const double par_kappa = gamParam.kParameter;
174  const double par_theta = gamParam.thetaParameter;
175  const double most_prob_time = getMostProbableArrivalTime(distToWire);
176  // Compute the most probable value of the gamma pdf
177  double gamma_mpv = (par_kappa - 1) * par_theta;
178  // If the most probable value is less than zero, then set it to zero
179  if (gamma_mpv < 0.) {gamma_mpv = 0.;}
180  const double t0_par = most_prob_time - gamma_mpv;
181 
182  // Digit time follows a gamma distribution, so a value val is
183  // chosen using a gamma random generator then is shifted by t0
184  // to account for drift time.
185  // Note: CLHEP::RandGamma takes the parameters k and lambda,
186  // where lambda = 1 / theta.
187  double digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.rndEngine, par_kappa, 1/par_theta);
188 
189  // Sometimes, digitTime is negative because t0_par can be negative.
190  // In such case, discard the negative value and shoot RandGamma for another value.
191  // However, if that has already been done many times then set digitTime to zero
192  // in order to avoid runaway loop.
193 
194  constexpr int shoot_limit = 4;
195  int shoot_counter = 0;
196  while (digitTime < 0.) {
197  if (shoot_counter > shoot_limit) {
198  digitTime = 0.;
199  break;
200  }
201  digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.rndEngine, par_kappa, 1/par_theta);
202  ++shoot_counter;
203  }
204 
205  ATH_MSG_DEBUG("sTgcDigitMaker distance = " << distToWire
206  << ", time = " << digitTime
207  << ", k parameter = " << par_kappa
208  << ", theta parameter = " << par_theta
209  << ", most probable time = " << most_prob_time);
210 
212  if (condContainers.efficiencies) {
213  const double efficiency = condContainers.efficiencies->getEfficiency(wireLayId);
214  // Lose Hits to match HV efficiency
215  if (CLHEP::RandFlat::shoot(condContainers.rndEngine,0.0,1.0) > efficiency) return {};
216  }
217 
218  sTgcDigitVec digits{};
219 
220  double sDigitTimeWire = digitTime;
221  double sDigitTimePad = sDigitTimeWire;
222  double sDigitTimeStrip = sDigitTimeWire;
223 
224  uint16_t bctag = 0;
225 
226  //##################################################################################
227  //######################################### strip readout ##########################
228  //##################################################################################
229  ATH_MSG_DEBUG("sTgcDigitMaker::strip response ");
230  bool isValid = false;
231  int channelType = ReadoutChannelType::Strip;
232  const Identifier stripLayId = idHelper.channelID(wireLayId, idHelper.multilayer(wireLayId),
233  idHelper.gasGap(wireLayId), channelType, 1, isValid);
234 
235  const MuonGMR4::StripDesign& stripDesign{readoutElement->stripDesign(stripLayId)};
236  // Rotate clockwise 90 in xy plane to get to strip plane orientation, and then extract x,y
237  const Amg::Vector2D hitOnStripSurf = (Amg::getRotateZ3D(-90. * Gaudi::Units::deg) * hitOnWireSurf).block<2,1>(0,0);
238 
239  bool insideBounds = stripDesign.insideTrapezoid(hitOnStripSurf);
240  if(!insideBounds) {
241  ATH_MSG_DEBUG("Outside of the strip surface boundary : " << m_idHelperSvc->toString(stripLayId) << "; local position " <<Amg::toString(hitOnStripSurf, 2));
242  return {};
243  }
244 
245  //************************************ find the nearest readout element **************************************
246  // Required precision on length in mm
247  constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
248  int stripNumber = stripDesign.stripNumber(hitOnStripSurf);
249  if( stripNumber < 0){
250  // Verify if the energy deposit is at the boundary
251  const double newPosX = (hitOnStripSurf.x() > 0.0)? hitOnStripSurf.x() - tolerance_length
252  : hitOnStripSurf.x() + tolerance_length;
253  const Amg::Vector2D newPos(newPosX, hitOnStripSurf.y());
254  stripNumber = stripDesign.stripNumber(newPos);
255  // Skip hit if still unable to obtain strip number
256  if (stripNumber < 0) {
257  ATH_MSG_WARNING("Failed to obtain strip number " << m_idHelperSvc->toString(stripLayId) );
258  ATH_MSG_WARNING("Position on strip surface = (" << hitOnStripSurf.x() << ", " << hitOnStripSurf.y() << ")");
259  return {};
260  }
261  }
262  isValid = false;
263  const Identifier stripHitId = idHelper.channelID(stripLayId, idHelper.multilayer(stripLayId),
264  idHelper.gasGap(stripLayId), channelType, stripNumber, isValid);
265  if(!isValid && stripNumber != -1) {
266  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(stripHitId) );
267  return {};
268  }
269 
270  const int numStrips = stripDesign.numStrips();
271  const double stripHalfPitch = 0.5 * stripDesign.stripPitch();
272 
273  //************************************ conversion of energy to charge **************************************
274 
275  // Typical ionized charge in pC per keV deposited. The constant is determined from ionization
276  // study with Garfield program. A note titled "Charge Energy Relation" which outlines
277  // conversion can be found here:
278  // https://gitlab.cern.ch/carleton-ATLAS/carleton-nsw/stgc-ionization-gain/-/blob/master/Charge_Energy_Relation.pdf?ref_type=heads
279  const double ionized_charge = (5.65E-6)*energyDeposit/CLHEP::keV;
280 
281  // To get avalanche gain, polya function is taken from Blum paper https://inspirehep.net/literature/807304
282  // 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);
283 
284  // Mean value for total gain due to E field;
285  // To calculate this gain from polya distibution, we replace in gamma PDF:
286  // alpha = 1+theta and
287  // beta = 1+theta/mean
288  // With these substitutions, gamma PDF gives the same sampling values as those from polya PDF.
289  const double gain = CLHEP::RandGamma::shoot(condContainers.rndEngine, 1. + m_theta, (1. + m_theta)/m_meanGasGain);
290 
291  // total charge after avalanche
292  const double total_charge = gain*ionized_charge;
293 
294  // Charge Spread including tan(theta) resolution term.
295  const double tan_theta = locHitDir.perp()/locHitDir.z();
296  // The angle dependance on strip resolution goes as tan^2(angle)
297  const double angle_dependency = std::hypot(m_posResIncident, m_posResAngular * tan_theta);
298 
299  const double cluster_posX = hitOnStripSurf.x();
300  double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.rndEngine, cluster_posX, m_StripResolution*angle_dependency);
301 
302  // Each readout plane reads about half the total charge produced on the wire,
303  // including a tan(theta) term to describe the increase of charge with incident angle
304  const double norm = 0.5 * total_charge;
305 
306  // Lower limit on strip charge (arbitrary limit), in pC, which has the same units as the parameter ionized_charge.
307  constexpr double tolerance_charge = 0.0005;
308  // Set a maximum number of neighbour strips to avoid very long loop. This is an arbitrary number.
309  constexpr unsigned int max_neighbor = 10;
310 
311  // Spread charge on the strips that are on the upper half of the strip cluster
312  for (unsigned int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
313  int currentStrip = stripNumber + iStrip;
314  if (currentStrip > numStrips) {
315  ATH_MSG_VERBOSE("Breaking the upper half strip loop stripNumber: " << currentStrip << " > " << numStrips);
316  break;
317  }
318 
319  // Get the strip identifier and create the digit
320  isValid = false;
321  const Identifier currentStripId = idHelper.channelID(stripLayId, idHelper.multilayer(stripLayId), idHelper.gasGap(stripLayId),
322  channelType, currentStrip, isValid);
323  if (isValid) {
324  Amg::Vector2D currentStripPos = readoutElement->localChannelPosition(currentStripId);
325  if (currentStripPos == Amg::Vector2D::Zero()) {
326  ATH_MSG_VERBOSE("Failed to obtain local position for identifier " << m_idHelperSvc->toString(currentStripId) );
327  }
328 
329  // Estimate the digit charge
330  // Position with respect to the peak of the charge curve
331  double x_relative = currentStripPos.x() - peak_position;
332  // In clusterProfile curve, position should be in the units of strip channel
333  double charge = std::hypot(1, m_chargeAngularFactor * tan_theta) * norm * chargeIntegral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
334  // If charge is too small, stop creating neighbor strip
335  if (charge < tolerance_charge) {
336  ATH_MSG_VERBOSE("Breaking the upper half strip loop stripCharge: " << charge << " < " << tolerance_charge);
337  break;
338  }
339 
340  // Estimate digit time
341  double strip_time = sDigitTimeStrip;
342  // Strip time response can be delayed due to the resistive layer.
343  // A correction would be required if the actual VMM front-end doesn't re-align the strip timing.
344  if (m_doTimeOffsetStrip) {
345  // Determine how far the current strip is from the middle strip
346  int indexFromMiddleStrip = iStrip;
347  if ((iStrip > 0) && ((x_relative + (0.75 - iStrip * 2) * stripHalfPitch) < 0))
348  indexFromMiddleStrip = iStrip - 1;
349  // Add time delay due to resistive layer
350  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
351  }
352 
353  addDigit(digits, currentStripId, bctag, strip_time, charge);
354 
355  ATH_MSG_VERBOSE("Created a strip digit: strip number = " << currentStrip << ", charge = " << charge
356  << ", time = " << strip_time << ", time offset = " << strip_time-sDigitTimeStrip
357  << ", neighbor index = " << iStrip
358  << ", strip position = "<<Amg::toString(currentStripPos, 2));
359  }
360  }
361 
362  // The lower half of the strip cluster
363  for (unsigned int iStrip = 1; iStrip <= max_neighbor; ++iStrip) {
364  int currentStrip = stripNumber - iStrip;
365  if (currentStrip < 1) {
366  ATH_MSG_VERBOSE("Breaking the lower half strip loop stripNumber: " << currentStrip << " < 1 ");
367  break;
368  }
369 
370  isValid = false;
371  const Identifier currentStripId = idHelper.channelID(stripLayId, idHelper.multilayer(stripLayId), idHelper.gasGap(stripLayId),
372  channelType, currentStrip, isValid);
373  if (isValid) {
374  Amg::Vector2D currentStripPos = readoutElement->localChannelPosition(currentStripId);
375  if (currentStripPos == Amg::Vector2D::Zero()) {
376  ATH_MSG_WARNING("Failed to obtain local position for identifier " << m_idHelperSvc->toString(currentStripId) );
377  }
378 
379  // Estimate the digit charge
380  double x_relative = currentStripPos.x() - peak_position;
381  double charge = std::hypot(1, m_chargeAngularFactor * tan_theta) * norm * chargeIntegral((x_relative/(2*stripHalfPitch) - 0.5), (x_relative/(2*stripHalfPitch) + 0.5));
382  if (charge < tolerance_charge) {
383  ATH_MSG_VERBOSE("Breaking the lower half strip loop stripCharge: " << charge << " < " << tolerance_charge);
384  break;
385  }
386 
387  // Estimate digit time
388  double strip_time = sDigitTimeStrip;
389  // Time delay due to resistive layer
390  if (m_doTimeOffsetStrip) {
391  int indexFromMiddleStrip = ((x_relative + (iStrip * 2 - 0.75) * stripHalfPitch) > 0)? iStrip - 1 : iStrip;
392  strip_time += getTimeOffsetStrip(indexFromMiddleStrip);
393  }
394 
395  addDigit(digits, currentStripId, 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(currentStripPos, 2));
401  }
402  }
403  // end of strip digitization
404 
405  if(m_digitMode == digitMode::StripsOnly) {
406  ATH_MSG_WARNING("Only digitize strip response !");
407  return digits;
408  }
409 
410  //##################################################################################
411  //######################################### pad readout ##########################
412  //##################################################################################
413  ATH_MSG_DEBUG("sTgcDigitMaker::pad response ");
414  channelType = ReadoutChannelType::Pad;
415 
416  Identifier padLayId = idHelper.channelID(wireLayId, idHelper.multilayer(wireLayId),
417  idHelper.gasGap(wireLayId), channelType, 1);
418 
419  const MuonGMR4::PadDesign& padDesign{readoutElement->padDesign(padLayId)};
420  //************************************ find the nearest readout element **************************************
421 
422  // Since the orientation of pad layer is the same as wirelayer, the x,y can be taken directly
423  // from the hit position on wire surface
424  const Amg::Vector2D hitOnPadSurf = hitOnWireSurf.block<2,1>(0,0);
425 
426 
427  insideBounds = padDesign.insideTrapezoid(hitOnPadSurf);
428 
429  if(insideBounds) {
430  std::pair<uint, uint> padEtaPhi = padDesign.channelNumber(hitOnPadSurf);
431  unsigned int padEta = padEtaPhi.first;
432  unsigned int padPhi = padEtaPhi.second;
433  if( padEta < 1 || padPhi < 1){
434  // Verify if the energy deposit is at the boundary
435  const double newPosX = (hitOnPadSurf.x()>0.0)? hitOnPadSurf.x() - tolerance_length
436  : hitOnPadSurf.x() + tolerance_length;
437  const double newPosY = (hitOnPadSurf.y()>0.0)? hitOnPadSurf.y() - tolerance_length
438  : hitOnPadSurf.y() + tolerance_length;
439  const Amg::Vector2D newPosition(newPosX, newPosY);
440  padEtaPhi = padDesign.channelNumber(newPosition);
441  padEta = padEtaPhi.first;
442  padPhi = padEtaPhi.second;
443  // Skip hit if still unable to obtain pad number
444  if( padEta < 1 || padPhi < 1){
445  ATH_MSG_WARNING("Failed to obtain pad number for " << m_idHelperSvc->toString(padLayId) );
446  ATH_MSG_WARNING("Position on pad surface = (" << hitOnPadSurf.x() << ", " << hitOnPadSurf.y() << ")");
447  return digits;
448  }
449  }
450  isValid = false;
451  const Identifier padHitId = idHelper.padID(padLayId, idHelper.multilayer(padLayId),
452  idHelper.gasGap(padLayId), channelType, padEta, padPhi, isValid);
453  if(isValid) {
454  // Find centre position of pad
455  Amg::Vector2D padPos = readoutElement->localChannelPosition(padHitId);
456  if (padPos == Amg::Vector2D::Zero()) {
457  ATH_MSG_ERROR("Failed to obtain local position for neighbor pad with identifier " << m_idHelperSvc->toString(padHitId) );
458  return digits;
459  }
460 
461  // Pad sharing needs to look at position on hit vs pad boundaries
462  const Amg::Vector2D diff = hitOnPadSurf - padPos;
463  double halfPadHeight = 0.5 * readoutElement->padHeight(padHitId);
464  // Extracting pad corners to get the width of the pad at hit position
465  std::array<Amg::Vector2D, 4> padHitCorners = padDesign.padCorners(padEtaPhi);
466  double padBottomBase = (padHitCorners[0] - padHitCorners[1]).norm();
467  double padTopBase = (padHitCorners[2] - padHitCorners[3]).norm();
468  // Linearly interpolating pad width (as done by channelWidth function in R3)
469  // Interpolating pad width at the y-position of the hit (diff.y()) using pad top and bottom bases.
470  // The expression below computes the width at a relative vertical position inside the pad.
471  // Specifically: (1 + diff.y() / halfPadHeight) * 0.25 = (y_from_bottom / padHeight)
472  double halfPadWidth = 0.5 * padBottomBase + 0.25 * (1 + diff.y()/halfPadHeight) * (padTopBase - padBottomBase);
473 
474  // Charge sharing happens within 4mm window for pads
475  // i.e. the charge is spread over a 2D gaussian of total radius about 4mm
476  // This value depends on actual width of the distribution, but we don't have the
477  // width of the charge distribution for pads. So lets consider 2.5*sigma, where
478  // sigma is the width of the charge distribution for strips.
479  double deltaX = halfPadWidth - std::abs(diff.x());
480  double deltaY = halfPadHeight - std::abs(diff.y());
481  bool isNeighX = deltaX < 2.5*m_clusterParams[1];
482  bool isNeighY = deltaY < 2.5*m_clusterParams[1];
483  // Pad width can be calculated to be very slightly larger than it should due to rounding errors
484  // 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.
485  if (deltaX < 0.) deltaX = 0.1;
486  if (deltaY < 0.) deltaY = 0.1;
487 
488  if (m_doPadSharing && (isNeighX || isNeighY)){
489  // Phi == 1 at the right in the local geometry
490  // In local coordinates, the pad to the right has phi' = phi - 1
491  unsigned int newPhi = diff.x() > 0 ? padEtaPhi.second-1 : padEtaPhi.second+1;
492  unsigned int newEta = diff.y() > 0 ? padEtaPhi.first+1 : padEtaPhi.first-1;
493  bool validEta = newEta > 0 && newEta < readoutElement->numPadEta(padHitId) + 1;
494  bool validPhi = newPhi > 0 && newPhi < readoutElement->numPadPhi(padHitId) + 1;
495 
496  if (isNeighX && isNeighY && validEta && validPhi){
497  // 4 pads total; makes life a bit harder. Corner of 4 valid pads
498  isValid = false;
499  const Identifier neigh_ID_X = idHelper.padID(padHitId, idHelper.multilayer(padHitId), idHelper.gasGap(padHitId),
500  channelType, padEta, newPhi, isValid);
501  isValid = false;
502  const Identifier neigh_ID_Y = idHelper.padID(padHitId, idHelper.multilayer(padHitId), idHelper.gasGap(padHitId),
503  channelType, newEta, padPhi, isValid);
504  isValid = false;
505  const Identifier neigh_ID_XY = idHelper.padID(padHitId, idHelper.multilayer(padHitId), idHelper.gasGap(padHitId),
506  channelType, newEta, newPhi, isValid);
507  double xQfraction = getPadChargeFraction(deltaX);
508  double yQfraction = getPadChargeFraction(deltaY);
509 
510  // Main pad gets 1 - Qfraction of total
511  addDigit(digits, neigh_ID_X, bctag, sDigitTimePad, xQfraction*(1.-yQfraction)*0.5*total_charge);
512  addDigit(digits, neigh_ID_Y, bctag, sDigitTimePad, yQfraction*(1.-xQfraction)*0.5*total_charge);
513  addDigit(digits, neigh_ID_XY, bctag, sDigitTimePad, xQfraction*yQfraction*0.5*total_charge);
514  addDigit(digits, padHitId, bctag, sDigitTimePad, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*total_charge);
515  } else if (isNeighX && validPhi){
516  // There is only 1 neighbor, immediately to the left or right.
517  isValid = false;
518  const Identifier neigh_ID = idHelper.padID(padHitId, idHelper.multilayer(padHitId), idHelper.gasGap(padHitId),
519  channelType, padEta, newPhi, isValid);
520 
521  // NeighborPad gets Qfraction of the total pad charge: 0.5*total_charge
522  // Main pad gets 1 - Qfraction of total
523  double xQfraction = getPadChargeFraction(deltaX);
524  addDigit(digits, padHitId, bctag, sDigitTimePad, (1.-xQfraction)*0.5*total_charge);
525  addDigit(digits, neigh_ID, bctag, sDigitTimePad, xQfraction*0.5*total_charge);
526  }
527  else if (isNeighY && validEta){
528  // There is only 1 neighbor, immediately above or below
529  isValid = false;
530  const Identifier neigh_ID = idHelper.padID(padHitId, idHelper.multilayer(padHitId), idHelper.gasGap(padHitId),
531  channelType, newEta, padPhi, isValid);
532 
533  // NeighborPad gets Qfraction of the total pad charge: 0.5*total_charge
534  // Main pad gets 1 - Qfraction of total
535  double yQfraction = getPadChargeFraction(deltaX);
536  addDigit(digits, padHitId, bctag, sDigitTimePad, (1.-yQfraction)*0.5*total_charge);
537  addDigit(digits, neigh_ID, bctag, sDigitTimePad, yQfraction*0.5*total_charge);
538  }
539 
540  }
541  else{
542  // No charge sharing: hit is nicely isolated within pad
543  addDigit(digits, padHitId, bctag, sDigitTimePad, 0.5*total_charge);
544  }
545 
546  }
547  else if(padEta != 0 || padPhi != 0) {
548  ATH_MSG_ERROR("Failed to obtain identifier " << m_idHelperSvc->toString(padHitId) );
549  }
550  }
551  else {
552  ATH_MSG_DEBUG("Outside of the pad surface boundary :" << m_idHelperSvc->toString(padLayId)<< " local position " <<Amg::toString(hitOnPadSurf, 2));
553  }
554 
555  if(m_digitMode == digitMode::StripsAndPads) {
556  ATH_MSG_WARNING("Only digitize strip/pad response !");
557  return digits;
558  }
559 
560  //##################################################################################
561  //######################################### wire readout ##########################
562  //##################################################################################
563  ATH_MSG_DEBUG("sTgcDigitMaker::wire response ");
564  channelType = ReadoutChannelType::Wire;
565 
566  // wireLayId already defined at the start
567  //************************************ find the nearest readout element **************************************
568  insideBounds = wireDesign.insideTrapezoid(hitOnWireSurf.block<2,1>(0,0));
569 
570  if(insideBounds) {
571  // Determine the wire number
572  int wiregroupNumber = (wireDesign.wireNumber(hitOnWireSurf.block<2,1>(0,0))).first;
573  if( wiregroupNumber < 1){
574  // Verify if the energy deposit is at the boundary
575  const double newPosX = (hitOnWireSurf.x() > 0.0)? hitOnWireSurf.x() - tolerance_length
576  : hitOnWireSurf.x() + tolerance_length;
577  const Amg::Vector2D newPos(newPosX, hitOnWireSurf.y());
578  wiregroupNumber = (wireDesign.wireNumber(newPos)).first;
579  // Skip hit if still unable to obtain wire number
580  if (wiregroupNumber < 1) {
581  ATH_MSG_WARNING("Failed to obtain wire number " << m_idHelperSvc->toString(wireLayId) );
582  ATH_MSG_WARNING("Position on wire surface = (" << hitOnWireSurf.x() << ", " << hitOnWireSurf.y() << ")");
583  return digits;
584  }
585  }
586 
587  // Find ID of the actual wiregroup
588  isValid = false;
589  const Identifier wireGroupHitId = idHelper.channelID(wireLayId, idHelper.multilayer(wireLayId), idHelper.gasGap(wireLayId),
590  channelType, wiregroupNumber, isValid);
591 
592  if(isValid) {
593  int numWireGroups = readoutElement->numChannels(wireLayId);
594  if(wiregroupNumber >=1 && wiregroupNumber <= numWireGroups) {
595  addDigit(digits, wireGroupHitId, bctag, sDigitTimeWire, total_charge);
596  }
597  } // end of if(isValid)
598  else if (wiregroupNumber != -1){
599  ATH_MSG_ERROR("Failed to obtain wiregroup identifier " << m_idHelperSvc->toString(wireGroupHitId));
600  }
601  }
602  else {
603  ATH_MSG_DEBUG("Outside of the wire surface boundary :" << m_idHelperSvc->toString(wireLayId)<< " local position " <<Amg::toString(hitOnWireSurf, 2));
604  }
605  // end of wire digitization
606 
607  return digits;
608 }

◆ getGammaParameter() [1/2]

sTgcDigitMaker::GammaParameter sTgcDigitMaker::getGammaParameter ( double  distance) const
private

Find the gamma pdf parameters of a given distance.

Definition at line 795 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

795  {
796 
797  const double d = std::abs(distance);
798  // Find the parameters assuming the container is sorted in ascending order of 'lowEdge'
799  int index{-1};
800  for (const auto& par: m_gammaParameter) {
801  if (d < par.lowEdge) {
802  break;
803  }
804  ++index;
805  }
806  return m_gammaParameter.at(index);
807 }

◆ getGammaParameter() [2/2]

GammaParameter sTgcDigitMaker::getGammaParameter ( double  distance) const
private

Retrieves gamma distribution parameters based on distance.

◆ getMostProbableArrivalTime() [1/2]

double sTgcDigitMaker::getMostProbableArrivalTime ( double  distance) const
private

Get the most probable time of arrival.

Definition at line 810 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

810  {
811 
812  const double d{std::abs(distance)};
813  double mpt{0};
814  for (size_t t = 0 ; t < m_mostProbableArrivalTime.size(); ++t){
815  mpt += m_mostProbableArrivalTime[t] * std::pow(d, t);
816  }
817  return mpt;
818 }

◆ getMostProbableArrivalTime() [2/2]

double sTgcDigitMaker::getMostProbableArrivalTime ( double  distance) const
private

Computes the most probable arrival time based on the distance of closest approach.

◆ getPadChargeFraction() [1/2]

double sTgcDigitMaker::getPadChargeFraction ( double  distance)
staticprivate

Definition at line 865 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

865  {
866  // The charge fraction that is found past a distance x away from the
867  // centre of a 2D gaussian distribution of width of cluster profile is
868  // described by a modified error function.
869 
870  // The modified error function perfectly describes
871  // the pad charge sharing distribution figure 16 of the sTGC
872  // testbeam paper https://arxiv.org/pdf/1509.06329.pdf
873 
874  return 0.5 * (1.0 - std::erf( distance / (std::sqrt(2) * m_clusterProfile[3])));
875 }

◆ getPadChargeFraction() [2/2]

static double sTgcDigitMaker::getPadChargeFraction ( double  distance)
staticprivate

Computes charge fraction shared among pads.

◆ getTimeOffsetStrip() [1/2]

double sTgcDigitMaker::getTimeOffsetStrip ( size_t  neighbor_index) const
private

Get digit time offset of a strip depending on its relative position to the strip at the centre of the cluster.

It returns 0 ns by default, as well as when it fails or container is empty.

Definition at line 860 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

860  {
861  return m_timeOffsetStrip.at(std::min(neighbor_index, m_timeOffsetStrip.size() -1));
862 }

◆ getTimeOffsetStrip() [2/2]

double sTgcDigitMaker::getTimeOffsetStrip ( size_t  neighbor_index) const
private

Gets the time offset for a strip cluster.

Get digit time offset of a strip depending on its relative position to the strip at the centre of the cluster. It returns 0 ns by default, as well as when it fails or container is empty.

◆ initialize() [1/2]

StatusCode sTgcDigitMaker::initialize ( )

Initializes sTgcHitIdHelper and sTgcIdHelper, and call the functions to read files containing digitization parameters.

Definition at line 52 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

52  {
53 
54  // Read share/sTGC_Digitization_timeArrivale.dat, containing the digit time of arrival
56 
57  // Read share/sTGC_Digitization_timeOffsetStrip.dat if the the strip time correction is enable
58  if (m_doTimeOffsetStrip) {
60  }
61  // check the digitization channel type
62  if(m_channelTypes!=1 && m_channelTypes!=2 && m_channelTypes!=3) {
63  ATH_MSG_ERROR("Invalid ChannelTypes : " << m_channelTypes << " (valid values are : 1 --> strips ; 2 --> strips / wires ; 3 --> strips / wires / pads)");
64  return StatusCode::FAILURE;
65  }
66  return StatusCode::SUCCESS;
67 }

◆ initialize() [2/2]

StatusCode sTgcDigitMaker::initialize ( )

Initialize digitization parameters, including reading necessary data files.

◆ initMessaging()

void AthMessaging::initMessaging ( ) const
privateinherited

Initialize our message level and MessageSvc.

This method should only be called once.

Definition at line 39 of file AthMessaging.cxx.

40 {
42  m_lvl = m_imsg ?
43  static_cast<MSG::Level>( m_imsg.load()->outputLevel(m_nm) ) :
44  MSG::INFO;
45 }

◆ msg() [1/2]

MsgStream & AthMessaging::msg ( ) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 164 of file AthMessaging.h.

165 {
166  MsgStream* ms = m_msg_tls.get();
167  if (!ms) {
168  if (!m_initialized.test_and_set()) initMessaging();
169  ms = new MsgStream(m_imsg,m_nm);
170  m_msg_tls.reset( ms );
171  }
172 
173  ms->setLevel (m_lvl);
174  return *ms;
175 }

◆ msg() [2/2]

MsgStream & AthMessaging::msg ( const MSG::Level  lvl) const
inlineinherited

The standard message stream.

Returns a reference to the default message stream May not be invoked before sysInitialize() has been invoked.

Definition at line 179 of file AthMessaging.h.

180 { return msg() << lvl; }

◆ msgLvl()

bool AthMessaging::msgLvl ( const MSG::Level  lvl) const
inlineinherited

Test the output level.

Parameters
lvlThe message level to test against
Returns
boolean Indicating if messages at given level will be printed
Return values
trueMessages at level "lvl" will be printed

Definition at line 151 of file AthMessaging.h.

152 {
153  if (!m_initialized.test_and_set()) initMessaging();
154  if (m_lvl <= lvl) {
155  msg() << lvl;
156  return true;
157  } else {
158  return false;
159  }
160 }

◆ pointClosestApproach() [1/2]

sTgcDigitMaker::Ionization sTgcDigitMaker::pointClosestApproach ( const MuonGM::sTgcReadoutElement readoutEle,
const Identifier id,
int  wireNumber,
const Amg::Vector3D preStepPos,
const Amg::Vector3D postStepPos 
) const
private

Determine the points where the distance between two segments is smallest.

Given two segments, e.g. a particle trajectory and a sTGC wire, solve for the two points, the point on the trajectory and the point on the wire, where the distance between the two segments is the smallest.

Positions returned are in the local coordinate frame of the wire plane. Returns an object with distance of -9.99 in case of error.

Definition at line 642 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

646  {
647  // tolerance in mm
648  constexpr double tolerance = 0.001;
649  // minimum segment length
650  constexpr double min_length = 0.1;
651 
652  // Position of the ionization
653  Ionization ionization;
654 
655  // Wire number should be one or greater
656  if (wireNumber < 1) return ionization;
657 
658  // Wire number too large
659  if (wireNumber > detEl->numberOfWires(id)) return ionization;
660 
661  // Finding smallest distance and the points at the smallest distance.
662  // The smallest distance between two lines is perpendicular to both lines.
663  // We can construct two lines in the wire surface local coordinate frame:
664  // - one for the hit segment with equation h0 + t * v_h, where h0 is a point
665  // and v_h is the unit vector of the hit segment
666  // - another for the wire with similar equation w0 + s * v_w, where w0 is a
667  // point and v_w is the unit vector of the wire line
668  // Then it is possible to determine the factors s and t by requiring the
669  // dot product to be zero:
670  // 1. (h0 - w0) \dot v_h = 0
671  // 2. (h0 - w0) \dot v_w = 0
672 
673  // Wire pitch
674  const double wire_pitch = detEl->wirePitch();
675  // Wire local position on the wire plane, the y-coordinate is arbitrary and z-coordinate is zero
676  const double wire_posX = detEl->positionFirstWire(id) + (wireNumber - 1) * wire_pitch;
677  const Amg::Vector3D wire_position(wire_posX, postStepPos.y(), 0.);
678  // The wires are parallel to Y in the wire plane's coordinate frame
679  const Amg::Vector3D wire_direction{Amg::Vector3D::UnitY()};
680 
681  // particle trajectory
682  Amg::Vector3D hit_direction{postStepPos - preStepPos};
683  const double seg_length = hit_direction.mag();
684  if (seg_length > tolerance) hit_direction /= seg_length;
685 
686  // Find the point on the track segment that is closest to the wire
687  if (seg_length < min_length) {
688  ionization.posOnSegment = postStepPos;
689  ionization.posOnWire = wire_position;
690  ionization.distance = std::hypot(postStepPos.x() - wire_posX, postStepPos.z());
691  return ionization;
692  }
693 
694  // Dot product between the wire and hit direction
695  const double cos_theta = wire_direction.dot(hit_direction);
696  // distance between the hit and wire
697  const Amg::Vector3D dist_wire_hit = postStepPos - wire_position;
698 
699  // Verifier the special case where the two lines are parallel
700  if (std::abs(cos_theta - 1.0) < tolerance) {
701  ATH_MSG_DEBUG("The track segment is parallel to the wire, position of digit is undefined");
702  ionization.posOnSegment = 0.5 * (postStepPos + preStepPos);
703  ionization.posOnWire = Amg::Vector3D(wire_posX, ionization.posOnSegment.y(), 0.0);
704  ionization.distance = std::hypot(ionization.posOnSegment.x() - wire_posX,
705  ionization.posOnSegment.z());
706  return ionization;
707  }
708 
709  // Perpendicular component squared
710  const double sin_theta_2 = 1.0 - cos_theta * cos_theta;
711 
712  //* Point on the hit segment
713  const double factor_hit = (-dist_wire_hit.dot(hit_direction)
714  + dist_wire_hit.dot(wire_direction) * cos_theta) / sin_theta_2;
715  Amg::Vector3D ionization_pos = postStepPos + factor_hit * hit_direction;
716 
717  // If the point is on the track segment, then compute the other point on the wire.
718  // Otherwise, set the ionization at the pre-step position and compute where it
719  // should be on the wire.
720  Amg::Vector3D pos_on_wire{Amg::Vector3D::Zero()};
721  if (factor_hit < seg_length) {
722  //* Point on the wire line
723  const double factor_wire = (dist_wire_hit.dot(wire_direction)
724  - dist_wire_hit.dot(hit_direction) * cos_theta) / sin_theta_2;
725  pos_on_wire = wire_position + factor_wire * wire_direction;
726  } else {
727  ionization_pos = preStepPos;
728  pos_on_wire = wire_position - (seg_length * cos_theta) * wire_direction;
729  }
730 
731  // Save the distance and ionization position
732  ionization.distance = (ionization_pos - pos_on_wire).mag();
733  ionization.posOnSegment = ionization_pos;
734  ionization.posOnWire = pos_on_wire;
735 
736  return ionization;
737 }

◆ pointClosestApproach() [2/2]

sTgcDigitMaker::Ionization sTgcDigitMaker::pointClosestApproach ( const MuonGMR4::WireGroupDesign wireDesign,
int  wireNumber,
const Amg::Vector3D locHitPos,
const Amg::Vector3D locHitDir,
const double  stepLength 
) const
private

Computes the closest approach between a trajectory and a wire segment.

Given two segments, e.g. a particle trajectory and a sTGC wire, solve for the two points, the point on the trajectory and the point on the wire, where the distance between the two segments is the smallest.

Positions returned are in the local coordinate frame of the wire plane. Returns an object with distance of -9.99 in case of error.

Definition at line 613 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/src/sTgcDigitMaker.cxx.

617  {
618 
619  constexpr double angular_tolerance = 1e-3;
620  // Position of the ionization
621  Ionization ionization;
622  // Finding smallest distance and the points at the smallest distance.
623  // The smallest distance between two lines is perpendicular to both lines.
624  // Previous logic was to find the perpendicular distance between two lines using projection geometry
625  // We can construct two lines in the wire surface local coordinate frame:
626  // - one for the hit segment with equation h0 + t * v_h, where h0 is a point
627  // and v_h is the unit vector of the hit segment
628  // - another for the wire with similar equation w0 + s * v_w, where w0 is a
629  // point and v_w is the unit vector of the wire line
630  // Then it is possible to determine the closest points on each line
631  // by requiring that the vector between them is perpendicular to both:
632  // 1. (h0 + t*v_h - w0 - s*v_w) · v_h = 0
633  // 2. (h0 + t*v_h - w0 - s*v_w) · v_w = 0
634 
635  // We have replaced this logic with using Amg::intersect<3> method
636 
637  // Geometry setup
638  const double wirePitch = wireDesign.stripPitch();
639  const double wirePosX = wireDesign.firstStripPos().x() + (wireNumber - 1) * wirePitch;
640  const Amg::Vector3D wireDir(wireDesign.stripDir().x(), wireDesign.stripDir().y(), 0.0);
641  const Amg::Vector3D wirePos(wirePosX, locHitPos.y(), 0.);
642 
643  // Use Amg::intersect to find closest point on hit segment to wire plane
644  std::optional<double> scaleHit = Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0);
645  if (!scaleHit || std::abs(std::abs(wireDir.dot(locHitDir)) - 1.0) < angular_tolerance) {
646  ATH_MSG_DEBUG("The track segment is parallel to the wire, position of digit is undefined");
647  ionization.posOnSegment = locHitPos;
648  ionization.posOnWire = wirePos;
649  ionization.distance = std::hypot(locHitPos.x() - wirePosX, locHitPos.z());
650  return ionization;
651  }
652  // Position on hit segment
653  Amg::Vector3D ionizationPos = locHitPos + scaleHit.value() * locHitDir;
654 
655  if (scaleHit.value() > stepLength) {
656  ionization.posOnSegment = locHitPos;
657  const Amg::Vector3D closestPointToWirePlane = locHitPos + stepLength * locHitDir;
658  ionization.posOnWire = wirePos;
659  ionization.distance = std::abs(closestPointToWirePlane.z());
660  return ionization;
661  }
662 
663  // Project ionization position onto the wire line
664  const double scaleWire = (ionizationPos - wirePos).dot(wireDir);
665  const Amg::Vector3D closestPointOnWire = wirePos + scaleWire * wireDir;
666 
667  // Fill ionization result
668  ionization.posOnSegment = ionizationPos;
669  ionization.posOnWire = closestPointOnWire;
670  ionization.distance = (ionizationPos - closestPointOnWire).mag();
671 
672  return ionization;
673 }

◆ readFileOfTimeArrival() [1/2]

StatusCode sTgcDigitMaker::readFileOfTimeArrival ( )
private

Read share/sTGC_Digitization_timeArrival.dat.

Definition at line 755 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

755  {
756  // Verify the file sTGC_Digitization_timeArrival.dat exists
757  const std::string file_name = "sTGC_Digitization_timeArrival.dat";
758  std::string file_path = PathResolver::find_file(file_name, "DATAPATH");
759  if(file_path.empty()) {
760  ATH_MSG_FATAL("readFileOfTimeWindowOffset(): Could not find file " << file_name );
761  return StatusCode::FAILURE;
762  }
763 
764  // Open the sTGC_Digitization_timeArrival.dat file
765  std::ifstream ifs{file_path, std::ios::in};
766  if(ifs.bad()) {
767  ATH_MSG_FATAL("sTgcDigitMaker: Failed to open time of arrival file " << file_name );
768  return StatusCode::FAILURE;
769  }
770 
771  // Read the sTGC_Digitization_timeWindowOffset.dat file
772  std::string line;
773  GammaParameter param{};
774 
775  while (std::getline(ifs, line)) {
776  std::string key;
777  std::istringstream iss(line);
778  iss >> key;
779  if (key.compare("bin") == 0) {
780  iss >> param.lowEdge >> param.kParameter >> param.thetaParameter;
781  m_gammaParameter.push_back(param);
782  } else if (key.compare("mpv") == 0) {
783  double mpt;
784  int idx{0};
785  while (iss >> mpt) {m_mostProbableArrivalTime[idx++] = mpt;}
786  }
787  }
788 
789  // Close the file
790  ifs.close();
791  return StatusCode::SUCCESS;
792 }

◆ readFileOfTimeArrival() [2/2]

StatusCode sTgcDigitMaker::readFileOfTimeArrival ( )
private

Reads time arrival data file.

◆ readFileOfTimeOffsetStrip() [1/2]

StatusCode sTgcDigitMaker::readFileOfTimeOffsetStrip ( )
private

Read share/sTGC_Digitization_timeOffsetStrip.dat.

Definition at line 821 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

821  {
822  // Verify the file sTGC_Digitization_timeOffsetStrip.dat exists
823  const std::string file_name = "sTGC_Digitization_timeOffsetStrip.dat";
824  std::string file_path = PathResolver::find_file(file_name, "DATAPATH");
825  if(file_path.empty()) {
826  ATH_MSG_FATAL("readFileOfTimeWindowOffset(): Could not find file " << file_name );
827  return StatusCode::FAILURE;
828  }
829 
830  // Open the sTGC_Digitization_timeOffsetStrip.dat file
831  std::ifstream ifs{file_path, std::ios::in};
832  if(ifs.bad()) {
833  ATH_MSG_FATAL("Failed to open time of arrival file " << file_name );
834  return StatusCode::FAILURE;
835  }
836 
837  // Initialize the container to store the time offset.
838  // The number of parameters, 6, corresponds to the number of lines to be read
839  // from sTGC_Digitization_timeOffsetStrip.dat.
840  // Setting the default offset to 0 ns.
841 
842  // Read the input file
843  std::string line;
844  size_t index{0};
845  double value{0.0};
846  while (std::getline(ifs, line)) {
847  std::string key;
848  std::istringstream iss(line);
849  iss >> key;
850  if (key.compare("strip") == 0) {
851  iss >> index >> value;
852  if (index >= m_timeOffsetStrip.size()) continue;
854  }
855  }
856  return StatusCode::SUCCESS;
857 }

◆ readFileOfTimeOffsetStrip() [2/2]

StatusCode sTgcDigitMaker::readFileOfTimeOffsetStrip ( )
private

Reads strip time offset data file.

◆ setLevel()

void AthMessaging::setLevel ( MSG::Level  lvl)
inherited

Change the current logging level.

Use this rather than msg().setLevel() for proper operation with MT.

Definition at line 28 of file AthMessaging.cxx.

29 {
30  m_lvl = lvl;
31 }

Member Data Documentation

◆ ATLAS_THREAD_SAFE

std::atomic_flag m_initialized AthMessaging::ATLAS_THREAD_SAFE = ATOMIC_FLAG_INIT
mutableprivateinherited

Messaging initialized (initMessaging)

Definition at line 141 of file AthMessaging.h.

◆ m_applyAsBuiltBLines

bool sTgcDigitMaker::m_applyAsBuiltBLines {false}
private

◆ m_channelTypes

int sTgcDigitMaker::m_channelTypes {3}
private

define offsets and widths of time windows for signals from wiregangs and strips.

The offsets are defined as relative time diffference with respect to the time after TOF and cable length corrections. Bunch crossing time is specified.

Definition at line 148 of file MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h.

◆ m_chargeAngularFactor

double sTgcDigitMaker::m_chargeAngularFactor {4.0}
private

◆ m_clusterParams

constexpr std::array<double, 2> sTgcDigitMaker::m_clusterParams {0.573, 1.092}
staticconstexprprivate

◆ m_clusterProfile

constexpr std::array<double, 4> sTgcDigitMaker::m_clusterProfile {0.350, 0.573, 0.186, 1.092}
staticconstexprprivate

◆ m_digitMode

digitMode sTgcDigitMaker::m_digitMode = AllChType
private

define offsets and widths of time windows for signals from wiregroups and strips.

The offsets are defined as relative time diffference with respect to the time after TOF and cable length corrections. Bunch crossing time is specified.

Definition at line 185 of file MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h.

◆ m_doPadSharing

bool sTgcDigitMaker::m_doPadSharing {false}
private

◆ m_doTimeOffsetStrip

bool sTgcDigitMaker::m_doTimeOffsetStrip {false}
private

◆ m_gammaParameter

std::vector< GammaParameter > sTgcDigitMaker::m_gammaParameter
private

◆ m_idHelperSvc

const Muon::IMuonIdHelperSvc * sTgcDigitMaker::m_idHelperSvc {nullptr}
private

◆ m_imsg

std::atomic<IMessageSvc*> AthMessaging::m_imsg { nullptr }
mutableprivateinherited

MessageSvc pointer.

Definition at line 135 of file AthMessaging.h.

◆ m_lvl

std::atomic<MSG::Level> AthMessaging::m_lvl { MSG::NIL }
mutableprivateinherited

Current logging level.

Definition at line 138 of file AthMessaging.h.

◆ m_meanGasGain

double sTgcDigitMaker::m_meanGasGain {5.e4}
private

◆ m_mostProbableArrivalTime

std::array< double, 5 > sTgcDigitMaker::m_mostProbableArrivalTime {make_array<double, 5>(0.)}
private

◆ m_msg_tls

boost::thread_specific_ptr<MsgStream> AthMessaging::m_msg_tls
mutableprivateinherited

MsgStream instance (a std::cout like with print-out levels)

Definition at line 132 of file AthMessaging.h.

◆ m_nm

std::string AthMessaging::m_nm
privateinherited

Message source name.

Definition at line 129 of file AthMessaging.h.

◆ m_posResAngular

double sTgcDigitMaker::m_posResAngular {0.305/m_StripResolution}
private

◆ m_posResIncident

double sTgcDigitMaker::m_posResIncident {1.}
private

◆ m_stripChargeScale

double sTgcDigitMaker::m_stripChargeScale {0.4}
private

◆ m_StripResolution

double sTgcDigitMaker::m_StripResolution {0.0949}
private

◆ m_theta

double sTgcDigitMaker::m_theta {0.8}
private

◆ m_timeOffsetStrip

std::array< double, 6 > sTgcDigitMaker::m_timeOffsetStrip {make_array<double, 6>(0.)}
private

The documentation for this class was generated from the following files:
MuonGMR4::sTgcReadoutElement::localChannelPosition
Amg::Vector2D localChannelPosition(const Identifier &measId) const
Returns the local pad/strip/wireGroup position.
AthMessaging::m_lvl
std::atomic< MSG::Level > m_lvl
Current logging level.
Definition: AthMessaging.h:138
AllowedVariables::e
e
Definition: AsgElectronSelectorTool.cxx:37
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
MuonGMR4::sTgcReadoutElement::padHeight
double padHeight(const Identifier &measId) const
Returns the height of all the pads that are not adjacent to the bottom edge of the trapezoid active a...
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
MuonGMR4::StripDesign
Definition: StripDesign.h:30
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:158
python.SystemOfUnits.mm
float mm
Definition: SystemOfUnits.py:98
MuonGMR4::WireGroupDesign
Definition: WireGroupDesign.h:23
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
MuonGMR4::MuonDetectorManager
Definition: MuonPhaseII/MuonDetDescr/MuonReadoutGeometryR4/MuonReadoutGeometryR4/MuonDetectorManager.h:62
MuonGMR4::sTgcReadoutElement::numPadEta
unsigned int numPadEta(const Identifier &measId) const
Returns the number of pads in the eta direction in the given layer.
MuonGMR4::sTgcReadoutElement::numChannels
unsigned int numChannels(const Identifier &measId) const
Returns the number of strips / wires / pads in a given gasGap.
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:860
Amg::Vector2D
Eigen::Matrix< double, 2, 1 > Vector2D
Definition: GeoPrimitives.h:48
sTgcDigitMaker::readFileOfTimeOffsetStrip
StatusCode readFileOfTimeOffsetStrip()
Read share/sTGC_Digitization_timeOffsetStrip.dat.
Definition: MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx:821
index
Definition: index.py:1
sTgcDigitMaker::m_timeOffsetStrip
std::array< double, 6 > m_timeOffsetStrip
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:137
hist_file_dump.d
d
Definition: hist_file_dump.py:142
sTgcDigitMaker::m_clusterParams
static constexpr std::array< double, 2 > m_clusterParams
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h:175
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
sTgcDigitMaker::chargeIntegral
double chargeIntegral(double N, double M) const
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/src/sTgcDigitMaker.cxx:763
MuonGMR4::sTgcReadoutElement::stripDesign
const StripDesign & stripDesign(const Identifier &measId) const
Retrieves the readoutElement Layer given the Identifier/Hash.
MuonGMR4::sTgcReadoutElement::numPadPhi
unsigned int numPadPhi(const Identifier &measId) const
Returns the number of pads in the Phi direction in the given gasGap layer.
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:810
sTgcDigitMaker::m_chargeAngularFactor
double m_chargeAngularFactor
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:164
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
deg
#define deg
Definition: SbPolyhedron.cxx:17
mc.diff
diff
Definition: mc.SFGenPy8_MuMu_DD.py:14
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:755
JetTiledMap::N
@ N
Definition: TiledEtaPhiMap.h:44
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
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:867
yodamerge_tmp.scale
scale
Definition: yodamerge_tmp.py:138
AthMessaging::m_imsg
std::atomic< IMessageSvc * > m_imsg
MessageSvc pointer.
Definition: AthMessaging.h:135
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
Athena::getMessageSvc
IMessageSvc * getMessageSvc(bool quiet=false)
Definition: getMessageSvc.cxx:20
Trk::energyDeposit
@ energyDeposit
Definition: MeasurementType.h:32
Amg::getRotateZ3D
Amg::Transform3D getRotateZ3D(double angle)
get a rotation transformation around Z-axis
Definition: GeoPrimitivesHelpers.h:270
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:642
physics_parameters.file_name
string file_name
Definition: physics_parameters.py:32
MuonGMR4::StripDesign::stripDir
const Amg::Vector2D & stripDir() const
Vector pointing along the strip.
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.
sTgcDigitMaker::m_digitMode
digitMode m_digitMode
define offsets and widths of time windows for signals from wiregroups and strips.
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h:185
MuonGMR4::sTgcReadoutElement::wireDesign
const WireGroupDesign & wireDesign(const Identifier &measId) const
Retrieves the readoutElement Layer given the Identifier/Hash.
AthMessaging::AthMessaging
AthMessaging()
Default constructor:
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
TrigConf::MSGTC::Level
Level
Definition: Trigger/TrigConfiguration/TrigConfBase/TrigConfBase/MsgStream.h:21
sTgcDigitMaker::m_posResAngular
double m_posResAngular
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:159
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
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:148
sTgcDigitMaker::getGammaParameter
GammaParameter getGammaParameter(double distance) const
Find the gamma pdf parameters of a given distance.
Definition: MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx:795
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
sTgcDigitMaker::AllChType
@ AllChType
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h:43
MuonGMR4::WireGroupDesign::wireNumber
std::pair< int, int > wireNumber(const Amg::Vector2D &extPos) const
Returns a pair where the first component indicate the wire group number and the second one returns th...
MuonGMR4::StripDesign::insideTrapezoid
bool insideTrapezoid(const Amg::Vector2D &extPos) const
Checks whether an external point is inside the trapezoidal area.
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
sTgcDigitMaker::StripsOnly
@ StripsOnly
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h:41
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:132
sTGCSimHit::sTGCId
HitID sTGCId() const
Definition: sTGCSimHit.h:51
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
Preparation.mode
mode
Definition: Preparation.py:107
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:740
compareGeometries.deltaX
float deltaX
Definition: compareGeometries.py:32
dot.dot
def dot(G, fn, nodesToHighlight=[])
Definition: dot.py:5
MuonGM::MuonPadDesign::channelWidth
double channelWidth(const Amg::Vector2D &pos, bool measPhi, bool preciseMeas=false) const
calculate local channel width
Definition: MuonPadDesign.h:142
AthMessaging::msg
MsgStream & msg() const
The standard message stream.
Definition: AthMessaging.h:164
MuonGM::sTgcReadoutElement::channelPitch
double channelPitch(const Identifier &id) const
Channel pitch.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:589
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:155
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:151
charge
double charge(const T &p)
Definition: AtlasPID.h:986
sTgcDigitMaker::StripsAndPads
@ StripsAndPads
Definition: MuonPhaseII/MuonDigitization/sTgcDigitizationR4/sTgcDigitizationR4/sTgcDigitMaker.h:42
sTgcIdHelper
Definition: sTgcIdHelper.h:55
sTgcDigitMaker::getPadChargeFraction
static double getPadChargeFraction(double distance)
Definition: MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx:865
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
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
MuonGMR4::sTgcReadoutElement
Definition: MuonPhaseII/MuonDetDescr/MuonReadoutGeometryR4/MuonReadoutGeometryR4/sTgcReadoutElement.h:20
sTGCSimHit::particleEncoding
int particleEncoding() const
Definition: sTGCSimHit.h:45
MuonGMR4::sTgcReadoutElement::padDesign
const PadDesign & padDesign(const Identifier &measId) const
Retrieves the readoutElement Layer given the Identifier/Hash.
sTgcDigitMaker::m_clusterProfile
static constexpr std::array< double, 4 > m_clusterProfile
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:162
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:139
sTgcDigitMaker::m_mostProbableArrivalTime
std::array< double, 5 > m_mostProbableArrivalTime
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:134
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
python.Constants.INFO
int INFO
Definition: Control/AthenaCommon/python/Constants.py:15
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
sTgcDigitMaker::m_stripChargeScale
double m_stripChargeScale
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:166
known
Definition: TrigBStoxAODTool.cxx:107
AthMessaging::m_nm
std::string m_nm
Message source name.
Definition: AthMessaging.h:129
DeMoScan.first
bool first
Definition: DeMoScan.py:534
sTgcDigitMaker::m_theta
double m_theta
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:149
LArNewCalib_DelayDump_OFC_Cali.idx
idx
Definition: LArNewCalib_DelayDump_OFC_Cali.py:69
sTGCSimHit::depositEnergy
double depositEnergy() const
Definition: sTGCSimHit.h:47
sTgcDigitMaker::m_applyAsBuiltBLines
bool m_applyAsBuiltBLines
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:168
sTgcDigitMaker::m_StripResolution
double m_StripResolution
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:157
sTgcDigitMaker::sTgcDigitVec
std::vector< std::unique_ptr< sTgcDigit > > sTgcDigitVec
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:65
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
MuonGMR4::StripDesign::stripPitch
double stripPitch() const
Distance between two adjacent strips.
AthMessaging::initMessaging
void initMessaging() const
Initialize our message level and MessageSvc.
Definition: AthMessaging.cxx:39
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:150
MuonGMR4::PadDesign
Definition: PadDesign.h:24
AthMessaging::m_msg_tls
boost::thread_specific_ptr< MsgStream > m_msg_tls
MsgStream instance (a std::cout like with print-out levels)
Definition: AthMessaging.h:132
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
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
python.SystemOfUnits.ms
float ms
Definition: SystemOfUnits.py:148
Identifier
Definition: IdentifierFieldParser.cxx:14
MuonGMR4::StripDesign::firstStripPos
const Amg::Vector2D & firstStripPos() const
Vector indicating the first strip position.