Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-11-04 09:27:11

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Wouter Deconinck, Matt Posik
0003 
0004 /** \addtogroup PID
0005  * \brief Type: **Barrel bar detector (rectangular geom.) with frames surrounding the perimeter.
0006  * \author S. Joosten
0007  * Modified by M. Posik
0008  *
0009  * \ingroup PID
0010  * \ingroup Tracking
0011  *
0012  *
0013  * \code
0014  * \endcode
0015  *
0016  * @{
0017  */
0018 
0019 #include "DD4hep/DetFactoryHelper.h"
0020 #include "DD4hep/Printout.h"
0021 #include "DD4hep/Shapes.h"
0022 #include "DD4hepDetectorHelper.h"
0023 #include "DDRec/DetectorData.h"
0024 #include "DDRec/Surface.h"
0025 #include "XML/Layering.h"
0026 #include "XML/Utilities.h"
0027 #include <array>
0028 
0029 using namespace std;
0030 using namespace dd4hep;
0031 using namespace dd4hep::rec;
0032 
0033 /** Barrel Bar detector with optional frame
0034  *
0035  * - Optional "frame" tag within the module element (frame eats part of the module width and length
0036  *   and surrounds the rectangular perimeter)
0037  * - Detector is setup as a "tracker" so we can use the hits
0038  *
0039  */
0040 static Ref_t create_BarrelPlanarMPGDTracker_geo(Detector& description, xml_h e,
0041                                                 SensitiveDetector sens) {
0042   typedef vector<PlacedVolume> Placements;
0043   xml_det_t x_det = e;
0044   // Material                                air      = description.air();
0045   int det_id      = x_det.id();
0046   string det_name = x_det.nameStr();
0047   DetElement sdet(det_name, det_id);
0048   map<string, Volume> volumes;
0049   map<string, Placements> sensitives;
0050   map<string, std::vector<rec::VolPlane>> volplane_surfaces;
0051   PlacedVolume pv;
0052   dd4hep::xml::Dimension dimensions(x_det.dimensions());
0053   xml_dim_t mpgd_pos = x_det.position();
0054   Assembly assembly(det_name);
0055 
0056   double pcb_feb_ext = 0.0; //extension of PCB board to hold FEBs.
0057 
0058   // Set detector type flag
0059   dd4hep::xml::setDetectorTypeFlag(x_det, sdet);
0060   auto& params = DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(sdet);
0061 
0062   // Add the volume boundary material if configured
0063   for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) {
0064     xml_comp_t x_boundary_material = bmat;
0065     DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, params,
0066                                                     "boundary_material");
0067   }
0068 
0069   map<string, std::array<double, 2>> module_thicknesses;
0070   sens.setType("tracker");
0071 
0072   // loop over the modules
0073   for (xml_coll_t mi(x_det, _U(module)); mi; ++mi) {
0074     xml_comp_t x_mod = mi;
0075     string m_nam     = x_mod.nameStr();
0076 
0077     if (volumes.find(m_nam) != volumes.end()) {
0078       printout(ERROR, "BarrelPlanarMPGDTracker_geo",
0079                string((string("Module with named ") + m_nam + string(" already exists."))).c_str());
0080       throw runtime_error("Logics error in building modules.");
0081     }
0082 
0083     int ncomponents        = 0;
0084     int sensor_number      = 1;
0085     double total_thickness = 0;
0086 
0087     // Compute module total thickness from components
0088     xml_coll_t ci(x_mod, _U(module_component));
0089     for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0090       total_thickness += xml_comp_t(ci).thickness();
0091     }
0092     // the module assembly volume
0093     Assembly m_vol(m_nam);
0094     volumes[m_nam] = m_vol;
0095     m_vol.setVisAttributes(description, x_mod.visStr());
0096 
0097     // Optional module frame.
0098     // frame is 4  bars around the perimeter of the rectangular module. The frame will eat the
0099     // overlapping module area
0100     //
0101     //  ___
0102     // |___| <-- example module cross section (x-y plane), frame is flush with the
0103     //           bottom of the module and protrudes on the top if needed
0104 
0105     // Get frame width, as it impacts the main module for being built. We
0106     // construct the actual frame structure later (once we know the module width)
0107     double frame_width = 0;
0108     if (x_mod.hasChild(_U(frame))) {
0109       xml_comp_t m_frame = x_mod.child(_U(frame));
0110       frame_width        = m_frame.width();
0111     }
0112 
0113     double thickness_so_far     = 0.0;
0114     double thickness_sum        = -total_thickness / 2.0;
0115     double max_component_width  = 0;
0116     double max_component_length = 0;
0117     double gas_thickness        = 0.0;
0118     for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci, ++ncomponents) {
0119       xml_comp_t x_comp = mci;
0120       string c_nam      = _toString(ncomponents, "component%d");
0121       string comp_name  = x_comp.nameStr();
0122 
0123       double box_width  = x_comp.width();
0124       double box_length = x_comp.length();
0125       Box c_box;
0126       // Since MPGD frames are layed over the MPGD foils, the foil material is pressent under the frame as well.
0127       // The gas volumes are not present under the frames, so our frames must eat only the gas module areas
0128       //
0129       //   ------------------- MPGD foil
0130       //   --               -- Frame
0131       //   --  gas volume   -- Frame
0132       //   --               -- Frame
0133       //   ------------------- MPGD foil
0134 
0135       // Look for gas modules to subtract frame thickness from
0136       // FIXME: these module names are hard coded for now. Should find
0137       // a way to set a arribut via the moduel tag to flag what components
0138       // need to have frame thickness subtracted.
0139       if ((comp_name == "DriftGap" || comp_name == "WindowGasGap")) {
0140         box_width            = x_comp.width() - 2.0 * frame_width;
0141         box_length           = x_comp.length() - 2.0 * frame_width;
0142         max_component_width  = box_width;
0143         max_component_length = box_length;
0144         gas_thickness += x_comp.thickness();
0145         c_box = {box_width / 2, box_length / 2, x_comp.thickness() / 2};
0146         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "gas: %s", comp_name.c_str());
0147         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_width: %f", box_width);
0148         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_length: %f", box_length);
0149         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_thickness: %f", x_comp.thickness());
0150       } else {
0151         c_box = {x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2};
0152         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Not gas: %s", comp_name.c_str());
0153         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_width: %f", x_comp.width());
0154         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_length: %f", x_comp.length());
0155         printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_thickness: %f",
0156                  x_comp.thickness());
0157       }
0158       Volume c_vol{c_nam, c_box, description.material(x_comp.materialStr())};
0159 
0160       c_vol.setRegion(description, x_comp.regionStr());
0161       c_vol.setLimitSet(description, x_comp.limitsStr());
0162       c_vol.setVisAttributes(description, x_comp.visStr());
0163 
0164       pcb_feb_ext = x_comp.offset();
0165 
0166       pv = m_vol.placeVolume(
0167           c_vol, Position(0, -pcb_feb_ext / 2.0, thickness_sum + x_comp.thickness() / 2.0));
0168 
0169       if (x_comp.isSensitive()) {
0170         pv.addPhysVolID("sensor", sensor_number++);
0171         c_vol.setSensitiveDetector(sens);
0172         sensitives[m_nam].push_back(pv);
0173         module_thicknesses[m_nam] = {thickness_so_far + x_comp.thickness() / 2.0,
0174                                      total_thickness - thickness_so_far - x_comp.thickness() / 2.0};
0175         // -------- create a measurement plane for the tracking surface attched to the sensitive volume -----
0176         Vector3D u(-1., 0., 0.);
0177         Vector3D v(0., -1., 0.);
0178         Vector3D n(0., 0., 1.);
0179 
0180         // compute the inner and outer thicknesses that need to be assigned to the tracking surface
0181         // depending on wether the support is above or below the sensor
0182         double inner_thickness = module_thicknesses[m_nam][0];
0183         double outer_thickness = module_thicknesses[m_nam][1];
0184 
0185         SurfaceType type(rec::SurfaceType::Sensitive);
0186 
0187         VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ;
0188         volplane_surfaces[m_nam].push_back(surf);
0189       }
0190       thickness_sum += x_comp.thickness();
0191       thickness_so_far += x_comp.thickness();
0192     }
0193     // Now add-on the frame
0194     if (x_mod.hasChild(_U(frame))) {
0195       xml_comp_t m_frame     = x_mod.child(_U(frame));
0196       double frame_thickness = getAttrOrDefault<double>(m_frame, _U(thickness), total_thickness);
0197 
0198       Box lframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0,
0199                      frame_thickness / 2.0};
0200       Box rframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0,
0201                      frame_thickness / 2.0};
0202       Box tframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0};
0203       Box bframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0};
0204 
0205       // Keep track of frame with so we can adjust the module bars appropriately
0206 
0207       Volume lframe_vol{"left_frame", lframe_box, description.material(m_frame.materialStr())};
0208       Volume rframe_vol{"right_frame", rframe_box, description.material(m_frame.materialStr())};
0209       Volume tframe_vol{"top_frame", tframe_box, description.material(m_frame.materialStr())};
0210       Volume bframe_vol{"bottom_frame", bframe_box, description.material(m_frame.materialStr())};
0211 
0212       lframe_vol.setVisAttributes(description, m_frame.visStr());
0213       rframe_vol.setVisAttributes(description, m_frame.visStr());
0214       tframe_vol.setVisAttributes(description, m_frame.visStr());
0215       bframe_vol.setVisAttributes(description, m_frame.visStr());
0216 
0217       printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness: %f", frame_thickness);
0218       printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "total_thickness: %f", total_thickness);
0219       printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness + total_thickness: %f",
0220                frame_thickness + total_thickness);
0221 
0222       m_vol.placeVolume(lframe_vol, Position(frame_width / 2.0 + max_component_width / 2, 0.0,
0223                                              frame_thickness / 2.0 - total_thickness / 2.0 -
0224                                                  gas_thickness / 2.0));
0225       m_vol.placeVolume(rframe_vol, Position(-frame_width / 2.0 - max_component_width / 2.0, 0.0,
0226                                              frame_thickness / 2.0 - total_thickness / 2.0 -
0227                                                  gas_thickness / 2.0));
0228       m_vol.placeVolume(tframe_vol, Position(0.0, frame_width / 2.0 + max_component_length / 2,
0229                                              frame_thickness / 2.0 - total_thickness / 2.0 -
0230                                                  gas_thickness / 2.0));
0231       m_vol.placeVolume(bframe_vol, Position(0.0, -frame_width / 2.0 - max_component_length / 2.0,
0232                                              frame_thickness / 2.0 - total_thickness / 2.0 -
0233                                                  gas_thickness / 2.0));
0234     }
0235   }
0236 
0237   // build the layers the modules will be arranged around
0238   for (xml_coll_t li(x_det, _U(layer)); li; ++li) {
0239     xml_comp_t x_layer            = li;
0240     xml_comp_t x_layout           = x_layer.child(_U(rphi_layout));
0241     xml_comp_t z_layout           = x_layer.child(_U(z_layout));
0242     int lay_id                    = x_layer.id();
0243     string m_nam                  = x_layer.moduleStr();
0244     string lay_nam                = det_name + _toString(x_layer.id(), "_layer%d");
0245     xml_comp_t envelope_tolerance = x_layer.child(_Unicode(envelope_tolerance), false);
0246     double envelope_r_min         = 0;
0247     double envelope_r_max         = 0;
0248     double envelope_z_min         = 0;
0249     double envelope_z_max         = 0;
0250     if (envelope_tolerance) {
0251       envelope_r_min = getAttrOrDefault(envelope_tolerance, _Unicode(r_min), 0);
0252       envelope_r_max = getAttrOrDefault(envelope_tolerance, _Unicode(r_max), 0);
0253       envelope_z_min = getAttrOrDefault(envelope_tolerance, _Unicode(z_min), 0);
0254       envelope_z_max = getAttrOrDefault(envelope_tolerance, _Unicode(z_max), 0);
0255     }
0256 
0257     double phi0     = x_layout.phi0();     // starting phi of first module
0258     double phi_tilt = x_layout.phi_tilt(); // Phi tilit of module
0259     double rc       = x_layout.rc();       // Radius of the module
0260     int nphi        = x_layout.nphi();     // Number of modules in phi
0261     double rphi_dr  = x_layout.dr();       // The delta radius of every other module
0262     double phi_incr = (2 * M_PI) / nphi;   // Phi increment for one module
0263     double phic     = phi0;                // Phi of the module
0264     int nz          = 2;                   // Number of modules placed in z
0265     double z_dr     = z_layout.dr();       // Radial offest of modules in z
0266     double z0       = z_layout.z0();       // Sets how much overlap in z the nz modules have
0267 
0268     Assembly layer_assembly(lay_nam);
0269     Volume module_env = volumes[m_nam];
0270     DetElement lay_elt(sdet, lay_nam, lay_id);
0271     Placements& sensVols = sensitives[m_nam];
0272     auto& layerParams =
0273         DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(lay_elt);
0274 
0275     pv = assembly.placeVolume(layer_assembly);
0276     pv.addPhysVolID("layer", lay_id);
0277     lay_elt.setPlacement(pv);
0278 
0279     int module = 1;
0280     // loop over the modules in phi
0281     for (int ii = 0; ii < nphi; ii++) {
0282       double xc = rc * std::cos(phic);              // Basic x position of module
0283       double yc = rc * std::sin(phic);              // Basic y position of module
0284       double dx = z_dr * std::cos(phic + phi_tilt); // Deta x of module position
0285       double dy = z_dr * std::sin(phic + phi_tilt); // Deta y of module position
0286       // loop over the modules in z
0287       for (int j = 0; j < nz; j++) {
0288         string module_name = _toString(module, "module%d");
0289         DetElement mod_elt(lay_elt, module_name, module);
0290         double mod_z       = 0.5 * dimensions.length();
0291         double z_placement = mod_z - 0.5 * pcb_feb_ext -
0292                              j * (nz * mod_z - pcb_feb_ext); // z location for module placement
0293         double z_offset =
0294             z_placement > 0
0295                 ? -z0 / 2.0
0296                 : z0 / 2.0; // determine the amount of overlap in z the z nz modules have
0297 
0298         Transform3D tr(
0299             RotationZYX(0, ((M_PI / 2) - phic - phi_tilt), -M_PI / 2) * RotationZ(j * M_PI),
0300             Position(
0301                 xc, yc,
0302                 mpgd_pos.z() + z_placement +
0303                     z_offset)); //RotZYX rotates planes around azimuth, RotZ flips plane so pcb_feb_ext is facing endcaps
0304 
0305         pv = layer_assembly.placeVolume(module_env, tr);
0306         pv.addPhysVolID("module", module);
0307         mod_elt.setPlacement(pv);
0308         for (size_t ic = 0; ic < sensVols.size(); ++ic) {
0309           PlacedVolume sens_pv = sensVols[ic];
0310           DetElement comp_de(mod_elt, std::string("de_") + sens_pv.volume().name(), module);
0311           comp_de.setPlacement(sens_pv);
0312         }
0313         // increas module counter
0314         module++;
0315         // adjust x and y coordinates
0316         xc += dx;
0317         yc += dy;
0318       }
0319       // increment counters
0320       phic += phi_incr;
0321       rc += rphi_dr;
0322     }
0323     layer_assembly->GetShape()->ComputeBBox();
0324     layerParams.set<double>("envelope_r_min", envelope_r_min);
0325     layerParams.set<double>("envelope_r_max", envelope_r_max);
0326     layerParams.set<double>("envelope_z_min", envelope_z_min);
0327     layerParams.set<double>("envelope_z_max", envelope_z_max);
0328 
0329     for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) {
0330       xml_comp_t x_layer_material = lmat;
0331       DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, layerParams,
0332                                                       "layer_material");
0333     }
0334   }
0335   sdet.setAttributes(description, assembly, x_det.regionStr(), x_det.limitsStr(), x_det.visStr());
0336   assembly.setVisAttributes(description.invisible());
0337   pv = description.pickMotherVolume(sdet).placeVolume(assembly);
0338   pv.addPhysVolID("system", det_id); // Set the subdetector system ID
0339   sdet.setPlacement(pv);
0340   return sdet;
0341 }
0342 
0343 //@}
0344 // clang-format off
0345 DECLARE_DETELEMENT(epic_OuterMPGDBarrel, create_BarrelPlanarMPGDTracker_geo)