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;
69 const double scale =
Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0.).value_or(0);
74 ReadoutChannelType::Wire, 1);
83 std::pair<int, int> wireGrpWireNum = wireDesign.
wireNumber(hitOnWire2D);
84 if (wireGrpWireNum.first < 0) {
85 ATH_MSG_WARNING(__func__<<
"() "<<__LINE__<<
" - Unable to retrieve the wire number, skipping the hit: "
89 int wireNumber = wireDesign.
numPitchesToGroup(wireGrpWireNum.first) + wireGrpWireNum.second;
90 const int numWires = wireDesign.
nAllWires();
92 if((wireNumber < 1) || (wireNumber > numWires)) {
93 ATH_MSG_WARNING(__func__<<
"() "<<__LINE__<<
" - Unable to retrieve the wire number, skipping the hit: "
100 double distToWire = ionization.
distance;
102 if(distToWire > 0.) {
107 double distToWireAdj = ionizationAdj.
distance;
109 if ((distToWireAdj > 0.) && (distToWireAdj < distToWire)) {
110 distToWire = distToWireAdj;
111 wireNumber += adjacent;
112 ionization = std::move(ionizationAdj);
121 ATH_MSG_DEBUG(
"Distance to nearest wire: " << distToWire <<
" greater than wirePitch.");
129 const double t0_par = most_prob_time - gamma_mpv;
132 double digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndEngine, gamParam.
kParameter, inv_theta);
134 constexpr unsigned shoot_limit = 4;
135 unsigned shoot_counter = 0;
136 while (digitTime < 0. && ++shoot_counter <= shoot_limit) {
137 digitTime = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndEngine, gamParam.
kParameter,inv_theta);
140 ionization.
time = std::max(0., digitTime);
149 const double ionized_charge = (5.65E-6) * energyDeposit / CLHEP::keV;
154 return gain * ionized_charge;
165 if (energyDeposit < std::numeric_limits<float>::epsilon()) {
182 <<
" energyDeposit "<< energyDeposit );
193 digiInput.
hitId = hitId;
210 allDigits.insert(allDigits.end(),
211 std::make_move_iterator(stripDigits.begin()),
212 std::make_move_iterator(stripDigits.end()));
219 allDigits.insert(allDigits.end(),
220 std::make_move_iterator(padDigits.begin()),
221 std::make_move_iterator(padDigits.end()));
232 allDigits.insert(allDigits.end(),
233 std::make_move_iterator(wireDigits.begin()),
234 std::make_move_iterator(wireDigits.end()));
248 ReadoutChannelType::Strip, 1);
260 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
261 int stripNumber = stripDesign.
stripNumber(hitOnStripSurf);
262 if( stripNumber < 0){
263 const double newPosX = hitOnStripSurf.x() - std::copysign(tolerance_length, hitOnStripSurf.x());
266 if (stripNumber < 0) {
273 const double tan_theta = digiInput.
hitDir.perp() / digiInput.
hitDir.z();
276 const double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.
rndEngine,
278 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Smeared hit from "<<hitOnStripSurf.x()<<
" -> "<<
279 peak_position<<
". Assign strip charges");
288 const double peak_position,
289 const int stripNumber)
const {
294 const double tan_theta = digiInput.
hitDir.perp() / digiInput.
hitDir.z();
296 constexpr double tolerance_charge = 0.0005;
297 constexpr int max_neighbor = 10;
302 for (
int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
303 for (
int sign : {-1 , 1}) {
304 int currentStrip = stripNumber +
sign*iStrip;
305 if (currentStrip > design.numStrips() || currentStrip < 1) {
306 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Breaking the upper half strip loop stripNumber: "
307 << currentStrip <<
" > " << design.numStrips());
313 gasGap, ReadoutChannelType::Strip,
319 const double x_relative = (currentStripPos.x() - peak_position);
320 const double normX = x_relative / design.stripPitch();
324 if (
charge < tolerance_charge) {
325 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Breaking the upper half strip loop stripCharge: "
326 <<
charge <<
" < " << tolerance_charge);
330 double strip_time = digiInput.
time;
332 const bool shift = ((iStrip > 0) && ((x_relative + (0.5*0.75 - iStrip) * design.stripPitch()) < 0));
337 ATH_MSG_VERBOSE(__func__<<
"() - "<<__LINE__<<
" Created a strip digit: strip number = "
338 << currentStrip <<
", charge = " <<
charge);
357 ATH_MSG_DEBUG(__func__<<
"() -"<<__LINE__<<
" Outside of the pad surface boundary :"
363 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
364 auto [padEta, padPhi] = padDesign.
channelNumber(hitOnPadSurf);
366 if( padEta < 1 || padPhi < 1){
367 const double newPosX = hitOnPadSurf.x() - std::copysign(tolerance_length, hitOnPadSurf.x());
368 const double newPosY = hitOnPadSurf.y() - std::copysign(tolerance_length, hitOnPadSurf.y());
370 auto [newPadEta, newPadPhi] = padDesign.
channelNumber(newPosition);
373 if( padEta < 1 || padPhi < 1) {
387 const int padPhi)
const {
393 gasGap, ReadoutChannelType::Pad, padEta, padPhi,
isValid);
406 const double halfPadHeight = 0.5 * digiInput.
reEle->
padHeight(padHitHash);
408 const std::array<Amg::Vector2D, 4> padHitCorners = padDesign.
padCorners(std::make_pair(padEta, padPhi));
409 const double padBottomBase = (padHitCorners[0] - padHitCorners[1]).norm();
410 const double padTopBase = (padHitCorners[2] - padHitCorners[3]).norm();
411 const double halfPadWidth = 0.5 * padBottomBase + 0.25 * (1 +
diff.y()/halfPadHeight) * (padTopBase - padBottomBase);
413 double deltaX = halfPadWidth - std::abs(
diff.x());
414 double deltaY = halfPadHeight - std::abs(
diff.y());
426 unsigned newPhi = padPhi - Acts::copySign(1,
diff.x());
427 unsigned newEta = padEta + Acts::copySign(1,
diff.y());
428 bool validEta = newEta > 0 && newEta <= digiInput.
reEle->
numPadEta(padHitHash);
429 bool validPhi = newPhi > 0 && newPhi <= digiInput.
reEle->
numPadPhi(padHitHash);
431 if (isNeighX && isNeighY && validEta && validPhi) {
433 gasGap, ReadoutChannelType::Pad,
436 ReadoutChannelType::Pad, newEta, padPhi);
438 ReadoutChannelType::Pad, newEta, newPhi);
445 addDigit(digits, padHitId, digiInput.
time, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*digiInput.
totalCharge);
446 }
else if (isNeighX && validPhi){
448 ReadoutChannelType::Pad, padEta, newPhi);
453 else if (isNeighY && validEta){
455 ReadoutChannelType::Pad, newEta, padPhi);
475 ReadoutChannelType::Wire, 1);
485 constexpr double tolerance_length = 0.01*Gaudi::Units::mm;
486 int wiregroupNumber = (wireDesign.
wireNumber(hitOnWireSurf)).first;
487 if( wiregroupNumber < 1) {
488 const double newPosX = hitOnWireSurf.x() - std::copysign(tolerance_length, hitOnWireSurf.x());
490 wiregroupNumber = (wireDesign.
wireNumber(newPos)).first;
491 if (wiregroupNumber < 1) {
499 ReadoutChannelType::Wire, wiregroupNumber,
isValid);
516 const double stepLength)
const {
518 constexpr double angular_tolerance = 1e-3;
537 const double wirePitch = wireDesign.
stripPitch();
538 const double wirePosX = wireDesign.
firstStripPos().x() + (wireNumber - 1) * wirePitch;
543 std::optional<double> scaleHit =
Amg::intersect<3>(locHitPos, locHitDir, Amg::Vector3D::UnitZ(), 0);
544 if (!scaleHit || std::abs(std::abs(wireDir.dot(locHitDir)) - 1.0) < angular_tolerance) {
545 ATH_MSG_DEBUG(
"The track segment is parallel to the wire, position of digit is undefined");
548 ionization.
distance = std::hypot(locHitPos.y() + wirePosX, locHitPos.z());
549 if (ionization.
distance > wirePitch) {
556 Amg::Vector3D ionizationPos = locHitPos + scaleHit.value() * locHitDir;
558 if (scaleHit.value() > stepLength) {
560 const Amg::Vector3D closestPointToWirePlane = locHitPos + stepLength * locHitDir;
562 ionization.
distance = std::abs(closestPointToWirePlane.z());
567 const double scaleWire = (ionizationPos - wirePos).
dot(wireDir);
568 const Amg::Vector3D closestPointOnWire = wirePos + scaleWire * wireDir;
572 ionization.
posOnWire = closestPointOnWire;
573 ionization.
distance = (ionizationPos - closestPointOnWire).
mag();
583 const double digittime,
587 if (!std::ranges::any_of(digits, [&](std::unique_ptr<sTgcDigit>&
known) {
590 digits.push_back(std::make_unique<sTgcDigit>(
id, 0, digittime,
charge, 0, 0));
598 const std::string file_name =
"sTGC_Digitization_timeArrival.dat";
600 if(file_path.empty()) {
601 ATH_MSG_FATAL(
"readFileOfTimeWindowOffset(): Could not find file " << file_name );
602 return StatusCode::FAILURE;
605 std::ifstream ifs{file_path, std::ios::in};
607 ATH_MSG_FATAL(
"sTgcDigitMaker: Failed to open time of arrival file " << file_name );
608 return StatusCode::FAILURE;
614 while (std::getline(ifs, line)) {
616 std::istringstream iss(line);
621 }
else if (key ==
"mpv") {
628 return StatusCode::SUCCESS;
635 const double d = std::abs(distance);
642 if (d < par.lowEdge) {
662 double term1 = 0.25 * std::erf( M / (std::sqrt(2) *
m_clusterParams[0]));
663 double term2 = 0.25 * std::erf( N / (std::sqrt(2) *
m_clusterParams[0]));
664 double term3 = 0.25 * std::erf( M / (std::sqrt(2) *
m_clusterParams[1]));
665 double term4 = 0.25 * std::erf( N / (std::sqrt(2) *
m_clusterParams[1]));
667 return (term1 - term2 + term3 - term4);
673 const std::string file_name =
"sTGC_Digitization_timeOffsetStrip.dat";
675 if(file_path.empty()) {
676 ATH_MSG_FATAL(
"readFileOfTimeWindowOffset(): Could not find file " << file_name );
677 return StatusCode::FAILURE;
681 std::ifstream ifs{file_path, std::ios::in};
683 ATH_MSG_FATAL(
"Failed to open time of arrival file " << file_name );
684 return StatusCode::FAILURE;
694 while (std::getline(ifs, line)) {
696 std::istringstream iss(line);
698 if (key ==
"strip") {
699 iss >>
index >> value;
704 return StatusCode::SUCCESS;
725 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.
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