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
249template <class CondType> StatusCode sTgcDigitizationTool::retrieveCondData(const EventContext& ctx,
251 const CondType* & condPtr) const{
252 if (key.empty()) {
253 ATH_MSG_DEBUG("No key has been configured for object "<<typeid(CondType).name()<<". Clear pointer");
254 condPtr = nullptr;
255 return StatusCode::SUCCESS;
256 }
257 SG::ReadCondHandle<CondType> readHandle{key, ctx};
258 if (!readHandle.isValid()){
259 ATH_MSG_FATAL("Failed to load conditions object "<<key.fullKey()<<".");
260 return StatusCode::FAILURE;
261 }
262 condPtr = readHandle.cptr();
263 return StatusCode::SUCCESS;
264}
265
266/*******************************************************************************/
267StatusCode sTgcDigitizationTool::doDigitization(const EventContext& ctx) {
268
269 ATH_MSG_DEBUG ("sTgcDigitizationTool::doDigitization()" );
270 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
271
277
278
279 // create and record the Digit container in StoreGate
281 ATH_CHECK(digitContainer.record(std::make_unique<sTgcDigitContainer>(idHelper.module_hash_max())));
282 ATH_MSG_DEBUG ( "sTgcDigitContainer recorded in StoreGate." );
283
284 // Create and record the SDO container in StoreGate
286 ATH_CHECK(sdoContainer.record(std::make_unique<MuonSimDataCollection>()));
287 ATH_MSG_DEBUG( "sTgcSDOCollection recorded in StoreGate." );
288
289
291
292 // Collections of digits by digit type associated with a detector element
293 sTgcSimDigitCont unmergedPadDigits{}, unmergedStripDigits{}, unmergedWireDigits{};
294 sTgcDigtCont outputDigits{};
295
296 ATH_MSG_DEBUG("create Digit container of size " << idHelper.module_hash_max());
297
298 double earliestEventTime = 9999;
299
300 // --nextDetectorElement>sets an iterator range with the hits of current detector element , returns a bool when done
301 while(m_thpcsTGC->nextDetectorElement(i, e)) {
302 int nhits = 0;
303 ATH_MSG_VERBOSE("Next Detector Element");
304 while(i != e){ //loop through the hits on this Detector Element
305 ATH_MSG_VERBOSE("Looping over hit " << nhits+1 << " on this Detector Element." );
306
307 ++nhits;
308 TimedHitPtr<sTGCSimHit> phit = *i++;
309 const sTGCSimHit& hit = *phit;
311 ATH_MSG_VERBOSE("Hit is not from a muon - skipping ");
312 continue;
313 }
314 ATH_MSG_VERBOSE("Hit Particle ID : " << hit.particleEncoding() );
315 double eventTime = phit.eventTime();
316 if(eventTime < earliestEventTime) earliestEventTime = eventTime;
317 // Cut on energy deposit of the particle
319 ATH_MSG_VERBOSE("Hit with Energy Deposit of " << hit.depositEnergy()
320 << " less than " << m_energyDepositThreshold << ". Skip this hit." );
321 continue;
322 }
323
324 // Old HITS format doesn't have kinetic energy (i.e it is set to -1).
325 double hit_kineticEnergy = hit.kineticEnergy();
326
327 // Skip digitizing some problematic hits, if processing compatible HITS format
328 if (hit_kineticEnergy > 0.) {
329 // Skip electron with low kinetic energy, since electrons are mainly secondary particles.
330 if ((std::abs(hit.particleEncoding()) == 11) && (hit_kineticEnergy < m_limitElectronKineticEnergy)) {
331 ATH_MSG_DEBUG("Skip electron hit with kinetic energy " << hit_kineticEnergy
332 << ", which is less than the lower limit of " << m_limitElectronKineticEnergy);
333 continue;
334 }
335
336 // No support for particles with direction perpendicular to the beam line, since such particles
337 // can deposit energy on a lot of strips and pads of the gas gap. So a good model of charge
338 // spreading should be implemented. Also, these particles are rare, and most of them are
339 // secondary particles suh as electrons.
340 if (std::abs(hit.globalPosition().z() - hit.globalPrePosition().z()) < 0.00001) {
341 ATH_MSG_VERBOSE("Skip hit with a direction perpendicular to the beam line, ie z-component is less than 0.00001 mm.");
342 continue;
343 }
344 }
345
346 if(eventTime != 0){
347 ATH_MSG_DEBUG("Updated hit global time to include off set of " << eventTime << " ns from OOT bunch.");
348 }
349 else {
350 ATH_MSG_DEBUG("This hit came from the in time bunch.");
351 }
352 sTgcSimIdToOfflineId simToOffline(&idHelper);
353 const int idHit = hit.sTGCId();
354 ATH_MSG_VERBOSE("Hit ID " << idHit );
355 Identifier layid = simToOffline.convert(idHit);
356 int eventId = phit.eventId();
357
360 if (m_doSmearing) {
361 bool acceptHit = true;
362 ATH_CHECK(m_smearingTool->isAccepted(layid, acceptHit, digitCond.rndmEngine));
363 if ( !acceptHit ) {
364 ATH_MSG_DEBUG("Dropping the hit - smearing tool");
365 continue;
366 }
367 }
368
369 const MuonGM::sTgcReadoutElement* detEL = digitCond.detMgr->getsTgcReadoutElement(layid); //retreiving the sTGC this hit is located in
370 if(!detEL) {
371 ATH_MSG_WARNING("Failed to retrieve detector element for "
372 << m_idHelperSvc->toStringDetEl(layid));
373 continue;
374 }
375
376 // project the hit position to wire surface (along the incident angle)
377 ATH_MSG_VERBOSE("Projecting hit to Wire Surface" );
378 const Amg::Vector3D& HPOS{hit.globalPosition()}; //Global position of the hit
379 const Amg::Vector3D& GLODIRE{hit.globalDirection()};
380 const Amg::Vector3D& global_preStepPos{hit.globalPrePosition()};
381
382 ATH_MSG_VERBOSE("Global Direction " << Amg::toString(GLODIRE, 2) );
383 ATH_MSG_VERBOSE("Global Position " << Amg::toString(HPOS, 2) );
384
385 int surfHash_wire = detEL->surfaceHash(idHelper.gasGap(layid),
387 ATH_MSG_VERBOSE("Surface Hash for wire plane" << surfHash_wire );
388 const Trk::PlaneSurface& SURF_WIRE = detEL->surface(surfHash_wire); //Plane of the wire surface in this gasGap
389 ATH_MSG_VERBOSE("Wire Surface Defined " <<Amg::toString(SURF_WIRE.center(), 2) );
390
391 const Amg::Transform3D wireTrans = SURF_WIRE.transform().inverse();
392 Amg::Vector3D LOCDIRE = wireTrans.linear()*GLODIRE;
393 Amg::Vector3D LPOS = wireTrans * HPOS; //Position of the hit on the wire plane in local coordinates
394
395 ATH_MSG_VERBOSE("Local Direction: "<<Amg::toString(LOCDIRE, 2));
396 ATH_MSG_VERBOSE("Local Position: " << Amg::toString(LPOS, 2));
397
398 const double scale = Amg::intersect<3>(LPOS, LOCDIRE, Amg::Vector3D::UnitZ(), 0.).value_or(0);
399 // Hit on the wire surface in local coordinates
400 Amg::Vector3D hitOnSurf_wire = LPOS + scale * LOCDIRE;
401
402 //The hit on the wire in Global coordinates
403 Amg::Vector3D glob_hitOnSurf_wire = SURF_WIRE.transform() * hitOnSurf_wire;
404
405 ATH_MSG_VERBOSE("Local Hit on Wire Surface: " << Amg::toString(hitOnSurf_wire, 2));
406 ATH_MSG_VERBOSE("Global Hit on Wire Surface: " <<Amg::toString(glob_hitOnSurf_wire, 2));
407
408 ATH_MSG_DEBUG("sTgcDigitizationTool::doDigitization hits mapped");
409
410 const HepMcParticleLink particleLink = HepMcParticleLink::getRedirectedLink(hit.particleLink(), eventId, ctx); // This link should now correctly resolve to the TruthEvent McEventCollection in the main StoreGateSvc.
411 const sTGCSimHit temp_hit(hit.sTGCId(), hit.globalTime(),
412 HPOS,
413 hit.particleEncoding(),
414 hit.globalDirection(),
415 hit.depositEnergy(),
416 particleLink,
417 hit_kineticEnergy,
418 global_preStepPos);
419
420
421 double globalHitTime = temp_hit.globalTime() + eventTime;
422 double tof = temp_hit.globalPosition().mag()/CLHEP::c_light;
423 double bunchTime = globalHitTime - tof;
424
425 // Create all the digits for this particular Sim Hit
426 sTgcDigitVec digiHits = m_digitizer->executeDigi(digitCond, temp_hit);
427 if (digiHits.empty()) {
428 continue;
429 }
430 ATH_MSG_VERBOSE("Hit produced " << digiHits.size() << " digits." );
431 for( std::unique_ptr<sTgcDigit>& digit : digiHits) {
432 /*
433 NOTE:
434 -----
435 Since not every hit might end up resulting in a
436 digit, this construction might take place after the hit loop
437 in a loop of its own!
438 */
439 // make new sTgcDigit
440 Identifier newDigitId = digit->identify(); //This Identifier should be sufficient to determine which RE the digit is from
441 double newTime = digit->time();
442 int newChannelType = idHelper.channelType(newDigitId);
443
444 double timeJitterElectronicsStrip = CLHEP::RandGaussZiggurat::shoot(digitCond.rndmEngine, 0, m_timeJitterElectronicsStrip);
445 double timeJitterElectronicsPad = CLHEP::RandGaussZiggurat::shoot(digitCond.rndmEngine, 0, m_timeJitterElectronicsPad);
446 if(newChannelType== sTgcIdHelper::sTgcChannelTypes::Strip)
447 newTime += timeJitterElectronicsStrip;
448 else
449 newTime += timeJitterElectronicsPad;
450 uint16_t newBcTag = bcTagging(newTime+bunchTime);
451
453 newTime += bunchTime;
454 else
455 newTime += globalHitTime;
456
457 double newCharge = digit->charge();
458
459 bool isDead{false}, isPileup{eventId != 0};
460 ATH_MSG_VERBOSE("Hit is from the main signal subevent if eventId is zero, eventId = " << eventId << " newTime: " << newTime);
461
462
463 // Create a new digit with updated time and BCTag
464 sTgcDigit newDigit(newDigitId, newBcTag, newTime, newCharge, isDead, isPileup);
465 ATH_MSG_VERBOSE("Unmerged Digit "<<m_idHelperSvc->toString(newDigitId)
466 <<" BC tag = " << newDigit.bcTag()
467 <<" digitTime = " << newDigit.time()
468 <<" charge = " << newDigit.charge()) ;
469
470
471 // Create a MuonSimData (SDO) corresponding to the digit
472 MuonSimData::Deposit deposit(particleLink, MuonMCData(hit.depositEnergy(), tof));
473 std::vector<MuonSimData::Deposit> deposits;
474 deposits.push_back(std::move(deposit));
475 MuonSimData simData(std::move(deposits), hit.particleEncoding());
476 // The sTGC SDO should be placed at the center of the gap, on the wire plane.
477 // We use the position from the hit on the wire surface which is by construction in the center of the gap
478 // glob_hitOnSurf_wire projects the whole hit to the center of the gap
479 simData.setPosition(glob_hitOnSurf_wire);
480 simData.setTime(globalHitTime);
481 const unsigned int modHash = static_cast<unsigned>(m_idHelperSvc->detElementHash(newDigitId));
482 sTgcSimDigitCont& contToPush = newChannelType == sTgcIdHelper::sTgcChannelTypes::Pad ? unmergedPadDigits :
483 newChannelType == sTgcIdHelper::sTgcChannelTypes::Strip ? unmergedStripDigits : unmergedWireDigits;
485 if (contToPush.size() <= modHash) contToPush.resize(modHash + 1);
486 contToPush[modHash].emplace_back(std::move(simData), std::move(newDigit));
487 } // end of loop digiHits
488 } // end of while(i != e)
489 } //end of while(m_thpcsTGC->nextDetectorElement(i, e))
490
491
492 /*********************
493 * Process Strip Digits *
494 *********************/
495 /* Comments from Alexandre Laurier, October 2022:
496 Big update to VMM handling of digits to sTGC digitization
497 For each channel type, the digits are processed on a layer-by-layer level
498 This is done to improve the performance of strip neighborOn functionnality
499 For wires, pads, and neighborOn=false strips, the digits on each channel
500 are ordered by earlier to latest and processed in order.
501 The digits are merged according to the VMM merging time window.
502 Above threshold digits are saved to output unless a previous digit is found
503 within the deadtime window.
504 --- For neighborOn=true strips ---
505 A strip above threshold forces the VMM readout of neighbor strips, even if
506 neighbor strips are below threshold.
507 We apply the logic as above, but for strips below threshold we search for one
508 direct neighbor strip to be above VMM threshold which triggers the VMM
509 to read the strip digit.
510 */
511 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedStripDigits, m_deadtimeStrip,
512 m_doNeighborOn, outputDigits, *sdoContainer));
513 /*********************
514 * Process Pad Digits *
515 *********************/
516 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedPadDigits, m_deadtimePad,
517 false, outputDigits, *sdoContainer));
518 /*********************
519 * Process Wire Digits *
520 *********************/
521 ATH_CHECK(processDigitsWithVMM(ctx, digitCond, unmergedWireDigits, m_deadtimeWire,
522 false, outputDigits, *sdoContainer));
523 /*************************************************
524 * Output the digits to the StoreGate collection *
525 *************************************************/
526 for (sTgcDigitVec& digits : outputDigits) {
527 if (digits.empty()) continue;
528 const Identifier elemID = m_idHelperSvc->chamberId(digits[0]->identify());
529 const IdentifierHash modHash = m_idHelperSvc->moduleHash(elemID);
530 std::unique_ptr<sTgcDigitCollection> collection = std::make_unique<sTgcDigitCollection>(elemID, modHash);
531 collection->insert(collection->end(), std::make_move_iterator(digits.begin()),
532 std::make_move_iterator(digits.end()));
533 ATH_CHECK(digitContainer->addCollection(collection.release(), modHash));
534 }
535 return StatusCode::SUCCESS;
536}
537
538/*******************************************************************************/
539uint16_t sTgcDigitizationTool::bcTagging(const double digitTime) {
540
541 uint16_t bctag = 0;
542
543 int bunchInteger{0}; //Define the absolute distance from t0 in units of BX
544 if(digitTime > 0) bunchInteger = (int)(abs(digitTime/25.0)); //absolute bunch for future bunches
545 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
546 bctag = (bctag | bunchInteger); //Store bitwise the abs(BX). This should be equivalent to regular variable assignment
547 if(digitTime < 0) bctag = ~bctag; //If from a PREVIOUS BX, apply bitwise negation
548
549 return bctag;
550}
551
552double sTgcDigitizationTool::getChannelThreshold(const EventContext& ctx,
553 const Identifier& channelID,
554 const NswCalibDbThresholdData& thresholdData) const {
555
557 std::optional<float> elecThrsld = thresholdData.getThreshold(channelID);
558
559 if(!elecThrsld || !m_calibTool->pdoToCharge(ctx, true, *elecThrsld, channelID, threshold)) {
560 THROW_EXCEPTION("Cannot find retrieve VMM threshold from conditions data base!");
561 }
562
563 return threshold;
564}
565
566
567CLHEP::HepRandomEngine* sTgcDigitizationTool::getRandomEngine(const std::string& streamName, const EventContext& ctx) const
568{
569 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, streamName);
570 std::string rngName = name()+streamName;
571 rngWrapper->setSeed( rngName, ctx );
572 CLHEP::HepRandomEngine* engine = rngWrapper->getEngine(ctx);
573 ATH_MSG_VERBOSE(streamName<<" rngName "<<rngName<<" "<<engine);
574 return engine;
575}
576
577StatusCode sTgcDigitizationTool::processDigitsWithVMM(const EventContext& ctx,
578 const DigiConditions& digiCond,
579 sTgcSimDigitCont& unmergedDigits,
580 const double vmmDeadTime,
581 const bool isNeighbourOn,
582 sTgcDigtCont& outDigitContainer,
583 MuonSimDataCollection& outSdoContainer) const {
584
585 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
587 for (sTgcSimDigitVec& digitsInCham : unmergedDigits) {
588
589 if (digitsInCham.empty()) continue;
591 sTgcSimDigitVec mergedDigits = processDigitsWithVMM(ctx, digiCond, vmmDeadTime,
592 digitsInCham, isNeighbourOn);
594 if (mergedDigits.empty()) continue;
595
596 const IdentifierHash hash = m_idHelperSvc->moduleHash(mergedDigits.front().identify());
597 const unsigned int hashIdx = static_cast<unsigned>(hash);
599 if (hash >= outDigitContainer.size()) {
600 outDigitContainer.resize(hash + 1);
601 }
602 for (sTgcSimDigitData& merged : mergedDigits) {
604 outSdoContainer.insert(std::make_pair(merged.identify(), std::move(merged.getSimData())));
606 bool acceptDigit{true};
607 float chargeAfterSmearing = merged.getDigit().charge();
608 if (m_doSmearing) {
609 ATH_CHECK(m_smearingTool->smearCharge(merged.identify(), chargeAfterSmearing, acceptDigit,
610 digiCond.rndmEngine));
611 }
612 if (!acceptDigit) {
613 continue;
614 }
617 if (idHelper.channelType(merged.identify()) == sTgcIdHelper::sTgcChannelTypes::Strip &&
618 chargeAfterSmearing < 0.001) {
619 continue;
620 }
621 std::unique_ptr<sTgcDigit> finalDigit = std::make_unique<sTgcDigit>(std::move(merged.getDigit()));
622 if (m_doSmearing) {
623 finalDigit->set_charge(chargeAfterSmearing);
624 }
625 ATH_MSG_VERBOSE("Final Digit "<<m_idHelperSvc->toString(finalDigit->identify())<<
626 " BC tag = " << finalDigit->bcTag()<<
627 " digitTime = " << finalDigit->time() <<
628 " charge = " << finalDigit->charge());
629 outDigitContainer[hashIdx].push_back(std::move(finalDigit));
630 }
631 }
632 return StatusCode::SUCCESS;
633}
635 const DigiConditions& digiCond,
636 const double vmmDeadTime,
637 sTgcSimDigitVec& unmergedDigits,
638 const bool isNeighborOn) const {
639
640 const MuonGM::MuonDetectorManager* detMgr{digiCond.detMgr};
641 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
643 std::stable_sort(unmergedDigits.begin(), unmergedDigits.end(),
644 [&idHelper](const sTgcSimDigitData& a, const sTgcSimDigitData& b) {
645 const int layA = idHelper.gasGap(a.identify());
646 const int layB = idHelper.gasGap(b.identify());
647 if (layA != layB) return layA < layB;
648 const int chA = idHelper.channel(a.identify());
649 const int chB = idHelper.channel(b.identify());
650 if (chA != chB) return chA < chB;
651 return a.time() < b.time();
652 });
653 sTgcSimDigitVec savedDigits{}, premerged{};
654
655 premerged.reserve(unmergedDigits.size());
656 savedDigits.reserve(premerged.capacity());
657
658
659 auto passNeigbourLogic = [&](const sTgcSimDigitData& candidate) {
660 if (!isNeighborOn || savedDigits.empty()) return false;
661 if (savedDigits.back().identify() == candidate.identify() &&
662 std::abs(savedDigits.back().time() - candidate.time()) < vmmDeadTime) {
663 ATH_MSG_VERBOSE("Digits are too close in time ");
664 return false;
665 }
666 const Identifier digitId = candidate.identify();
667 const int channel = idHelper.channel(digitId);
668 const int maxChannel = detMgr->getsTgcReadoutElement(digitId)->numberOfStrips(digitId);
669 for (int neighbour : {std::max(1, channel -1), std::min(maxChannel, channel+1)}) {
671 if (neighbour == channel) continue;
672 const Identifier neighbourId = idHelper.channelID(digitId,
673 idHelper.multilayer(digitId),
674 idHelper.gasGap(digitId),
675 idHelper.channelType(digitId), neighbour);
676 const double threshold = m_useCondThresholds ? getChannelThreshold(ctx, neighbourId, *digiCond.thresholdData)
677 : m_chargeThreshold.value();
678 if (std::find_if(savedDigits.begin(), savedDigits.end(), [&](const sTgcSimDigitData& known){
679 return known.identify() == neighbourId &&
680 known.getDigit().charge() > threshold &&
681 std::abs(known.time() - candidate.time()) < m_hitTimeMergeThreshold;
682 }) != savedDigits.end()) return true;
683
684 }
685 return false;
686 };
687 // Sort digits on every channel by earliest to latest time
688 // Also do hit merging to help with neighborOn logic
690 for (sTgcSimDigitVec::iterator merge_me = unmergedDigits.begin(); merge_me!= unmergedDigits.end(); ++merge_me) {
692 threshold = getChannelThreshold(ctx, (*merge_me).identify(), *digiCond.thresholdData);
693 }
696 sTgcDigit& digit1{(*merge_me).getDigit()};
697 double totalCharge = digit1.charge();
698 double weightedTime = digit1.time();
699
700 sTgcSimDigitVec::iterator merge_with = merge_me + 1;
701 for ( ; merge_with!= unmergedDigits.end(); ++merge_with) {
703 if ((*merge_with).identify() != (*merge_me).identify()) {
704 break;
705 }
706 const sTgcDigit& mergeDigit{(*merge_with).getDigit()};
707 // If future digits are within window, digit1 absorbs its charge
708 if (mergeDigit.time() - digit1.time() > m_hitTimeMergeThreshold) break;
709 // If digit1 is not above threshold prior to merging, the new time is
710 // a weighted average. Do it for every merging pair.
711 if (totalCharge < threshold) {
712 weightedTime = (weightedTime * totalCharge + mergeDigit.time() * mergeDigit.charge())
713 / (totalCharge + mergeDigit.charge());
714 }
715 totalCharge += mergeDigit.charge();
716 }
717 digit1.set_charge(totalCharge);
718 digit1.set_time(weightedTime);
719 sTgcSimDigitData& mergedHit{*merge_me};
720 if (!savedDigits.empty() &&
721 savedDigits.back().identify() == digit1.identify() &&
722 std::abs(savedDigits.back().time() - digit1.time()) <= vmmDeadTime) continue;
723 if (digit1.charge() > threshold || passNeigbourLogic(mergedHit)){
724 savedDigits.emplace_back(std::move(mergedHit));
725 } else if (isNeighborOn) {
726 premerged.emplace_back(std::move(mergedHit));
727 }
728 } // end of time-ordering and hit merging loop
729 std::copy_if(std::make_move_iterator(premerged.begin()),
730 std::make_move_iterator(premerged.end()),
731 std::back_inserter(savedDigits), passNeigbourLogic);
732 return savedDigits;
733}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ATLAS-specific HepMC functions.
std::vector< xAOD::EventInfo::SubEvent >::const_iterator SubEventIterator
Definition IPileUpTool.h:22
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:169
CLHEP::HepRandomEngine * getEngine(const EventContext &ctx) const
Retrieve the random engine corresponding to the provided EventContext.
Definition RNGWrapper.h:134
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)
const_pointer_type cptr()
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.
const Amg::Vector3D & globalPrePosition() const
Definition sTGCSimHit.h:49
HitID sTGCId() const
Definition sTGCSimHit.h:51
double globalTime() const
Definition sTGCSimHit.h:41
const Amg::Vector3D & globalDirection() const
Definition sTGCSimHit.h:46
double kineticEnergy() const
Definition sTGCSimHit.h:48
const Amg::Vector3D & globalPosition() const
Definition sTGCSimHit.h:44
const HepMcParticleLink & particleLink() const
Definition sTGCSimHit.h:96
int particleEncoding() const
Definition sTGCSimHit.h:45
double depositEnergy() const
Definition sTGCSimHit.h:47
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)
When being run from PileUpToolsAlgs, this method is called for each active bunch-crossing to process ...
std::vector< std::unique_ptr< sTGCSimHitCollection > > m_STGCHitCollList
double getChannelThreshold(const EventContext &ctx, const Identifier &channelID, const NswCalibDbThresholdData &thresholdData) const
StatusCode retrieveCondData(const EventContext &ctx, SG::ReadCondHandleKey< CondType > &key, const CondType *&condPtr) const
StatusCode getNextEvent(const EventContext &ctx)
Get next event and extract collection of hit collections.
StatusCode prepareEvent(const EventContext &ctx, const unsigned int)
When being run from PileUpToolsAlgs, this method is called at the start of the subevts loop.
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 mergeEvent(const EventContext &ctx)
When being run from PileUpToolsAlgs, this method is called at the end of the subevts loop.
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
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