2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
4 #ifndef MUONREADOUTGEOMETRYR4_PADDESIGN_ICC
5 #define MUONREADOUTGEOMETRYR4_PADDESIGN_ICC
7 #include <MuonReadoutGeometryR4/PadDesign.h>
10 using CheckVector2D = PadDesign::CheckVector2D;
11 inline int PadDesign::numPads() const { return m_numPadPhi * m_numPadEta; }
12 inline double PadDesign::firstPadPhiDiv() const { return m_firstPadPhiDiv; }
13 inline int PadDesign::numPadPhi() const { return m_numPadPhi; }
14 inline double PadDesign::anglePadPhi() const { return m_anglePadPhi; }
15 inline double PadDesign::padPhiShift() const { return m_padPhiShift; }
16 inline double PadDesign::firstPadHeight() const { return m_firstPadHeight; }
17 inline int PadDesign::numPadEta() const { return m_numPadEta; }
18 inline double PadDesign::padHeight() const { return m_padHeight; }
19 inline int PadDesign::maxPadEta() const { return m_maxPadEta; }
20 inline std::pair<int, int> PadDesign::padNumber(const int SeqChannel) const {
21 int padEta = (SeqChannel -1) % numPadEta() + 1;
22 int padPhi = (SeqChannel -1) / numPadEta() + 1;
23 if (SeqChannel < 0 || padEta > numPadEta() || padPhi > numPadPhi()) {
24 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" Pad channel: "<< SeqChannel<< " resulted in invalid (eta, phi): ("<<padEta<< ", " <<padPhi
25 <<"), (maxEta, maxPhi): "<<numPadEta()<<", "<<numPadPhi()<<"). total pads available: "<<numPads());
26 return std::make_pair(0, 0);
28 return std::make_pair(padEta, padPhi);
30 inline std::pair<int, int> PadDesign::padEtaPhi(const int channel) const {
31 int padEta = (channel -1) % maxPadEta() + 1;
32 int padPhi = (channel -1) / maxPadEta() + 1;
33 if (channel < 0 || padEta > numPadEta() || padPhi > numPadPhi()) {
34 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" Pad channel: "<< channel<< " resulted in invalid (eta, phi): ("<<padEta<< ", " <<padPhi
35 <<"), (maxEta, maxPhi): "<<numPadEta()<<", "<<numPadPhi()<<"). total pads available: "<<numPads());
36 return std::make_pair(0, 0);
38 return std::make_pair(padEta, padPhi);
40 inline int PadDesign::padEta(const int channel) const {
41 return padEtaPhi(channel).first;
43 inline int PadDesign::padPhi(const int channel) const {
44 return padEtaPhi(channel).second;
46 inline double PadDesign::beamlineRadius() const {
50 using localCornerArray = std::array<Amg::Vector2D, 4>;
51 inline localCornerArray PadDesign::padCorners(const int channel) const {
52 return padCorners(padEtaPhi(channel));
54 inline Amg::Vector2D PadDesign::stripPosition(int channel) const {
55 if (padEta(channel) == 0 || padPhi(channel) == 0) {
56 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" The pad number "<<channel
57 <<" is out of range. Maximum range: " << numPads());
58 return Amg::Vector2D::Zero();
60 localCornerArray Corners = padCorners(channel);
61 Amg::Vector2D padCenter = 0.25 * (Corners[botLeft] + Corners[botRight] + Corners[topLeft] + Corners[topRight]);
64 inline std::pair<int, int> PadDesign::channelNumber(const Amg::Vector2D& hitPos) const {
65 /// distance from the short edge to the hit. No change for L3 since the center is at the center of the gas gap.
66 double hitY = halfWidth() + hitPos.y();
67 /// Variables to store padEta information
68 double padEtaDouble{0.};
70 /// Applying neagtive of staggering to the hit in Phi direction
71 double locPhi = std::atan2(-hitPos.x(), (beamlineRadius() + hitPos.y())) / Gaudi::Units::deg;
72 double locMaxPhi = std::atan2(maxActiveX(hitPos.y()), (beamlineRadius() + hitPos.y())) / Gaudi::Units::deg;
73 double shiftedX = hitPos.x() - padPhiShift() * std::cos(locPhi * Gaudi::Units::deg);
74 double shiftedLocPhi = std::atan2(-shiftedX, (beamlineRadius() + hitPos.y())) / Gaudi::Units::deg;
75 /// Booleans to define bounds
76 bool bottomHalf = (hitY < 0);
77 bool outsidePhiRange = (std::abs(locPhi) > locMaxPhi) || (std::abs(shiftedLocPhi) > locMaxPhi);
78 /// Evaluating Eta of the pad where the hit is contained
79 if(insideBoundaries(hitPos) && !bottomHalf) {
80 if (hitY > firstPadHeight()) {
81 /// + 1 for ignoring the firstPadRow and another + 1 because remainder means hit is in the next row (eg. 3.1 = 4)
82 padEtaDouble = ((hitY - firstPadHeight()) / padHeight()) + 2;
83 padEta = padEtaDouble;
90 ATH_MSG_WARNING("The failed Hit: "<< Amg::toString(hitPos, 2) << " is outside the trapezoid bounds. "<< "HitY: " << hitY);
92 /// Variables to store padPhi information
93 double padPhiDouble{0.};
95 /// Hits on the pads near the side edges of the trapezoid are shifted but
96 /// the pads are not hence, those hits must be corrected.
97 if (outsidePhiRange) {
98 padPhiDouble = (locPhi - firstPadPhiDiv()) / anglePadPhi();
99 ATH_MSG_DEBUG("The Hit: "<< Amg::toString(hitPos, 2) << " is out of bounds." << " maxActive X: " << maxActiveX(hitPos.y()));
100 ATH_MSG_DEBUG("HitPhi: "<< locPhi << " MaxPhi: " << locMaxPhi << " Staggered Hit phi: " << shiftedLocPhi
101 << " padPhi: " << padPhiDouble << " firsPadPhiDiv: " << firstPadPhiDiv() << " anglePadPhi: " << anglePadPhi());
104 padPhiDouble = (shiftedLocPhi - firstPadPhiDiv()) / anglePadPhi();
106 /// +1 for the firstPadColumn to be 1, and another +1 because remainder means hit is in the next column (eg. 2.1 = 3)
107 padPhi = padPhiDouble + 2;
108 /// Adjusting the padEta and padPhi bounds
109 if (padEta == numPadEta() + 1) { padEta -= 1; }
110 if (padPhi == 0) { padPhi = 1; }
111 if (padPhi == numPadPhi() + 1) { padPhi = numPadPhi(); }
112 /// Final check on bounds
113 bool etaOutsideRange = (padEta > numPadEta());
114 bool phiOutsideRange = (padPhi < 0 || padPhi > numPadPhi() + 1);
115 if(etaOutsideRange or phiOutsideRange) {
116 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" "<<__func__<<"() Given hit: "<<Amg::toString(hitPos, 2)<< " gives pad (Eta, Phi): ("
117 <<padEta<<", "<<padPhi<<") out of range. Maximum Range: ("<< numPadEta()<<", "<<numPadPhi()<<").");
118 return std::make_pair(0, 0);
121 return std::make_pair(padEta, padPhi);
124 inline double PadDesign::maxActiveX(const double locY) const {
125 if (std::abs(locY) > halfWidth()) {
126 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" Local Y of the hit: "<<locY
127 <<" is out of range. Maximum range: "<<halfWidth());
130 double halfSectorAngle = 0.5 * sectorAngle() * Gaudi::Units::deg;
132 if (locY > (halfWidth() - yCutout())) {
133 return longHalfHeight();
135 return longHalfHeight() + locY * std::tan(halfSectorAngle);
137 return longHalfHeight() - (halfWidth() - locY) * std::tan(halfSectorAngle);
139 inline double PadDesign::sectorAngle() const {
140 double sectorAngle = 2 * std::atan2(shortHalfHeight(), (beamlineRadius() - halfWidth())) / Gaudi::Units::deg;;
141 if (sectorAngle > (m_largeSectorAngle - 5) && sectorAngle < (m_largeSectorAngle + 5)) {
142 return m_largeSectorAngle;
144 else if (sectorAngle > (m_smallSectorAngle - 5) && sectorAngle < (m_smallSectorAngle + 5)) {
145 return m_smallSectorAngle;
148 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" Dimensions of the given gasGap result into invalid sector opening angle: "<<
149 sectorAngle<<" angle should be around 17 for small and 28 for large sector.");