8#include "CLHEP/Random/RandGaussZiggurat.h"
9#include "GaudiKernel/PhysicalConstants.h"
36 ATH_MSG_WARNING(
"STGC run voltage must be within fit domain of 2.3 kV to 3.2 kV");
37 return StatusCode::FAILURE;
39 double meanGasGain = 2.15 * 1E-4 * std::exp(6.88*
m_runVoltage);
44 return StatusCode::SUCCESS;
49 int bunchInteger = (digitTime > 0) ?
static_cast<int>(std::abs(digitTime / 25.0))
50 :
static_cast<int>(std::abs(digitTime / 25.0)) + 1;
51 bctag = (bctag | bunchInteger);
52 if (digitTime < 0) bctag = ~bctag;
60 std::optional<float> elecThreshold = thresholdData.
getThreshold(channelID);
62 THROW_EXCEPTION(
"Cannot retrieve VMM threshold from conditions database!");
66 THROW_EXCEPTION(
"Cannot convert VMM charge threshold via conditions data!");
87 double earliestEventTime = std::numeric_limits<double>::max();
89 const double angular_tolerance = 1e-3;
92 if (viewer.
size() == 0) {
96 std::array<sTgcSimDigitVec , 3> simDigitsByChType{};
104 double eventTime = hit.eventTime();
105 earliestEventTime = std::min(earliestEventTime, eventTime);
113 const Amg::Vector3D locHitPos = xAOD::toEigen(hit->localPosition());
114 const Amg::Vector3D locHitDir = xAOD::toEigen(hit->localDirection());
116 const double hitKineticEnergy = hit->kineticEnergy();
118 ATH_MSG_DEBUG(
"Skip electron hit with kinetic energy " << hitKineticEnergy
125 const double theta = std::acos(locHitDir.z());
126 const double ninetyDegrees = std::numbers::pi / 2;
128 if (std::abs(
theta - ninetyDegrees) < angular_tolerance) {
132 if (std::abs(locHitDir.z()) < 0.00001) {
137 ATH_MSG_DEBUG(
"Updated hit global time to include off set of " << eventTime <<
" ns from OOT bunch.");
147 bool acceptHit =
true;
156 double globalHitTime = hit->globalTime() + eventTime;
157 double tofCorrection = globalHitPos.mag() / Gaudi::Units::c_light;
158 double bunchTime = globalHitTime - tofCorrection;
163 <<
" globalHitPosition " <<
Amg::toString(globalHitPos) <<
" hit: r " << globalHitPos.perp() <<
" z " << globalHitPos.z()
164 <<
" mclink " << particleLink <<
"Kinetic energy " << hitKineticEnergy);
165 ATH_MSG_VERBOSE(
"Total hits passed to digitize: " << hitsToDigit.size());
167 if(digitizedHits.empty()) {
170 ATH_MSG_VERBOSE(
"Hit produced " << digitizedHits.size() <<
" digits." );
172 for (std::unique_ptr<sTgcDigit>& digit : digitizedHits) {
182 double digitTime = digit->time();
185 if(digitChType == ReadoutChannelType::Strip) {
192 uint16_t digitBCTag =
bcTagging(digitTime + bunchTime);
195 double digitCharge = digit->charge();
197 int eventId = hit.eventId();
198 bool isDead{
false}, isPileup{eventId != 0};
199 ATH_MSG_VERBOSE(
"Hit is from the main signal subevent if eventId is zero, eventId = "
200 << eventId <<
" newDigit time: " << digitTime);
201 auto newDigitPtr = std::make_unique<sTgcDigit>(digitId, digitBCTag, digitTime, digitCharge, isDead, isPileup);
202 if (digitChType == ReadoutChannelType::Strip) {
204 <<
" BC tag = " << newDigitPtr->bcTag()
205 <<
" digitTime = " << newDigitPtr->time()
206 <<
" charge = " << newDigitPtr->charge());
208 simDigitsByChType[digitChType].emplace_back(hit, std::move(newDigitPtr));
220 for (
auto& [simHit, assocIds]: sdoIdMap) {
229 if (typeA != typeB) {
235 return typeA > typeB;
239 const double globalHitTime = sdoHit->
globalTime() + simHit.eventTime();
243 assocIds.erase(assocIds.begin());
245 using ChVec_t = std::vector<std::uint16_t>;
249 ChVec_t& stripCh{dec_stripCh(*sdoHit)}, wireCh{dec_wireCh(*sdoHit)}, padCh{dec_padCh(*sdoHit)};
251 std::ranges::for_each(assocIds,[&](
const Identifier& secId){
252 const int ch = idHelper.
channel(secId);
256 stripCh.push_back(ch);
259 wireCh.push_back(ch);
270 }
while (viewer.
next());
274 return StatusCode::SUCCESS;
280 const double vmmDeadTime,
281 const bool isNeighbourOn,
285 if (digitsInChamber.empty()) {
287 return StatusCode::SUCCESS;
293 isNeighbourOn, std::move(digitsInChamber));
295 if (mergedDigits.empty()) {
296 return StatusCode::SUCCESS;
302 bool acceptDigit{
true};
303 float chargeAfterSmearing = merged.getDigit().charge();
313 if (idHelper.
channelType(merged.identify()) == ReadoutChannelType::Strip &&
314 chargeAfterSmearing < 0.001) {
317 auto finalDigit = merged.releaseDigit();
319 finalDigit->set_charge(chargeAfterSmearing);
322 " BC tag = " << finalDigit->bcTag()<<
323 " digitTime = " << finalDigit->time() <<
324 " charge = " << finalDigit->charge());
327 sdoIdMap[merged.getSimHit()].push_back(finalDigit->identify());
329 outColl.
push_back(std::move(finalDigit));
331 return StatusCode::SUCCESS;
337 const double vmmDeadTime,
338 const bool isNeighbourOn,
343 std::ranges::stable_sort(unmergedDigits,
345 const int layA = idHelper.
gasGap(
a.identify());
346 const int layB = idHelper.
gasGap(b.identify());
350 const int chA = idHelper.
channel(
a.identify());
351 const int chB = idHelper.
channel(b.identify());
355 return a.time() < b.time();
360 premerged.reserve(unmergedDigits.size());
361 savedDigits.reserve(premerged.capacity());
364 if (!isNeighbourOn || savedDigits.empty()) {
367 if (savedDigits.back().identify() == candidate.identify() &&
368 std::abs(savedDigits.back().time() - candidate.time()) < vmmDeadTime) {
372 const Identifier digitId = candidate.identify();
373 const int channel = idHelper.
channel(digitId);
376 const int maxChannel = reEle->
numChannels(hitHash);
377 for (
int neighbour : {std::max(1, channel -1), std::min(maxChannel, channel+1)}) {
379 if (neighbour == channel) {
389 return known.identify() == neighbourId &&
401 for (sTgcSimDigitVec::iterator merge_me = unmergedDigits.begin(); merge_me!= unmergedDigits.end(); ++merge_me) {
407 sTgcDigit& digit1{(*merge_me).getDigit()};
408 double totalCharge = digit1.
charge();
409 double weightedTime = digit1.
time();
411 sTgcSimDigitVec::iterator merge_with = merge_me + 1;
412 for ( ; merge_with!= unmergedDigits.end(); ++merge_with) {
414 if ((*merge_with).identify() != (*merge_me).identify()) {
417 const sTgcDigit& mergeDigit{(*merge_with).getDigit()};
423 weightedTime = (weightedTime * totalCharge + mergeDigit.
time() * mergeDigit.
charge())
424 / (totalCharge + mergeDigit.
charge());
426 totalCharge += mergeDigit.
charge();
431 if (!savedDigits.empty() &&
432 savedDigits.back().identify() == digit1.
identify() &&
433 std::abs(savedDigits.back().time() - digit1.
time()) <= vmmDeadTime)
continue;
435 savedDigits.emplace_back(std::move(mergedHit));
436 }
else if (isNeighbourOn) {
437 premerged.emplace_back(std::move(mergedHit));
440 std::copy_if(std::make_move_iterator(premerged.begin()),
441 std::make_move_iterator(premerged.end()),
442 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)