ATLAS Offline Software
Loading...
Searching...
No Matches
MuonPadDesign.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
4
8#include <ext/alloc_traits.h>
9#include <stdexcept>
11
14 double top_H1 = maxSensitiveY();
15 double bot_H2 = minSensitiveY();
16 double max_x = maxAbsSensitiveX(pos.y());
17 bool y_in_range = (pos.y() <= top_H1 and pos.y() >= bot_H2);
18 bool x_in_range = std::abs(pos.x()) <= max_x + 0.01;
19 return y_in_range and x_in_range;
20}
21//----------------------------------------------------------
22double MuonPadDesign::minSensitiveY() const { return yCutout ? -(Size - yCutout) : -0.5 * Size; }
23//----------------------------------------------------------
24double MuonPadDesign::maxSensitiveY() const { return yCutout ? yCutout : 0.5 * Size; }
25//----------------------------------------------------------
26double MuonPadDesign::maxAbsSensitiveX(const double& y) const {
27 double half_openingAngle = sectorOpeningAngle / 2.0;
28 if (isLargeSector && yCutout) { // if QL3
29 if (y > 0) // In cutout region
30 return 0.5 * lPadWidth;
31 else
32 return y * std::tan(half_openingAngle * CLHEP::degree) + 0.5 * lPadWidth;
33 } else
34 return (y - Size * 0.5) * std::tan(half_openingAngle * CLHEP::degree) + 0.5 * lPadWidth;
35
36 return -1;
37}
38//----------------------------------------------------------
39std::pair<int, int> MuonPadDesign::channelNumber(const Amg::Vector2D& pos) const {
40 /* Changes in this package are due to new geometry implementations
41 * Correct active area position and inclusion of proper QL3 shape.
42 * coordinates (0,0) now point to the center of the active region, not gas volume
43 * for QL3, where ycutout !=0, (0,0) is at start of ycutout */
44
45 // perform check of the sensitive area
46 std::pair<int, int> result(-1, -1);
47
48 // padEta
49 double y1 = yCutout ? Size - yCutout + pos.y() : 0.5 * Size + pos.y(); // distance from small edge to hit
50 double padEtadouble;
51 int padEta = 0;
52 // padPhi
53 // To obtain the pad number of a given position, its easier to apply the
54 // pad staggering to the position instead of the pad: apply -PadPhiShift.
55 // Pad corners and positions however fully account for their staggering
56 double locPhi = std::atan(-1.0 * pos.x() / (radialDistance + pos.y())) / CLHEP::degree;
57 double maxlocPhi = std::atan(maxAbsSensitiveX(pos.y()) / (radialDistance + pos.y())) / CLHEP::degree;
58 double fuzziedX = pos.x() - 1. * PadPhiShift * std::cos(locPhi * CLHEP::degree);
59 double fuzziedlocPhi = std::atan(-1.0 * fuzziedX / (radialDistance + pos.y())) / CLHEP::degree;
60
61 bool below_half_length = (y1 < 0);
62 bool outside_phi_range = (std::abs(locPhi) > maxlocPhi) || (std::abs(fuzziedlocPhi) > maxlocPhi);
63
64 if (withinSensitiveArea(pos) && !below_half_length) {
65 if (y1 > firstRowPos) {
66 //+1 for firstRow, +1 because a remainder means another row (3.1=4)
67 padEtadouble = ((y1 - firstRowPos) / inputRowPitch) + 1 + 1;
68 padEta = padEtadouble;
69 } else if (y1 >= 0) {
70 padEta = 1;
71 }
72 double padPhidouble;
73 // These are separated as the hits on the pads closest to the side edges are not fuzzied
74 // We must do a correction in order to stay consistent with indexing
75 if (outside_phi_range)
76 padPhidouble = (locPhi - firstPhiPos) / inputPhiPitch;
77 else // Look for the index of the fuzzied hit
78 padPhidouble = (fuzziedlocPhi - firstPhiPos) / inputPhiPitch;
79 int padPhi = padPhidouble + 2; //(+1 because remainder means next column e.g. 1.1=2, +1 so rightmostcolumn=1)
80
81 // adjust indices if necessary
82 if (padEta == nPadH + 1) { padEta -= 1; } // the top row can be bigger, therefore it is really in the nPadH row.
83 if (padPhi == 0) { padPhi = 1; } // adjust rightmost
84 if (padPhi == nPadColumns + 1) { padPhi = nPadColumns; } // adjust lefmost
85
86 // final check on range
87 bool ieta_out_of_range = (padEta > nPadH + 1);
88 bool iphi_out_of_range = (padPhi < 0 || padPhi > nPadColumns + 1);
89 bool index_out_of_range = ieta_out_of_range or iphi_out_of_range;
90 if (index_out_of_range) {
91 std::stringstream sstr{};
92 if (ieta_out_of_range){
93 sstr<<__FILE__<<":"<<__LINE__<<" "<<__func__<<"() eta out of range "
94 <<Amg::toString(pos, 2)<<" (ieta, iphi) = ("<<padEta<<","<<padPhi<<").";
95 } else {
96 sstr<<__FILE__<<":"<<__LINE__<<" "<<__func__<<"() phi out of range "
97 <<Amg::toString(pos, 2)<<" (ieta, iphi) = ("<<padEta<<","<<padPhi<<").";
98 }
99 throw std::runtime_error(sstr.str());
100
101 } else {
102 result = std::make_pair(padEta, padPhi);
103 }
104 }
105 return result;
106}
107
108//----------------------------------------------------------
109bool MuonPadDesign::channelPosition(const int channel, Amg::Vector2D& pos) const {
110 return channelPosition(etaPhiId(channel), pos);
111}
112bool MuonPadDesign::channelPosition(const std::pair<int, int>& pad, Amg::Vector2D& pos) const {
113 CornerArray corners{make_array<Amg::Vector2D, 4>(Amg::Vector2D::Zero())};
114 channelCorners(pad, corners);
115 pos = 0.25 * (corners[botLeft] + corners[botRight] + corners[topLeft] + corners[topRight]);
116 return true;
117}
119 CornerArray corners{make_array<Amg::Vector2D, 4>(Amg::Vector2D::Zero())};
120 channelCorners(channel, corners);
121
122 Amg::Vector2D leftEdge = corners[topLeft] - corners[botLeft];
123 const double lenLeft = std::hypot(leftEdge.x(), leftEdge.y());
124 leftEdge /= lenLeft;
125 const double leftIsect = Amg::intersect<2>(pos, Amg::Vector2D::UnitX(),
126 corners[botLeft], leftEdge).value_or(1.e9);
127
128 const Amg::Vector2D leftPad = corners[botLeft] + leftIsect * leftEdge;
129 const Amg::Vector2D rightPad = corners[botRight] + leftIsect * (corners[topRight] - corners[botRight]).unit();
130 const double deltaX = pos.x() - leftPad.x();
131 const double lenX = rightPad.x() - leftPad.x();
132
134 if (leftIsect >= 0. && leftIsect <= lenLeft) {
136 if (deltaX >= 0. && deltaX < lenX) {
137 return Amg::Vector2D::Zero();
138 } else if (deltaX < 0.) {
139 return deltaX * Amg::Vector2D::UnitX();
140 }
141 return (deltaX - lenX) * Amg::Vector2D::UnitX();
142 }
143 if (deltaX > 0. && deltaX < lenX) {
144 return (leftIsect < 0 ? corners[botRight].y() - pos.y()
145 : pos.y() - corners[topRight].y())* Amg::Vector2D::UnitY();
146 }
147 return (leftIsect < 0 ? corners[botRight].y() - pos.y()
148 : pos.y() - corners[topRight].y())* Amg::Vector2D::UnitY() +
149 (deltaX < 0. ? deltaX : (deltaX - lenX) )* Amg::Vector2D::UnitX();
150
151}
152
153//----------------------------------------------------------
154bool MuonPadDesign::channelCorners(const int channel, CornerArray& corners) const {
155 return channelCorners(etaPhiId(channel), corners);
156}
157bool MuonPadDesign::channelCorners(const std::pair<int, int>& pad, CornerArray& corners) const {
158 // DG-2015-11-30: todo check whether the offset subtraction is still needed
159 int iEta = pad.first; // -1 + padEtaMin;
160 int iPhi = pad.second; // -1 + padPhiMin;
161 // bool invalid_indices = iEta<1 || iPhi<1; // DG-2015-11-30 do we still need to check this?
162 // if(invalid_indices) return false;
163 // double yBot = -0.5*Length + firstRowPos + ysFrame + iEta*inputRowPitch;
164 // double yTop = -0.5*Length + firstRowPos + ysFrame + (iEta+1)*inputRowPitch;
165
167 double yBot = 0., yTop = 0.;
168 if (iEta == 1) {
169 yBot = yCutout ? -(Size - yCutout) : -0.5 * Size;
170 yTop = yBot + firstRowPos;
171 } else if (iEta > 1) {
172 yBot = yCutout ? -(Size - yCutout) + firstRowPos + (iEta - 2) * inputRowPitch
173 : -0.5 * Size + firstRowPos + (iEta - 2) * inputRowPitch;
174 yTop = yBot + inputRowPitch;
175 if (iEta == nPadH) yTop = maxSensitiveY();
176 } else { // Unkwown ieta
177 return false;
178 }
180
181 // restrict y to the module sensitive area
182 double minY = minSensitiveY();
183 double maxY = maxSensitiveY();
184 if (yBot < minY) yBot = minY;
185 if (yTop > maxY) yTop = maxY;
186
187 // here L/R are defined as if you were looking from the IP to the
188 // detector (same a clockwise/counterclockwise phi but shorter)
189 double phiRight = firstPhiPos + (iPhi - 2) * inputPhiPitch;
190 double phiLeft = firstPhiPos + (iPhi - 1) * inputPhiPitch;
191
192 const double tanRight = std::tan(phiRight *CLHEP::degree);
193 const double tanLeft = std::tan(phiLeft *CLHEP::degree);
194 double xBotRight = -(yBot + radialDistance) * tanRight;
195 double xBotLeft = -(yBot + radialDistance) * tanLeft;
196 double xTopRight = -(yTop + radialDistance) * tanRight;
197 double xTopLeft = -(yTop + radialDistance) * tanLeft;
198
199 const double cosRight = (radialDistance + yBot) / std::hypot(xBotRight, (radialDistance + yBot));
200 const double cosLeft = (radialDistance + yBot) / std::hypot(xBotLeft, (radialDistance + yBot));
201
202 xBotRight += 1.*PadPhiShift*cosRight;
203 xBotLeft += 1.*PadPhiShift*cosLeft;
204 xTopRight += 1.*PadPhiShift*cosRight;
205 xTopLeft += 1.*PadPhiShift*cosLeft;
206
207 // For the R3 geometry converted from Phase II, we are shifting the gasgap center
208 // to the center of the gap for L3, originally defined at the cutout base.
209 // Hence, we are defining an offset, yCutoutOffset to be used in the pad corner calculations.
210 double yCutoutOffset{0.};
212 yCutoutOffset = 24.74;
213 }
214 // Adjust outer columns
215 // No staggering from fuziness in the outer edges
216 if (iPhi == 1) {
217 double yLength = yCutout ? Size - yCutout + yCutoutOffset: Size;
218 xBotRight = 0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yBot - minY) / yLength);
219 xTopRight = 0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yTop - minY) / yLength);
220 }
221 if (iPhi == nPadColumns) {
222 double yLength = yCutout ? Size - yCutout + yCutoutOffset: Size;
223 xBotLeft = -0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yBot - minY) / yLength);
224 xTopLeft = -0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yTop - minY) / yLength);
225 }
226
227 // Adjust for cutout region
228 if (yCutout && (yTop - yCutoutOffset) > 0) {
229 float cutoutXpos = 0.5 * lPadWidth;
230 if (iPhi == 1) {
231 xTopRight = cutoutXpos;
232 if (yBot - yCutoutOffset > 0) xBotRight = cutoutXpos;
233 } else if (iPhi == nPadColumns) {
234 xTopLeft = -1.0 * cutoutXpos;
235 if (yBot - yCutoutOffset > 0) xBotLeft = -1.0 * cutoutXpos;
236 }
237 }
238 if (yBot > yTop) {
239 ATH_MSG_VERBOSE("Swap top and bottom side "<<pad.first<<"/"<<pad.second);
240 std::swap(yBot, yTop);
241 }
242 if (xBotLeft > xBotRight) {
243 ATH_MSG_VERBOSE("Swap bottom left and right points "<<pad.first<<"/"<<pad.second);
244 std::swap(xBotLeft, xBotRight);
245 }
246 if (xTopLeft > xTopRight) {
247 ATH_MSG_VERBOSE("Swap top left and right points "<<pad.first<<"/"<<pad.second);
248 std::swap(xTopLeft, xTopRight);
249 }
250 corners[botLeft] = Amg::Vector2D(xBotLeft, yBot);
251 corners[botRight] = Amg::Vector2D(xBotRight, yBot);
252 corners[topLeft] = Amg::Vector2D(xTopLeft, yTop);
253 corners[topRight] = Amg::Vector2D(xTopRight, yTop);
254 return true;
255}
256//----------------------------------------------------------
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
constexpr std::array< T, N > make_array(const T &def_val)
Helper function to initialize in-place arrays with non-zero values.
Definition ArrayHelper.h:10
#define ATH_MSG_VERBOSE(x)
#define y
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
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
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
Parameters defining the design of the readout sTGC pads.
double radialDistance
DT-2015-11-29 distance from the beamline to the center of the module.
bool channelPosition(const std::pair< int, int > &pad, Amg::Vector2D &pos) const
calculate local channel position for a given channel number
std::pair< int, int > etaPhiId(const int channel) const
bool withinSensitiveArea(const Amg::Vector2D &pos) const
whether pos is within the sensitive area of the module
double maxAbsSensitiveX(const double &y) const
largest (abs, local) x of the sensitive volume
bool channelCorners(const std::pair< int, int > &pad, CornerArray &corners) const
std::pair< int, int > channelNumber(const Amg::Vector2D &pos) const
calculate local channel number, range 1=nstrips like identifiers.
Amg::Vector2D distanceToPad(const Amg::Vector2D &pos, int channel) const
std::array< Amg::Vector2D, 4 > CornerArray
calculate local channel corners for a given channel number
double maxSensitiveY() const
highest y (local) of the sensitive volume
double minSensitiveY() const
lowest y (local) of the sensitive volume