File indexing completed on 2025-09-18 08:18:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include "DD4hep/DetFactoryHelper.h"
0018 #include "DD4hep/Printout.h"
0019 #include "DDRec/Surface.h"
0020 #include "Math/Point2D.h"
0021 #include "TGeoPolygon.h"
0022 #include "XML/Layering.h"
0023 #include "XML/Utilities.h"
0024 #include <functional>
0025
0026 #include "DD4hepDetectorHelper.h"
0027
0028 using namespace dd4hep;
0029
0030 typedef ROOT::Math::XYPoint Point;
0031
0032
0033 static void buildSupport(Detector& desc, Volume& mother, xml_comp_t x_support,
0034 const std::tuple<double, double, double, double>& dimensions);
0035
0036
0037 static Ref_t create_detector(Detector& desc, xml_h e, SensitiveDetector sens) {
0038 xml_det_t x_detector = e;
0039 int detector_id = x_detector.id();
0040 std::string detector_name = x_detector.nameStr();
0041 double offset = x_detector.attr<double>(_Unicode(offset));
0042 xml_comp_t x_dimensions = x_detector.dimensions();
0043 int nsides = x_dimensions.numsides();
0044 double inner_r = x_dimensions.rmin();
0045 double dphi = (2 * M_PI / nsides);
0046 double half_dphi = dphi / 2;
0047
0048 DetElement sdet(detector_name, detector_id);
0049 Volume mother_volume = desc.pickMotherVolume(sdet);
0050
0051 Assembly detector_volume(detector_name);
0052 detector_volume.setAttributes(desc, x_detector.regionStr(), x_detector.limitsStr(),
0053 x_detector.visStr());
0054
0055 Transform3D detector_tr = Translation3D(0, 0, offset) * RotationZ(0);
0056 PlacedVolume detector_physvol = mother_volume.placeVolume(detector_volume, detector_tr);
0057 sens.setType("calorimeter");
0058
0059
0060 dd4hep::xml::setDetectorTypeFlag(x_detector, sdet);
0061
0062 detector_physvol.addPhysVolID("system", detector_id);
0063 sdet.setPlacement(detector_physvol);
0064
0065
0066 DetElement envelope1_element("envelope1", detector_id);
0067 Assembly envelope1_volume("envelope1");
0068 auto& envelope1_params =
0069 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(envelope1_element);
0070 if (x_detector.hasChild(_Unicode(envelope))) {
0071 xml_comp_t x_envelope = x_detector.child(_Unicode(envelope));
0072 envelope1_params.set<double>("envelope_r_min",
0073 getAttrOrDefault(x_envelope, _Unicode(envelope_r_min), 0.));
0074 envelope1_params.set<double>("envelope_r_max",
0075 getAttrOrDefault(x_envelope, _Unicode(envelope_r_max), 0.));
0076 envelope1_params.set<double>("envelope_z_min",
0077 getAttrOrDefault(x_envelope, _Unicode(envelope_z_min), 0.));
0078 envelope1_params.set<double>("envelope_z_max",
0079 getAttrOrDefault(x_envelope, _Unicode(envelope_z_max), 0.));
0080 }
0081
0082 for (xml_coll_t boundary_material(x_detector, _Unicode(boundary_material)); boundary_material;
0083 ++boundary_material) {
0084 xml_comp_t x_boundary_material = boundary_material;
0085 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, envelope1_params,
0086 "boundary_material");
0087 }
0088
0089 for (xml_coll_t layer_material(x_detector, _Unicode(layer_material)); layer_material;
0090 ++layer_material) {
0091 xml_comp_t x_layer_material = layer_material;
0092 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, envelope1_params,
0093 "layer_material");
0094 }
0095
0096
0097 DetElement envelope26_element("envelope26", detector_id);
0098 Assembly envelope26_volume("envelope26");
0099
0100
0101 DetElement sector1_element("sector1", detector_id);
0102 Assembly sector1_volume("sector1");
0103 if (x_detector.hasChild(_Unicode(sectors))) {
0104 xml_comp_t x_sectors = x_detector.child(_Unicode(sectors));
0105 sector1_volume.setVisAttributes(desc.visAttributes(x_sectors.visStr()));
0106 }
0107
0108
0109 DetElement sector26_element("sector26", detector_id);
0110 Assembly sector26_volume("sector26");
0111 if (x_detector.hasChild(_Unicode(sectors))) {
0112 xml_comp_t x_sectors = x_detector.child(_Unicode(sectors));
0113 sector26_volume.setVisAttributes(desc.visAttributes(x_sectors.visStr()));
0114 }
0115
0116
0117 double layer_pos_z = inner_r;
0118
0119
0120 double tan_half_dphi = std::tan(half_dphi);
0121 double layer_dim_y = x_dimensions.z() / 2.;
0122
0123
0124 using dd4hep::rec::VolPlane;
0125 std::map<std::string, Volume> volumes;
0126 std::map<std::string, std::vector<PlacedVolume>> sensitives;
0127 std::map<std::string, std::vector<VolPlane>> volplane_surfaces;
0128 std::map<std::string, std::array<double, 2>> module_thicknesses;
0129 for (xml_coll_t i_module(x_detector, _U(module)); i_module; ++i_module) {
0130 xml_comp_t x_module = i_module;
0131 std::string module_name = x_module.nameStr();
0132
0133 if (volumes.find(module_name) != volumes.end()) {
0134 printout(ERROR, "BarrelCalorimeterImaging", "Module with name %s already exists",
0135 module_name.c_str());
0136 throw std::runtime_error("Logics error in building modules.");
0137 }
0138
0139
0140 double total_thickness = 0;
0141 xml_coll_t ci(x_module, _U(module_component));
0142 for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0143 total_thickness += xml_comp_t(ci).thickness();
0144 }
0145
0146
0147 Assembly module_volume(module_name);
0148 volumes[module_name] = module_volume;
0149 module_volume.setVisAttributes(desc.visAttributes(x_module.visStr()));
0150
0151
0152 int sensor_number = 1;
0153 int ncomponents = 0;
0154 double thickness_so_far = 0.0;
0155 double thickness_sum = -total_thickness / 2.0;
0156 for (xml_coll_t i_component(x_module, _U(module_component)); i_component;
0157 ++i_component, ++ncomponents) {
0158 xml_comp_t x_component = i_component;
0159 xml_comp_t x_component_pos = x_component.position(false);
0160 xml_comp_t x_component_rot = x_component.rotation(false);
0161 const std::string component_name = _toString(ncomponents, "component%d");
0162 Box component_box(x_component.width() / 2, x_component.length() / 2,
0163 x_component.thickness() / 2);
0164 Volume component_volume(component_name, component_box,
0165 desc.material(x_component.materialStr()));
0166 component_volume.setAttributes(desc, x_component.regionStr(), x_component.limitsStr(),
0167 x_component.visStr());
0168
0169
0170 int slice_num = 1;
0171 double slice_pos_z = -x_component.thickness() / 2;
0172 for (xml_coll_t i_slice(x_component, _U(slice)); i_slice; ++i_slice) {
0173 xml_comp_t x_slice = i_slice;
0174 std::string slice_name = Form("slice%d", slice_num);
0175 double slice_dim_x = x_component.width() / 2;
0176 double slice_dim_y = x_component.length() / 2;
0177 double slice_dim_z = x_slice.thickness() / 2;
0178 Box slice_shape(slice_dim_x, slice_dim_y, slice_dim_z);
0179 Volume slice_volume(slice_name, slice_shape, desc.material(x_slice.materialStr()));
0180 slice_volume.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(),
0181 x_slice.visStr());
0182
0183
0184 PlacedVolume slice_physvol = component_volume.placeVolume(
0185 slice_volume, Position(0, 0, slice_pos_z + x_slice.thickness() / 2));
0186
0187
0188 if (x_slice.isSensitive()) {
0189 slice_physvol.addPhysVolID("slice", slice_num);
0190 slice_volume.setSensitiveDetector(sens);
0191 sensitives[module_name].push_back(slice_physvol);
0192 }
0193
0194
0195 slice_pos_z += x_slice.thickness();
0196 ++slice_num;
0197 }
0198
0199
0200 const double zoff = thickness_sum + x_component.thickness() / 2.0;
0201 PlacedVolume component_physvol;
0202 if (x_component_pos && x_component_rot) {
0203 Position component_pos(x_component_pos.x(0), x_component_pos.y(0),
0204 x_component_pos.z(0) + zoff);
0205 RotationZYX component_rot(x_component_rot.z(0), x_component_rot.y(0), x_component_rot.x(0));
0206 component_physvol =
0207 module_volume.placeVolume(component_volume, Transform3D(component_rot, component_pos));
0208 } else if (x_component_rot) {
0209 Position component_pos(0, 0, zoff);
0210 RotationZYX component_rot(x_component_rot.z(0), x_component_rot.y(0), x_component_rot.x(0));
0211 component_physvol =
0212 module_volume.placeVolume(component_volume, Transform3D(component_rot, component_pos));
0213 } else if (x_component_pos) {
0214 Position component_pos(x_component_pos.x(0), x_component_pos.y(0),
0215 x_component_pos.z(0) + zoff);
0216 component_physvol = module_volume.placeVolume(component_volume, component_pos);
0217 } else {
0218 Position component_pos(0, 0, zoff);
0219 component_physvol = module_volume.placeVolume(component_volume, component_pos);
0220 }
0221
0222 if (x_component.isSensitive()) {
0223 component_physvol.addPhysVolID("sensor", sensor_number++);
0224 component_volume.setSensitiveDetector(sens);
0225 sensitives[module_name].push_back(component_physvol);
0226 module_thicknesses[module_name] = {thickness_so_far + x_component.thickness() / 2.0,
0227 total_thickness - thickness_so_far -
0228 x_component.thickness() / 2.0};
0229 }
0230
0231 thickness_sum += x_component.thickness();
0232 thickness_so_far += x_component.thickness();
0233
0234
0235 if (x_component_pos) {
0236 thickness_sum += x_component_pos.z(0);
0237 thickness_so_far += x_component_pos.z(0);
0238 }
0239 }
0240 }
0241 if (x_detector.hasChild(_Unicode(support))) {
0242 buildSupport(desc, sector1_volume, x_detector.child(_Unicode(support)),
0243 {inner_r, layer_pos_z, x_dimensions.z(), half_dphi});
0244 }
0245
0246 int layer_num = 1;
0247 for (xml_coll_t i_layer(x_detector, _U(layer)); i_layer; ++i_layer) {
0248 xml_comp_t x_layer = i_layer;
0249 int layer_repeat = x_layer.repeat();
0250 double layer_thickness = x_layer.thickness();
0251 bool layer_has_frame = x_layer.hasChild(_Unicode(frame));
0252 double layer_space_between = getAttrOrDefault(x_layer, _Unicode(space_between), 0.);
0253 double layer_space_before = getAttrOrDefault(x_layer, _Unicode(space_before), 0.);
0254 layer_pos_z += layer_space_before;
0255
0256
0257 for (int layer_j = 0; layer_j < layer_repeat; layer_j++) {
0258
0259
0260 std::string layer_name = Form("layer%d", layer_num);
0261 double layer_dim_x = tan_half_dphi * layer_pos_z;
0262 auto layer_mat = desc.air();
0263
0264 Position layer_pos(0, 0, layer_pos_z + layer_thickness / 2.);
0265 double layer_trd_x1 = layer_dim_x;
0266 double layer_trd_x2 = layer_dim_x + layer_thickness * tan_half_dphi;
0267 double layer_trd_y1 = layer_dim_y;
0268 double layer_trd_y2 = layer_trd_y1;
0269 double layer_trd_z = layer_thickness / 2;
0270 Trapezoid layer_shape(layer_trd_x1, layer_trd_x2, layer_trd_y1, layer_trd_y2, layer_trd_z);
0271 Volume layer_volume(layer_name, layer_shape, layer_mat);
0272 DetElement layer_element(layer_name, detector_id);
0273 if (layer_num == 1) {
0274 sector1_element.add(layer_element);
0275 } else {
0276 sector26_element.add(layer_element);
0277 }
0278
0279
0280 layer_volume.setAttributes(desc, x_layer.regionStr(), x_layer.limitsStr(), x_layer.visStr());
0281
0282
0283 int stave_num = 1;
0284 for (xml_coll_t i_stave(x_layer, _U(stave)); i_stave; ++i_stave) {
0285 xml_comp_t x_stave = i_stave;
0286
0287
0288
0289 if (getAttrOrDefault(x_stave, _Unicode(enable), 1) == 0) {
0290
0291 continue;
0292 }
0293
0294 int stave_repeat = x_stave.repeat();
0295 double stave_thick = x_stave.thickness();
0296 double stave_dim_x = x_stave.width() / 2.0;
0297 double stave_dim_y = x_stave.length() / 2.0;
0298 double stave_dim_z = stave_thick / 2.0;
0299 double stave_rot_y = getAttrOrDefault(x_stave, _Unicode(angle), 0.);
0300 double stave_off_z = getAttrOrDefault(x_stave, _Unicode(offset), 0.);
0301
0302
0303 double stave_pos_x = -layer_dim_x + stave_dim_x;
0304 double stave_pitch = -2.0 * stave_pos_x / (stave_repeat - 1);
0305
0306
0307 std::string stave_name = Form("stave%d", stave_num);
0308 auto stave_material = desc.air();
0309 Box stave_shape(stave_dim_x, stave_dim_y, stave_dim_z);
0310 Volume stave_volume(stave_name, stave_shape, stave_material);
0311 stave_volume.setAttributes(desc, x_stave.regionStr(), x_stave.limitsStr(),
0312 x_stave.visStr());
0313
0314 DetElement stave_element(stave_name, detector_id);
0315
0316
0317 double slice_pos_z = -(stave_thick / 2.);
0318
0319
0320 if (x_stave.hasChild(_Unicode(xy_layout))) {
0321 auto module_str = x_stave.moduleStr();
0322 auto& module_volume = volumes[module_str];
0323 auto& module_sensitives = sensitives[module_str];
0324
0325
0326 xml_comp_t x_xy_layout = x_stave.child(_Unicode(xy_layout));
0327 auto dx = x_xy_layout.attr<double>(_Unicode(dx));
0328 auto dy = x_xy_layout.attr<double>(_Unicode(dy));
0329
0330
0331 auto nx = getAttrOrDefault<int>(x_xy_layout, _Unicode(nx), floor(2. * stave_dim_x / dx));
0332 auto ny = getAttrOrDefault<int>(x_xy_layout, _Unicode(ny), floor(2. * stave_dim_y / dy));
0333 printout(DEBUG, "BarrelCalorimeterImaging", "Stave %s layout with %d by %d modules",
0334 stave_name.c_str(), nx, ny);
0335
0336
0337 auto x0 = getAttrOrDefault<double>(x_xy_layout, _Unicode(x0), -(nx - 1) * dx / 2.);
0338 auto y0 = getAttrOrDefault<double>(x_xy_layout, _Unicode(x0), -(ny - 1) * dy / 2.);
0339 printout(DEBUG, "BarrelCalorimeterImaging", "Stave %s modules starting at x=%f, y=%f",
0340 stave_name.c_str(), x0, y0);
0341
0342
0343 int i_module = 0;
0344 for (auto i_x = 0; i_x < nx; ++i_x) {
0345 for (auto i_y = 0; i_y < ny; ++i_y) {
0346
0347
0348 std::string module_name = _toString(i_module, "module%d");
0349 DetElement module_element(stave_element, module_name, i_module);
0350
0351
0352 auto x = x0 + i_x * dx;
0353 auto y = y0 + i_y * dy;
0354 Position module_pos(x, y, 0);
0355 PlacedVolume module_physvol = stave_volume.placeVolume(module_volume, module_pos);
0356 module_physvol.addPhysVolID("module", i_module);
0357 module_element.setPlacement(module_physvol);
0358
0359
0360 for (auto& sensitive_physvol : module_sensitives) {
0361 DetElement sensitive_element(module_element, sensitive_physvol.volume().name(),
0362 i_module);
0363 sensitive_element.setPlacement(sensitive_physvol);
0364
0365 auto& sensitive_element_params =
0366 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(
0367 sensitive_element);
0368 sensitive_element_params.set<std::string>("axis_definitions", "XYZ");
0369 }
0370
0371 i_module++;
0372 }
0373 }
0374 slice_pos_z += module_thicknesses[module_str][0] + module_thicknesses[module_str][1];
0375 }
0376
0377
0378 int slice_num = 1;
0379 for (xml_coll_t i_slice(x_stave, _U(slice)); i_slice; ++i_slice) {
0380 xml_comp_t x_slice = i_slice;
0381 std::string slice_name = Form("slice%d", slice_num);
0382 double slice_thick = x_slice.thickness();
0383 double slice_dim_x = stave_dim_x;
0384 double slice_dim_y = stave_dim_y;
0385 double slice_dim_z = slice_thick / 2.;
0386 Box slice_shape(slice_dim_x, slice_dim_y, slice_dim_z);
0387 Volume slice_volume(slice_name, slice_shape, desc.material(x_slice.materialStr()));
0388 slice_volume.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(),
0389 x_slice.visStr());
0390 DetElement slice_element(stave_element, slice_name, detector_id);
0391
0392
0393 if (x_slice.isSensitive()) {
0394 slice_volume.setSensitiveDetector(sens);
0395 }
0396
0397
0398 PlacedVolume slice_physvol =
0399 stave_volume.placeVolume(slice_volume, Position(0, 0, slice_pos_z + slice_thick / 2));
0400 slice_physvol.addPhysVolID("slice", slice_num);
0401 slice_element.setPlacement(slice_physvol);
0402
0403
0404 slice_pos_z += slice_thick;
0405 ++slice_num;
0406 }
0407
0408
0409 for (int stave_j = 0; stave_j < stave_repeat; stave_j++) {
0410
0411
0412 Position stave_pos(stave_pos_x, 0, layer_j % 2 == 0 ? +stave_off_z : -stave_off_z);
0413 RotationY stave_rot(layer_j % 2 == 0 ? +stave_rot_y : -stave_rot_y);
0414 Transform3D stave_tr(stave_rot, stave_pos);
0415 PlacedVolume stave_physvol = layer_volume.placeVolume(stave_volume, stave_tr);
0416 stave_physvol.addPhysVolID("stave", stave_num + stave_j);
0417 DetElement stave_j_element =
0418 (stave_j == 0) ? stave_element
0419 : stave_element.clone(Form("stave%d", stave_num + stave_j));
0420 stave_j_element.setPlacement(stave_physvol);
0421 layer_element.add(stave_j_element);
0422
0423
0424 stave_pos_x += stave_pitch;
0425 }
0426 }
0427
0428
0429 if (layer_has_frame) {
0430 xml_comp_t x_frame = x_layer.child(_Unicode(frame));
0431 double frame_height = x_frame.height();
0432 double frame_thickness = x_frame.thickness();
0433 auto frame_material = desc.material(x_frame.materialStr());
0434
0435 std::string frame1_name = Form("frame_inner%d", layer_num);
0436 double frame1_thick = frame_thickness;
0437 double frame1_trd_x1 =
0438 layer_dim_x + (layer_thickness / 2 - frame_height / 2) * tan_half_dphi;
0439 double frame1_trd_x2 =
0440 layer_dim_x + (layer_thickness / 2 - frame_height / 2 + frame1_thick) * tan_half_dphi;
0441 double frame1_trd_y1 = layer_trd_y1;
0442 double frame1_trd_y2 = frame1_trd_y1;
0443 double frame1_trd_z = frame1_thick / 2.;
0444 Trapezoid frame1_shape(frame1_trd_x1, frame1_trd_x2, frame1_trd_y1, frame1_trd_y2,
0445 frame1_trd_z);
0446 Volume frame1_volume(frame1_name, frame1_shape, frame_material);
0447 layer_volume.placeVolume(frame1_volume,
0448 Position(0, 0, -frame_height / 2.0 + frame_thickness / 2.0));
0449
0450 std::string frame2_name = Form("frame_outer%d", layer_num);
0451 double frame2_thick = frame_thickness;
0452 double frame2_trd_x1 =
0453 layer_dim_x + (layer_thickness / 2 - frame_height / 2) * tan_half_dphi;
0454 double frame2_trd_x2 =
0455 layer_dim_x + (layer_thickness / 2 - frame_height / 2 + frame2_thick) * tan_half_dphi;
0456 double frame2_trd_y1 = layer_trd_y1;
0457 double frame2_trd_y2 = frame2_trd_y1;
0458 double frame2_trd_z = frame2_thick / 2.;
0459 Trapezoid frame2_shape(frame2_trd_x1, frame2_trd_x2, frame2_trd_y1, frame2_trd_y2,
0460 frame2_trd_z);
0461 Volume frame2_volume(frame2_name, frame2_shape, frame_material);
0462 layer_volume.placeVolume(frame2_volume,
0463 Position(0, 0, +frame_height / 2.0 - frame_thickness / 2.0));
0464 }
0465
0466
0467 PlacedVolume layer_physvol = (layer_num == 1)
0468 ? sector1_volume.placeVolume(layer_volume, layer_pos)
0469 : sector26_volume.placeVolume(layer_volume, layer_pos);
0470 layer_physvol.addPhysVolID("layer", layer_num);
0471 layer_element.setPlacement(layer_physvol);
0472
0473
0474 layer_pos_z += layer_thickness;
0475 if (layer_j < layer_repeat - 1) {
0476 layer_pos_z += layer_space_between;
0477 }
0478 ++layer_num;
0479 }
0480 }
0481
0482
0483 double phi = M_PI / nsides;
0484
0485
0486 for (int sector_num = 0; sector_num < nsides; sector_num++, phi -= dphi) {
0487
0488 Transform3D tr(RotationZYX(0, phi, M_PI * 0.5), Translation3D(0, 0, 0));
0489
0490
0491 PlacedVolume sector1_physvol = envelope1_volume.placeVolume(sector1_volume, tr);
0492 sector1_physvol.addPhysVolID("sector", sector_num + 1);
0493 DetElement sd1 =
0494 (sector_num == 0) ? sector1_element : sector1_element.clone(Form("sector1_%d", sector_num));
0495 sd1.setPlacement(sector1_physvol);
0496 envelope1_element.add(sd1);
0497
0498
0499 PlacedVolume sector26_physvol = envelope26_volume.placeVolume(sector26_volume, tr);
0500 sector26_physvol.addPhysVolID("sector", sector_num + 1);
0501 DetElement sd26 = (sector_num == 0) ? sector26_element
0502 : sector26_element.clone(Form("sector26_%d", sector_num));
0503 sd26.setPlacement(sector26_physvol);
0504 envelope26_element.add(sd26);
0505 }
0506
0507
0508 PlacedVolume envelope1_physvol = detector_volume.placeVolume(envelope1_volume);
0509 envelope1_element.setPlacement(envelope1_physvol);
0510 sdet.add(envelope1_element);
0511
0512 PlacedVolume envelope26_physvol = detector_volume.placeVolume(envelope26_volume);
0513 envelope26_element.setPlacement(envelope26_physvol);
0514 sdet.add(envelope26_element);
0515
0516 return sdet;
0517 }
0518
0519
0520
0521 static void buildSupport(Detector& desc, Volume& sector_volume, xml_comp_t x_support,
0522 const std::tuple<double, double, double, double>& dimensions) {
0523 auto [inner_r, pos_z, sector_length, hphi] = dimensions;
0524 double support_thickness = getAttrOrDefault(x_support, _Unicode(thickness), 0.5 * cm);
0525 auto material = desc.material(x_support.materialStr());
0526 double trd_y = sector_length / 2.;
0527 double trd_x1_support = std::tan(hphi) * pos_z;
0528 double trd_x2_support = std::tan(hphi) * (pos_z + support_thickness);
0529
0530 Trapezoid s_shape(trd_x1_support, trd_x2_support, trd_y, trd_y, support_thickness / 2.);
0531 Volume s_vol("support_layer", s_shape, material);
0532 s_vol.setVisAttributes(desc.visAttributes(x_support.visStr()));
0533 sector_volume.placeVolume(s_vol, Position(0.0, 0.0, pos_z + support_thickness / 2.));
0534 }
0535 DECLARE_DETELEMENT(epic_EcalBarrelImaging, create_detector)