Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-02 07:54:56

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 - 2024, Nicolas Schmidt, Chun Yuen Tsang
0003 
0004 /** \addtogroup Trackers Trackers
0005  * \brief Type: **Endcap Tracker with TOF**.
0006  *
0007  * \ingroup trackers
0008  *
0009  * @{
0010  */
0011 #include "DD4hep/DetFactoryHelper.h"
0012 #include "DD4hep/Printout.h"
0013 #include "DD4hep/Shapes.h"
0014 #include "DD4hepDetectorHelper.h"
0015 #include "DDRec/DetectorData.h"
0016 #include "DDRec/Surface.h"
0017 #include "XML/Layering.h"
0018 #include "XML/Utilities.h"
0019 #include <array>
0020 #include <map>
0021 #include <unordered_set>
0022 
0023 using namespace std;
0024 using namespace dd4hep;
0025 using namespace dd4hep::rec;
0026 using namespace dd4hep::detail;
0027 
0028 static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector sens) {
0029   xml_det_t x_det      = e;
0030   int det_id           = x_det.id();
0031   std::string det_name = x_det.nameStr();
0032   DetElement sdet(det_name, det_id);
0033   Material air    = description.material("Air");
0034   Material carbon = description.material("CarbonFiber");
0035   PlacedVolume pv;
0036 
0037   map<string, std::array<double, 2>> module_thicknesses;
0038 
0039   // Set detector type flag
0040   dd4hep::xml::setDetectorTypeFlag(x_det, sdet);
0041   auto& params = DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(sdet);
0042   // Add the volume boundary material if configured
0043   for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) {
0044     xml_comp_t x_boundary_material = bmat;
0045     DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, params,
0046                                                     "boundary_material");
0047   }
0048 
0049   Assembly assembly(det_name);
0050   assembly.setVisAttributes(description.invisible());
0051   sens.setType("tracker");
0052 
0053   float zPos = 0;
0054   // now build the envelope for the detector
0055   xml_comp_t x_layer  = x_det.child(_Unicode(layer));
0056   xml_comp_t envelope = x_layer.child(_Unicode(envelope), false);
0057   int lay_id          = x_layer.id();
0058   string l_nam        = x_layer.moduleStr();
0059   string lay_nam      = det_name + _toString(x_layer.id(), "_layer%d");
0060   Tube lay_tub(envelope.rmin(), envelope.rmax(), envelope.length() / 2.0);
0061   Volume lay_vol(lay_nam, lay_tub, air); // Create the layer envelope volume.
0062   zPos = envelope.zstart();
0063   Position lay_pos(0, 0, 0);
0064   lay_vol.setVisAttributes(description.visAttributes(x_layer.visStr()));
0065 
0066   DetElement lay_elt(sdet, lay_nam, lay_id);
0067 
0068   // the local coordinate systems of modules in dd4hep and acts differ
0069   // see http://acts.web.cern.ch/ACTS/latest/doc/group__DD4hepPlugins.html
0070   auto& layerParams =
0071       DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(lay_elt);
0072 
0073   for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) {
0074     xml_comp_t x_layer_material = lmat;
0075     DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, layerParams,
0076                                                     "layer_material");
0077   }
0078 
0079   // dimensions of the modules (2x2 sensors)
0080   xml_comp_t x_modsz = x_det.child(_Unicode(modsize));
0081 
0082   double module_x       = x_modsz.length();
0083   double module_y       = x_modsz.width();
0084   double module_overlap = getAttrOrDefault(x_modsz, _Unicode(overlap), 0.); // x_modsz.overlap();
0085   double module_spacing = getAttrOrDefault(x_modsz, _Unicode(spacing), 0.); // x_modsz.overlap();
0086   double board_gap      = getAttrOrDefault(x_modsz, _Unicode(board_gap), 0.);
0087 
0088   //! Add support structure
0089   xml_comp_t x_supp          = x_det.child(_Unicode(support));
0090   xml_comp_t x_supp_envelope = x_supp.child(_Unicode(envelope), false);
0091 
0092   xml_comp_t x_modFrontLeft  = x_det.child(_Unicode(moduleFrontLeft));
0093   xml_comp_t x_modFrontRight = x_det.child(_Unicode(moduleFrontRight));
0094   xml_comp_t x_modBackLeft   = x_det.child(_Unicode(moduleBackLeft));
0095   xml_comp_t x_modBackRight  = x_det.child(_Unicode(moduleBackRight));
0096 
0097   xml_comp_t x_sensor_layout_front_left  = x_det.child(_Unicode(sensor_layout_front_left));
0098   xml_comp_t x_sensor_layout_back_left   = x_det.child(_Unicode(sensor_layout_back_left));
0099   xml_comp_t x_sensor_layout_front_right = x_det.child(_Unicode(sensor_layout_front_right));
0100   xml_comp_t x_sensor_layout_back_right  = x_det.child(_Unicode(sensor_layout_back_right));
0101 
0102   for (bool left : std::vector<bool>{true, false}) {
0103     for (bool front : std::vector<bool>{true, false}) {
0104       int module = (front << 1) + left;
0105       const std::string locStr =
0106           std::string(left ? "Left" : "Right") + std::string(front ? "Front" : "Back");
0107       float ycoord = envelope.rmax() -
0108                      module_y / 2.; // y-center-coord of the top sensor. Start from the top row
0109       int iy                     = 0;
0110       xml_comp_t x_sensor_layout = x_sensor_layout_front_left;
0111       xml_comp_t x_modCurr       = x_modFrontLeft;
0112 
0113       if (front) {
0114         if (left) {
0115           x_sensor_layout = x_sensor_layout_front_left;
0116           x_modCurr       = x_modFrontLeft;
0117         } else {
0118           x_sensor_layout = x_sensor_layout_front_right;
0119           x_modCurr       = x_modFrontRight;
0120         }
0121       } else {
0122         if (left) {
0123           x_sensor_layout = x_sensor_layout_back_left;
0124           x_modCurr       = x_modBackLeft;
0125         } else {
0126           x_sensor_layout = x_sensor_layout_back_right;
0127           x_modCurr       = x_modBackRight;
0128         }
0129       }
0130 
0131       double total_thickness = 0;
0132       // Compute module total thickness from components
0133       xml_coll_t ci(x_modCurr, _U(module_component));
0134 
0135       for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0136         xml_comp_t x_comp    = ci;
0137         bool keep_same_layer = getAttrOrDefault<bool>(x_comp, _Unicode(keep_layer), false);
0138         if (!keep_same_layer)
0139           total_thickness += x_comp.thickness();
0140       }
0141 
0142       for (xml_coll_t lrow(x_sensor_layout, _Unicode(row)); lrow; ++lrow) {
0143         xml_comp_t x_row = lrow;
0144         double deadspace = getAttrOrDefault<double>(x_row, _Unicode(deadspace), 0);
0145         if (deadspace > 0) {
0146           ycoord -= deadspace;
0147           continue;
0148         }
0149         double x_offset = getAttrOrDefault<double>(x_row, _Unicode(x_offset), 0);
0150         int nsensors    = getAttrOrDefault<int>(x_row, _Unicode(nsensors), 0);
0151 
0152         // find the sensor id that corrsponds to the rightmost sensor in a board
0153         // we need to know where to apply additional spaces between neighboring board
0154         std::unordered_set<int> sensors_id_board_edge;
0155         int curr_ix = nsensors; // the first sensor to the right of center has ix of nsensors
0156         for (xml_coll_t lboard(x_row, _Unicode(board)); lboard; ++lboard) {
0157           xml_comp_t x_board = lboard;
0158           int nboard_sensors = getAttrOrDefault<int>(x_board, _Unicode(nsensors), 1);
0159           curr_ix += nboard_sensors;
0160           sensors_id_board_edge.insert(curr_ix);
0161           sensors_id_board_edge.insert(2 * nsensors - curr_ix -
0162                                        1); // reflected to sensor id on the left
0163         }
0164 
0165         double accum_xoffset = x_offset;
0166         for (int ix = (left ? nsensors - 1 : nsensors); (ix >= 0) && (ix < 2 * nsensors);
0167              ix     = ix + (left ? -1 : 1)) {
0168           // add board spacing
0169           if (sensors_id_board_edge.find(ix) != sensors_id_board_edge.end())
0170             accum_xoffset = accum_xoffset + board_gap;
0171 
0172           // there is a hole in the middle, with radius = x_offset
0173           float xcoord = (ix - nsensors + 0.5) * (module_x + module_spacing) +
0174                          +(left ? -accum_xoffset : accum_xoffset);
0175           //! Note the module ordering is different for front and back side
0176 
0177           double module_z = x_supp_envelope.length() / 2.0 + total_thickness / 2;
0178           if (front)
0179             module_z *= -1;
0180 
0181           string module_name = Form("module%d_%d_%d", module, ix, iy);
0182           DetElement mod_elt(lay_elt, module_name, module);
0183 
0184           // create individual sensor layers here
0185           string m_nam = Form("EndcapTOF_Module%d_%d_%d", module, ix, iy);
0186 
0187           int ncomponents = 0;
0188           // the module assembly volume
0189           Assembly m_vol(m_nam);
0190           m_vol.setVisAttributes(description.visAttributes(x_modCurr.visStr()));
0191 
0192           double thickness_so_far     = 0.0;
0193           double thickness_sum        = -total_thickness / 2.0;
0194           double thickness_carbonsupp = 0.0;
0195           int sensitive_id            = 0;
0196           for (xml_coll_t mci(x_modCurr, _U(module_component)); mci; ++mci, ++ncomponents) {
0197             xml_comp_t x_comp = mci;
0198             xml_comp_t x_pos  = x_comp.position(false);
0199             xml_comp_t x_rot  = x_comp.rotation(false);
0200             const string c_nam =
0201                 Form("component_%s_%s_ix%d_iy%d", x_comp.nameStr().c_str(), locStr.c_str(), ix, iy);
0202 
0203             Box c_box(x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2);
0204             Volume c_vol(c_nam, c_box, description.material(x_comp.materialStr()));
0205             if (x_comp.materialStr() == "CarbonFiber") {
0206               thickness_carbonsupp = x_comp.thickness();
0207             }
0208             // Utility variable for the relative z-offset based off the previous components
0209             const double zoff = thickness_sum + x_comp.thickness() / 2.0;
0210             if (x_pos && x_rot) {
0211               Position c_pos(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff);
0212               RotationZYX c_rot(x_rot.z(0), x_rot.y(0), x_rot.x(0));
0213               pv = m_vol.placeVolume(c_vol, Transform3D(c_rot, c_pos));
0214             } else if (x_rot) {
0215               Position c_pos(0, 0, zoff);
0216               pv = m_vol.placeVolume(
0217                   c_vol, Transform3D(RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0)), c_pos));
0218             } else if (x_pos) {
0219               pv = m_vol.placeVolume(c_vol, Position(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff));
0220             } else {
0221               pv = m_vol.placeVolume(c_vol, Position(0, 0, zoff));
0222             }
0223             c_vol.setRegion(description, x_comp.regionStr());
0224             c_vol.setLimitSet(description, x_comp.limitsStr());
0225             c_vol.setVisAttributes(description, x_comp.visStr());
0226             if (x_comp.isSensitive()) {
0227               pv.addPhysVolID("idx", ix);
0228               pv.addPhysVolID("idy", iy);
0229               pv.addPhysVolID("ids", sensitive_id);
0230               ++sensitive_id;
0231 
0232               c_vol.setSensitiveDetector(sens);
0233               module_thicknesses[m_nam] = {thickness_so_far + x_comp.thickness() / 2.0,
0234                                            total_thickness - thickness_so_far -
0235                                                x_comp.thickness() / 2.0};
0236 
0237               // -------- create a measurement plane for the tracking surface attched to the sensitive volume -----
0238               Vector3D u(-1., 0., 0.);
0239               Vector3D v(0., -1., 0.);
0240               Vector3D n(0., 0., 1.);
0241 
0242               // compute the inner and outer thicknesses that need to be assigned to the tracking surface
0243               // depending on wether the support is above or below the sensor
0244               double inner_thickness = module_thicknesses[m_nam][0];
0245               double outer_thickness = module_thicknesses[m_nam][1];
0246 
0247               SurfaceType type(SurfaceType::Sensitive);
0248 
0249               VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n);
0250 
0251               DetElement comp_de(mod_elt,
0252                                  std::string("de_") + pv.volume().name() + "_" +
0253                                      std::to_string(sensitive_id),
0254                                  module);
0255               comp_de.setPlacement(pv);
0256 
0257               auto& comp_de_params =
0258                   DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(comp_de);
0259               comp_de_params.set<string>("axis_definitions", "XYZ");
0260               volSurfaceList(comp_de)->push_back(surf);
0261 
0262               //--------------------------------------------
0263             }
0264             bool keep_same_layer = getAttrOrDefault<bool>(x_comp, _Unicode(keep_layer), false);
0265             if (!keep_same_layer) {
0266               thickness_sum += x_comp.thickness();
0267               thickness_so_far += x_comp.thickness();
0268               // apply relative offsets in z-position used to stack components side-by-side
0269               if (x_pos) {
0270                 thickness_sum += x_pos.z(0);
0271                 thickness_so_far += x_pos.z(0);
0272               }
0273             }
0274           }
0275 
0276           if (front) {
0277             // only draw support bar on one side
0278             // if you draw on both sides, they may overlap
0279             const string suppb_nam =
0280                 Form("suppbar_%d_%d", ix, iy); //_toString(ncomponents, "component%d");
0281             Box suppb_box((module_x + module_spacing) / 2, thickness_carbonsupp / 2,
0282                           x_supp_envelope.length() / 2);
0283             Volume suppb_vol(suppb_nam, suppb_box, carbon);
0284             Transform3D trsupp(RotationZYX(0, 0, 0),
0285                                Position(xcoord, ycoord + module_y / 2 - module_overlap / 2, 0));
0286             suppb_vol.setVisAttributes(description, "AnlGray");
0287 
0288             pv = lay_vol.placeVolume(suppb_vol, trsupp);
0289           }
0290           // module built!
0291 
0292           Transform3D tr(RotationZYX(M_PI / 2, 0, 0), Position(xcoord, ycoord, module_z));
0293 
0294           pv = lay_vol.placeVolume(m_vol, tr);
0295           pv.addPhysVolID("module", module);
0296           mod_elt.setPlacement(pv);
0297         }
0298         ycoord -= (module_y - module_overlap);
0299         ++iy;
0300       }
0301     }
0302   }
0303   // Create the PhysicalVolume for the layer.
0304   pv = assembly.placeVolume(lay_vol, lay_pos); // Place layer in mother
0305   pv.addPhysVolID("layer", lay_id);            // Set the layer ID.
0306   lay_elt.setAttributes(description, lay_vol, x_layer.regionStr(), x_layer.limitsStr(),
0307                         x_layer.visStr());
0308   lay_elt.setPlacement(pv);
0309 
0310   pv = description.pickMotherVolume(sdet).placeVolume(assembly, Position(0, 0, zPos));
0311   pv.addPhysVolID("system", det_id);
0312   sdet.setPlacement(pv);
0313 
0314   return sdet;
0315 }
0316 
0317 //@}
0318 // clang-format off
0319 DECLARE_DETELEMENT(epic_TOFEndcap, create_detector)