File indexing completed on 2025-07-05 08:15:40
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 envelope_element("envelope", detector_id);
0067 Assembly envelope_volume("envelope");
0068 auto& envelope_params =
0069 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(envelope_element);
0070 if (x_detector.hasChild(_Unicode(envelope))) {
0071 xml_comp_t x_envelope = x_detector.child(_Unicode(envelope));
0072 envelope_params.set<double>("envelope_r_min",
0073 getAttrOrDefault(x_envelope, _Unicode(envelope_r_min), 0.));
0074 envelope_params.set<double>("envelope_r_max",
0075 getAttrOrDefault(x_envelope, _Unicode(envelope_r_max), 0.));
0076 envelope_params.set<double>("envelope_z_min",
0077 getAttrOrDefault(x_envelope, _Unicode(envelope_z_min), 0.));
0078 envelope_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, envelope_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, envelope_params,
0093 "layer_material");
0094 }
0095
0096
0097 DetElement sector_element("sector0", detector_id);
0098 Assembly sector_volume("sector");
0099 if (x_detector.hasChild(_Unicode(sectors))) {
0100 xml_comp_t x_sectors = x_detector.child(_Unicode(sectors));
0101 sector_volume.setVisAttributes(desc.visAttributes(x_sectors.visStr()));
0102 }
0103
0104
0105 double layer_pos_z = inner_r;
0106
0107
0108 double tan_half_dphi = std::tan(half_dphi);
0109 double layer_dim_y = x_dimensions.z() / 2.;
0110
0111
0112 using dd4hep::rec::VolPlane;
0113 std::map<std::string, Volume> volumes;
0114 std::map<std::string, std::vector<PlacedVolume>> sensitives;
0115 std::map<std::string, std::vector<VolPlane>> volplane_surfaces;
0116 std::map<std::string, std::array<double, 2>> module_thicknesses;
0117 for (xml_coll_t i_module(x_detector, _U(module)); i_module; ++i_module) {
0118 xml_comp_t x_module = i_module;
0119 std::string module_name = x_module.nameStr();
0120
0121 if (volumes.find(module_name) != volumes.end()) {
0122 printout(ERROR, "BarrelCalorimeterImaging", "Module with name %s already exists",
0123 module_name.c_str());
0124 throw std::runtime_error("Logics error in building modules.");
0125 }
0126
0127
0128 double total_thickness = 0;
0129 xml_coll_t ci(x_module, _U(module_component));
0130 for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0131 total_thickness += xml_comp_t(ci).thickness();
0132 }
0133
0134
0135 Assembly module_volume(module_name);
0136 volumes[module_name] = module_volume;
0137 module_volume.setVisAttributes(desc.visAttributes(x_module.visStr()));
0138
0139
0140 int sensor_number = 1;
0141 int ncomponents = 0;
0142 double thickness_so_far = 0.0;
0143 double thickness_sum = -total_thickness / 2.0;
0144 for (xml_coll_t i_component(x_module, _U(module_component)); i_component;
0145 ++i_component, ++ncomponents) {
0146 xml_comp_t x_component = i_component;
0147 xml_comp_t x_component_pos = x_component.position(false);
0148 xml_comp_t x_component_rot = x_component.rotation(false);
0149 const std::string component_name = _toString(ncomponents, "component%d");
0150 Box component_box(x_component.width() / 2, x_component.length() / 2,
0151 x_component.thickness() / 2);
0152 Volume component_volume(component_name, component_box,
0153 desc.material(x_component.materialStr()));
0154 component_volume.setAttributes(desc, x_component.regionStr(), x_component.limitsStr(),
0155 x_component.visStr());
0156
0157
0158 int slice_num = 1;
0159 double slice_pos_z = -x_component.thickness() / 2;
0160 for (xml_coll_t i_slice(x_component, _U(slice)); i_slice; ++i_slice) {
0161 xml_comp_t x_slice = i_slice;
0162 std::string slice_name = Form("slice%d", slice_num);
0163 double slice_dim_x = x_component.width() / 2;
0164 double slice_dim_y = x_component.length() / 2;
0165 double slice_dim_z = x_slice.thickness() / 2;
0166 Box slice_shape(slice_dim_x, slice_dim_y, slice_dim_z);
0167 Volume slice_volume(slice_name, slice_shape, desc.material(x_slice.materialStr()));
0168 slice_volume.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(),
0169 x_slice.visStr());
0170
0171
0172 PlacedVolume slice_physvol = component_volume.placeVolume(
0173 slice_volume, Position(0, 0, slice_pos_z + x_slice.thickness() / 2));
0174
0175
0176 if (x_slice.isSensitive()) {
0177 slice_physvol.addPhysVolID("slice", slice_num);
0178 slice_volume.setSensitiveDetector(sens);
0179 sensitives[module_name].push_back(slice_physvol);
0180 }
0181
0182
0183 slice_pos_z += x_slice.thickness();
0184 ++slice_num;
0185 }
0186
0187
0188 const double zoff = thickness_sum + x_component.thickness() / 2.0;
0189 PlacedVolume component_physvol;
0190 if (x_component_pos && x_component_rot) {
0191 Position component_pos(x_component_pos.x(0), x_component_pos.y(0),
0192 x_component_pos.z(0) + zoff);
0193 RotationZYX component_rot(x_component_rot.z(0), x_component_rot.y(0), x_component_rot.x(0));
0194 component_physvol =
0195 module_volume.placeVolume(component_volume, Transform3D(component_rot, component_pos));
0196 } else if (x_component_rot) {
0197 Position component_pos(0, 0, zoff);
0198 RotationZYX component_rot(x_component_rot.z(0), x_component_rot.y(0), x_component_rot.x(0));
0199 component_physvol =
0200 module_volume.placeVolume(component_volume, Transform3D(component_rot, component_pos));
0201 } else if (x_component_pos) {
0202 Position component_pos(x_component_pos.x(0), x_component_pos.y(0),
0203 x_component_pos.z(0) + zoff);
0204 component_physvol = module_volume.placeVolume(component_volume, component_pos);
0205 } else {
0206 Position component_pos(0, 0, zoff);
0207 component_physvol = module_volume.placeVolume(component_volume, component_pos);
0208 }
0209
0210 if (x_component.isSensitive()) {
0211 component_physvol.addPhysVolID("sensor", sensor_number++);
0212 component_volume.setSensitiveDetector(sens);
0213 sensitives[module_name].push_back(component_physvol);
0214 module_thicknesses[module_name] = {thickness_so_far + x_component.thickness() / 2.0,
0215 total_thickness - thickness_so_far -
0216 x_component.thickness() / 2.0};
0217 }
0218
0219 thickness_sum += x_component.thickness();
0220 thickness_so_far += x_component.thickness();
0221
0222
0223 if (x_component_pos) {
0224 thickness_sum += x_component_pos.z(0);
0225 thickness_so_far += x_component_pos.z(0);
0226 }
0227 }
0228 }
0229 if (x_detector.hasChild(_Unicode(support))) {
0230 buildSupport(desc, sector_volume, x_detector.child(_Unicode(support)),
0231 {inner_r, layer_pos_z, x_dimensions.z(), half_dphi});
0232 }
0233
0234 int layer_num = 1;
0235 for (xml_coll_t i_layer(x_detector, _U(layer)); i_layer; ++i_layer) {
0236 xml_comp_t x_layer = i_layer;
0237 int layer_repeat = x_layer.repeat();
0238 double layer_thickness = x_layer.thickness();
0239 bool layer_has_frame = x_layer.hasChild(_Unicode(frame));
0240 double layer_space_between = getAttrOrDefault(x_layer, _Unicode(space_between), 0.);
0241 double layer_space_before = getAttrOrDefault(x_layer, _Unicode(space_before), 0.);
0242 layer_pos_z += layer_space_before;
0243
0244
0245 for (int layer_j = 0; layer_j < layer_repeat; layer_j++) {
0246
0247
0248 std::string layer_name = Form("layer%d", layer_num);
0249 double layer_dim_x = tan_half_dphi * layer_pos_z;
0250 auto layer_mat = desc.air();
0251
0252 Position layer_pos(0, 0, layer_pos_z + layer_thickness / 2.);
0253 double layer_trd_x1 = layer_dim_x;
0254 double layer_trd_x2 = layer_dim_x + layer_thickness * tan_half_dphi;
0255 double layer_trd_y1 = layer_dim_y;
0256 double layer_trd_y2 = layer_trd_y1;
0257 double layer_trd_z = layer_thickness / 2;
0258 Trapezoid layer_shape(layer_trd_x1, layer_trd_x2, layer_trd_y1, layer_trd_y2, layer_trd_z);
0259 Volume layer_volume(layer_name, layer_shape, layer_mat);
0260 DetElement layer_element(sector_element, layer_name, detector_id);
0261
0262
0263 layer_volume.setAttributes(desc, x_layer.regionStr(), x_layer.limitsStr(), x_layer.visStr());
0264
0265
0266 int stave_num = 1;
0267 for (xml_coll_t i_stave(x_layer, _U(stave)); i_stave; ++i_stave) {
0268 xml_comp_t x_stave = i_stave;
0269
0270
0271
0272 if (getAttrOrDefault(x_stave, _Unicode(enable), 1) == 0) {
0273
0274 continue;
0275 }
0276
0277 int stave_repeat = x_stave.repeat();
0278 double stave_thick = x_stave.thickness();
0279 double stave_dim_x = x_stave.width() / 2.0;
0280 double stave_dim_y = x_stave.length() / 2.0;
0281 double stave_dim_z = stave_thick / 2.0;
0282 double stave_rot_y = getAttrOrDefault(x_stave, _Unicode(angle), 0.);
0283 double stave_off_z = getAttrOrDefault(x_stave, _Unicode(offset), 0.);
0284
0285
0286 double stave_pos_x = -layer_dim_x + stave_dim_x;
0287 double stave_pitch = -2.0 * stave_pos_x / (stave_repeat - 1);
0288
0289
0290 std::string stave_name = Form("stave%d", stave_num);
0291 auto stave_material = desc.air();
0292 Box stave_shape(stave_dim_x, stave_dim_y, stave_dim_z);
0293 Volume stave_volume(stave_name, stave_shape, stave_material);
0294 stave_volume.setAttributes(desc, x_stave.regionStr(), x_stave.limitsStr(),
0295 x_stave.visStr());
0296
0297 DetElement stave_element(stave_name, detector_id);
0298
0299
0300 double slice_pos_z = -(stave_thick / 2.);
0301
0302
0303 if (x_stave.hasChild(_Unicode(xy_layout))) {
0304 auto module_str = x_stave.moduleStr();
0305 auto& module_volume = volumes[module_str];
0306 auto& module_sensitives = sensitives[module_str];
0307
0308
0309 xml_comp_t x_xy_layout = x_stave.child(_Unicode(xy_layout));
0310 auto dx = x_xy_layout.attr<double>(_Unicode(dx));
0311 auto dy = x_xy_layout.attr<double>(_Unicode(dy));
0312
0313
0314 auto nx = getAttrOrDefault<int>(x_xy_layout, _Unicode(nx), floor(2. * stave_dim_x / dx));
0315 auto ny = getAttrOrDefault<int>(x_xy_layout, _Unicode(ny), floor(2. * stave_dim_y / dy));
0316 printout(DEBUG, "BarrelCalorimeterImaging", "Stave %s layout with %d by %d modules",
0317 stave_name.c_str(), nx, ny);
0318
0319
0320 auto x0 = getAttrOrDefault<double>(x_xy_layout, _Unicode(x0), -(nx - 1) * dx / 2.);
0321 auto y0 = getAttrOrDefault<double>(x_xy_layout, _Unicode(x0), -(ny - 1) * dy / 2.);
0322 printout(DEBUG, "BarrelCalorimeterImaging", "Stave %s modules starting at x=%f, y=%f",
0323 stave_name.c_str(), x0, y0);
0324
0325
0326 int i_module = 0;
0327 for (auto i_x = 0; i_x < nx; ++i_x) {
0328 for (auto i_y = 0; i_y < ny; ++i_y) {
0329
0330
0331 std::string module_name = _toString(i_module, "module%d");
0332 DetElement module_element(stave_element, module_name, i_module);
0333
0334
0335 auto x = x0 + i_x * dx;
0336 auto y = y0 + i_y * dy;
0337 Position module_pos(x, y, 0);
0338 PlacedVolume module_physvol = stave_volume.placeVolume(module_volume, module_pos);
0339 module_physvol.addPhysVolID("module", i_module);
0340 module_element.setPlacement(module_physvol);
0341
0342
0343 for (auto& sensitive_physvol : module_sensitives) {
0344 DetElement sensitive_element(module_element, sensitive_physvol.volume().name(),
0345 i_module);
0346 sensitive_element.setPlacement(sensitive_physvol);
0347
0348 auto& sensitive_element_params =
0349 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(
0350 sensitive_element);
0351 sensitive_element_params.set<std::string>("axis_definitions", "XYZ");
0352 }
0353
0354 i_module++;
0355 }
0356 }
0357 slice_pos_z += module_thicknesses[module_str][0] + module_thicknesses[module_str][1];
0358 }
0359
0360
0361 int slice_num = 1;
0362 for (xml_coll_t i_slice(x_stave, _U(slice)); i_slice; ++i_slice) {
0363 xml_comp_t x_slice = i_slice;
0364 std::string slice_name = Form("slice%d", slice_num);
0365 double slice_thick = x_slice.thickness();
0366 double slice_dim_x = stave_dim_x;
0367 double slice_dim_y = stave_dim_y;
0368 double slice_dim_z = slice_thick / 2.;
0369 Box slice_shape(slice_dim_x, slice_dim_y, slice_dim_z);
0370 Volume slice_volume(slice_name, slice_shape, desc.material(x_slice.materialStr()));
0371 slice_volume.setAttributes(desc, x_slice.regionStr(), x_slice.limitsStr(),
0372 x_slice.visStr());
0373 DetElement slice_element(stave_element, slice_name, detector_id);
0374
0375
0376 if (x_slice.isSensitive()) {
0377 slice_volume.setSensitiveDetector(sens);
0378 }
0379
0380
0381 PlacedVolume slice_physvol =
0382 stave_volume.placeVolume(slice_volume, Position(0, 0, slice_pos_z + slice_thick / 2));
0383 slice_physvol.addPhysVolID("slice", slice_num);
0384 slice_element.setPlacement(slice_physvol);
0385
0386
0387 slice_pos_z += slice_thick;
0388 ++slice_num;
0389 }
0390
0391
0392 for (int stave_j = 0; stave_j < stave_repeat; stave_j++) {
0393
0394
0395 Position stave_pos(stave_pos_x, 0, layer_j % 2 == 0 ? +stave_off_z : -stave_off_z);
0396 RotationY stave_rot(layer_j % 2 == 0 ? +stave_rot_y : -stave_rot_y);
0397 Transform3D stave_tr(stave_rot, stave_pos);
0398 PlacedVolume stave_physvol = layer_volume.placeVolume(stave_volume, stave_tr);
0399 stave_physvol.addPhysVolID("stave", stave_num + stave_j);
0400 DetElement stave_j_element =
0401 (stave_j == 0) ? stave_element
0402 : stave_element.clone(Form("stave%d", stave_num + stave_j));
0403 stave_j_element.setPlacement(stave_physvol);
0404 layer_element.add(stave_j_element);
0405
0406
0407 stave_pos_x += stave_pitch;
0408 }
0409 }
0410
0411
0412 if (layer_has_frame) {
0413 xml_comp_t x_frame = x_layer.child(_Unicode(frame));
0414 double frame_height = x_frame.height();
0415 double frame_thickness = x_frame.thickness();
0416 auto frame_material = desc.material(x_frame.materialStr());
0417
0418 std::string frame1_name = Form("frame_inner%d", layer_num);
0419 double frame1_thick = frame_thickness;
0420 double frame1_trd_x1 =
0421 layer_dim_x + (layer_thickness / 2 - frame_height / 2) * tan_half_dphi;
0422 double frame1_trd_x2 =
0423 layer_dim_x + (layer_thickness / 2 - frame_height / 2 + frame1_thick) * tan_half_dphi;
0424 double frame1_trd_y1 = layer_trd_y1;
0425 double frame1_trd_y2 = frame1_trd_y1;
0426 double frame1_trd_z = frame1_thick / 2.;
0427 Trapezoid frame1_shape(frame1_trd_x1, frame1_trd_x2, frame1_trd_y1, frame1_trd_y2,
0428 frame1_trd_z);
0429 Volume frame1_volume(frame1_name, frame1_shape, frame_material);
0430 layer_volume.placeVolume(frame1_volume,
0431 Position(0, 0, -frame_height / 2.0 + frame_thickness / 2.0));
0432
0433 std::string frame2_name = Form("frame_outer%d", layer_num);
0434 double frame2_thick = frame_thickness;
0435 double frame2_trd_x1 =
0436 layer_dim_x + (layer_thickness / 2 - frame_height / 2) * tan_half_dphi;
0437 double frame2_trd_x2 =
0438 layer_dim_x + (layer_thickness / 2 - frame_height / 2 + frame2_thick) * tan_half_dphi;
0439 double frame2_trd_y1 = layer_trd_y1;
0440 double frame2_trd_y2 = frame2_trd_y1;
0441 double frame2_trd_z = frame2_thick / 2.;
0442 Trapezoid frame2_shape(frame2_trd_x1, frame2_trd_x2, frame2_trd_y1, frame2_trd_y2,
0443 frame2_trd_z);
0444 Volume frame2_volume(frame2_name, frame2_shape, frame_material);
0445 layer_volume.placeVolume(frame2_volume,
0446 Position(0, 0, +frame_height / 2.0 - frame_thickness / 2.0));
0447 }
0448
0449
0450 PlacedVolume layer_physvol = sector_volume.placeVolume(layer_volume, layer_pos);
0451 layer_physvol.addPhysVolID("layer", layer_num);
0452 layer_element.setPlacement(layer_physvol);
0453
0454
0455 layer_pos_z += layer_thickness;
0456 if (layer_j < layer_repeat - 1) {
0457 layer_pos_z += layer_space_between;
0458 }
0459 ++layer_num;
0460 }
0461 }
0462
0463
0464 double phi = M_PI / nsides;
0465
0466
0467 for (int sector_num = 0; sector_num < nsides; sector_num++, phi -= dphi) {
0468
0469 Transform3D tr(RotationZYX(0, phi, M_PI * 0.5), Translation3D(0, 0, 0));
0470 PlacedVolume sector_physvol = envelope_volume.placeVolume(sector_volume, tr);
0471 sector_physvol.addPhysVolID("sector", sector_num + 1);
0472 DetElement sd =
0473 (sector_num == 0) ? sector_element : sector_element.clone(Form("sector%d", sector_num));
0474 sd.setPlacement(sector_physvol);
0475 envelope_element.add(sd);
0476 }
0477
0478
0479 PlacedVolume envelope_physvol = detector_volume.placeVolume(envelope_volume);
0480 envelope_element.setPlacement(envelope_physvol);
0481 sdet.add(envelope_element);
0482
0483 return sdet;
0484 }
0485
0486
0487
0488 static void buildSupport(Detector& desc, Volume& sector_volume, xml_comp_t x_support,
0489 const std::tuple<double, double, double, double>& dimensions) {
0490 auto [inner_r, pos_z, sector_length, hphi] = dimensions;
0491 double support_thickness = getAttrOrDefault(x_support, _Unicode(thickness), 0.5 * cm);
0492 auto material = desc.material(x_support.materialStr());
0493 double trd_y = sector_length / 2.;
0494 double trd_x1_support = std::tan(hphi) * pos_z;
0495 double trd_x2_support = std::tan(hphi) * (pos_z + support_thickness);
0496
0497 Trapezoid s_shape(trd_x1_support, trd_x2_support, trd_y, trd_y, support_thickness / 2.);
0498 Volume s_vol("support_layer", s_shape, material);
0499 s_vol.setVisAttributes(desc.visAttributes(x_support.visStr()));
0500 sector_volume.placeVolume(s_vol, Position(0.0, 0.0, pos_z + support_thickness / 2.));
0501 }
0502 DECLARE_DETELEMENT(epic_EcalBarrelImaging, create_detector)