File indexing completed on 2026-09-22 08:06:40
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include "DD4hep/DetFactoryHelper.h"
0020 #include <XML/Helper.h>
0021 #include <XML/Utilities.h>
0022 #include <array>
0023 #include <cmath>
0024
0025 using namespace dd4hep;
0026
0027 static Ref_t createDetector(Detector& desc, xml_h handle, SensitiveDetector sens) {
0028
0029 xml_det_t detElem = handle;
0030 std::string detName = detElem.nameStr();
0031 int detID = detElem.id();
0032 sens.setType("calorimeter");
0033
0034
0035 xml_dim_t dim = detElem.dimensions();
0036 double width = dim.x();
0037 double height = dim.y();
0038 double length = dim.z();
0039
0040
0041 xml_dim_t pos = detElem.position();
0042 Material air = desc.material("Air");
0043
0044
0045 const xml::Component& beam_xml = detElem.child(_Unicode(beampipe_cutout));
0046 const xml::Component& slots_xml = detElem.child(_Unicode(pcb_slots));
0047 const xml::Component& pcb_xml = detElem.child(_Unicode(pcb_boards));
0048 const xml::Component& tiles_xml = detElem.child(_Unicode(tiles));
0049 const xml::Component& casing_xml = detElem.child(_Unicode(casing));
0050 const xml::Component& cover_xml = detElem.child(_Unicode(pcb_covers));
0051 const xml::Component& back_cutouts_xml = detElem.child(_Unicode(backplate_cutouts));
0052
0053
0054 const double center_x = beam_xml.attr<double>(_Unicode(center_x)) - pos.x();
0055 const double center_y = beam_xml.attr<double>(_Unicode(center_y)) - pos.y();
0056
0057
0058 const double left_radius = beam_xml.attr<double>(_Unicode(left_radius));
0059 const double right_radius = beam_xml.attr<double>(_Unicode(right_radius));
0060
0061
0062 const double left_right_gap = detElem.attr<double>(_Unicode(left_right_gap));
0063 const double right_split_x = -left_right_gap / 2. - pos.x();
0064 const double left_split_x = left_right_gap / 2. - pos.x();
0065
0066
0067 const double rect_width = beam_xml.attr<double>(_Unicode(rect_width));
0068 const double rect_height = beam_xml.attr<double>(_Unicode(rect_height));
0069 const double rect_x = right_split_x - center_x - rect_width / 2.;
0070
0071
0072 const double h_pcb_slot_width = slots_xml.attr<double>(_Unicode(horizontal_width));
0073 const double h_pcb_slot_height = slots_xml.attr<double>(_Unicode(horizontal_height));
0074 const double h_pcb_slot_y = (rect_height + h_pcb_slot_height) / 2.;
0075
0076
0077 const double v_pcb_slot_width = slots_xml.attr<double>(_Unicode(vertical_width));
0078 const double right_v_pcb_slot_height = slots_xml.attr<double>(_Unicode(right_vertical_height));
0079 const double left_v_pcb_slot_height = slots_xml.attr<double>(_Unicode(left_vertical_height));
0080 const double v_pcb_slot_offset_y = slots_xml.attr<double>(_Unicode(vertical_offset_y));
0081 const double right_v_pcb_slot_y =
0082 height / 2. - v_pcb_slot_offset_y - right_v_pcb_slot_height / 2.;
0083 const double left_v_pcb_slot_y = height / 2. - v_pcb_slot_offset_y - left_v_pcb_slot_height / 2.;
0084
0085
0086 const double right_v_pcb_slot_x = right_split_x - v_pcb_slot_width / 2. - center_x;
0087 const double left_v_pcb_slot_x = left_split_x + v_pcb_slot_width / 2. - center_x;
0088
0089
0090 const double pcb_length = pcb_xml.attr<double>(_Unicode(length));
0091 const double pcb_thickness = pcb_xml.attr<double>(_Unicode(thickness));
0092 const double pcb_edge_clearance = pcb_xml.attr<double>(_Unicode(edge_clearance));
0093
0094
0095 const double casing_length = casing_xml.attr<double>(_Unicode(length));
0096 const double casing_thickness = casing_xml.attr<double>(_Unicode(thickness));
0097 const double casing_left_radius = casing_xml.attr<double>(_Unicode(left_radius));
0098 const double cover_thickness = cover_xml.attr<double>(_Unicode(thickness));
0099 const double right_back_cutout_margin = back_cutouts_xml.attr<double>(_Unicode(right_margin));
0100 const double left_back_cutout_margin = back_cutouts_xml.attr<double>(_Unicode(left_margin));
0101 const double back_cutout_vertical_margin =
0102 back_cutouts_xml.attr<double>(_Unicode(vertical_margin));
0103 const double right_back_cutout_width =
0104 width / 2. - left_right_gap / 2. - pos.x() - 2. * right_back_cutout_margin;
0105 const double left_back_cutout_width =
0106 width / 2. - left_right_gap / 2. + pos.x() - 2. * left_back_cutout_margin;
0107 const double back_cutout_height =
0108 height / 2. - center_y - rect_height / 2. - 2. * back_cutout_vertical_margin;
0109
0110
0111
0112 const double h_pcb_width = h_pcb_slot_width - 2. * pcb_edge_clearance;
0113 const double right_v_pcb_height = right_v_pcb_slot_height - 2. * pcb_edge_clearance;
0114 const double left_v_pcb_height = left_v_pcb_slot_height - 2. * pcb_edge_clearance;
0115
0116
0117 const double pcb_z = -(length - pcb_length) / 2. + casing_thickness;
0118
0119
0120
0121 const double tile_size = tiles_xml.attr<double>(_Unicode(size));
0122 const double tile_gap = tiles_xml.attr<double>(_Unicode(gap));
0123 const double tile_pitch = tile_size + tile_gap;
0124
0125
0126 const double right_tile_outer_margin =
0127 (width / 2. + center_x + right_v_pcb_slot_x - v_pcb_slot_width / 2. -
0128 (8. * tile_size + 7. * tile_gap)) /
0129 5.;
0130 const double left_x0 = width / 2. - tile_gap - tile_size / 2.;
0131 const double right_x0 = -width / 2. + right_tile_outer_margin + tile_size / 2.;
0132 const double top_y0 = 5.5 * tile_pitch;
0133
0134
0135 const std::array<int, 12> left_tiles_per_row = {4, 4, 4, 3, 3, 2, 2, 3, 3, 4, 4, 4};
0136 const std::array<int, 12> right_tiles_per_row = {8, 8, 4, 3, 2, 2, 2, 2, 3, 4, 8, 8};
0137
0138
0139 auto left_tile_position = [left_x0, top_y0, tile_pitch](int row, int column) {
0140 double x = left_x0 - column * tile_pitch;
0141 double y = top_y0 - row * tile_pitch;
0142 return Position(x, y, 0.);
0143 };
0144
0145
0146 auto right_tile_position = [right_x0, top_y0, tile_pitch](int row, int column) {
0147 double x = right_x0 + column * tile_pitch;
0148 double y = top_y0 - row * tile_pitch;
0149 return Position(x, y, 0.);
0150 };
0151
0152
0153 auto make_cutout = [&](double thickness, int side) {
0154 Solid cutout = Tube(0., left_radius, thickness / 2.);
0155
0156
0157 if (side == 0) {
0158 Box rect_cutout(rect_width / 2., rect_height / 2., thickness / 2.);
0159 Tube right_circle(0., right_radius, thickness / 2.);
0160 cutout = UnionSolid(right_circle, rect_cutout, Position(rect_x, 0., 0.));
0161
0162 Box h_pcb_slot(h_pcb_slot_width / 2., h_pcb_slot_height / 2., thickness / 2.);
0163 cutout = UnionSolid(cutout, h_pcb_slot, Position(rect_x, h_pcb_slot_y, 0.));
0164 cutout = UnionSolid(cutout, h_pcb_slot, Position(rect_x, -h_pcb_slot_y, 0.));
0165
0166 Box v_pcb_slot(v_pcb_slot_width / 2., right_v_pcb_slot_height / 2., thickness / 2.);
0167 cutout = UnionSolid(cutout, v_pcb_slot, Position(right_v_pcb_slot_x, right_v_pcb_slot_y, 0.));
0168 cutout =
0169 UnionSolid(cutout, v_pcb_slot, Position(right_v_pcb_slot_x, -right_v_pcb_slot_y, 0.));
0170 }
0171
0172
0173 else {
0174 Box v_pcb_slot(v_pcb_slot_width / 2., left_v_pcb_slot_height / 2., thickness / 2.);
0175 cutout = UnionSolid(cutout, v_pcb_slot, Position(left_v_pcb_slot_x, left_v_pcb_slot_y, 0.));
0176 cutout = UnionSolid(cutout, v_pcb_slot, Position(left_v_pcb_slot_x, -left_v_pcb_slot_y, 0.));
0177 }
0178
0179 return cutout;
0180 };
0181
0182
0183 Assembly assembly(detName);
0184
0185 assembly.setVisAttributes(desc.visAttributes("InvisibleWithDaughters"));
0186 PlacedVolume pv;
0187
0188
0189 for (int side = 0; side < 2; side++) {
0190 std::string side_name = side == 1 ? "L" : "R";
0191
0192 double z_distance_traversed = 0.;
0193 int layer_num = 1;
0194
0195
0196 for (xml_coll_t c(detElem, _U(layer)); c; c++) {
0197 xml_comp_t x_layer = c;
0198 int repeat = x_layer.repeat();
0199 double layer_thickness = x_layer.thickness();
0200
0201
0202 for (int i = 0; i < repeat; i++) {
0203 std::string layer_name = detName + _toString(layer_num, "_layer%d") + "_" + side_name;
0204 Box layer(width / 2., height / 2., layer_thickness / 2.);
0205
0206
0207 Solid layer_cutout = make_cutout(layer_thickness, side);
0208 Solid layer_with_cutout =
0209 SubtractionSolid(layer, layer_cutout, Position(center_x, center_y, 0.));
0210
0211
0212 if (side == 1) {
0213 Box tile_pocket(tile_size / 2., tile_size / 2., layer_thickness / 2.);
0214 layer_with_cutout = UnionSolid(layer_with_cutout, tile_pocket, left_tile_position(4, 2));
0215 layer_with_cutout = UnionSolid(layer_with_cutout, tile_pocket, left_tile_position(7, 2));
0216 }
0217
0218
0219 Box side_cut(width / 2., height, layer_thickness);
0220 Position side_cut_position(
0221 side == 0 ? right_split_x + width / 2. : left_split_x - width / 2., 0., 0.);
0222 SubtractionSolid layer_side_with_cutout(layer_with_cutout, side_cut, side_cut_position);
0223 Volume layer_vol(layer_name, layer_side_with_cutout, air);
0224
0225 int slice_num = 1;
0226 double slice_z = -layer_thickness / 2.;
0227
0228
0229 for (xml_coll_t l(x_layer, _U(slice)); l; l++) {
0230 xml_comp_t x_slice = l;
0231 double slice_thickness = x_slice.thickness();
0232 std::string slice_name = layer_name + _toString(slice_num, "slice%d");
0233 Material slice_mat = desc.material(x_slice.materialStr());
0234 slice_z += slice_thickness / 2.;
0235
0236
0237 if (x_slice.isSensitive()) {
0238 Assembly tile_assembly(slice_name + "_tiles");
0239 Box tile(tile_size / 2., tile_size / 2., slice_thickness / 2.);
0240 Volume tile_vol(slice_name + "_tile", tile, slice_mat);
0241
0242
0243 tile_vol.setSensitiveDetector(sens);
0244 tile_vol.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(),
0245 x_slice.visStr());
0246
0247
0248 for (std::size_t row = 0; row < left_tiles_per_row.size(); row++) {
0249
0250 int tiles_in_row = side == 0 ? right_tiles_per_row[row] : left_tiles_per_row[row];
0251
0252
0253 for (int column = 0; column < tiles_in_row; column++) {
0254
0255
0256 Position tile_position =
0257 side == 0 ? right_tile_position(row, column) : left_tile_position(row, column);
0258 PlacedVolume tile_pv = tile_assembly.placeVolume(tile_vol, tile_position);
0259
0260
0261 tile_pv.addPhysVolID("x", column).addPhysVolID("y", row);
0262 }
0263 }
0264
0265
0266 pv = layer_vol.placeVolume(
0267 tile_assembly, Transform3D(RotationZYX(0, 0, 0), Position(0., 0., slice_z)));
0268 pv.addPhysVolID("slice", slice_num);
0269 pv.addPhysVolID("side", side);
0270 slice_z += slice_thickness / 2.;
0271 z_distance_traversed += slice_thickness;
0272 slice_num++;
0273 continue;
0274 }
0275
0276
0277 Box slice(width / 2., height / 2., slice_thickness / 2.);
0278 Solid slice_cutout = make_cutout(slice_thickness, side);
0279 SubtractionSolid slice_with_cutout(slice, slice_cutout, Position(center_x, center_y, 0.));
0280 Box side_cut_slice(width / 2., height, layer_thickness);
0281 Position side_cut_position_slice(
0282 side == 0 ? right_split_x + width / 2. : left_split_x - width / 2., 0, 0.);
0283 SubtractionSolid slice_side_with_cutout(slice_with_cutout, side_cut_slice,
0284 side_cut_position_slice);
0285 Volume slice_vol(slice_name, slice_side_with_cutout, slice_mat);
0286
0287
0288 slice_vol.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(), x_slice.visStr());
0289
0290
0291 pv = layer_vol.placeVolume(slice_vol,
0292 Transform3D(RotationZYX(0, 0, 0), Position(0., 0., slice_z)));
0293 pv.addPhysVolID("slice", slice_num);
0294 pv.addPhysVolID("side", side);
0295 slice_z += slice_thickness / 2.;
0296 z_distance_traversed += slice_thickness;
0297 slice_num++;
0298 }
0299
0300
0301 layer_vol.setAttributes(desc, x_layer.regionStr(), x_layer.limitsStr(), x_layer.visStr());
0302
0303
0304 pv = assembly.placeVolume(
0305 layer_vol, Transform3D(RotationZYX(0, 0, 0),
0306 Position(0., 0.,
0307 -length / 2. + casing_thickness +
0308 (z_distance_traversed - layer_thickness) +
0309 layer_thickness / 2.)));
0310
0311 pv.addPhysVolID("layer", layer_num);
0312 pv.addPhysVolID("side", side);
0313 layer_num++;
0314 }
0315 }
0316 }
0317
0318
0319 Material casing_material = desc.material(casing_xml.materialStr());
0320 const double casing_width = width + 2. * casing_thickness;
0321 const double casing_height = height + 2. * casing_thickness;
0322 const double casing_z = -(length - casing_length) / 2.;
0323
0324 Box casing_outer(casing_width / 2., casing_height / 2., casing_length / 2.);
0325 Box casing_inner(width / 2., height / 2., casing_length / 2. - casing_thickness);
0326 SubtractionSolid casing_box(casing_outer, casing_inner);
0327 const double back_cutout_y = (height / 2. + center_y + rect_height / 2.) / 2.;
0328 const double back_cutout_z = casing_length / 2. - casing_thickness / 2.;
0329
0330
0331 for (int side = 0; side < 2; side++) {
0332 Box side_cut(casing_width / 2., casing_height, casing_length);
0333 Position side_cut_position(
0334 side == 0 ? right_split_x + casing_width / 2. : left_split_x - casing_width / 2., 0., 0.);
0335 Solid casing_side = SubtractionSolid(casing_box, side_cut, side_cut_position);
0336
0337
0338 Solid casing_cutout = Tube(0., casing_left_radius, casing_length / 2. + casing_thickness);
0339 if (side == 0) {
0340 const double right_rect_width = right_split_x - center_x;
0341 Tube right_cap(0., rect_height / 2., casing_length / 2. + casing_thickness, M_PI / 2.,
0342 3. * M_PI / 2.);
0343 Box right_rect(right_rect_width / 2., rect_height / 2.,
0344 casing_length / 2. + casing_thickness);
0345 casing_cutout = UnionSolid(right_cap, right_rect, Position(right_rect_width / 2., 0., 0.));
0346 }
0347
0348 Solid casing_with_cutout =
0349 SubtractionSolid(casing_side, casing_cutout, Position(center_x, center_y, 0.));
0350
0351
0352 const double cutout_width = side == 0 ? right_back_cutout_width : left_back_cutout_width;
0353 const double cutout_x = side == 0 ? (-casing_width / 2. + right_split_x) / 2.
0354 : (left_split_x + casing_width / 2.) / 2.;
0355 Box back_cutout(cutout_width / 2., back_cutout_height / 2., casing_thickness);
0356 casing_with_cutout = SubtractionSolid(casing_with_cutout, back_cutout,
0357 Position(cutout_x, back_cutout_y, back_cutout_z));
0358 casing_with_cutout = SubtractionSolid(casing_with_cutout, back_cutout,
0359 Position(cutout_x, -back_cutout_y, back_cutout_z));
0360 std::string casing_name = detName + (side == 0 ? "_RightCasing" : "_LeftCasing");
0361 Volume casing_vol(casing_name, casing_with_cutout, casing_material);
0362 casing_vol.setVisAttributes(desc.visAttributes(casing_xml.visStr()));
0363 assembly.placeVolume(casing_vol, Position(0., 0., casing_z));
0364 }
0365
0366
0367 const double left_split_from_center = left_split_x - center_x;
0368 Material cover_material = desc.material(cover_xml.materialStr());
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384 Material pcb_material = desc.material("Fr4");
0385 Box h_pcb(h_pcb_width / 2., pcb_thickness / 2., pcb_length / 2.);
0386 Volume h_pcb_vol(detName + "_HorizontalPCB", h_pcb, pcb_material);
0387 h_pcb_vol.setVisAttributes(desc.visAttributes("AnlDarkGreen"));
0388 assembly.placeVolume(h_pcb_vol, Position(center_x + rect_x, center_y + h_pcb_slot_y, pcb_z));
0389 assembly.placeVolume(h_pcb_vol, Position(center_x + rect_x, center_y - h_pcb_slot_y, pcb_z));
0390
0391
0392 Box h_pcb_cover(rect_width / 2., cover_thickness / 2., pcb_length / 2.);
0393 Volume h_pcb_cover_vol(detName + "_HorizontalPCBCover", h_pcb_cover, cover_material);
0394 h_pcb_cover_vol.setVisAttributes(desc.visAttributes(cover_xml.visStr()));
0395 assembly.placeVolume(
0396 h_pcb_cover_vol,
0397 Position(center_x + rect_x, center_y + rect_height / 2. - cover_thickness / 2., pcb_z));
0398 assembly.placeVolume(
0399 h_pcb_cover_vol,
0400 Position(center_x + rect_x, center_y - rect_height / 2. + cover_thickness / 2., pcb_z));
0401
0402
0403 Box right_v_pcb(pcb_thickness / 2., right_v_pcb_height / 2., pcb_length / 2.);
0404 Volume right_v_pcb_vol(detName + "_RightVerticalPCB", right_v_pcb, pcb_material);
0405 right_v_pcb_vol.setVisAttributes(desc.visAttributes("AnlDarkGreen"));
0406 assembly.placeVolume(right_v_pcb_vol, Position(center_x + right_v_pcb_slot_x,
0407 center_y + right_v_pcb_slot_y, pcb_z));
0408 assembly.placeVolume(right_v_pcb_vol, Position(center_x + right_v_pcb_slot_x,
0409 center_y - right_v_pcb_slot_y, pcb_z));
0410
0411
0412 const double right_v_pcb_cover_inner_y = rect_height / 2. - cover_thickness;
0413 const double right_v_pcb_cover_outer_y = height / 2. + casing_thickness;
0414 const double right_v_pcb_cover_height = right_v_pcb_cover_outer_y - right_v_pcb_cover_inner_y;
0415 const double right_v_pcb_cover_y = (right_v_pcb_cover_inner_y + right_v_pcb_cover_outer_y) / 2.;
0416 Box right_v_pcb_cover(cover_thickness / 2., right_v_pcb_cover_height / 2., pcb_length / 2.);
0417 Volume right_v_pcb_cover_vol(detName + "_RightVerticalPCBCover", right_v_pcb_cover,
0418 cover_material);
0419 right_v_pcb_cover_vol.setVisAttributes(desc.visAttributes(cover_xml.visStr()));
0420 assembly.placeVolume(
0421 right_v_pcb_cover_vol,
0422 Position(center_x + right_v_pcb_slot_x + v_pcb_slot_width / 2. + cover_thickness / 2.,
0423 center_y + right_v_pcb_cover_y, pcb_z));
0424 assembly.placeVolume(
0425 right_v_pcb_cover_vol,
0426 Position(center_x + right_v_pcb_slot_x + v_pcb_slot_width / 2. + cover_thickness / 2.,
0427 center_y - right_v_pcb_cover_y, pcb_z));
0428
0429
0430 Box left_v_pcb(pcb_thickness / 2., left_v_pcb_height / 2., pcb_length / 2.);
0431 Volume left_v_pcb_vol(detName + "_LeftVerticalPCB", left_v_pcb, pcb_material);
0432 left_v_pcb_vol.setVisAttributes(desc.visAttributes("AnlDarkGreen"));
0433 assembly.placeVolume(left_v_pcb_vol,
0434 Position(center_x + left_v_pcb_slot_x, center_y + left_v_pcb_slot_y, pcb_z));
0435 assembly.placeVolume(left_v_pcb_vol,
0436 Position(center_x + left_v_pcb_slot_x, center_y - left_v_pcb_slot_y, pcb_z));
0437
0438
0439 const double left_v_pcb_cover_inner_y = std::sqrt(
0440 casing_left_radius * casing_left_radius - left_split_from_center * left_split_from_center);
0441 const double left_v_pcb_cover_outer_y = height / 2. + casing_thickness;
0442 const double left_v_pcb_cover_height = left_v_pcb_cover_outer_y - left_v_pcb_cover_inner_y;
0443 const double left_v_pcb_cover_y = (left_v_pcb_cover_inner_y + left_v_pcb_cover_outer_y) / 2.;
0444 Box left_v_pcb_cover(cover_thickness / 2., left_v_pcb_cover_height / 2., pcb_length / 2.);
0445 Volume left_v_pcb_cover_vol(detName + "_LeftVerticalPCBCover", left_v_pcb_cover, cover_material);
0446 left_v_pcb_cover_vol.setVisAttributes(desc.visAttributes(cover_xml.visStr()));
0447 assembly.placeVolume(
0448 left_v_pcb_cover_vol,
0449 Position(center_x + left_v_pcb_slot_x - v_pcb_slot_width / 2. - cover_thickness / 2.,
0450 center_y + left_v_pcb_cover_y, pcb_z));
0451 assembly.placeVolume(
0452 left_v_pcb_cover_vol,
0453 Position(center_x + left_v_pcb_slot_x - v_pcb_slot_width / 2. - cover_thickness / 2.,
0454 center_y - left_v_pcb_cover_y, pcb_z));
0455
0456 DetElement det(detName, detID);
0457 Volume motherVol = desc.pickMotherVolume(det);
0458
0459
0460 dd4hep::xml::setDetectorTypeFlag(detElem, det);
0461
0462
0463 auto tr = Transform3D(Position(pos.x(), pos.y(), pos.z() + length / 2.));
0464 PlacedVolume phv = motherVol.placeVolume(assembly, tr);
0465 phv.addPhysVolID("system", detID);
0466 det.setPlacement(phv);
0467
0468 return det;
0469 }
0470 DECLARE_DETELEMENT(epic_InsertCalorimeter, createDetector)