ATLAS Offline Software
Loading...
Searching...
No Matches
MuonDigitization/sTGC_Digitization/src/sTgcDigitizationTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6//
7// sTgcDigitizationTool
8// ------------
9// Authors: Nectarios Benekos <nectarios.benekos@cern.ch>
10// Jiaming Yu <jiaming.yu@cern.ch>
12
13//sTGC digitization includes
15
18
19//Outputs
21
22//Geometry
27
28//Truth
32#include "CLHEP/Random/RandGaussZiggurat.h"
33
34#include <memory>
35
36
37using namespace MuonGM;
39
40
41/*******************************************************************************/
42sTgcDigitizationTool::sTgcDigitizationTool(const std::string& type, const std::string& name, const IInterface* parent) :
43 PileUpToolBase(type, name, parent) {}
44
45/*******************************************************************************/
46// member function implementation
47//--------------------------------------------
49
50 ATH_MSG_INFO (" sTgcDigitizationTool retrieved");
51 ATH_MSG_INFO ( "Configuration sTgcDigitizationTool" );
52 ATH_MSG_INFO ( "doSmearing "<< m_doSmearing);
53 ATH_MSG_INFO ( "RndmSvc " << m_rndmSvc );
54 ATH_MSG_INFO ( "RndmEngine " << m_rndmEngineName );
55 ATH_MSG_INFO ( "InputObjectName " << m_hitsContainerKey.key());
56 ATH_MSG_INFO ( "OutputObjectName " << m_outputDigitCollectionKey.key());
57 ATH_MSG_INFO ( "OutputSDOName " << m_outputSDO_CollectionKey.key());
58 ATH_MSG_INFO ( "HV " << m_runVoltage);
59 ATH_MSG_INFO ( "threshold " << m_chargeThreshold);
60 ATH_MSG_INFO ( "useCondThresholds " << m_useCondThresholds);
61
62 if (m_hitsContainerKey.key().empty()) {
63 ATH_MSG_FATAL("Property InputObjectName not set !");
64 return StatusCode::FAILURE;
65 }
66
68 ATH_MSG_DEBUG("Input objects in container: '" << m_inputObjectName << "'");
69
70 // Pile-up merge service
72 ATH_CHECK(m_mergeSvc.retrieve());
73 }
74
75 // retrieve MuonDetctorManager from DetectorStore
76 ATH_CHECK(m_detMgrKey.initialize());
77 ATH_CHECK(m_idHelperSvc.retrieve());
79
80
81 // calibration tool
82 ATH_CHECK(m_calibTool.retrieve());
83 // initialize ReadCondHandleKey
85 // Initialize ReadHandleKey
86 ATH_CHECK(m_hitsContainerKey.initialize());
87
88 //initialize the output WriteHandleKeys
91
92 // initialize sTgcDigitMaker class to digitize hits
93 // meanGasGain is the mean value of the polya gas gain function describing the
94 // avalanche of electrons caused by the electric field
95 // Parameterization is obtained from ATL-MUON-PUB-2014-001 and the corrected
96 // fit to data to parameterize gain vs HV in kV
97 // m_runVoltage MUST BE in kV!
99 ATH_MSG_ERROR("STGC run voltage must be in kV and within fit domain of 2.3 kV to 3.2 kV");
100 return StatusCode::FAILURE;
101 }
102 double meanGasGain = 2.15 * 1E-4 * std::exp(6.88*m_runVoltage);
103 m_digitizer = std::make_unique<sTgcDigitMaker>(m_idHelperSvc.get(), m_doChannelTypes, meanGasGain, m_doPadSharing, m_applyAsBuiltBLines);
104 m_digitizer->setLevel(static_cast<MSG::Level>(msgLevel()));
105 ATH_CHECK(m_digitizer->initialize());
106
107 ATH_CHECK(m_rndmSvc.retrieve());
108 // getting our random numbers stream
109 ATH_MSG_DEBUG("Getting random number engine : <" << m_rndmEngineName << ">");
110
111 return StatusCode::SUCCESS;
112}
113/*******************************************************************************/
114StatusCode sTgcDigitizationTool::prepareEvent(const EventContext& /*ctx*/, unsigned int nInputEvents) {
115
116 ATH_MSG_DEBUG("sTgcDigitizationTool::prepareEvent() called for " << nInputEvents << " input events" );
117 m_STGCHitCollList.clear();
118
119 return StatusCode::SUCCESS;
120}
121/*******************************************************************************/
122
124 SubEventIterator bSubEvents,
125 SubEventIterator eSubEvents) {
126 ATH_MSG_DEBUG ( "sTgcDigitizationTool::in processBunchXing()" );
127 if (m_thpcsTGC == nullptr) {
128 m_thpcsTGC = std::make_unique<TimedHitCollection<sTGCSimHit>>();
129 }
131 TimedHitCollList hitCollList;
132
133 if (!(m_mergeSvc->retrieveSubSetEvtData(m_inputObjectName, hitCollList, bunchXing,
134 bSubEvents, eSubEvents).isSuccess()) &&
135 hitCollList.empty()) {
136 ATH_MSG_ERROR("Could not fill TimedHitCollList");
137 return StatusCode::FAILURE;
138 } else {
139 ATH_MSG_VERBOSE(hitCollList.size() << " sTGCSimHitCollection with key " <<
140 m_inputObjectName << " found");
141 }
142
143 TimedHitCollList::iterator iColl(hitCollList.begin());
144 TimedHitCollList::iterator endColl(hitCollList.end());
145
146 // Iterating over the list of collections
147 for( ; iColl != endColl; ++iColl){
148
149 auto hitCollPtr = std::make_unique<sTGCSimHitCollection>(*iColl->second);
150 PileUpTimeEventIndex timeIndex(iColl->first);
151
152 ATH_MSG_DEBUG("sTGCSimHitCollection found with " << hitCollPtr->size() << " hits");
153 ATH_MSG_VERBOSE("time index info. time: " << timeIndex.time()
154 << " index: " << timeIndex.index()
155 << " type: " << timeIndex.type());
156
157 m_thpcsTGC->insert(timeIndex, hitCollPtr.get());
158 m_STGCHitCollList.push_back(std::move(hitCollPtr));
159 }
160 return StatusCode::SUCCESS;
161}
162/*******************************************************************************/
163StatusCode sTgcDigitizationTool::getNextEvent(const EventContext& ctx) {
164
165 ATH_MSG_DEBUG ( "sTgcDigitizationTool::getNextEvent()" );
166
167 // get the container(s)
169
170 // In case of single hits container just load the collection using read handles
173 if (!hitCollection.isValid()) {
174 ATH_MSG_ERROR("Could not get sTGCSimHitCollection container " << hitCollection.name() << " from store " << hitCollection.store());
175 return StatusCode::FAILURE;
176 }
177
178 // create a new hits collection
179 m_thpcsTGC = std::make_unique<TimedHitCollection<sTGCSimHit>>(1);
180 m_thpcsTGC->insert(0, hitCollection.cptr());
181 ATH_MSG_DEBUG("sTGCSimHitCollection found with " << hitCollection->size() << " hits");
182 return StatusCode::SUCCESS;
183 }
184
185 //this is a list<info<time_t, DataLink<sTGCSimHitCollection> > >
186 TimedHitCollList hitCollList;
187
188 if (!(m_mergeSvc->retrieveSubEvtsData(m_inputObjectName, hitCollList).isSuccess()) ) {
189 ATH_MSG_ERROR ( "Could not fill TimedHitCollList" );
190 return StatusCode::FAILURE;
191 }
192 if (hitCollList.empty()) {
193 ATH_MSG_ERROR ( "TimedHitCollList has size 0" );
194 return StatusCode::FAILURE;
195 }
196 else {
197 ATH_MSG_DEBUG ( hitCollList.size() << " sTGC SimHitCollections with key " << m_inputObjectName << " found" );
198 }
199
200 //Perform null check on m_thpcsTGC. If pointer is not null throw error
201 if (!m_thpcsTGC) {
202 m_thpcsTGC = std::make_unique<TimedHitCollection<sTGCSimHit>>();
203 }else{
204 ATH_MSG_ERROR ( "m_thpcsTGC is not null" );
205 return StatusCode::FAILURE;
206 }
207
208 //now merge all collections into one
209 TimedHitCollList::iterator iColl(hitCollList.begin());
210 TimedHitCollList::iterator endColl(hitCollList.end());
211 while (iColl != endColl) {
212 const sTGCSimHitCollection* p_collection(iColl->second);
213 m_thpcsTGC->insert(iColl->first, p_collection);
214 ATH_MSG_DEBUG ( "sTGC SimHitCollection found with " << p_collection->size() << " hits" );
215 ++iColl;
216 }
217
218 return StatusCode::SUCCESS;
219}
220
221/*******************************************************************************/
222StatusCode sTgcDigitizationTool::mergeEvent(const EventContext& ctx) {
223 ATH_MSG_DEBUG ( "sTgcDigitizationTool::in mergeEvent()" );
225 // reset the pointer
226 m_thpcsTGC.reset();
227 m_STGCHitCollList.clear();
228
229 return StatusCode::SUCCESS;
230}
231/*******************************************************************************/
232StatusCode sTgcDigitizationTool::digitize(const EventContext& ctx) {
233 return this->processAllSubEvents(ctx);
234}
235/*******************************************************************************/
236StatusCode sTgcDigitizationTool::processAllSubEvents(const EventContext& ctx) {
237 ATH_MSG_DEBUG (" sTgcDigitizationTool::processAllSubEvents()" );
238 //merging of the hit collection in getNextEvent method
239 if (!m_thpcsTGC ) {
241 }
243 // reset the pointer
244 m_thpcsTGC.reset();
245
246 return StatusCode::SUCCESS;
247}
248
249/*******************************************************************************/
250StatusCode sTgcDigitizationTool::doDigitization(const EventContext& ctx) {
251
252 ATH_MSG_DEBUG ("sTgcDigitizationTool::doDigitization()" );
253 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
254
257 ATH_CHECK(SG::get(digitCond.detMgr, m_detMgrKey, ctx));
258 ATH_CHECK(SG::get(digitCond.efficiencies, m_effiKey, ctx));
260
261
262 // create and record the Digit container in StoreGate
264 ATH_CHECK(digitContainer.record(std::make_unique<sTgcDigitContainer>(idHelper.module_hash_max())));
265 ATH_MSG_DEBUG ( "sTgcDigitContainer recorded in StoreGate." );
266
267 // Create and record the SDO container in StoreGate
269 ATH_CHECK(sdoContainer.record(std::make_unique<MuonSimDataCollection>()));
270 ATH_MSG_DEBUG( "sTgcSDOCollection recorded in StoreGate." );
271
272
274
275 // Collections of digits by digit type associated with a detector element
276 sTgcSimDigitCont unmergedPadDigits{}, unmergedStripDigits{}, unmergedWireDigits{};
277 sTgcDigtCont outputDigits{};
278
279 ATH_MSG_DEBUG("create Digit container of size " << idHelper.module_hash_max());
280
281 double earliestEventTime = 9999;
282
283 // --nextDetectorElement>sets an iterator range with the hits of current detector element , returns a bool when done
284 while(m_thpcsTGC->nextDetectorElement(i, e)) {
285 int nhits = 0;
286 ATH_MSG_VERBOSE("Next Detector Element");
287 while(i != e){ //loop through the hits on this Detector Element
288 ATH_MSG_VERBOSE("Looping over hit " << nhits+1 << " on this Detector Element." );
289
290 ++nhits;
291 TimedHitPtr<sTGCSimHit> phit = *i++;
292 const sTGCSimHit& hit = *phit;
293 if (m_digitizeMuonOnly && !MC::isMuon(hit.particleEncoding())) {
294 ATH_MSG_VERBOSE("Hit is not from a muon - skipping ");
295 continue;
296 }
297 ATH_MSG_VERBOSE("Hit Particle ID : " << hit.particleEncoding() );
298 double eventTime = phit.eventTime();
299 if(eventTime < earliestEventTime) earliestEventTime = eventTime;
300 // Cut on energy deposit of the particle
301 if(hit.depositEnergy() < m_energyDepositThreshold) {
302 ATH_MSG_VERBOSE("Hit with Energy Deposit of " << hit.depositEnergy()
303 << " less than " << m_energyDepositThreshold << ". Skip this hit." );
304 continue;
305 }
306
307 // Old HITS format doesn't have kinetic energy (i.e it is set to -1).
308 double hit_kineticEnergy = hit.kineticEnergy();
309
310 // Skip digitizing some problematic hits, if processing compatible HITS format
311 if (hit_kineticEnergy > 0.) {
312 // Skip electron with low kinetic energy, since electrons are mainly secondary particles.
313 if ((std::abs(hit.particleEncoding()) == 11) && (hit_kineticEnergy < m_limitElectronKineticEnergy)) {
314 ATH_MSG_DEBUG("Skip electron hit with kinetic energy " << hit_kineticEnergy
315 << ", which is less than the lower limit of " << m_limitElectronKineticEnergy);
316 continue;
317 }
318
319 // No support for particles with direction perpendicular to the beam line, since such particles
320 // can deposit energy on a lot of strips and pads of the gas gap. So a good model of charge
321 // spreading should be implemented. Also, these particles are rare, and most of them are
322 // secondary particles suh as electrons.
323 if (std::abs(hit.globalPosition().z() - hit.globalPrePosition().z()) < 0.00001) {
324 ATH_MSG_VERBOSE("Skip hit with a direction perpendicular to the beam line, ie z-component is less than 0.00001 mm.");
325 continue;
326 }
327 }
328
329 if(eventTime != 0){
330 ATH_MSG_DEBUG("Updated hit global time to include off set of " << eventTime << " ns from OOT bunch.");
331 }
332 else {
333 ATH_MSG_DEBUG("This hit came from the in time bunch.");
334 }
335 sTgcSimIdToOfflineId simToOffline(&idHelper);
336 const int idHit = hit.sTGCId();
337 ATH_MSG_VERBOSE("Hit ID " << idHit );
338 Identifier layid = simToOffline.convert(idHit);
339 int eventId = phit.eventId();
340
343 if (m_doSmearing) {
344 bool acceptHit = true;
345 ATH_CHECK(m_smearingTool->isAccepted(layid, acceptHit, digitCond.rndmEngine));
346 if ( !acceptHit ) {
347 ATH_MSG_DEBUG("Dropping the hit - smearing tool");
348 continue;
349 }
350 }
351
352 const MuonGM::sTgcReadoutElement* detEL = digitCond.detMgr->getsTgcReadoutElement(layid); //retreiving the sTGC this hit is located in
353 if(!detEL) {
354 ATH_MSG_WARNING("Failed to retrieve detector element for "
355 << m_idHelperSvc->toStringDetEl(layid));
356 continue;
357 }
358
359 // project the hit position to wire surface (along the incident angle)
360 ATH_MSG_VERBOSE("Projecting hit to Wire Surface" );
361 const Amg::Vector3D& HPOS{hit.globalPosition()}; //Global position of the hit
362 const Amg::Vector3D& GLODIRE{hit.globalDirection()};
363 const Amg::Vector3D& global_preStepPos{hit.globalPrePosition()};
364
365 ATH_MSG_VERBOSE("Global Direction " << Amg::toString(GLODIRE, 2) );
366 ATH_MSG_VERBOSE("Global Position " << Amg::toString(HPOS, 2) );
367
368 int surfHash_wire = detEL->surfaceHash(idHelper.gasGap(layid),
370 ATH_MSG_VERBOSE("Surface Hash for wire plane" << surfHash_wire );
371 const Trk::PlaneSurface& SURF_WIRE = detEL->surface(surfHash_wire); //Plane of the wire surface in this gasGap
372 ATH_MSG_VERBOSE("Wire Surface Defined " <<Amg::toString(SURF_WIRE.center(), 2) );
373
374 const Amg::Transform3D wireTrans = SURF_WIRE.transform().inverse();
375 Amg::Vector3D LOCDIRE = wireTrans.linear()*GLODIRE;
376 Amg::Vector3D LPOS = wireTrans * HPOS; //Position of the hit on the wire plane in local coordinates
377
378 ATH_MSG_VERBOSE("Local Direction: "<<Amg::toString(LOCDIRE, 2));
379 ATH_MSG_VERBOSE("Local Position: " << Amg::toString(LPOS, 2));
380
381 const double scale = Amg::intersect<3>(LPOS, LOCDIRE, Amg::Vector3D::UnitZ(), 0.).value_or(0);
382 // Hit on the wire surface in local coordinates
383 Amg::Vector3D hitOnSurf_wire = LPOS + scale * LOCDIRE;
384
385 //The hit on the wire in Global coordinates
386 Amg::Vector3D glob_hitOnSurf_wire = SURF_WIRE.transform() * hitOnSurf_wire;
387
388 ATH_MSG_VERBOSE("Local Hit on Wire Surface: " << Amg::toString(hitOnSurf_wire, 2));
389 ATH_MSG_VERBOSE("Global Hit on Wire Surface: " <<Amg::toString(glob_hitOnSurf_wire, 2));
390
391 ATH_MSG_DEBUG("sTgcDigitizationTool::doDigitization hits mapped");
392
393 const HepMcParticleLink particleLink = HepMcParticleLink::getRedirectedLink(hit.particleLink(), eventId, ctx); // This link should now correctly resolve to the TruthEvent McEventCollection in the main StoreGateSvc.
394 const sTGCSimHit temp_hit(hit.sTGCId(), hit.globalTime(),
395 HPOS,
396 hit.particleEncoding(),
397 hit.globalDirection(),
398 hit.depositEnergy(),
399 particleLink,
400 hit_kineticEnergy,
401 global_preStepPos);
402
403
404 double globalHitTime = temp_hit.globalTime() + eventTime;
405 double tof = temp_hit.globalPosition().mag()/CLHEP::c_light;
406 double bunchTime = globalHitTime - tof;
407
408 // Create all the digits for this particular Sim Hit
409 sTgcDigitVec digiHits = m_digitizer->executeDigi(digitCond, temp_hit);
410 if (digiHits.empty()) {
411 continue;
412 }
413 ATH_MSG_VERBOSE("Hit produced " << digiHits.size() << " digits." );
414 for( std::unique_ptr<sTgcDigit>& digit : digiHits) {
415 /*
416 NOTE:
417 -----
418 Since not every hit might end up resulting in a
419 digit, this construction might take place after the hit loop
420 in a loop of its own!
421 */
422 // make new sTgcDigit
423 Identifier newDigitId = digit->identify(); //This Identifier should be sufficient to determine which RE the digit is from
424 double newTime = digit->time();
425 int newChannelType = idHelper.channelType(newDigitId);
426
427 double timeJitterElectronicsStrip = CLHEP::RandGaussZiggurat::shoot(digitCond.rndmEngine, 0, m_timeJitterElectronicsStrip);
428 double timeJitterElectronicsPad = CLHEP::RandGaussZiggurat::shoot(digitCond.rndmEngine, 0, m_timeJitterElectronicsPad);
429 if(newChannelType== sTgcIdHelper::sTgcChannelTypes::Strip)
430 newTime += timeJitterElectronicsStrip;
431 else
432 newTime += timeJitterElectronicsPad;
433 uint16_t newBcTag = bcTagging(newTime+bunchTime);
434
436 newTime += bunchTime;
437 else
438 newTime += globalHitTime;
439
440 double newCharge = digit->charge();
441
442 bool isDead{false}, isPileup{eventId != 0};
443 ATH_MSG_VERBOSE("Hit is from the main signal subevent if eventId is zero, eventId = " << eventId << " newTime: " << newTime);
444
445
446 // Create a new digit with updated time and BCTag
447 sTgcDigit newDigit(newDigitId, newBcTag, newTime, newCharge, isDead, isPileup);
448 ATH_MSG_VERBOSE("Unmerged Digit "<<m_idHelperSvc->toString(newDigitId)
449 <<" BC tag = " << newDigit.bcTag()
450 <<" digitTime = " << newDigit.time()
451 <<" charge = " << newDigit.charge()) ;
452
453
454 // Create a MuonSimData (SDO) corresponding to the digit
455 MuonSimData::Deposit deposit(particleLink, MuonMCData(hit.depositEnergy(), tof));
456 std::vector<MuonSimData::Deposit> deposits;
457 deposits.push_back(std::move(deposit));
458 MuonSimData simData(std::move(deposits), hit.particleEncoding());
459 // The sTGC SDO should be placed at the center of the gap, on the wire plane.
460 // We use the position from the hit on the wire surface which is by construction in the center of the gap
461 // glob_hitOnSurf_wire projects the whole hit to the center of the gap
462 simData.setPosition(glob_hitOnSurf_wire);
463 simData.setTime(globalHitTime);
464 const unsigned int modHash = static_cast<unsigned>(m_idHelperSvc->detElementHash(newDigitId));
465 sTgcSimDigitCont& contToPush = newChannelType == sTgcIdHelper::sTgcChannelTypes::Pad ? unmergedPadDigits :
466 newChannelType == sTgcIdHelper::sTgcChannelTypes::Strip ? unmergedStripDigits : unmergedWireDigits;
468 if (contToPush.size() <= modHash) contToPush.resize(modHash + 1);
469 contToPush[modHash].emplace_back(std::move(simData), std::move(newDigit));
470 } // end of loop digiHits
471 } // end of while(i != e)
472 } //end of while(m_thpcsTGC->nextDetectorElement(i, e))
473
474
475 /*********************
476 * Process Strip Digits *
477 *********************/
478 /* Comments from Alexandre Laurier, October 2022:
479 Big update to VMM handling of digits to sTGC digitization
480 For each channel type, the digits are processed on a layer-by-layer level
481 This is done to improve the performance of strip neighborOn functionnality
482 For wires, pads, and neighborOn=false strips, the digits on each channel
483 are ordered by earlier to latest and processed in order.
484 The digits are merged according to the VMM merging time window.
485 Above threshold digits are saved to output unless a previous digit is found
486 within the deadtime window.
487 --- For neighborOn=true strips ---
488 A strip above threshold forces the VMM readout of neighbor strips, even if
489 neighbor strips are below threshold.
490 We apply the logic as above, but for strips below threshold we search for one
491 direct neighbor strip to be above VMM threshold which triggers the VMM
492 to read the strip digit.
493 */
494 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedStripDigits, m_deadtimeStrip,
495 m_doNeighborOn, outputDigits, *sdoContainer));
496 /*********************
497 * Process Pad Digits *
498 *********************/
499 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedPadDigits, m_deadtimePad,
500 false, outputDigits, *sdoContainer));
501 /*********************
502 * Process Wire Digits *
503 *********************/
504 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedWireDigits, m_deadtimeWire,
505 false, outputDigits, *sdoContainer));
506 /*************************************************
507 * Output the digits to the StoreGate collection *
508 *************************************************/
509 for (sTgcDigitVec& digits : outputDigits) {
510 if (digits.empty()) continue;
511 const Identifier elemID = m_idHelperSvc->chamberId(digits[0]->identify());
512 const IdentifierHash modHash = m_idHelperSvc->moduleHash(elemID);
513 std::unique_ptr<sTgcDigitCollection> collection = std::make_unique<sTgcDigitCollection>(elemID, modHash);
514 collection->insert(collection->end(), std::make_move_iterator(digits.begin()),
515 std::make_move_iterator(digits.end()));
516 ATH_CHECK(digitContainer->addCollection(collection.release(), modHash));
517 }
518 return StatusCode::SUCCESS;
519}
520
521/*******************************************************************************/
522uint16_t sTgcDigitizationTool::bcTagging(const double digitTime) {
523
524 uint16_t bctag = 0;
525
526 int bunchInteger{0}; //Define the absolute distance from t0 in units of BX
527 if(digitTime > 0) bunchInteger = (int)(abs(digitTime/25.0)); //absolute bunch for future bunches
528 else bunchInteger = (int)(abs(digitTime/25.0)) + 1; //The absolute bunch for negative time needs to be shifted by 1 as there is no negative zero bunch
529 bctag = (bctag | bunchInteger); //Store bitwise the abs(BX). This should be equivalent to regular variable assignment
530 if(digitTime < 0) bctag = ~bctag; //If from a PREVIOUS BX, apply bitwise negation
531
532 return bctag;
533}
534
535double sTgcDigitizationTool::getChannelThreshold(const EventContext& ctx,
536 const Identifier& channelID,
537 const NswCalibDbThresholdData& thresholdData) const {
538
540 std::optional<float> elecThrsld = thresholdData.getThreshold(channelID);
541
542 if(!elecThrsld || !m_calibTool->pdoToCharge(ctx, true, *elecThrsld, channelID, threshold)) {
543 THROW_EXCEPTION("Cannot find retrieve VMM threshold from conditions data base!");
544 }
545
546 return threshold;
547}
548
549
550CLHEP::HepRandomEngine* sTgcDigitizationTool::getRandomEngine(const std::string& streamName, const EventContext& ctx) const
551{
552 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, streamName);
553 std::string rngName = name()+streamName;
554 rngWrapper->setSeed( rngName, ctx );
555 CLHEP::HepRandomEngine* engine = rngWrapper->getEngine(ctx);
556 ATH_MSG_VERBOSE(streamName<<" rngName "<<rngName<<" "<<engine);
557 return engine;
558}
559
560StatusCode sTgcDigitizationTool::processDigitsWithVMM(const EventContext& ctx,
561 const DigiConditions& digiCond,
562 sTgcSimDigitCont& unmergedDigits,
563 const double vmmDeadTime,
564 const bool isNeighbourOn,
565 sTgcDigtCont& outDigitContainer,
566 MuonSimDataCollection& outSdoContainer) const {
567
568 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
570 for (sTgcSimDigitVec& digitsInCham : unmergedDigits) {
571
572 if (digitsInCham.empty()) continue;
574 sTgcSimDigitVec mergedDigits = processDigitsWithVMM(ctx, digiCond, vmmDeadTime,
575 digitsInCham, isNeighbourOn);
577 if (mergedDigits.empty()) continue;
578
579 const IdentifierHash hash = m_idHelperSvc->moduleHash(mergedDigits.front().identify());
580 const unsigned int hashIdx = static_cast<unsigned>(hash);
582 if (hash >= outDigitContainer.size()) {
583 outDigitContainer.resize(hash + 1);
584 }
585 for (sTgcSimDigitData& merged : mergedDigits) {
587 outSdoContainer.insert(std::make_pair(merged.identify(), std::move(merged.getSimData())));
589 bool acceptDigit{true};
590 float chargeAfterSmearing = merged.getDigit().charge();
591 if (m_doSmearing) {
592 ATH_CHECK(m_smearingTool->smearCharge(merged.identify(), chargeAfterSmearing, acceptDigit,
593 digiCond.rndmEngine));
594 }
595 if (!acceptDigit) {
596 continue;
597 }
600 if (idHelper.channelType(merged.identify()) == sTgcIdHelper::sTgcChannelTypes::Strip &&
601 chargeAfterSmearing < 0.001) {
602 continue;
603 }
604 std::unique_ptr<sTgcDigit> finalDigit = std::make_unique<sTgcDigit>(std::move(merged.getDigit()));
605 if (m_doSmearing) {
606 finalDigit->set_charge(chargeAfterSmearing);
607 }
608 ATH_MSG_VERBOSE("Final Digit "<<m_idHelperSvc->toString(finalDigit->identify())<<
609 " BC tag = " << finalDigit->bcTag()<<
610 " digitTime = " << finalDigit->time() <<
611 " charge = " << finalDigit->charge());
612 outDigitContainer[hashIdx].push_back(std::move(finalDigit));
613 }
614 }
615 return StatusCode::SUCCESS;
616}
618 const DigiConditions& digiCond,
619 const double vmmDeadTime,
620 sTgcSimDigitVec& unmergedDigits,
621 const bool isNeighborOn) const {
622
623 const MuonGM::MuonDetectorManager* detMgr{digiCond.detMgr};
624 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
626 std::stable_sort(unmergedDigits.begin(), unmergedDigits.end(),
627 [&idHelper](const sTgcSimDigitData& a, const sTgcSimDigitData& b) {
628 const int layA = idHelper.gasGap(a.identify());
629 const int layB = idHelper.gasGap(b.identify());
630 if (layA != layB) return layA < layB;
631 const int chA = idHelper.channel(a.identify());
632 const int chB = idHelper.channel(b.identify());
633 if (chA != chB) return chA < chB;
634 return a.time() < b.time();
635 });
636 sTgcSimDigitVec savedDigits{}, premerged{};
637
638 premerged.reserve(unmergedDigits.size());
639 savedDigits.reserve(premerged.capacity());
640
641
642 auto passNeigbourLogic = [&](const sTgcSimDigitData& candidate) {
643 if (!isNeighborOn || savedDigits.empty()) return false;
644 if (savedDigits.back().identify() == candidate.identify() &&
645 std::abs(savedDigits.back().time() - candidate.time()) < vmmDeadTime) {
646 ATH_MSG_VERBOSE("Digits are too close in time ");
647 return false;
648 }
649 const Identifier digitId = candidate.identify();
650 const int channel = idHelper.channel(digitId);
651 const int maxChannel = detMgr->getsTgcReadoutElement(digitId)->numberOfStrips(digitId);
652 for (int neighbour : {std::max(1, channel -1), std::min(maxChannel, channel+1)}) {
654 if (neighbour == channel) continue;
655 const Identifier neighbourId = idHelper.channelID(digitId,
656 idHelper.multilayer(digitId),
657 idHelper.gasGap(digitId),
658 idHelper.channelType(digitId), neighbour);
659 const double threshold = m_useCondThresholds ? getChannelThreshold(ctx, neighbourId, *digiCond.thresholdData)
660 : m_chargeThreshold.value();
661 if (std::find_if(savedDigits.begin(), savedDigits.end(), [&](const sTgcSimDigitData& known){
662 return known.identify() == neighbourId &&
663 known.getDigit().charge() > threshold &&
664 std::abs(known.time() - candidate.time()) < m_hitTimeMergeThreshold;
665 }) != savedDigits.end()) return true;
666
667 }
668 return false;
669 };
670 // Sort digits on every channel by earliest to latest time
671 // Also do hit merging to help with neighborOn logic
673 for (sTgcSimDigitVec::iterator merge_me = unmergedDigits.begin(); merge_me!= unmergedDigits.end(); ++merge_me) {
675 threshold = getChannelThreshold(ctx, (*merge_me).identify(), *digiCond.thresholdData);
676 }
679 sTgcDigit& digit1{(*merge_me).getDigit()};
680 double totalCharge = digit1.charge();
681 double weightedTime = digit1.time();
682
683 sTgcSimDigitVec::iterator merge_with = merge_me + 1;
684 for ( ; merge_with!= unmergedDigits.end(); ++merge_with) {
686 if ((*merge_with).identify() != (*merge_me).identify()) {
687 break;
688 }
689 const sTgcDigit& mergeDigit{(*merge_with).getDigit()};
690 // If future digits are within window, digit1 absorbs its charge
691 if (mergeDigit.time() - digit1.time() > m_hitTimeMergeThreshold) break;
692 // If digit1 is not above threshold prior to merging, the new time is
693 // a weighted average. Do it for every merging pair.
694 if (totalCharge < threshold) {
695 weightedTime = (weightedTime * totalCharge + mergeDigit.time() * mergeDigit.charge())
696 / (totalCharge + mergeDigit.charge());
697 }
698 totalCharge += mergeDigit.charge();
699 }
700 digit1.set_charge(totalCharge);
701 digit1.set_time(weightedTime);
702 sTgcSimDigitData& mergedHit{*merge_me};
703 if (!savedDigits.empty() &&
704 savedDigits.back().identify() == digit1.identify() &&
705 std::abs(savedDigits.back().time() - digit1.time()) <= vmmDeadTime) continue;
706 if (digit1.charge() > threshold || passNeigbourLogic(mergedHit)){
707 savedDigits.emplace_back(std::move(mergedHit));
708 } else if (isNeighborOn) {
709 premerged.emplace_back(std::move(mergedHit));
710 }
711 } // end of time-ordering and hit merging loop
712 std::copy_if(std::make_move_iterator(premerged.begin()),
713 std::make_move_iterator(premerged.end()),
714 std::back_inserter(savedDigits), passNeigbourLogic);
715 return savedDigits;
716}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
ATLAS-specific HepMC functions.
std::vector< xAOD::EventInfo::SubEvent >::const_iterator SubEventIterator
Definition IPileUpTool.h:22
bool hit(const Container &ids, int pdgId)
static Double_t a
sTgcDigitizationTool::sTgcSimDigitVec sTgcSimDigitVec
A wrapper class for event-slot-local random engines.
Definition RNGWrapper.h:56
void setSeed(const std::string &algName, const EventContext &ctx)
Set the random seed using a string (e.g.
Definition RNGWrapper.h:154
CLHEP::HepRandomEngine * getEngine(const EventContext &ctx) const
Retrieve the random engine corresponding to the provided EventContext.
Definition RNGWrapper.h:108
size_type size() const
This is a "hash" representation of an Identifier.
Identifier identify() const
Definition MuonDigit.h:30
virtual const Trk::PlaneSurface & surface() const override
access to chamber surface (phi orientation), uses the first gas gap
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
const sTgcReadoutElement * getsTgcReadoutElement(const Identifier &id) const
access via extended identifier (requires unpacking)
An sTgcReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station c...
virtual int numberOfStrips(const Identifier &layerId) const override final
number of strips per layer
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...
size_type module_hash_max() const
the maximum hash value
std::pair< HepMcParticleLink, MuonMCData > Deposit
Definition MuonSimData.h:66
Conditions data to model a channel dependent energy deposit threshold such that the electronics retur...
std::optional< float > getThreshold(const Identifier &channelId) const
PileUpToolBase(const std::string &type, const std::string &name, const IInterface *parent)
virtual bool isValid() override final
Can the handle be successfully dereferenced?
const_pointer_type cptr()
Dereference the pointer.
std::string store() const
Return the name of the store holding the object we are proxying.
const std::string & name() const
Return the StoreGate ID for the referenced object.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
TimedVector::const_iterator const_iterator
a smart pointer to a hit that also provides access to the extended timing info of the host event.
Definition TimedHitPtr.h:18
unsigned short eventId() const
the index of the component event in PileUpEventInfo.
Definition TimedHitPtr.h:47
float eventTime() const
t0 offset of the bunch xing containing the hit in ns.
Definition TimedHitPtr.h:55
Class for a planaer rectangular or trapezoidal surface in the ATLAS detector.
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
const Amg::Vector3D & center() const
Returns the center position of the Surface.
double globalTime() const
Definition sTGCSimHit.h:41
const Amg::Vector3D & globalPosition() const
Definition sTGCSimHit.h:44
void set_time(float newTime)
Definition sTgcDigit.cxx:76
float time() const
Definition sTgcDigit.cxx:61
uint16_t bcTag() const
Definition sTgcDigit.cxx:34
float charge() const
Definition sTgcDigit.cxx:46
void set_charge(float newCharge)
Definition sTgcDigit.cxx:71
StatusCode processBunchXing(int bunchXing, SubEventIterator bSubEvents, SubEventIterator eSubEvents)
std::vector< std::unique_ptr< sTGCSimHitCollection > > m_STGCHitCollList
double getChannelThreshold(const EventContext &ctx, const Identifier &channelID, const NswCalibDbThresholdData &thresholdData) const
StatusCode getNextEvent(const EventContext &ctx)
Get next event and extract collection of hit collections.
StatusCode prepareEvent(const EventContext &ctx, const unsigned int)
sTgcDigitizationTool(const std::string &type, const std::string &name, const IInterface *parent)
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_detMgrKey
StatusCode processDigitsWithVMM(const EventContext &ctx, const DigiConditions &digiCond, sTgcSimDigitCont &unmergedContainer, const double vmmDeadTime, const bool isNeighbourOn, sTgcDigtCont &outDigitContainer, MuonSimDataCollection &outSdoContainer) const
StatusCode digitize(const EventContext &ctx)
Just calls processAllSubEvents - leaving for back-compatibility (IMuonDigitizationTool).
virtual StatusCode processAllSubEvents(const EventContext &ctx)
alternative interface which uses the PileUpMergeSvc to obtain all the required SubEvents.
CLHEP::HepRandomEngine * getRandomEngine(const std::string &streamName, const EventContext &ctx) const
StatusCode doDigitization(const EventContext &ctx)
Core part of digitization use by mergeEvent (IPileUpTool) and digitize (IMuonDigitizationTool).
int multilayer(const Identifier &id) const
int channelType(const Identifier &id) const
int channel(const Identifier &id) const override
int gasGap(const Identifier &id) const override
get the hashes
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channelType, int channel) const
constexpr bool simData
Definition constants.h:36
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
bool isMuon(const T &p)
Ensure that the Athena extensions are properly loaded.
Definition GeoMuonHits.h:27
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
AtlasHitsVector< sTGCSimHit > sTGCSimHitCollection
std::list< value_t > type
type of the collection of timed data object
a struct encapsulating the identifier of a pile-up event
index_type index() const
the index of the component event in PileUpEventInfo
PileUpType type() const
the pileup type - minbias, cavern, beam halo, signal?
time_type time() const
bunch xing time in ns
Digitize a given hit, determining the time and charge spread on wires, pads and strips.
Identifier convert(int simId) const
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10