ATLAS Offline Software
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 
12 MuonPadDesign::MuonPadDesign(): AthMessaging{"MuonPadDesign"}{}
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 //----------------------------------------------------------
22 double MuonPadDesign::minSensitiveY() const { return yCutout ? -(Size - yCutout) : -0.5 * Size; }
23 //----------------------------------------------------------
24 double MuonPadDesign::maxSensitiveY() const { return yCutout ? yCutout : 0.5 * Size; }
25 //----------------------------------------------------------
26 double 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 //----------------------------------------------------------
39 std::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 //----------------------------------------------------------
111 }
112 bool 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 //----------------------------------------------------------
154 bool MuonPadDesign::channelCorners(const int channel, CornerArray& corners) const {
155  return channelCorners(etaPhiId(channel), corners);
156 }
157 bool 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  // Adjust outer columns
208  // No staggering from fuziness in the outer edges
209  if (iPhi == 1) {
210  double yLength = yCutout ? Size - yCutout : Size;
211  xBotRight = 0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yBot - minY) / yLength);
212  xTopRight = 0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yTop - minY) / yLength);
213  }
214  if (iPhi == nPadColumns) {
215  double yLength = yCutout ? Size - yCutout : Size;
216  xBotLeft = -0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yBot - minY) / yLength);
217  xTopLeft = -0.5 * (sPadWidth + (lPadWidth - sPadWidth) * (yTop - minY) / yLength);
218  }
219 
220  // Adjust for cutout region
221  if (yCutout && yTop > 0) {
222  float cutoutXpos = 0.5 * lPadWidth;
223  if (iPhi == 1) {
224  xTopRight = cutoutXpos;
225  if (yBot > 0) xBotRight = cutoutXpos;
226  } else if (iPhi == nPadColumns) {
227  xTopLeft = -1.0 * cutoutXpos;
228  if (yBot > 0) xBotLeft = -1.0 * cutoutXpos;
229  }
230  }
231  if (yBot > yTop) {
232  ATH_MSG_VERBOSE("Swap top and bottom side "<<pad.first<<"/"<<pad.second);
233  std::swap(yBot, yTop);
234  }
235  if (xBotLeft > xBotRight) {
236  ATH_MSG_VERBOSE("Swap bottom left and right points "<<pad.first<<"/"<<pad.second);
237  std::swap(xBotLeft, xBotRight);
238  }
239  if (xTopLeft > xTopRight) {
240  ATH_MSG_VERBOSE("Swap top left and right points "<<pad.first<<"/"<<pad.second);
241  std::swap(xTopLeft, xTopRight);
242  }
243  corners[botLeft] = Amg::Vector2D(xBotLeft, yBot);
244  corners[botRight] = Amg::Vector2D(xBotRight, yBot);
245  corners[topLeft] = Amg::Vector2D(xTopLeft, yTop);
246  corners[topRight] = Amg::Vector2D(xTopRight, yTop);
247  return true;
248 }
249 //----------------------------------------------------------
MuonGM::MuonPadDesign::topRight
@ topRight
Definition: MuonPadDesign.h:105
MuonGM::MuonPadDesign::firstPhiPos
double firstPhiPos
Definition: MuonPadDesign.h:53
MuonGM::MuonPadDesign
Parameters defining the design of the readout sTGC pads.
Definition: MuonPadDesign.h:40
MuonGM::MuonPadDesign::channelNumber
std::pair< int, int > channelNumber(const Amg::Vector2D &pos) const
calculate local channel number, range 1=nstrips like identifiers.
Definition: MuonPadDesign.cxx:39
MuonGM::MuonPadDesign::CornerArray
std::array< Amg::Vector2D, 4 > CornerArray
calculate local channel corners for a given channel number
Definition: MuonPadDesign.h:104
get_generator_info.result
result
Definition: get_generator_info.py:21
MuonGM::MuonPadDesign::channelPosition
bool channelPosition(const std::pair< int, int > &pad, Amg::Vector2D &pos) const
calculate local channel position for a given channel number
Definition: MuonPadDesign.cxx:112
MuonGM::MuonPadDesign::botLeft
@ botLeft
Definition: MuonPadDesign.h:105
plotting.yearwise_efficiency.channel
channel
Definition: yearwise_efficiency.py:24
Amg::Vector2D
Eigen::Matrix< double, 2, 1 > Vector2D
Definition: GeoPrimitives.h:48
MuonGM::MuonPadDesign::maxAbsSensitiveX
double maxAbsSensitiveX(const double &y) const
largest (abs, local) x of the sensitive volume
Definition: MuonPadDesign.cxx:26
MuonGM::MuonPadDesign::minSensitiveY
double minSensitiveY() const
lowest y (local) of the sensitive volume
Definition: MuonPadDesign.cxx:22
drawFromPickle.cos
cos
Definition: drawFromPickle.py:36
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
MuonGM::MuonPadDesign::Size
double Size
Definition: MuonPadDesign.h:58
MuonGM::MuonPadDesign::radialDistance
double radialDistance
DT-2015-11-29 distance from the beamline to the center of the module.
Definition: MuonPadDesign.h:60
MuonGM::MuonPadDesign::lPadWidth
double lPadWidth
Definition: MuonPadDesign.h:63
makeTRTBarrelCans.y1
tuple y1
Definition: makeTRTBarrelCans.py:15
drawFromPickle.atan
atan
Definition: drawFromPickle.py:36
MuonGM::MuonPadDesign::nPadH
int nPadH
Definition: MuonPadDesign.h:68
MuonGM::MuonPadDesign::maxSensitiveY
double maxSensitiveY() const
highest y (local) of the sensitive volume
Definition: MuonPadDesign.cxx:24
MuonGM::MuonPadDesign::botRight
@ botRight
Definition: MuonPadDesign.h:105
Amg::toString
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Definition: GeoPrimitivesToStringConverter.h:40
MuonGM::MuonPadDesign::inputRowPitch
double inputRowPitch
Definition: MuonPadDesign.h:49
AthMessaging
Class to provide easy MsgStream access and capabilities.
Definition: AthMessaging.h:55
drawFromPickle.tan
tan
Definition: drawFromPickle.py:36
WriteCalibToCool.swap
swap
Definition: WriteCalibToCool.py:94
compareGeometries.deltaX
float deltaX
Definition: compareGeometries.py:32
GlobalUtilities.h
MuonPadDesign.h
Trk::locPhi
@ locPhi
local polar
Definition: ParamDefs.h:45
MuonGM::MuonPadDesign::nPadColumns
int nPadColumns
Definition: MuonPadDesign.h:69
MuonGM::MuonPadDesign::isLargeSector
int isLargeSector
Definition: MuonPadDesign.h:72
MuonGM::MuonPadDesign::firstRowPos
double firstRowPos
Definition: MuonPadDesign.h:52
python.LumiBlobConversion.pos
pos
Definition: LumiBlobConversion.py:18
Trk::iPhi
@ iPhi
Definition: ParamDefs.h:47
MuonGM::MuonPadDesign::channelCorners
bool channelCorners(const std::pair< int, int > &pad, CornerArray &corners) const
Definition: MuonPadDesign.cxx:157
y
#define y
unit
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
Definition: AmgMatrixBasePlugin.h:21
MuonGM::MuonPadDesign::PadPhiShift
double PadPhiShift
Definition: MuonPadDesign.h:70
GeoPrimitivesToStringConverter.h
MuonGM::MuonPadDesign::yCutout
double yCutout
Definition: MuonPadDesign.h:67
MuonGM::MuonPadDesign::sectorOpeningAngle
double sectorOpeningAngle
Definition: MuonPadDesign.h:73
MuonGM::MuonPadDesign::sPadWidth
double sPadWidth
Definition: MuonPadDesign.h:62
MuonGM::MuonPadDesign::withinSensitiveArea
bool withinSensitiveArea(const Amg::Vector2D &pos) const
whether pos is within the sensitive area of the module
Definition: MuonPadDesign.cxx:13
xAOD::iEta
setScale setgFexType iEta
Definition: gFexJetRoI_v1.cxx:77
MuonGM::MuonPadDesign::distanceToPad
Amg::Vector2D distanceToPad(const Amg::Vector2D &pos, int channel) const
Definition: MuonPadDesign.cxx:118
MuonGM::MuonPadDesign::topLeft
@ topLeft
Definition: MuonPadDesign.h:105
python.SystemOfUnits.degree
tuple degree
Definition: SystemOfUnits.py:106
MuonGM::MuonPadDesign::inputPhiPitch
double inputPhiPitch
Definition: MuonPadDesign.h:50
MuonGM::MuonPadDesign::etaPhiId
std::pair< int, int > etaPhiId(const int channel) const
Definition: MuonPadDesign.h:115
generate::Zero
void Zero(TH1D *hin)
Definition: generate.cxx:32