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, 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 {10}
 
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}
 
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, 2 > m_clusterProfile {0.573, 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,
bool  applyAsBuiltBLines 
)

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

38  : AthMessaging ("sTgcDigitMaker"),
39  m_idHelperSvc{idHelperSvc},
40  m_channelTypes{channelTypes},
41  m_meanGasGain{meanGasGain},
42  m_doPadSharing{doPadChargeSharing},
43  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 735 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 69 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 790 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 805 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

805  {
806 
807  const double d{std::abs(distance)};
808  double mpt{0};
809  for (size_t t = 0 ; t < m_mostProbableArrivalTime.size(); ++t){
810  mpt += m_mostProbableArrivalTime[t] * std::pow(d, t);
811  }
812  return mpt;
813 }

◆ 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 860 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 855 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

855  {
856  return m_timeOffsetStrip.at(std::min(neighbor_index, m_timeOffsetStrip.size() -1));
857 }

◆ 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 49 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 637 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 750 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 816 of file MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx.

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

◆ 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 147 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, 2> sTgcDigitMaker::m_clusterProfile {0.573, 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_StripResolution

double sTgcDigitMaker::m_StripResolution {0.0949}
private

◆ m_theta

double sTgcDigitMaker::m_theta {10}
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:157
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:855
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:816
index
Definition: index.py:1
sTgcDigitMaker::m_timeOffsetStrip
std::array< double, 6 > m_timeOffsetStrip
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:136
hist_file_dump.d
d
Definition: hist_file_dump.py:142
sTgcDigitMaker::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:805
sTgcDigitMaker::m_chargeAngularFactor
double m_chargeAngularFactor
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:163
InDet::adjacent
bool adjacent(unsigned int strip1, unsigned int strip2)
Definition: SCT_ClusteringTool.cxx:45
MuonGM::sTgcReadoutElement::getDesign
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:279
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:750
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:872
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:637
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:158
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:147
sTgcDigitMaker::getGammaParameter
GammaParameter getGammaParameter(double distance) const
Find the gamma pdf parameters of a given distance.
Definition: MuonDigitization/sTGC_Digitization/src/sTgcDigitMaker.cxx:790
MuonGM::sTgcReadoutElement::surfaceHash
virtual int surfaceHash(const Identifier &id) const override final
returns the hash to be used to look up the surface and transform in the MuonClusterReadoutElement tra...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:257
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:131
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:735
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:154
sTGCSimHit::kineticEnergy
double kineticEnergy() const
Definition: sTGCSimHit.h:48
sTGCSimHit::globalTime
double globalTime() const
Definition: sTGCSimHit.h:41
createCoolChannelIdFile.par
par
Definition: createCoolChannelIdFile.py:28
sTgcDigitMaker::m_doPadSharing
bool m_doPadSharing
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:150
charge
double charge(const T &p)
Definition: AtlasPID.h:991
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:860
MuonGM::MuonPadDesign::nPadColumns
int nPadColumns
Definition: MuonPadDesign.h:69
MuonGM::MuonChannelDesign::wireNumber
int wireNumber(const Amg::Vector2D &pos) const
calculate the sTGC wire number. The method can return a value outside the range [1,...
Definition: MuonChannelDesign.h:259
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.
MuonGM::sTgcReadoutElement::stripNumber
virtual int stripNumber(const Amg::Vector2D &pos, const Identifier &id) const override final
strip number corresponding to local position.
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:309
DeMoScan.index
string index
Definition: DeMoScan.py:362
python.SystemOfUnits.keV
float keV
Definition: SystemOfUnits.py:174
sTgcDigitMaker::m_idHelperSvc
const Muon::IMuonIdHelperSvc * m_idHelperSvc
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:138
sTgcDigitMaker::m_mostProbableArrivalTime
std::array< double, 5 > m_mostProbableArrivalTime
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:133
Muon::IMuonIdHelperSvc::toString
virtual std::string toString(const Identifier &id) const =0
print all fields to string
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
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:221
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:148
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:165
sTgcDigitMaker::m_StripResolution
double m_StripResolution
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:156
sTgcDigitMaker::sTgcDigitVec
std::vector< std::unique_ptr< sTgcDigit > > sTgcDigitVec
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:64
MuonGM::sTgcReadoutElement::getPadDesign
const MuonPadDesign * getPadDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:285
MuonGM::sTgcReadoutElement::wirePitch
double wirePitch(int gas_gap=1) const
single wire pitch.
Definition: MuonDetDescr/MuonReadoutGeometry/src/sTgcReadoutElement.cxx:664
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:149
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
sTgcDigitMaker::m_clusterProfile
static constexpr std::array< double, 2 > m_clusterProfile
Definition: MuonDigitization/sTGC_Digitization/sTGC_Digitization/sTgcDigitMaker.h:161
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
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.