LDMX Software
HcalGeometry.cxx
2
3#include <assert.h>
4
5#include <algorithm>
6#include <iostream>
7
8#include "Framework/Exception/Exception.h"
9
10namespace ldmx {
11
13 : framework::ConditionsObject(HcalGeometry::CONDITIONS_OBJECT_NAME) {
14 scint_thickness_ = ps.get<double>("scint_thickness");
15 scint_width_ = ps.get<double>("scint_width");
16 zero_layer_ = ps.get<std::vector<double>>("zero_layer");
17 layer_thickness_ = ps.get<std::vector<double>>("layer_thickness");
18 num_layers_ = ps.get<std::vector<int>>("num_layers");
19 num_sections_ = ps.get<int>("num_sections");
20 ecal_dx_ = ps.get<double>("ecal_dx");
21 ecal_dy_ = ps.get<double>("ecal_dy");
22 verbose_ = ps.get<int>("verbose");
23 back_horizontal_parity_ = ps.get<int>("back_horizontal_parity");
24 side_3d_readout_ = ps.get<int>("side_3d_readout");
25 y_offset_ = ps.get<double>("y_offset");
26
27 auto detectors_valid = ps.get<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.get<std::vector<std::vector<int>>>("num_strips");
38 ps.get<std::vector<std::vector<double>>>("half_total_width");
39 zero_strip_ = ps.get<std::vector<std::vector<double>>>("zero_strip");
40 scint_length_ = ps.get<std::vector<std::vector<double>>>("scint_length");
41
42 // optional explicit layout, empty for the LDMX and prototype geometries
43 layer_z_ = ps.get<std::vector<std::vector<double>>>("layer_z", {});
45 ps.get<std::vector<std::vector<int>>>("layer_long_axis", {});
47 ps.get<std::vector<std::vector<double>>>("layer_bar_center", {});
49 ps.get<std::vector<std::vector<double>>>("layer_strip_zero", {});
50 double_ended_ = ps.get<std::vector<int>>("double_ended", {});
51 if (hasExplicitLayout()) {
52 for (int section = 0; section < num_sections_; ++section) {
53 const auto n{static_cast<std::size_t>(num_layers_.at(section))};
54 if (layer_z_.at(section).size() != n ||
55 layer_long_axis_.at(section).size() != n ||
56 layer_bar_center_.at(section).size() != n ||
57 layer_strip_zero_.at(section).size() != n ||
58 double_ended_.size() != static_cast<std::size_t>(num_sections_)) {
59 EXCEPTION_RAISE(
60 "HcalGeometry",
61 "Explicit layout does not match num_layers in section " +
62 std::to_string(section));
63 }
64 }
65 }
66
68
69 if (verbose_ > 0) {
71 }
72}
73double HcalGeometry::getDistanceAlongBar(ldmx::HcalID id, double x, double y,
74 double z) const {
75 const auto center{getStripCenterPosition(id)};
76 switch (getScintillatorOrientation(id)) {
77 case ScintillatorOrientation::horizontal:
78 return x - center.X();
79 case ScintillatorOrientation::vertical:
80 return y - center.Y();
81 default:
82 return z - center.Z();
83 }
84}
85
87 const std::vector<double>& globalPosition, const ldmx::HcalID& id) const {
88 const auto orientation{getScintillatorOrientation(id)};
89 // explicit layout bars lie flat in z like the back HCal
90 const unsigned int section{
92 ? static_cast<unsigned int>(ldmx::HcalID::HcalSection::BACK)
93 : id.section()};
94 switch (section) {
95 case ldmx::HcalID::HcalSection::BACK:
96 switch (orientation) {
97 case ScintillatorOrientation::horizontal:
98 return {globalPosition[2], globalPosition[1], globalPosition[0]};
99 case ScintillatorOrientation::vertical:
100 return {globalPosition[2], globalPosition[0], globalPosition[1]};
101 default: // Should not be possible with current geometries
102 EXCEPTION_RAISE("InvalidRotation",
103 "Attempted to rotate into an invalid "
104 "orientation for a scintillator bar!");
105 }
106 case ldmx::HcalID::HcalSection::TOP:
107 [[fallthrough]];
108 case ldmx::HcalID::HcalSection::BOTTOM:
109 switch (orientation) {
110 case ScintillatorOrientation::horizontal:
111 return {globalPosition[1], globalPosition[2], globalPosition[0]};
112 case ScintillatorOrientation::depth:
113 return {globalPosition[1], globalPosition[0], globalPosition[2]};
114 default: // Should not be possible with current geometries
115 EXCEPTION_RAISE("InvalidRotation",
116 "Attempted to rotate into an invalid "
117 "orientation for a scintillator bar!");
118 }
119 case ldmx::HcalID::HcalSection::LEFT:
120 [[fallthrough]];
121 case ldmx::HcalID::HcalSection::RIGHT:
122 switch (orientation) {
123 case ScintillatorOrientation::vertical:
124 return {globalPosition[0], globalPosition[2], globalPosition[1]};
125 case ScintillatorOrientation::depth:
126 return globalPosition;
127 default: // Should not be possible with current geometries
128 EXCEPTION_RAISE("InvalidRotation",
129 "Attempted to rotate into an invalid "
130 "orientation for a scintillator bar!");
131 }
132 default:
133 // Can only reach this part if we somehow didn't match any of the options
134 // above. This could happen if someone introduces a new geometry but
135 // doesn't patch this part.
136 EXCEPTION_RAISE("InvalidRotation",
137 "Attempted to rotate into an invalid "
138 "orientation for a scintillator bar!");
139 }
140}
141
142HcalGeometry::ScintillatorOrientation HcalGeometry::getScintillatorOrientation(
143 const ldmx::HcalID id) const {
144 if (hasExplicitLayout()) {
145 return layer_long_axis_.at(id.section()).at(id.layer() - 1) == 0
146 ? ScintillatorOrientation::horizontal
147 : ScintillatorOrientation::vertical;
148 }
149 if (hasSide3DReadout()) {
150 // v14 or later detector
151 switch (id.section()) {
152 case ldmx::HcalID::HcalSection::TOP:
153 case ldmx::HcalID::HcalSection::BOTTOM:
154 // Odd layers are in z/depth direction, even are in the x/horizontal
155 // direction
156 return id.layer() % 2 == 0 ? ScintillatorOrientation::horizontal
157 : ScintillatorOrientation::depth;
158
159 case ldmx::HcalID::HcalSection::LEFT:
160 case ldmx::HcalID::HcalSection::RIGHT:
161 // Odd layers are in the z/depth direction, even are in the y/vertical
162 // direction
163 return id.layer() % 2 == 0 ? ScintillatorOrientation::vertical
164 : ScintillatorOrientation::depth;
165 case ldmx::HcalID::HcalSection::BACK:
166 // Configurable
167 return id.layer() % 2 == back_horizontal_parity_
168 ? ScintillatorOrientation::horizontal
169 : ScintillatorOrientation::vertical;
170 } // V14 or later detector
171 }
172 if (isPrototype()) {
173 // The prototype only has the back section. However, the orientation
174 // depends on the configuration so we delegate to the
175 // back_horizontal_parity parameter
176 return id.layer() % 2 == back_horizontal_parity_
177 ? ScintillatorOrientation::horizontal
178 : ScintillatorOrientation::vertical;
179 } // Prototype detector
180 // v13/v12
181 switch (id.section()) {
182 // For the v13 side hcal, the bars in each section have the same
183 // orientation
184 case ldmx::HcalID::HcalSection::TOP:
185 case ldmx::HcalID::HcalSection::BOTTOM:
186 return ScintillatorOrientation::horizontal;
187 case ldmx::HcalID::HcalSection::LEFT:
188 case ldmx::HcalID::HcalSection::RIGHT:
189 return ScintillatorOrientation::vertical;
190 case ldmx::HcalID::HcalSection::BACK:
191 // Configurable
192 return id.layer() % 2 == back_horizontal_parity_
193 ? ScintillatorOrientation::horizontal
194 : ScintillatorOrientation::vertical;
195 } // v13/v12 detector
196 // Can only reach this part if we somehow didn't match any of the options
197 // above. This could happen if someone introduces a new geometry but doesn't
198 // patch this part.
199 EXCEPTION_RAISE("InvalidRotation",
200 "Attempted to rotate into an invalid "
201 "orientation for a scintillator bar!");
202}
203void HcalGeometry::printPositionMap(int section) const {
204 // Note that layer numbering starts at 1 rather than 0
205 for (int layer = 1; layer <= num_layers_[section]; ++layer) {
206 for (int strip = 0; strip < getNumStrips(section, layer); ++strip) {
207 HcalID id(section, layer, strip);
208 auto center_position = getStripCenterPosition(id);
209 auto x = center_position.X();
210 auto y = center_position.Y();
211 auto z = center_position.Z();
212 std::cout << id << ": Center position: (" << x << ", " << y << ", " << z
213 << ")\n";
214 }
215 }
216}
217
219 // We hard-code the number of sections as seen in HcalID
220 for (unsigned int section = 0; section < num_sections_; section++) {
221 for (unsigned int layer = 1; layer <= num_layers_[section]; layer++) {
222 for (unsigned int strip = 0; strip < getNumStrips(section, layer);
223 strip++) {
224 // initialize values
225 double x{-99999}, y{-99999}, z{-99999};
226
227 // get hcal section
228 ldmx::HcalID::HcalSection hcalsection =
230
231 const ldmx::HcalID id{section, layer, strip};
232 const auto orientation{getScintillatorOrientation(id)};
233 // the center of a layer: (layer-1) * (layer_thickness) +
234 // scint_thickness/2
235 double layercenter =
236 (layer - 1) * layer_thickness_.at(section) + 0.5 * scint_thickness_;
237
238 // the center of a strip: (strip + 0.5) * (strip_dx)
239 double stripcenter = (strip + 0.5) * scint_width_;
240
241 if (hasExplicitLayout()) {
242 const auto i_layer{layer - 1};
243 const double along{layer_bar_center_.at(section).at(i_layer)};
244 const double across{layer_strip_zero_.at(section).at(i_layer) +
245 strip * scint_width_};
246 if (orientation == ScintillatorOrientation::horizontal) {
247 x = along;
248 y = across;
249 } else {
250 x = across;
251 y = along;
252 }
253 z = layer_z_.at(section).at(i_layer);
254 } else if (hcalsection == ldmx::HcalID::HcalSection::BACK) {
262 // z position: zero-layer(z) + layer_z + scint_thickness / 2
263 z = zero_layer_.at(section) + layercenter;
264
273 if (orientation == ScintillatorOrientation::horizontal) {
274 y = stripcenter - getZeroStrip(section, layer);
275 x = 0;
276 } else {
277 x = stripcenter - getZeroStrip(section, layer);
278 y = 0;
279 }
280 } else {
281 if (side_3d_readout_) {
282 /*
283 *
284 * For 3D readout:
285 * - odd layers have strips in z
286 * - even layers have strips in x(y) for top-bottom (left-right)
287 * sections
288 * - odd layers have strips occupying width of scintillator in x(y)
289 * - even layers have strips occupying width of scintillator in z
290 *
291 */
292 switch (hcalsection) {
293 case ldmx::HcalID::HcalSection::BACK:
294 // Handled earlier in the code!
295 case ldmx::HcalID::HcalSection::LEFT:
296 case ldmx::HcalID::HcalSection::RIGHT:
297 if (orientation == ScintillatorOrientation::vertical) {
298 x = zero_layer_[section] + 0.5 * scint_thickness_ +
299 (layer - 1) * layer_thickness_[section];
300 y = ecal_dy_ -
301 (getScintillatorLength({id.section(), 2, id.strip()}) -
303 2;
304 z = getZeroStrip(section, layer) +
305 (strip + 0.5) * getScintillatorWidth();
306 } else if (orientation == ScintillatorOrientation::depth) {
307 x = zero_layer_[section] + 0.5 * scint_thickness_ +
308 layer_thickness_[section] * (layer - 1);
309 y = -ecal_dy_ / 2 + (strip + 0.5) * getScintillatorWidth();
310 z = getZeroStrip(section, layer + 1) +
311 getScintillatorLength(id) / 2;
312 }
313 if (section == ldmx::HcalID::HcalSection::LEFT) {
314 y *= -1;
315 x *= -1;
316 }
317 break;
318
319 case ldmx::HcalID::HcalSection::BOTTOM:
320 case ldmx::HcalID::HcalSection::TOP:
321 if (orientation == ScintillatorOrientation::horizontal) {
322 //
323 // Second half of the expression is the difference between the
324 // longest strips (first module) and the current module.
325 //
326 // 22 mm extra for space for 1 absorber and one air box
327 x = -ecal_dx_ / 2 - 2 - 20 +
328 (getScintillatorLength({id.section(), 2, id.strip()}) -
330 2;
331 y = zero_layer_[section] + 0.5 * scint_thickness_ +
332 (layer - 1) * layer_thickness_[section];
333 z = getZeroStrip(section, layer) +
334 (strip + 0.5) * getScintillatorWidth();
335 }
336 if (orientation == ScintillatorOrientation::depth) {
337 x = (ecal_dx_ / 2) - (strip + 0.5) * getScintillatorWidth();
338 y = zero_layer_[section] + 0.5 * scint_thickness_ +
339 layer_thickness_[section] * (layer - 1);
340 z = getZeroStrip(section, layer + 1) +
341 getScintillatorLength(id) / 2;
342 }
343 if (section == ldmx::HcalID::HcalSection::BOTTOM) {
344 y *= -1;
345 x *= -1;
346 }
347 break;
348 }
349
350 } else {
360 // z position: zero-strip(z) + strip_center(z)
361 z = getZeroStrip(section, layer) + stripcenter;
362 if (hcalsection == ldmx::HcalID::HcalSection::TOP or
363 hcalsection == ldmx::HcalID::HcalSection::BOTTOM) {
364 y = zero_layer_.at(section) + layercenter;
365 x = getHalfTotalWidth(section, layer);
366 if (hcalsection == ldmx::HcalID::HcalSection::BOTTOM) {
367 y *= -1;
368 x *= -1;
369 }
370
371 } else {
372 x = zero_layer_.at(section) + layercenter;
373 y = getHalfTotalWidth(section, layer);
374 if (hcalsection == ldmx::HcalID::HcalSection::RIGHT) {
375 x *= -1;
376 y *= -1;
377 }
378 }
379 }
380 }
381
382 y += y_offset_;
383 ROOT::Math::XYZVector pos;
384 pos.SetXYZ(x, y, z);
385 strip_position_map_[ldmx::HcalID(section, layer, strip)] = pos;
386 } // loop over strips
387 } // loop over layers
388 } // loop over sections
389} // strip position map
390
391} // namespace ldmx
Class that translates HCal ID into positions of strip hits.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
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< int > double_ended_
Explicit layout: section read out at both ends.
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.
double getDistanceAlongBar(ldmx::HcalID id, double x, double y, double z) const
Signed distance along the bar from the strip center [mm].
int getZeroStrip(int isection, int layer=1) const
Get the location of the zeroStrip in a given section and layer.
bool hasExplicitLayout() const
Are the strip positions given layer by layer (e.g.
std::vector< std::vector< double > > layer_bar_center_
Explicit layout: world along-bar coordinate of the bar centers [mm].
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].
std::vector< std::vector< double > > layer_z_
Explicit layout: world z of each layer center [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< double > > layer_strip_zero_
Explicit layout: world transverse coordinate of strip 0 [mm].
std::vector< std::vector< int > > num_strips_
Number of strips per layer in each section and each layer.
std::vector< std::vector< int > > layer_long_axis_
Explicit layout: 0 = bar long along x, 1 = along y.
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.