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