7#include "CLHEP/Units/SystemOfUnits.h"
8#include "CLHEP/Random/RandomEngine.h"
9#include "CLHEP/Random/RandFlat.h"
10#include "CLHEP/Random/RandGaussZiggurat.h"
11#include "CLHEP/Random/RandGamma.h"
14#include "Acts/Utilities/detail/Polynomials.hpp"
30 bool doPadChargeSharing)
51 return StatusCode::SUCCESS;
70 const double scale =
Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0.).value_or(0);
71 const double halfStep = 0.5 * hit->
stepLength();
75 if (std::abs(scale) <= halfStep) {
76 hitOnWireSurf = locHitPos + scale * locHitDir;
79 hitOnWireSurf = locHitPos + Acts::copySign(halfStep, scale) * locHitDir;
84 ReadoutChannelType::Wire, 1);
96 std::pair<int, int> wireGrpWireNum = wireDesign.
wireNumber(hitOnWire2D);
99 if (wireGrpWireNum.first < 0) {
101 wireGrpWireNum = wireDesign.
wireNumber(locHitPos2D);
104 if (wireGrpWireNum.first < 0) {
105 const Amg::Vector3D preStepPos = locHitPos - halfStep * locHitDir;
107 wireGrpWireNum = wireDesign.
wireNumber(preStepPos2D);
110 if (wireGrpWireNum.first < 0) {
111 const Amg::Vector3D postStepPos = locHitPos + halfStep * locHitDir;
113 wireGrpWireNum = wireDesign.
wireNumber(postStepPos2D);
116 if (wireGrpWireNum.first < 0) {
117 ATH_MSG_WARNING(__func__<<
"() "<<__LINE__<<
" - Unable to retrieve the wire number, skipping the hit: "
124 int wireNumber = wireDesign.
numPitchesToGroup(wireGrpWireNum.first) + wireGrpWireNum.second;
125 const int numWires = wireDesign.
nAllWires();
127 if((wireNumber < 1) || (wireNumber > numWires)) {
128 ATH_MSG_WARNING(__func__<<
"() "<<__LINE__<<
" - Unable to retrieve the wire number, skipping the hit: "
135 double distToWire = ionization.
distance;
137 if(distToWire > 0.) {
142 double distToWireAdj = ionizationAdj.
distance;
144 if ((distToWireAdj > 0.) && (distToWireAdj < distToWire)) {
145 distToWire = distToWireAdj;
146 wireNumber += adjacent;
147 ionization = std::move(ionizationAdj);
156 ATH_MSG_DEBUG(
"Distance to nearest wire: " << distToWire <<
" greater than wirePitch.");
164 const double t0_par = most_prob_time - gamma_mpv;
167 double digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndEngine, gamParam.
kParameter, inv_theta);
169 constexpr unsigned shoot_limit = 4;
170 unsigned shoot_counter = 0;
171 while (digitTime < 0. && ++shoot_counter <= shoot_limit) {
172 digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndEngine, gamParam.
kParameter,inv_theta);
175 ionization.
time = std::max(0., digitTime);
184 const double ionized_charge = (5.65E-6) * energyDeposit / CLHEP::keV;
189 return gain * ionized_charge;
200 if (energyDeposit < std::numeric_limits<float>::epsilon()) {
217 <<
" energyDeposit "<< energyDeposit );
228 digiInput.
hitId = hitId;
241 <<
" EDep: " << energyDeposit
253 allDigits.insert(allDigits.end(),
254 std::make_move_iterator(stripDigits.begin()),
255 std::make_move_iterator(stripDigits.end()));
262 allDigits.insert(allDigits.end(),
263 std::make_move_iterator(padDigits.begin()),
264 std::make_move_iterator(padDigits.end()));
275 allDigits.insert(allDigits.end(),
276 std::make_move_iterator(wireDigits.begin()),
277 std::make_move_iterator(wireDigits.end()));
291 ReadoutChannelType::Strip, 1);
303 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
304 int stripNumber = stripDesign.
stripNumber(hitOnStripSurf);
305 if( stripNumber < 0){
306 const double newPosX = hitOnStripSurf.x() - std::copysign(tolerance_length, hitOnStripSurf.x());
309 if (stripNumber < 0) {
316 const double tan_theta = digiInput.
hitDir.perp() / digiInput.
hitDir.z();
319 const double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.
rndEngine,
321 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Smeared hit from "<<hitOnStripSurf.x()<<
" -> "<<
322 peak_position<<
". Assign strip charges");
331 const double peak_position,
332 const int stripNumber)
const {
337 const double tan_theta = digiInput.
hitDir.perp() / digiInput.
hitDir.z();
339 constexpr double tolerance_charge = 0.0005;
340 constexpr int max_neighbor = 10;
345 for (
int sign : {-1, 1}) {
346 for (
int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
348 if (iStrip == 0 &&
sign == +1)
continue;
349 int currentStrip = stripNumber +
sign*iStrip;
350 if (currentStrip > design.numStrips() || currentStrip < 1) {
351 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Breaking the upper half strip loop stripNumber: "
352 << currentStrip <<
" > " << design.numStrips());
358 gasGap, ReadoutChannelType::Strip,
364 const double x_relative = (currentStripPos.x() - peak_position);
365 const double normX = x_relative / design.stripPitch();
369 if (
charge < tolerance_charge) {
370 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Breaking the upper half strip loop stripCharge: "
371 <<
charge <<
" < " << tolerance_charge);
375 double strip_time = digiInput.
time;
377 const bool shift = ((iStrip > 0) && ((x_relative + (0.5*0.75 - iStrip) * design.stripPitch()) < 0));
382 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Created a strip digit: strip number = "
383 << currentStrip <<
", charge = " <<
charge);
402 ATH_MSG_DEBUG(__func__<<
"() -"<<__LINE__<<
" Outside of the pad surface boundary :"
408 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
409 auto [padEta, padPhi] = padDesign.
channelNumber(hitOnPadSurf);
411 if( padEta < 1 || padPhi < 1){
412 const double newPosX = hitOnPadSurf.x() - std::copysign(tolerance_length, hitOnPadSurf.x());
413 const double newPosY = hitOnPadSurf.y() - std::copysign(tolerance_length, hitOnPadSurf.y());
415 auto [newPadEta, newPadPhi] = padDesign.
channelNumber(newPosition);
418 if( padEta < 1 || padPhi < 1) {
432 const int padPhi)
const {
438 gasGap, ReadoutChannelType::Pad, padEta, padPhi,
isValid);
451 const double halfPadHeight = 0.5 * digiInput.
reEle->
padHeight(padHitHash);
453 const std::array<Amg::Vector2D, 4> padHitCorners = padDesign.
padCorners(std::make_pair(padEta, padPhi));
454 const double padBottomBase = (padHitCorners[0] - padHitCorners[1]).norm();
455 const double padTopBase = (padHitCorners[2] - padHitCorners[3]).norm();
456 const double halfPadWidth = 0.5 * padBottomBase + 0.25 * (1 +
diff.y()/halfPadHeight) * (padTopBase - padBottomBase);
458 double deltaX = halfPadWidth - std::abs(
diff.x());
459 double deltaY = halfPadHeight - std::abs(
diff.y());
471 unsigned newPhi = padPhi - Acts::copySign(1,
diff.x());
472 unsigned newEta = padEta + Acts::copySign(1,
diff.y());
473 bool validEta = newEta > 0 && newEta <= digiInput.
reEle->
numPadEta(padHitHash);
474 bool validPhi = newPhi > 0 && newPhi <= digiInput.
reEle->
numPadPhi(padHitHash);
476 if (isNeighX && isNeighY && validEta && validPhi) {
478 gasGap, ReadoutChannelType::Pad,
481 ReadoutChannelType::Pad, newEta, padPhi);
483 ReadoutChannelType::Pad, newEta, newPhi);
490 addDigit(digits, padHitId, digiInput.
time, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*digiInput.
totalCharge);
491 }
else if (isNeighX && validPhi){
493 ReadoutChannelType::Pad, padEta, newPhi);
498 else if (isNeighY && validEta){
500 ReadoutChannelType::Pad, newEta, padPhi);
520 ReadoutChannelType::Wire, 1);
536 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
537 int wiregroupNumber = (wireDesign.
wireNumber(hitOnWireSurf)).first;
538 if( wiregroupNumber < 1) {
539 const double newPosX = hitOnWireSurf.x() - std::copysign(tolerance_length, hitOnWireSurf.x());
541 wiregroupNumber = (wireDesign.
wireNumber(newPos)).first;
542 if (wiregroupNumber < 1) {
550 ReadoutChannelType::Wire, wiregroupNumber,
isValid);
567 const double stepLength)
const {
569 constexpr double angular_tolerance = 1e-3;
588 const double wirePitch = wireDesign.
stripPitch();
589 const double wirePosX = wireDesign.
firstStripPos().x() + (wireNumber - 1) * wirePitch;
594 std::optional<double> scaleHit =
Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0);
595 if (!scaleHit || std::abs(std::abs(wireDir.dot(locHitDir)) - 1.0) < angular_tolerance) {
596 ATH_MSG_DEBUG(
"The track segment is parallel to the wire, position of digit is undefined");
599 ionization.
distance = std::hypot(locHitPos.y() + wirePosX, locHitPos.z());
600 if (ionization.
distance > wirePitch) {
607 Amg::Vector3D ionizationPos = locHitPos + scaleHit.value() * locHitDir;
609 if (scaleHit.value() > stepLength) {
611 const Amg::Vector3D closestPointToWirePlane = locHitPos + stepLength * locHitDir;
613 ionization.
distance = std::abs(closestPointToWirePlane.z());
618 const double scaleWire = (ionizationPos - wirePos).
dot(wireDir);
619 const Amg::Vector3D closestPointOnWire = wirePos + scaleWire * wireDir;
623 ionization.
posOnWire = closestPointOnWire;
624 ionization.
distance = (ionizationPos - closestPointOnWire).
mag();
634 const double digittime,
638 if (!std::ranges::any_of(digits, [&](std::unique_ptr<sTgcDigit>& known) {
639 return known->identify() ==
id && std::abs(digittime - known->time()) <
tolerance;
641 digits.push_back(std::make_unique<sTgcDigit>(
id, 0, digittime,
charge, 0, 0));
649 const std::string file_name =
"sTGC_Digitization_timeArrival.dat";
651 if(file_path.empty()) {
652 ATH_MSG_FATAL(
"readFileOfTimeWindowOffset(): Could not find file " << file_name );
653 return StatusCode::FAILURE;
656 std::ifstream ifs{file_path, std::ios::in};
658 ATH_MSG_FATAL(
"sTgcDigitMaker: Failed to open time of arrival file " << file_name );
659 return StatusCode::FAILURE;
665 while (std::getline(ifs, line)) {
667 std::istringstream iss(line);
672 }
else if (key ==
"mpv") {
679 return StatusCode::SUCCESS;
686 const double d = std::abs(distance);
693 if (d < par.lowEdge) {
713 double term1 = 0.25 * std::erf( M / (std::sqrt(2) *
m_clusterParams[0]));
714 double term2 = 0.25 * std::erf( N / (std::sqrt(2) *
m_clusterParams[0]));
715 double term3 = 0.25 * std::erf( M / (std::sqrt(2) *
m_clusterParams[1]));
716 double term4 = 0.25 * std::erf( N / (std::sqrt(2) *
m_clusterParams[1]));
718 return (term1 - term2 + term3 - term4);
724 const std::string file_name =
"sTGC_Digitization_timeOffsetStrip.dat";
726 if(file_path.empty()) {
727 ATH_MSG_FATAL(
"readFileOfTimeWindowOffset(): Could not find file " << file_name );
728 return StatusCode::FAILURE;
732 std::ifstream ifs{file_path, std::ios::in};
734 ATH_MSG_FATAL(
"Failed to open time of arrival file " << file_name );
735 return StatusCode::FAILURE;
745 while (std::getline(ifs, line)) {
747 std::istringstream iss(line);
749 if (key ==
"strip") {
750 iss >>
index >> value;
755 return StatusCode::SUCCESS;
776 return 0.5 * (1.0 - std::erf( distance / (std::sqrt(2) *
m_clusterParams[1])));
Scalar mag() const
mag method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
double charge(const T &p)
bool isValid(const T &p)
Av: we implement here an ATLAS-sepcific convention: all particles which are 99xxxxx are fine.
void diff(const Jet &rJet1, const Jet &rJet2, std::map< std::string, double > varDiff)
Difference between jets - Non-Class function required by trigger.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
This is a "hash" representation of an Identifier.
std::pair< int, int > channelNumber(const Amg::Vector2D &hitPos) const
Function to retrieve the pad eta and phi given a local position coordinate.
const Amg::Vector2D & firstStripPos() const
Vector indicating the first strip position.
double stripPitch() const
Distance between two adjacent strips.
bool insideTrapezoid(const Amg::Vector2D &extPos) const
Checks whether an external point is inside the trapezoidal area.
virtual int stripNumber(const Amg::Vector2D &pos) const
Calculates the number of the strip whose center is closest to the given point.
const Amg::Vector2D & stripDir() const
Vector pointing along the strip.
The StripLayer interfaces the 2D description of the strip plane layout with the 3D description of the...
const StripDesign & design(bool phiView=false) const
Returns the underlying strip design.
Amg::Vector2D to2D(const Amg::Vector3D &vec, const bool phiView) const
Transforms a 3D vector from the strip design into a 2D vector.
Amg::Vector3D to3D(CheckVector2D &&vec, const bool phiView) const
Transforms the 2D vector from the strip design into a 3D vector If phi view is switched on,...
unsigned int numPitchesToGroup(unsigned int groupNum) const
Returns the number of wire pitches to reach the given group.
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...
unsigned int nAllWires() const
Returns the number of all wires.
IdentifierHash measurementHash(const Identifier &measId) const override final
Constructs the identifier hash from the full measurement Identifier.
const PadDesign & padDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
double padHeight(const IdentifierHash &measHash) const
Returns the height of all the pads that are not adjacent to the bottom edge of the trapezoid active a...
int multilayer() const
Returns the multilayer of the sTgcReadoutElement.
Identifier measurementId(const IdentifierHash &measHash) const override final
Converts the measurement hash back to the full Identifier.
const StripDesign & stripDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
bool isEtaZero(const IdentifierHash &measurementHash, const Amg::Vector2D &localPosition) const
unsigned numPadEta(const IdentifierHash &measHash) const
Returns the number of pads in the eta direction in the given layer.
unsigned numPadPhi(const IdentifierHash &measHash) const
Returns the number of pads in the Phi direction in the given gasGap layer.
Amg::Vector2D localChannelPosition(const IdentifierHash &measHash) const
Returns the local position of the measurement in the repsective frame.
const WireGroupDesign & wireDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
static IdentifierHash createHash(const unsigned gasGap, const unsigned channelType, const unsigned channel, const unsigned wireInGrp=0)
Create a measurement hash from the Identifier fields.
const StripLayer & stripLayer(const IdentifierHash &measId) const
std::vector< std::unique_ptr< sTgcDigit > > sTgcDigitVec
Digitize a given hit.
sTgcDigitVec processWireDigitization(const DigiInput &digiInput) const
Processes wire digitization for a given hit.
const Muon::IMuonIdHelperSvc * m_idHelperSvc
StatusCode readFileOfTimeOffsetStrip()
Reads strip time offset data file.
digitMode
Constructor initializing digitization parameters.
std::vector< GammaParameter > m_gammaParameter
std::array< double, 5 > m_mostProbableArrivalTime
static constexpr std::array< double, 2 > m_clusterParams
sTgcDigitVec processPadDigitization(const DigiInput &digiInput) const
Processes pad digitization for a given hit.
static double getPadChargeFraction(double distance)
Computes charge fraction shared among pads.
double getTimeOffsetStrip(size_t neighbor_index) const
Gets the time offset for a strip cluster.
bool getIonizationPoint(const TimedHit &hit, const DigiConditions &condContainers, Ionization &ionization) const
Computes the ionization point for a hit.
digitMode m_digitMode
define offsets and widths of time windows for signals from wiregroups and strips.
double chargeIntegral(double N, double M) const
GammaParameter getGammaParameter(double distance) const
Retrieves gamma distribution parameters based on distance.
static void addDigit(sTgcDigitVec &digits, const Identifier &id, double digittime, double charge)
Adds a digit to the appropriate cache.
sTgcDigitVec processStripChargeSharing(const DigiInput &digiInput, const double peak_position, const int stripNumber) const
Handles charge sharing for strip clusters.
StatusCode initialize()
Initialize digitization parameters, including reading necessary data files.
sTgcDigitMaker(const MuonGMR4::MuonDetectorManager *detMgr, digitMode mode, double meanGasGain, bool doPadChargeSharing)
sTgcDigitVec processStripDigitization(const DigiConditions &condContainers, const DigiInput &digiInput) const
Processes strip digitization for a given hit.
double getMostProbableArrivalTime(double distance) const
Computes the most probable arrival time based on the distance of closest approach.
const sTgcIdHelper & m_idHelper
double m_chargeAngularFactor
double calculateTotalCharge(double energyDeposit, CLHEP::HepRandomEngine *rndEngine) const
Calculates total charge from energy deposit, including gas gain.
Ionization pointClosestApproach(const MuonGMR4::StripLayer &stripLayer, 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.
sTgcDigitVec processPadChargeSharing(const DigiInput &digiInput, const int padEta, const int padPhi) const
Handles charge sharing for pad clusters.
sTgcDigitVec executeDigi(const DigiConditions &condContainers, const TimedHit &hit) const
TimedHitPtr< xAOD::MuonSimHit > TimedHit
std::array< double, 6 > m_timeOffsetStrip
virtual ~sTgcDigitMaker()
Destructor.
StatusCode readFileOfTimeArrival()
Reads time arrival data file.
const MuonGMR4::MuonDetectorManager * m_detMgr
double getEfficiency(const Identifier &channelId, bool isInnerQ1=false) const
Returns the signal generation efficiency of the sTgc channel.
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
ConstVectorMap< 3 > localDirection() const
Returns the local direction of the traversing particle.
int pdgId() const
Returns the pdgID of the traversing particle.
float stepLength() const
Returns the path length of the G4 step.
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
float energyDeposit() const
Returns the energy deposited by the traversing particle inside the gas volume.
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
const HepMcParticleLink & genParticleLink() const
Returns the link to the HepMC particle producing this hit.
float globalTime() const
Returns the time ellapsed since the collision of the traversing particle.
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="")
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::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
This header ties the generic definitions in this package.
sTgcDigitMaker::sTgcDigitVec sTgcDigitVec
Holds necessary conditions and data for digitization.
const Muon::DigitEffiData * efficiencies
CLHEP::HepRandomEngine * rndEngine
Stores gamma distribution parameters for estimating digit time.
Holds information about ionization points in the gas volume.
Amg::Vector3D posOnSegment