LDMX Software
HcalGeometry.cxx
2
3#include <assert.h>
4
5#include <algorithm>
6#include <iomanip>
7#include <iostream>
8
9namespace ldmx {
10
12 : framework::ConditionsObject(HcalGeometry::CONDITIONS_OBJECT_NAME) {
13 scint_thickness_ = ps.getParameter<double>("scint_thickness");
14 scint_width_ = ps.getParameter<double>("scint_width");
15 zero_layer_ = ps.getParameter<std::vector<double>>("zero_layer");
16 layer_thickness_ = ps.getParameter<std::vector<double>>("layer_thickness");
17 num_layers_ = ps.getParameter<std::vector<int>>("num_layers");
18 num_sections_ = ps.getParameter<int>("num_sections");
19 ecal_dx_ = ps.getParameter<double>("ecal_dx");
20 ecal_dy_ = ps.getParameter<double>("ecal_dy");
21 verbose_ = ps.getParameter<int>("verbose");
22 back_horizontal_parity_ = ps.getParameter<int>("back_horizontal_parity");
23 side_3d_readout_ = ps.getParameter<int>("side_3d_readout");
24 y_offset_ = ps.getParameter<double>("y_offset");
25
26 auto detectors_valid =
27 ps.getParameter<std::vector<std::string>>("detectors_valid");
28 // If one of the strings in detectors_valid is "ldmx-hcal-prototype", we
29 // will use prototype geometry initialization
30 is_prototype_ = std::find_if(detectors_valid.cbegin(), detectors_valid.cend(),
31 [](const auto detector) {
32 return detector.find("ldmx-hcal-prototype") !=
33 std::string::npos;
34 }) != detectors_valid.cend();
35
36 num_strips_ = ps.getParameter<std::vector<std::vector<int>>>("num_strips");
38 ps.getParameter<std::vector<std::vector<double>>>("half_total_width");
39 zero_strip_ = ps.getParameter<std::vector<std::vector<double>>>("zero_strip");
41 ps.getParameter<std::vector<std::vector<double>>>("scint_length");
42
44
45 if (verbose_ > 0) {
47 }
48}
50 const std::vector<double> &globalPosition, const ldmx::HcalID &id) const {
51 const auto orientation{getScintillatorOrientation(id)};
52 switch (id.section()) {
53 case ldmx::HcalID::HcalSection::BACK:
54 switch (orientation) {
55 case ScintillatorOrientation::horizontal:
56 return {globalPosition[2], globalPosition[1], globalPosition[0]};
57 case ScintillatorOrientation::vertical:
58 return {globalPosition[2], globalPosition[0], globalPosition[1]};
59 default: // Should not be possible with current geometries
60 EXCEPTION_RAISE("InvalidRotation",
61 "Attempted to rotate into an invalid "
62 "orientation for a scintillator bar!");
63 }
64 case ldmx::HcalID::HcalSection::TOP:
65 [[fallthrough]];
66 case ldmx::HcalID::HcalSection::BOTTOM:
67 switch (orientation) {
68 case ScintillatorOrientation::horizontal:
69 return {globalPosition[1], globalPosition[2], globalPosition[0]};
70 case ScintillatorOrientation::depth:
71 return {globalPosition[1], globalPosition[0], globalPosition[2]};
72 default: // Should not be possible with current geometries
73 EXCEPTION_RAISE("InvalidRotation",
74 "Attempted to rotate into an invalid "
75 "orientation for a scintillator bar!");
76 }
77 case ldmx::HcalID::HcalSection::LEFT:
78 [[fallthrough]];
79 case ldmx::HcalID::HcalSection::RIGHT:
80 switch (orientation) {
81 case ScintillatorOrientation::vertical:
82 return {globalPosition[0], globalPosition[2], globalPosition[1]};
83 case ScintillatorOrientation::depth:
84 return globalPosition;
85 default: // Should not be possible with current geometries
86 EXCEPTION_RAISE("InvalidRotation",
87 "Attempted to rotate into an invalid "
88 "orientation for a scintillator bar!");
89 }
90 default:
91 // Can only reach this part if we somehow didn't match any of the options
92 // above. This could happen if someone introduces a new geometry but
93 // doesn't patch this part.
94 EXCEPTION_RAISE("InvalidRotation",
95 "Attempted to rotate into an invalid "
96 "orientation for a scintillator bar!");
97 }
98}
99
100HcalGeometry::ScintillatorOrientation HcalGeometry::getScintillatorOrientation(
101 const ldmx::HcalID id) const {
102 if (hasSide3DReadout()) {
103 // v14 or later detector
104 switch (id.section()) {
105 case ldmx::HcalID::HcalSection::TOP:
106 case ldmx::HcalID::HcalSection::BOTTOM:
107 // Odd layers are in z/depth direction, even are in the x/horizontal
108 // direction
109 return id.layer() % 2 == 0 ? ScintillatorOrientation::horizontal
110 : ScintillatorOrientation::depth;
111
112 case ldmx::HcalID::HcalSection::LEFT:
113 case ldmx::HcalID::HcalSection::RIGHT:
114 // Odd layers are in the z/depth direction, even are in the y/vertical
115 // direction
116 return id.layer() % 2 == 0 ? ScintillatorOrientation::vertical
117 : ScintillatorOrientation::depth;
118 case ldmx::HcalID::HcalSection::BACK:
119 // Configurable
120 return id.layer() % 2 == back_horizontal_parity_
121 ? ScintillatorOrientation::horizontal
122 : ScintillatorOrientation::vertical;
123 } // V14 or later detector
124 }
125 if (isPrototype()) {
126 // The prototype only has the back section. However, the orientation
127 // depends on the configuration so we delegate to the
128 // back_horizontal_parity parameter
129 return id.layer() % 2 == back_horizontal_parity_
130 ? ScintillatorOrientation::horizontal
131 : ScintillatorOrientation::vertical;
132 } // Prototype detector
133 // v13/v12
134 switch (id.section()) {
135 // For the v13 side hcal, the bars in each section have the same
136 // orientation
137 case ldmx::HcalID::HcalSection::TOP:
138 case ldmx::HcalID::HcalSection::BOTTOM:
139 return ScintillatorOrientation::horizontal;
140 case ldmx::HcalID::HcalSection::LEFT:
141 case ldmx::HcalID::HcalSection::RIGHT:
142 return ScintillatorOrientation::vertical;
143 case ldmx::HcalID::HcalSection::BACK:
144 // Configurable
145 return id.layer() % 2 == back_horizontal_parity_
146 ? ScintillatorOrientation::horizontal
147 : ScintillatorOrientation::vertical;
148 } // v13/v12 detector
149 // Can only reach this part if we somehow didn't match any of the options
150 // above. This could happen if someone introduces a new geometry but doesn't
151 // patch this part.
152 EXCEPTION_RAISE("InvalidRotation",
153 "Attempted to rotate into an invalid "
154 "orientation for a scintillator bar!");
155}
156void HcalGeometry::printPositionMap(int section) const {
157 // Note that layer numbering starts at 1 rather than 0
158 for (int layer = 1; layer <= num_layers_[section]; ++layer) {
159 for (int strip = 0; strip < getNumStrips(section, layer); ++strip) {
160 HcalID id(section, layer, strip);
161 auto centerPosition = getStripCenterPosition(id);
162 auto x = centerPosition[0];
163 auto y = centerPosition[1];
164 auto z = centerPosition[2];
165 std::cout << id << ": Center position: (" << x << ", " << y << ", " << z
166 << ")\n";
167 }
168 }
169}
170
172 // We hard-code the number of sections as seen in HcalID
173 for (unsigned int section = 0; section < num_sections_; section++) {
174 for (unsigned int layer = 1; layer <= num_layers_[section]; layer++) {
175 for (unsigned int strip = 0; strip < getNumStrips(section, layer);
176 strip++) {
177 // initialize values
178 double x{-99999}, y{-99999}, z{-99999};
179
180 // get hcal section
181 ldmx::HcalID::HcalSection hcalsection =
183
184 const ldmx::HcalID id{section, layer, strip};
185 const auto orientation{getScintillatorOrientation(id)};
186 // the center of a layer: (layer-1) * (layer_thickness) +
187 // scint_thickness/2
188 double layercenter =
189 (layer - 1) * layer_thickness_.at(section) + 0.5 * scint_thickness_;
190
191 // the center of a strip: (strip + 0.5) * (strip_dx)
192 double stripcenter = (strip + 0.5) * scint_width_;
193
194 if (hcalsection == ldmx::HcalID::HcalSection::BACK) {
202 // z position: zero-layer(z) + layer_z + scint_thickness / 2
203 z = zero_layer_.at(section) + layercenter;
204
213 if (orientation == ScintillatorOrientation::horizontal) {
214 y = stripcenter - getZeroStrip(section, layer);
215 x = 0;
216 } else {
217 x = stripcenter - getZeroStrip(section, layer);
218 y = 0;
219 }
220 } else {
221 if (side_3d_readout_) {
222 /*
223 *
224 * For 3D readout:
225 * - odd layers have strips in z
226 * - even layers have strips in x(y) for top-bottom (left-right)
227 * sections
228 * - odd layers have strips occupying width of scintillator in x(y)
229 * - even layers have strips occupying width of scintillator in z
230 *
231 */
232 switch (hcalsection) {
233 case ldmx::HcalID::HcalSection::BACK:
234 // Handled earlier in the code!
235 case ldmx::HcalID::HcalSection::LEFT:
236 case ldmx::HcalID::HcalSection::RIGHT:
237 if (orientation == ScintillatorOrientation::vertical) {
238 x = zero_layer_[section] + 0.5 * scint_thickness_ +
239 (layer - 1) * layer_thickness_[section];
240 y = ecal_dy_ -
241 (getScintillatorLength({id.section(), 2, id.strip()}) -
243 2;
244 z = getZeroStrip(section, layer) +
245 (strip + 0.5) * getScintillatorWidth();
246 } else if (orientation == ScintillatorOrientation::depth) {
247 x = zero_layer_[section] + 0.5 * scint_thickness_ +
248 layer_thickness_[section] * (layer - 1);
249 y = -ecal_dy_ / 2 + (strip + 0.5) * getScintillatorWidth();
250 z = getZeroStrip(section, layer + 1) +
251 getScintillatorLength(id) / 2;
252 }
253 if (section == ldmx::HcalID::HcalSection::LEFT) {
254 y *= -1;
255 x *= -1;
256 }
257 break;
258
259 case ldmx::HcalID::HcalSection::BOTTOM:
260 case ldmx::HcalID::HcalSection::TOP:
261 if (orientation == ScintillatorOrientation::horizontal) {
262 //
263 // Second half of the expression is the difference between the
264 // longest strips (first module) and the current module.
265 //
266 // 22 mm extra for space for 1 absorber and one air box
267 x = -ecal_dx_ / 2 - 2 - 20 +
268 (getScintillatorLength({id.section(), 2, id.strip()}) -
270 2;
271 y = zero_layer_[section] + 0.5 * scint_thickness_ +
272 (layer - 1) * layer_thickness_[section];
273 z = getZeroStrip(section, layer) +
274 (strip + 0.5) * getScintillatorWidth();
275 }
276 if (orientation == ScintillatorOrientation::depth) {
277 x = (ecal_dx_ / 2) - (strip + 0.5) * getScintillatorWidth();
278 y = zero_layer_[section] + 0.5 * scint_thickness_ +
279 layer_thickness_[section] * (layer - 1);
280 z = getZeroStrip(section, layer + 1) +
281 getScintillatorLength(id) / 2;
282 }
283 if (section == ldmx::HcalID::HcalSection::BOTTOM) {
284 y *= -1;
285 x *= -1;
286 }
287 break;
288 }
289
290 } else {
300 // z position: zero-strip(z) + strip_center(z)
301 z = getZeroStrip(section, layer) + stripcenter;
302 if (hcalsection == ldmx::HcalID::HcalSection::TOP or
303 hcalsection == ldmx::HcalID::HcalSection::BOTTOM) {
304 y = zero_layer_.at(section) + layercenter;
305 x = getHalfTotalWidth(section, layer);
306 if (hcalsection == ldmx::HcalID::HcalSection::BOTTOM) {
307 y *= -1;
308 x *= -1;
309 }
310
311 } else {
312 x = zero_layer_.at(section) + layercenter;
313 y = getHalfTotalWidth(section, layer);
314 if (hcalsection == ldmx::HcalID::HcalSection::RIGHT) {
315 x *= -1;
316 y *= -1;
317 }
318 }
319 }
320 }
321
322 y += y_offset_;
323 TVector3 pos;
324 pos.SetXYZ(x, y, z);
325 strip_position_map_[ldmx::HcalID(section, layer, strip)] = pos;
326 } // loop over strips
327 } // loop over layers
328 } // loop over sections
329} // strip position map
330
331} // namespace ldmx
Class that translates HCal ID into positions of strip hits.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
Implementation of HCal strip readout.
std::vector< double > layer_thickness_
Thickness of the layers in each section [mm].
bool hasSide3DReadout() const
Does the Side Hcal have 3D readout?
TVector3 getStripCenterPosition(ldmx::HcalID id) const
Get a strip center position from a combined hcal ID.
std::vector< double > zero_layer_
Front of HCal relative to world geometry for each section [mm].
double ecal_dx_
Lenght of the Ecal (in x and y)
void buildStripPositionMap()
Map builder of HcalID and position.
std::vector< std::vector< double > > zero_strip_
The plane of the zero'th strip of each section [mm].
int getZeroStrip(int isection, int layer=1) const
Get the location of the zeroStrip in a given section and layer.
double getScintillatorLength(ldmx::HcalID id) const
Get the length of a scintillator bar.
void printPositionMap() const
Debugging utility, prints out the HcalID and corresponding value of all entries in the strip_position...
std::vector< std::vector< double > > scint_length_
Length of strips [mm].
HcalGeometry(const framework::config::Parameters &ps)
Class constructor, for use only by the provider.
int verbose_
Parameters that apply to all types of geometries Verbosity, not configurable but helpful if developin...
std::vector< std::vector< double > > half_total_width_
Half Total Width of Strips [mm].
double scint_width_
Width of Scintillator Strip [mm].
std::vector< double > rotateGlobalToLocalBarPosition(const std::vector< double > &globalPosition, const ldmx::HcalID &id) const
Coordinates that are given by Geant4 are typically global.
double getScintillatorWidth() const
Get the scitillator width.
double scint_thickness_
Thickness of scintillator.
std::vector< int > num_layers_
Number of layers in each section.
ScintillatorOrientation
Encodes the orientation of a bar.
int getNumStrips(int isection, int layer=1) const
Get the number of strips per layer for that section and layer.
std::map< ldmx::HcalID, TVector3 > strip_position_map_
Map of the HcalID position of strip centers relative to world geometry.
std::vector< std::vector< int > > num_strips_
Number of strips per layer in each section and each layer.
double getHalfTotalWidth(int isection, int layer=1) const
Get the half total width of a layer for a given section(strip) for back(side) Hcal.
int num_sections_
Number of sections.
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
HcalSection
Encodes the section of the HCal based on the 'section' field value.
Definition HcalID.h:24
All classes in the ldmx-sw project use this namespace.
Definition PerfDict.cxx:45