Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 08:18:20

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 for layer 1
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   // 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, envelope1_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, envelope1_params,
0093                                                     "layer_material");
0094   }
0095 
0096   // Build the envelope assembly for layers 2 to 6 (no Acts support)
0097   DetElement envelope26_element("envelope26", detector_id);
0098   Assembly envelope26_volume("envelope26");
0099 
0100   // Build a sector for layer 1
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   // Build a sector for layers 2 to 6
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   // keep tracking of the total thickness
0117   double layer_pos_z = inner_r;
0118 
0119   // Parameters for computing the layer X dimension:
0120   double tan_half_dphi = std::tan(half_dphi);
0121   double layer_dim_y   = x_dimensions.z() / 2.;
0122 
0123   // Loop over the modules
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     // Compute module total thickness from components
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     // Create the module assembly volume
0147     Assembly module_volume(module_name);
0148     volumes[module_name] = module_volume;
0149     module_volume.setVisAttributes(desc.visAttributes(x_module.visStr()));
0150 
0151     // Add components
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       // Loop over the slices for this component
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         // Place slice
0184         PlacedVolume slice_physvol = component_volume.placeVolume(
0185             slice_volume, Position(0, 0, slice_pos_z + x_slice.thickness() / 2));
0186 
0187         // Set sensitive
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         // Increment Z position of slice
0195         slice_pos_z += x_slice.thickness();
0196         ++slice_num;
0197       }
0198 
0199       // Utility variable for the relative z-offset based off the previous components
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       // apply relative offsets in z-position used to stack components side-by-side
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   //Loop over the sets of layer elements in the detector.
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     // Loop over number of repeats for this layer.
0257     for (int layer_j = 0; layer_j < layer_repeat; layer_j++) {
0258 
0259       // Make an envelope for this layer
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; // account for frame
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       // Set region, limitset, and vis of layer.
0280       layer_volume.setAttributes(desc, x_layer.regionStr(), x_layer.limitsStr(), x_layer.visStr());
0281 
0282       // Loop over the staves for this layer, if the tray is enabled
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         // Check if we enabled this silicon tray (actual silicon detector in the slots) at the top level
0287         // allowing us to easily disable layers in the XML file without needing multiple
0288         // copies of the XML file
0289         if (getAttrOrDefault(x_stave, _Unicode(enable), 1) == 0) {
0290           // disabled
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         // Arrange staves symmetrically around center of layer
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         // Make one stave
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         // No parent, since it will be added on cloning and placement
0314         DetElement stave_element(stave_name, detector_id);
0315 
0316         // Loop over the slices for this stave
0317         double slice_pos_z = -(stave_thick / 2.);
0318 
0319         // Place in xy_layout
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           // Get layout grid pitch
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           // Default to filling
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           // Default to centered
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           // Place modules
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               // Create module
0348               std::string module_name = _toString(i_module, "module%d");
0349               DetElement module_element(stave_element, module_name, i_module);
0350 
0351               // Place module
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               // Add sensitive volumes
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         // Loop over the slices for this stave
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           // Set sensitive
0393           if (x_slice.isSensitive()) {
0394             slice_volume.setSensitiveDetector(sens);
0395           }
0396 
0397           // Place slice
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           // Increment Z position of slice
0404           slice_pos_z += slice_thick;
0405           ++slice_num;
0406         }
0407 
0408         // Loop over number of repeats for this stave
0409         for (int stave_j = 0; stave_j < stave_repeat; stave_j++) {
0410 
0411           // Stave placement
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           // Increment X position of stave
0424           stave_pos_x += stave_pitch;
0425         }
0426       }
0427 
0428       // Place frame if defined.
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       // Place layer into sector
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       // Increment to next layer Z position. Do not add space_between for the last layer
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   // Phi start for a sector.
0483   double phi = M_PI / nsides;
0484 
0485   // Create nsides sectors.
0486   for (int sector_num = 0; sector_num < nsides; sector_num++, phi -= dphi) {
0487     // Compute the sector position
0488     Transform3D tr(RotationZYX(0, phi, M_PI * 0.5), Translation3D(0, 0, 0));
0489 
0490     // Place 1st layer
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     // Place layers 2 through 6
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   // Place envelope
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 // simple aluminum sheet cover
0520 // dimensions: (inner r, position in z, length, phi)
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)