Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:15:40

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Chao Peng, Maria Zurek, Whitney Armstrong
0003 
0004 // Detector plugin to support a hybrid central barrel calorimeter
0005 // The detector consists of interlayers of Pb/ScFi (segmentation in global r, phi) and W/Si (segmentation in local x, y)
0006 // Assembly is used as the envelope so two different detectors can be interlayered with each other
0007 //
0008 //
0009 // 06/19/2021: Implementation of the Sci Fiber geometry. M. Żurek
0010 // 07/09/2021: Support interlayers between multiple detectors. C. Peng
0011 // 07/23/2021: Add assemblies as mother volumes of fibers to reduce the number of daughter volumes. C. Peng, M. Żurek
0012 //     Reference: TGeo performance issue with large number of daughter volumes
0013 //     https://indico.cern.ch/event/967418/contributions/4075358/attachments/2128099/3583278/201009_shKo_dd4hep.pdf
0014 // 07/24/2021: Changed support implementation to avoid too many uses of boolean geometries. DAWN view seems to have
0015 //     issue dealing with it. C. Peng
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 // geometry helpers
0033 static void buildSupport(Detector& desc, Volume& mother, xml_comp_t x_support,
0034                          const std::tuple<double, double, double, double>& dimensions);
0035 
0036 // barrel ecal layers contained in an assembly
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   // Set detector type flag
0060   dd4hep::xml::setDetectorTypeFlag(x_detector, sdet);
0061 
0062   detector_physvol.addPhysVolID("system", detector_id);
0063   sdet.setPlacement(detector_physvol);
0064 
0065   // build the envelope assembly
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   // Add the volume boundary material if configured
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   // Add the volume layer material if configured
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   // build a sector
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   // keep tracking of the total thickness
0105   double layer_pos_z = inner_r;
0106 
0107   // Parameters for computing the layer X dimension:
0108   double tan_half_dphi = std::tan(half_dphi);
0109   double layer_dim_y   = x_dimensions.z() / 2.;
0110 
0111   // Loop over the modules
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     // Compute module total thickness from components
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     // Create the module assembly volume
0135     Assembly module_volume(module_name);
0136     volumes[module_name] = module_volume;
0137     module_volume.setVisAttributes(desc.visAttributes(x_module.visStr()));
0138 
0139     // Add components
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       // Loop over the slices for this component
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         // Place slice
0172         PlacedVolume slice_physvol = component_volume.placeVolume(
0173             slice_volume, Position(0, 0, slice_pos_z + x_slice.thickness() / 2));
0174 
0175         // Set sensitive
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         // Increment Z position of slice
0183         slice_pos_z += x_slice.thickness();
0184         ++slice_num;
0185       }
0186 
0187       // Utility variable for the relative z-offset based off the previous components
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       // apply relative offsets in z-position used to stack components side-by-side
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   //Loop over the sets of layer elements in the detector.
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     // Loop over number of repeats for this layer.
0245     for (int layer_j = 0; layer_j < layer_repeat; layer_j++) {
0246 
0247       // Make an envelope for this layer
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; // account for frame
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       // Set region, limitset, and vis of layer.
0263       layer_volume.setAttributes(desc, x_layer.regionStr(), x_layer.limitsStr(), x_layer.visStr());
0264 
0265       // Loop over the staves for this layer, if the tray is enabled
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         // Check if we enabled this silicon tray (actual silicon detector in the slots) at the top level
0270         // allowing us to easily disable layers in the XML file without needing multiple
0271         // copies of the XML file
0272         if (getAttrOrDefault(x_stave, _Unicode(enable), 1) == 0) {
0273           // disabled
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         // Arrange staves symmetrically around center of layer
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         // Make one stave
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         // No parent, since it will be added on cloning and placement
0297         DetElement stave_element(stave_name, detector_id);
0298 
0299         // Loop over the slices for this stave
0300         double slice_pos_z = -(stave_thick / 2.);
0301 
0302         // Place in xy_layout
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           // Get layout grid pitch
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           // Default to filling
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           // Default to centered
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           // Place modules
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               // Create module
0331               std::string module_name = _toString(i_module, "module%d");
0332               DetElement module_element(stave_element, module_name, i_module);
0333 
0334               // Place module
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               // Add sensitive volumes
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         // Loop over the slices for this stave
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           // Set sensitive
0376           if (x_slice.isSensitive()) {
0377             slice_volume.setSensitiveDetector(sens);
0378           }
0379 
0380           // Place slice
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           // Increment Z position of slice
0387           slice_pos_z += slice_thick;
0388           ++slice_num;
0389         }
0390 
0391         // Loop over number of repeats for this stave
0392         for (int stave_j = 0; stave_j < stave_repeat; stave_j++) {
0393 
0394           // Stave placement
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           // Increment X position of stave
0407           stave_pos_x += stave_pitch;
0408         }
0409       }
0410 
0411       // Place frame if defined.
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       // Place layer into sector
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       // Increment to next layer Z position. Do not add space_between for the last layer
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   // Phi start for a sector.
0464   double phi = M_PI / nsides;
0465 
0466   // Create nsides sectors.
0467   for (int sector_num = 0; sector_num < nsides; sector_num++, phi -= dphi) {
0468     // Compute the sector position
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   // Place envelope
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 // simple aluminum sheet cover
0487 // dimensions: (inner r, position in z, length, phi)
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)