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