8#include "CLHEP/Random/RandGaussZiggurat.h"
9#include "GaudiKernel/PhysicalConstants.h"
12 using ChVec_t = std::vector<std::uint16_t>;
45 ATH_MSG_WARNING(
"STGC run voltage must be within fit domain of 2.3 kV to 3.2 kV");
46 return StatusCode::FAILURE;
48 double meanGasGain = 2.15 * 1E-4 * std::exp(6.88*
m_runVoltage);
53 return StatusCode::SUCCESS;
58 int bunchInteger = (digitTime > 0) ?
static_cast<int>(std::abs(digitTime / 25.0))
59 :
static_cast<int>(std::abs(digitTime / 25.0)) + 1;
60 bctag = (bctag | bunchInteger);
61 if (digitTime < 0) bctag = ~bctag;
69 std::optional<float> elecThreshold = thresholdData.
getThreshold(channelID);
71 THROW_EXCEPTION(
"Cannot retrieve VMM threshold from conditions database!");
75 THROW_EXCEPTION(
"Cannot convert VMM charge threshold via conditions data!");
96 double earliestEventTime = std::numeric_limits<double>::max();
98 const double angular_tolerance = 1e-3;
101 if (viewer.
size() == 0) {
105 std::array<sTgcSimDigitVec , 3> simDigitsByChType{};
106 for (
const TimedHit& hit : viewer) {
113 double eventTime = hit.eventTime();
114 earliestEventTime = std::min(earliestEventTime, eventTime);
122 const Amg::Vector3D locHitPos = xAOD::toEigen(hit->localPosition());
123 const Amg::Vector3D locHitDir = xAOD::toEigen(hit->localDirection());
125 const double hitKineticEnergy = hit->kineticEnergy();
127 ATH_MSG_DEBUG(
"Skip electron hit with kinetic energy " << hitKineticEnergy
134 const double theta = std::acos(locHitDir.z());
135 const double ninetyDegrees = std::numbers::pi / 2;
137 if (std::abs(
theta - ninetyDegrees) < angular_tolerance) {
141 if (std::abs(locHitDir.z()) < 0.00001) {
146 ATH_MSG_DEBUG(
"Updated hit global time to include off set of " << eventTime <<
" ns from OOT bunch.");
156 bool acceptHit =
true;
165 double globalHitTime = hit->globalTime() + eventTime;
166 double tofCorrection = globalHitPos.mag() / Gaudi::Units::c_light;
167 double bunchTime = globalHitTime - tofCorrection;
172 <<
" globalHitPosition " <<
Amg::toString(globalHitPos) <<
" hit: r " << globalHitPos.perp() <<
" z " << globalHitPos.z()
173 <<
" mclink " << particleLink <<
"Kinetic energy " << hitKineticEnergy);
174 ATH_MSG_VERBOSE(
"Total hits passed to digitize: " << hitsToDigit.size());
176 if(digitizedHits.empty()) {
179 ATH_MSG_VERBOSE(
"Hit produced " << digitizedHits.size() <<
" digits." );
181 for (std::unique_ptr<sTgcDigit>& digit : digitizedHits) {
191 double digitTime = digit->time();
194 if(digitChType == ReadoutChannelType::Strip) {
201 uint16_t digitBCTag =
bcTagging(digitTime + bunchTime);
204 double digitCharge = digit->charge();
206 int eventId = hit.eventId();
207 bool isDead{
false}, isPileup{eventId != 0};
208 ATH_MSG_VERBOSE(
"Hit is from the main signal subevent if eventId is zero, eventId = "
209 << eventId <<
" newDigit time: " << digitTime);
210 auto newDigitPtr = std::make_unique<sTgcDigit>(digitId, digitBCTag, digitTime, digitCharge, isDead, isPileup);
211 if (digitChType == ReadoutChannelType::Strip) {
213 <<
" BC tag = " << newDigitPtr->bcTag()
214 <<
" digitTime = " << newDigitPtr->time()
215 <<
" charge = " << newDigitPtr->charge());
217 simDigitsByChType[digitChType].emplace_back(hit, std::move(newDigitPtr));
229 for (
auto& [simHit, assocIds]: sdoIdMap) {
238 if (typeA != typeB) {
244 return typeA > typeB;
248 const double globalHitTime = sdoHit->
globalTime() + simHit.eventTime();
252 assocIds.erase(assocIds.begin());
254 ChVec_t& stripCh{dec_stripCh(*sdoHit)}, wireCh{dec_wireCh(*sdoHit)}, padCh{dec_padCh(*sdoHit)};
256 std::ranges::for_each(assocIds,[&](
const Identifier& secId){
257 const int ch = idHelper.
channel(secId);
261 stripCh.push_back(ch);
264 wireCh.push_back(ch);
275 }
while (viewer.
next());
279 return StatusCode::SUCCESS;
285 const double vmmDeadTime,
286 const bool isNeighbourOn,
290 if (digitsInChamber.empty()) {
292 return StatusCode::SUCCESS;
298 isNeighbourOn, std::move(digitsInChamber));
300 if (mergedDigits.empty()) {
301 return StatusCode::SUCCESS;
307 bool acceptDigit{
true};
308 float chargeAfterSmearing = merged.getDigit().charge();
318 if (idHelper.
channelType(merged.identify()) == ReadoutChannelType::Strip &&
319 chargeAfterSmearing < 0.001) {
322 auto finalDigit = merged.releaseDigit();
324 finalDigit->set_charge(chargeAfterSmearing);
327 " BC tag = " << finalDigit->bcTag()<<
328 " digitTime = " << finalDigit->time() <<
329 " charge = " << finalDigit->charge());
332 sdoIdMap[merged.getSimHit()].push_back(finalDigit->identify());
334 outColl.
push_back(std::move(finalDigit));
336 return StatusCode::SUCCESS;
342 const double vmmDeadTime,
343 const bool isNeighbourOn,
348 std::ranges::stable_sort(unmergedDigits,
350 const int layA = idHelper.
gasGap(
a.identify());
351 const int layB = idHelper.
gasGap(b.identify());
355 const int chA = idHelper.
channel(
a.identify());
356 const int chB = idHelper.
channel(b.identify());
360 return a.time() < b.time();
365 premerged.reserve(unmergedDigits.size());
366 savedDigits.reserve(premerged.capacity());
369 if (!isNeighbourOn || savedDigits.empty()) {
372 if (savedDigits.back().identify() == candidate.identify() &&
373 std::abs(savedDigits.back().time() - candidate.time()) < vmmDeadTime) {
377 const Identifier digitId = candidate.identify();
378 const int channel = idHelper.
channel(digitId);
381 const int maxChannel = reEle->
numChannels(hitHash);
382 for (
int neighbour : {std::max(1, channel -1), std::min(maxChannel, channel+1)}) {
384 if (neighbour == channel) {
394 return known.identify() == neighbourId &&
406 for (sTgcSimDigitVec::iterator merge_me = unmergedDigits.begin(); merge_me!= unmergedDigits.end(); ++merge_me) {
412 sTgcDigit& digit1{(*merge_me).getDigit()};
413 double totalCharge = digit1.
charge();
414 double weightedTime = digit1.
time();
416 sTgcSimDigitVec::iterator merge_with = merge_me + 1;
417 for ( ; merge_with!= unmergedDigits.end(); ++merge_with) {
419 if ((*merge_with).identify() != (*merge_me).identify()) {
422 const sTgcDigit& mergeDigit{(*merge_with).getDigit()};
428 weightedTime = (weightedTime * totalCharge + mergeDigit.
time() * mergeDigit.
charge())
429 / (totalCharge + mergeDigit.
charge());
431 totalCharge += mergeDigit.
charge();
436 if (!savedDigits.empty() &&
437 savedDigits.back().identify() == digit1.
identify() &&
438 std::abs(savedDigits.back().time() - digit1.
time()) <= vmmDeadTime)
continue;
440 savedDigits.emplace_back(std::move(mergedHit));
441 }
else if (isNeighbourOn) {
442 premerged.emplace_back(std::move(mergedHit));
445 std::copy_if(std::make_move_iterator(premerged.begin()),
446 std::make_move_iterator(premerged.end()),
447 std::back_inserter(savedDigits), passNeigbourLogic);
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
ATLAS-specific HepMC functions.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
a link optimized in size for a GenParticle in a McEventCollection
This is a "hash" representation of an Identifier.
Identifier identify() const
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const
Returns the transformation from the local coordinate system of the readout element into the global AT...
unsigned numChannels(const IdentifierHash &measHash) const
Returns the number of strips / wires / pads in a given gasGap.
IdentifierHash measurementHash(const Identifier &measId) const override final
Constructs the identifier hash from the full measurement Identifier.
int multilayer() const
Returns the multilayer of the sTgcReadoutElement.
size_type module_hash_max() const
the maximum hash value
digitMode
Constructor initializing digitization parameters.
Conditions data to model a channel dependent energy deposit threshold such that the electronics retur...
std::optional< float > getThreshold(const Identifier &channelId) const
Helper class to provide type-safe access to aux data.
void set_time(float newTime)
void set_charge(float newCharge)
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
bool next() noexcept
Loads the hits from the next chamber.
std::size_t size() const noexcept
Returns how many hits are in the current chamber.
void setGlobalTime(const float time)
Sets the time of the traversing particle.
void setIdentifier(const Identifier &id)
Sets the global ATLAS identifier.
float globalTime() const
Returns the time ellapsed since the collision of the traversing particle.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Matrix< double, 3, 1 > Vector3D
bool isElectron(const T &p)
This header ties the generic definitions in this package.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
MuonSimHitContainer_v1 MuonSimHitContainer
Define the version of the pixel cluster container.
CLHEP::HepRandomEngine * rndEngine
const NswCalibDbThresholdData * thresholdData
#define THROW_EXCEPTION(MESSAGE)