Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2023  Wenliang (Bill) Li, Alexander Kiselev, Karthik Suresh
0003 
0004 //----------------------------------
0005 // pfRICH: Proximity Focusing RICH
0006 // Author: Wenliang (Bill) Li
0007 //
0008 // - Design Adapted from standalone Geant4 description by
0009 //   Alexander Kiselev and Chandradoy Chatterjee
0010 //----------------------------------
0011 
0012 #include "DD4hep/DetFactoryHelper.h"
0013 #include "DD4hep/OpticalSurfaces.h"
0014 #include "DD4hep/Printout.h"
0015 #include "DDRec/DetectorData.h"
0016 #include "DDRec/Surface.h"
0017 
0018 #include <XML/Helper.h>
0019 #include "XML/Layering.h"
0020 #include "TVector3.h"
0021 
0022 #include "TGeoElement.h"
0023 #include "TGeoManager.h"
0024 #include "TInterpreter.h"
0025 #include "TUri.h"
0026 
0027 using namespace std;
0028 using namespace dd4hep;
0029 using namespace dd4hep::rec;
0030 using namespace dd4hep::detail;
0031 
0032 static Ref_t createDetector(Detector& description, xml_h e, SensitiveDetector sens) {
0033 
0034   xml_det_t x_det = e;
0035   int det_id      = x_det.id();
0036 
0037   string det_name = x_det.nameStr();
0038   Material air    = description.air();
0039 
0040   DetElement sdet(det_name, det_id);
0041 
0042   std::vector<double> rmins = {100, 100};
0043   std::vector<double> rmaxs = {1300, 1300};
0044   std::vector<double> zs    = {-5000, 5000};
0045 
0046   sens.setType("tracker");
0047   description.invisible();
0048 
0049   xml::DetElement detElem = e;
0050   std::string detName     = detElem.nameStr();
0051   xml::Component dims     = detElem.dimensions();
0052 
0053   xml_dim_t x_par(x_det.child(_U(parent)));
0054 
0055   string name           = x_det.nameStr();
0056   string par_nam        = x_par.nameStr();
0057   DetElement det_parent = description.detector(par_nam);
0058 
0059   Volume mother = det_parent.volume();
0060   PlacedVolume pv;
0061 
0062   int id = x_det.hasAttr(_U(id)) ? x_det.id() : 0;
0063   xml_dim_t x_pos(x_det.child(_U(position), false));
0064   xml_dim_t x_rot(x_det.child(_U(rotation), false));
0065 
0066   Tube pfRICH_air_volume(0.0, 65.0, 25.0); // dimension of the pfRICH world in cm
0067 
0068   Rotation3D rot(RotationZYX(0, M_PI, 0));
0069   Transform3D transform(rot, Position(0, 0, -149));
0070 
0071   // BUILD SENSORS ///////////////////////
0072   // solid and volume: single sensor module
0073 
0074   OpticalSurfaceManager surfMgr = description.surfaceManager();
0075 
0076   // - sensor module
0077   auto sensorElem        = detElem.child(_Unicode(sensors)).child(_Unicode(module));
0078   auto sensorMat         = description.material(sensorElem.attr<std::string>(_Unicode(material)));
0079   auto sensorVis         = description.visAttributes(sensorElem.attr<std::string>(_Unicode(vis)));
0080   auto sensorSurf        = surfMgr.opticalSurface(sensorElem.attr<std::string>(_Unicode(surface)));
0081   double sensorSide      = sensorElem.attr<double>(_Unicode(side));
0082   double sensorThickness = sensorElem.attr<double>(_Unicode(thickness));
0083   auto readoutName       = detElem.attr<std::string>(_Unicode(readout));
0084 
0085   auto HRPPD_WindowMat = description.material(sensorElem.attr<std::string>(_Unicode(windowmat)));
0086   auto HRPPD_PCBMat    = description.material(sensorElem.attr<std::string>(_Unicode(pcbmat)));
0087   auto HRPPD_MPDMat    = description.material(sensorElem.attr<std::string>(_Unicode(mpdmat)));
0088   auto HRPPD_ASICMat   = description.material(sensorElem.attr<std::string>(_Unicode(asicmat)));
0089 
0090   double vesselRmin0 = dims.attr<double>(_Unicode(rmin0));
0091   double vesselRmin1 = dims.attr<double>(_Unicode(rmin1));
0092   double vesselRmax0 = dims.attr<double>(_Unicode(rmax0));
0093   double vesselRmax1 = dims.attr<double>(_Unicode(rmax1));
0094 
0095   int imod = 0; // module number
0096 
0097   auto gasvolMat = description.material(detElem.attr<std::string>(_Unicode(gas)));
0098   auto gasvolVis = description.visAttributes("DRICH_gas_vis");
0099   auto vesselVis = description.visAttributes(detElem.attr<std::string>(_Unicode(vis_vessel)));
0100 
0101   double windowThickness = dims.attr<double>(_Unicode(window_thickness));
0102   double wallThickness   = dims.attr<double>(_Unicode(wall_thickness));
0103 
0104   double proximityGap = dims.attr<double>(_Unicode(proximity_gap));
0105 
0106   long debug_optics_mode = description.constantAsLong("PFRICH_debug_optics");
0107 
0108   bool debug_optics = debug_optics_mode > 0;
0109 
0110   auto radiatorElem         = detElem.child(_Unicode(radiator));
0111   double radiatorFrontplane = radiatorElem.attr<double>(_Unicode(frontplane));
0112 
0113   auto aerogelElem        = radiatorElem.child(_Unicode(aerogel));
0114   double aerogelThickness = aerogelElem.attr<double>(_Unicode(thickness));
0115 
0116   double radiatorRmin = radiatorElem.attr<double>(_Unicode(rmin));
0117   double radiatorRmax = radiatorElem.attr<double>(_Unicode(rmax));
0118 
0119   auto filterElem = radiatorElem.child(_Unicode(filter));
0120 
0121   double airgapThickness = 0.1;
0122   double filterThickness = 1;
0123 
0124   auto aerogelMat = description.material(aerogelElem.attr<std::string>(_Unicode(material)));
0125   auto filterMat  = description.material(filterElem.attr<std::string>(_Unicode(material)));
0126 
0127   double vesselLength = dims.attr<double>(_Unicode(length));
0128   auto originFront    = Position(0., 0., vesselLength / 2.0);
0129   double sensorZpos = radiatorFrontplane - aerogelThickness - proximityGap - 0.5 * sensorThickness;
0130   auto sensorPlanePos = Position(0., 0., sensorZpos) + originFront; // reference position
0131 
0132   // readout coder <-> unique sensor ID
0133   /* - `sensorIDfields` is a list of readout fields used to specify a unique sensor ID
0134      * - `cellMask` is defined such that a hit's `cellID & cellMask` is the corresponding sensor's unique ID
0135      * - this redundant generalization is for future flexibility, and consistency with dRICH
0136      */
0137 
0138   std::vector<std::string> sensorIDfields = {"module"};
0139   const auto& readoutCoder                = *description.readout(readoutName).idSpec().decoder();
0140   // determine `cellMask` based on `sensorIDfields`
0141   uint64_t cellMask = 0;
0142   for (const auto& idField : sensorIDfields)
0143     cellMask |= readoutCoder[idField].mask();
0144   description.add(Constant("PFRICH_cell_mask", std::to_string(cellMask)));
0145   // create a unique sensor ID from a sensor's PlacedVolume::volIDs
0146   auto encodeSensorID = [&readoutCoder](auto ids) {
0147     uint64_t enc = 0;
0148     for (const auto& [idField, idValue] : ids)
0149       enc |= uint64_t(idValue) << readoutCoder[idField].offset();
0150     return enc;
0151   };
0152 
0153   auto mirrorElem = detElem.child(_Unicode(mirror));
0154   auto mirrorMat  = description.material(mirrorElem.attr<std::string>(_Unicode(material)));
0155   auto mirrorVis  = description.visAttributes(mirrorElem.attr<std::string>(_Unicode(vis)));
0156 
0157   Cone mirror_cone(vesselLength / 2.0, vesselRmax1 - 7, vesselRmax1 - 7 + 0.3, vesselRmax1 - 13,
0158                    vesselRmax1 - 13 + 0.3);
0159 
0160   /*--------------------------------------------------*/
0161   // Vessel
0162   auto vesselMat = description.material(detElem.attr<std::string>(_Unicode(material)));
0163   auto vesselGas = description.material(detElem.attr<std::string>(_Unicode(gas)));
0164 
0165   /*--------------------------------------------------*/
0166   // Flange
0167   float _FLANGE_EPIPE_DIAMETER_ = description.constant<double>("FLANGE_EPIPE_DIAMETER");
0168   float _FLANGE_HPIPE_DIAMETER_ = description.constant<double>("FLANGE_HPIPE_DIAMETER");
0169   float _FLANGE_HPIPE_OFFSET_   = description.constant<double>("FLANGE_HPIPE_OFFSET");
0170   double clearance              = description.constant<double>("CLEARANCE");
0171 
0172   // Mirrors
0173   float _CONICAL_MIRROR_INNER_RADIUS_ = description.constant<double>("CONICAL_MIRROR_INNER_RADIUS");
0174   float _CONICAL_MIRROR_OUTER_RADIUS_ = description.constant<double>("CONICAL_MIRROR_OUTER_RADIUS");
0175   float _INNER_MIRROR_THICKNESS_      = description.constant<double>("INNER_MIRROR_THICKNESS");
0176   float _OUTER_MIRROR_THICKNESS_      = description.constant<double>("OUTER_MIRROR_THICKNESS");
0177 
0178   // HRPPD
0179   float _FIDUCIAL_VOLUME_LENGTH_   = description.constant<double>("FIDUCIAL_VOLUME_LENGTH");
0180   float _SENSOR_AREA_LENGTH_       = description.constant<double>("SENSOR_AREA_LENGTH");
0181   float _HRPPD_CENTRAL_ROW_OFFSET_ = description.constant<double>("HRPPD_CENTRAL_ROW_OFFSET");
0182   float _HRPPD_WINDOW_THICKNESS_   = description.constant<double>("HRPPD_WINDOW_THICKNESS");
0183   float _HRPPD_CONTAINER_VOLUME_HEIGHT_ =
0184       description.constant<double>("HRPPD_CONTAINER_VOLUME_HEIGHT");
0185   float _HRPPD_INSTALLATION_GAP_ = description.constant<double>("HRPPD_INSTALLATION_GAP");
0186 
0187   float _HRPPD_SUPPORT_GRID_BAR_HEIGHT_ =
0188       description.constant<double>("HRPPD_SUPPORT_GRID_BAR_HEIGHT");
0189 
0190   float _HRPPD_TILE_SIZE_        = description.constant<double>("HRPPD_TILE_SIZE");
0191   float _HRPPD_OPEN_AREA_SIZE_   = description.constant<double>("HRPPD_OPEN_AREA_SIZE");
0192   float _HRPPD_ACTIVE_AREA_SIZE_ = description.constant<double>("HRPPD_ACTIVE_AREA_SIZE");
0193   float _HRPPD_CERAMIC_BODY_THICKNESS_ =
0194       description.constant<double>("HRPPD_CERAMIC_BODY_THICKNESS");
0195   float _HRPPD_BASEPLATE_THICKNESS_ = description.constant<double>("HRPPD_BASEPLATE_THICKNESS");
0196   float _HRPPD_PLATING_LAYER_THICKNESS_ =
0197       description.constant<double>("HRPPD_PLATING_LAYER_THICKNESS");
0198   float _EFFECTIVE_MCP_THICKNESS_ = description.constant<double>("EFFECTIVE_MCP_THICKNESS");
0199 
0200   float _READOUT_PCB_THICKNESS_ = description.constant<double>("READOUT_PCB_THICKNESS");
0201   float _READOUT_PCB_SIZE_      = description.constant<double>("READOUT_PCB_SIZE");
0202 
0203   float _ASIC_SIZE_XY_   = description.constant<double>("ASIC_SIZE_XY");
0204   float _ASIC_THICKNESS_ = description.constant<double>("ASIC_THICKNESS");
0205 
0206   // Aerogel
0207   float _AEROGEL_INNER_WALL_THICKNESS_ =
0208       description.constant<double>("AEROGEL_INNER_WALL_THICKNESS");
0209   float _VESSEL_INNER_WALL_THICKNESS_ = description.constant<double>("VESSEL_INNER_WALL_THICKNESS");
0210   float _VESSEL_OUTER_WALL_THICKNESS_ = description.constant<double>("VESSEL_OUTER_WALL_THICKNESS");
0211   float _VESSEL_OUTER_RADIUS_         = description.constant<double>("VESSEL_OUTER_RADIUS");
0212   double _VESSEL_FRONT_SIDE_THICKNESS_ =
0213       description.constant<double>("VESSEL_FRONT_SIDE_THICKNESS");
0214   float _FLANGE_CLEARANCE_         = description.constant<double>("FLANGE_CLEARANCE");
0215   float _BUILDING_BLOCK_CLEARANCE_ = description.constant<double>("BUILDING_BLOCK_CLEARANCE");
0216   //float _AEROGEL_BAND_COUNT_ = aerogel_band_count;
0217   float _AEROGEL_SEPARATOR_WALL_THICKNESS_ =
0218       description.constant<double>("AEROGEL_SEPARATOR_WALL_THICKNESS");
0219   float _AEROGEL_OUTER_WALL_THICKNESS_ =
0220       description.constant<double>("AEROGEL_OUTER_WALL_THICKNESS");
0221 
0222   double m_gas_volume_length =
0223       _FIDUCIAL_VOLUME_LENGTH_ - _VESSEL_FRONT_SIDE_THICKNESS_ - _SENSOR_AREA_LENGTH_;
0224   double m_gas_volume_radius = _VESSEL_OUTER_RADIUS_ - _VESSEL_OUTER_WALL_THICKNESS_;
0225 
0226   /// Inner mirror cone
0227   // A wedge bridging two cylinders;
0228 
0229   Tube eflange(0.0, _FLANGE_EPIPE_DIAMETER_ / 2 + clearance, 25);
0230   Tube hflange(0.0, _FLANGE_HPIPE_DIAMETER_ / 2 + clearance, 25);
0231 
0232   double r0 = _FLANGE_EPIPE_DIAMETER_ / 2 + clearance;
0233   double r1 = _FLANGE_HPIPE_DIAMETER_ / 2 + clearance;
0234   double L  = _FLANGE_HPIPE_OFFSET_;
0235   double a  = r0 * L / (r0 - r1);
0236   double b  = r0 * r0 / a;
0237   double c  = r1 * (a - b) / r0;
0238 
0239   // GEANT variables to define G4Trap;
0240   double pDz = 25, pTheta = 0.0, pPhi = 0.0, pDy1 = (a - b - c) / 2, pDy2 = pDy1;
0241   double pDx1 = sqrt(r0 * r0 - b * b), pDx2 = pDx1 * r1 / r0, pDx3 = pDx1, pDx4 = pDx2, pAlp1 = 0.0,
0242          pAlp2 = 0.0;
0243 
0244   Trap wedge(pDz, pTheta, pPhi, pDy1, pDx1, pDx2, pAlp1, pDy2, pDx3, pDx4, pAlp2);
0245 
0246   UnionSolid flange_shape(eflange, hflange, Position(-_FLANGE_HPIPE_OFFSET_, 0.0, 0.0));
0247   Rotation3D rZ(RotationZYX(M_PI / 2.0, 0.0, 0.0));
0248   Transform3D transform_flange(rZ, Position(-b - pDy1, 0.0, 0.0));
0249   UnionSolid flange_final_shape(flange_shape, wedge, transform_flange);
0250 
0251   Volume flangeVol(detName + "_flange", flange_final_shape, mirrorMat);
0252   flangeVol.setVisAttributes(mirrorVis);
0253 
0254   SubtractionSolid pfRICH_volume_shape(pfRICH_air_volume, flange_final_shape);
0255 
0256   Volume pfRICH_volume(detName + "_Vol", pfRICH_volume_shape,
0257                        vesselGas); // dimension of the pfRICH world in cm
0258 
0259   pv = mother.placeVolume(pfRICH_volume, transform);
0260 
0261   if (id != 0) {
0262     pv.addPhysVolID("system", id);
0263   }
0264   sdet.setPlacement(pv);
0265 
0266   /// tank solids
0267 
0268   double boreDelta = vesselRmin1 - vesselRmin0;
0269   Cone vesselTank(vesselLength / 2.0, vesselRmin1, vesselRmax1, vesselRmin0, vesselRmax0);
0270   Cone gasvolTank(vesselLength / 2.0 - windowThickness, vesselRmin1 + wallThickness,
0271                   vesselRmax1 - wallThickness, vesselRmin0 + wallThickness,
0272                   vesselRmax0 - wallThickness);
0273 
0274   Cone vesselWall(vesselLength / 2.0, vesselRmax1 - 0.1, vesselRmax1, vesselRmax0 - 0.1,
0275                   vesselRmax0);
0276 
0277   Box gasvolBox(1000, 1000, 1000);
0278 
0279   Solid gasvolSolid;
0280   gasvolSolid = gasvolTank;
0281 
0282   Solid vesselSolid;
0283   vesselSolid = vesselTank;
0284 
0285   Solid mirrorSolid;
0286   mirrorSolid = mirror_cone;
0287 
0288   Solid wallSolid;
0289   wallSolid = vesselWall;
0290 
0291   Volume vesselVol(detName + "_vesel_vol", wallSolid, vesselMat);
0292   vesselVol.setVisAttributes(vesselVis);
0293 
0294   PlacedVolume vesselPV = pfRICH_volume.placeVolume(vesselVol, Position(0, 0, 0));
0295   DetElement vesselDE(sdet, "vessel_de", 0);
0296   vesselDE.setPlacement(vesselPV);
0297 
0298   // BUILD RADIATOR //////////////////////////////////////
0299 
0300   // solid and volume: create aerogel and filter
0301   Cone aerogelSolid(aerogelThickness / 2,
0302                     radiatorRmin + boreDelta * aerogelThickness / vesselLength, /* at backplane */
0303                     radiatorRmax, radiatorRmin,                                 /* at frontplane */
0304                     radiatorRmax);
0305   Cone filterSolid(filterThickness / 2,
0306                    radiatorRmin + boreDelta *
0307                                       (aerogelThickness + airgapThickness + filterThickness) /
0308                                       vesselLength, /* at backplane */
0309                    radiatorRmax,
0310                    radiatorRmin + boreDelta * (aerogelThickness + airgapThickness) /
0311                                       vesselLength, /* at frontplane */
0312                    radiatorRmax);
0313   Volume aerogelVol(detName + "_aerogel", aerogelSolid, aerogelMat);
0314   Volume filterVol(detName + "_filter", filterSolid, filterMat);
0315 
0316   // radiator material names
0317   description.add(Constant("PFRICH_aerogel_material", aerogelMat.ptr()->GetName(), "string"));
0318   description.add(Constant("PFRICH_filter_material", filterMat.ptr()->GetName(), "string"));
0319   description.add(Constant("PFRICH_gasvol_material", gasvolMat.ptr()->GetName(), "string"));
0320 
0321   Box sensorSolid(sensorSide / 2., sensorSide / 2., sensorThickness / 2.);
0322   Volume sensorVol(detName + "_sensor", sensorSolid, sensorMat);
0323   sensorVol.setVisAttributes(sensorVis);
0324 
0325   // -- Mirrors ---------------------------------------------------------------------------------
0326   // Some "standard" value applied to all mirrors;
0327   // At the downstream (sensor plane) location; upstream radii are calculated automatically;
0328 
0329   Volume mirrorVol(detName, mirrorSolid, mirrorMat);
0330   mirrorVol.setVisAttributes(mirrorVis);
0331 
0332   double xysize = _HRPPD_TILE_SIZE_, wndthick = _HRPPD_WINDOW_THICKNESS_;
0333 
0334   // HRPPD assembly container volume;
0335   double hrppd_container_volume_thickness = _HRPPD_CONTAINER_VOLUME_HEIGHT_;
0336 
0337   double _ACRYLIC_THICKNESS_ = 0.3;
0338 
0339   /*--------------------------------------------------*/
0340   // HRPPD material definition:
0341 
0342   Box hrppd_Solid(xysize / 2, xysize / 2, hrppd_container_volume_thickness / 2);
0343 
0344   Volume hrppdVol_air(detName + "_air_hrppd", hrppd_Solid, air);
0345 
0346   hrppdVol_air.setSensitiveDetector(sens);
0347   hrppdVol_air.setVisAttributes(gasvolVis);
0348 
0349   // Quartz Window
0350   Box wnd_Solid(xysize / 2, xysize / 2, wndthick / 2);
0351 
0352   Volume wndVol(detName + "_wnd", wnd_Solid, HRPPD_WindowMat);
0353   wndVol.setVisAttributes(gasvolVis);
0354 
0355   double accu = -hrppd_container_volume_thickness / 2;
0356 
0357   PlacedVolume wndPV = hrppdVol_air.placeVolume(wndVol, Position(0, 0, accu + wndthick / 2));
0358 
0359   DetElement wndDE(sdet, "wnd_de", 0);
0360   wndDE.setPlacement(wndPV);
0361 
0362   //  double pitch = xysize + _HRPPD_INSTALLATION_GAP_;
0363   double xyactive = _HRPPD_ACTIVE_AREA_SIZE_;
0364   double xyopen   = _HRPPD_OPEN_AREA_SIZE_;
0365   double certhick = _HRPPD_CERAMIC_BODY_THICKNESS_; //, zcer = azOffset + wndthick + certhick/2;
0366 
0367   accu += wndthick;
0368 
0369   // Ceramic body
0370   Box cerbox(xysize / 2, xysize / 2, certhick / 2);
0371   Box cut_box(xyopen / 2, xyopen / 2, certhick / 2);
0372 
0373   SubtractionSolid ceramic(cerbox, cut_box, Position(0, 0, -_HRPPD_BASEPLATE_THICKNESS_));
0374 
0375   Volume ceramicVol(detName + "_ceramic", ceramic, HRPPD_MPDMat);
0376   ceramicVol.setVisAttributes(gasvolVis);
0377 
0378   PlacedVolume ceramicPV =
0379       hrppdVol_air.placeVolume(ceramicVol, Position(0.0, 0.0, accu + certhick / 2));
0380   DetElement ceramicDE(sdet, "ceramic_de", 0);
0381   ceramicDE.setPlacement(ceramicPV);
0382 
0383   // Plating body
0384 
0385   Box plating_solid(xyopen / 2, xyopen / 2, _HRPPD_PLATING_LAYER_THICKNESS_ / 2);
0386   Volume platingVol(detName + "_plating", plating_solid, HRPPD_MPDMat);
0387 
0388   platingVol.setVisAttributes(gasvolVis);
0389   PlacedVolume platingPV =
0390       hrppdVol_air.placeVolume(platingVol, Position(0.0, 0.0, accu + certhick / 2));
0391   DetElement platingDE(sdet, "plating_de", 0);
0392   platingDE.setPlacement(platingPV);
0393 
0394   // MCP body
0395 
0396   Box mcp_solid(xyopen / 2, xyopen / 2, _EFFECTIVE_MCP_THICKNESS_ / 2);
0397   Volume mcpVol(detName + "_mcp", mcp_solid, HRPPD_MPDMat);
0398 
0399   mcpVol.setVisAttributes(gasvolVis);
0400   PlacedVolume mcpPV = hrppdVol_air.placeVolume(
0401       mcpVol, Position(0.0, 0.0,
0402                        accu + certhick / 2 + _HRPPD_PLATING_LAYER_THICKNESS_ / 2 +
0403                            _EFFECTIVE_MCP_THICKNESS_ / 2));
0404   DetElement mcpDE(sdet, "mcp_de", 0);
0405   mcpDE.setPlacement(mcpPV);
0406 
0407   double pdthick = 0.001;
0408 
0409   Box pdbox_solid(xyactive / 2, xyactive / 2, pdthick / 2);
0410   Volume pdboxVol(detName + "_pd", pdbox_solid, HRPPD_MPDMat);
0411 
0412   pdboxVol.setVisAttributes(gasvolVis);
0413   PlacedVolume pdboxPV =
0414       hrppdVol_air.placeVolume(pdboxVol, Position(0.0, 0.0, accu + pdthick + pdthick / 2));
0415 
0416   DetElement pdboxDE(sdet, "pdbox_de", 0);
0417   pdboxDE.setPlacement(pdboxPV);
0418 
0419   Box qdbox_solid(xyactive / 2, xyactive / 2, pdthick / 2);
0420   Volume qdboxVol(detName + "_qd", qdbox_solid, HRPPD_MPDMat);
0421 
0422   qdboxVol.setVisAttributes(gasvolVis);
0423   PlacedVolume qdboxPV = hrppdVol_air.placeVolume(qdboxVol, Position(0.0, 0.0, accu + pdthick / 2));
0424 
0425   DetElement qdboxDE(sdet, "qdbox_de", 0);
0426   qdboxDE.setPlacement(qdboxPV);
0427 
0428   accu += certhick + 1 * mm;
0429 
0430   /// PCB Board
0431 
0432   Box pcb_solid(_READOUT_PCB_SIZE_ / 2, _READOUT_PCB_SIZE_ / 2, _READOUT_PCB_THICKNESS_ / 2);
0433   Volume pcbVol(detName + "_pcb", pcb_solid, HRPPD_PCBMat);
0434 
0435   pcbVol.setVisAttributes(gasvolVis);
0436   PlacedVolume pcbPV =
0437       hrppdVol_air.placeVolume(pcbVol, Position(0.0, 0.0, accu + _READOUT_PCB_THICKNESS_ / 2));
0438 
0439   DetElement pcbDE(sdet, "pcb_de", 0);
0440   pcbDE.setPlacement(pcbPV);
0441 
0442   accu += _READOUT_PCB_THICKNESS_ + 0.001;
0443 
0444   // ASIC Board
0445 
0446   Box asic_solid(_ASIC_SIZE_XY_ / 2, _ASIC_SIZE_XY_ / 2, _ASIC_THICKNESS_ / 2);
0447   Volume asicVol(detName + "_asic", asic_solid, HRPPD_ASICMat);
0448   asicVol.setVisAttributes(mirrorVis);
0449 
0450   double asic_pitch = _READOUT_PCB_SIZE_ / 2;
0451 
0452   imod = 0;
0453 
0454   for (unsigned ix = 0; ix < 2; ix++) {
0455     double xOffset = asic_pitch * (ix - (2 - 1) / 2.);
0456 
0457     for (unsigned iy = 0; iy < 2; iy++) {
0458       double yOffset = asic_pitch * (iy - (2 - 1) / 2.);
0459 
0460       auto asicPV = hrppdVol_air.placeVolume(
0461           asicVol, Position(xOffset, yOffset, accu + _ASIC_THICKNESS_ / 2));
0462 
0463       DetElement asicDE(sdet, "asic_de_" + std::to_string(imod), 0);
0464       asicDE.setPlacement(asicPV);
0465 
0466       imod++;
0467 
0468     } //for iy
0469   } //for ix
0470 
0471   accu += _ASIC_THICKNESS_ + 0.01 * mm;
0472 
0473   // Loading the coordinates
0474 
0475   unsigned const hdim              = 9;
0476   const unsigned flags[hdim][hdim] = {
0477       // NB: WYSIWIG fashion; well, it is top/ bottom and left/right symmetric;
0478       {0, 0, 1, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 1},
0479       {1, 1, 1, 1, 2, 1, 1, 1, 1}, {3, 3, 3, 4, 0, 2, 1, 1, 1}, {1, 1, 1, 1, 2, 1, 1, 1, 1},
0480       {1, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 0, 0}};
0481 
0482   std::vector<std::pair<TVector2, bool>> coord;
0483 
0484   for (unsigned ix = 0; ix < hdim; ix++) {
0485     double xOffset = (_HRPPD_TILE_SIZE_ + _HRPPD_INSTALLATION_GAP_) * (ix - (hdim - 1) / 2.);
0486 
0487     for (unsigned iy = 0; iy < hdim; iy++) {
0488       double yOffset = (_HRPPD_TILE_SIZE_ + _HRPPD_INSTALLATION_GAP_) * (iy - (hdim - 1) / 2.);
0489       unsigned flag  = flags[hdim - iy - 1][ix];
0490 
0491       if (!flag)
0492         continue;
0493 
0494       double qxOffset = xOffset + (flag >= 3 ? -_HRPPD_CENTRAL_ROW_OFFSET_ : 0.0);
0495       coord.push_back(std::make_pair(TVector2(qxOffset, yOffset), flag % 2));
0496     } //for iy
0497   } //for ix
0498 
0499   /// Set sensors into the coordinates
0500   ///
0501   for (auto xyptr : coord) {
0502     auto& xy = xyptr.first;
0503 
0504     double sx = xy.X();
0505     double sy = xy.Y();
0506 
0507     // placement (note: transformations are in reverse order)
0508     auto sensorPlacement =
0509         Transform3D(Translation3D(sensorPlanePos.x(), sensorPlanePos.y(),
0510                                   sensorPlanePos.z() + 34) * // move to reference position
0511                     Translation3D(sx, sy, 0.)                // move to grid position
0512         );
0513 
0514     auto sensorPV = pfRICH_volume.placeVolume(hrppdVol_air, sensorPlacement);
0515 
0516     // properties
0517     sensorPV.addPhysVolID("module", imod); // NOTE: must be consistent with `sensorIDfields`
0518     auto imodEnc = encodeSensorID(sensorPV.volIDs());
0519     DetElement sensorDE(sdet, "sensor_de_" + std::to_string(imod), imodEnc);
0520     sensorDE.setPlacement(sensorPV);
0521     if (!debug_optics) {
0522       SkinSurface sensorSkin(description, sensorDE,
0523                              "sensor_optical_surface_" + std::to_string(imod), sensorSurf,
0524                              sensorVol);
0525       sensorSkin.isValid();
0526     };
0527 
0528     // increment sensor module number
0529     imod++;
0530   }
0531 
0532   /// Aerogel
0533 
0534   const int _AEROGEL_BAND_COUNT_ = 3;
0535   float m_r0min = _FLANGE_EPIPE_DIAMETER_ / 2 + _FLANGE_CLEARANCE_ + _VESSEL_INNER_WALL_THICKNESS_ +
0536                   _BUILDING_BLOCK_CLEARANCE_;
0537   float m_r0max = m_gas_volume_radius - _BUILDING_BLOCK_CLEARANCE_;
0538 
0539   const unsigned adim[_AEROGEL_BAND_COUNT_] = {9, 14, 20};
0540   double rheight =
0541       (m_r0max - m_r0min - (_AEROGEL_BAND_COUNT_ - 1) * _AEROGEL_SEPARATOR_WALL_THICKNESS_ -
0542        _AEROGEL_INNER_WALL_THICKNESS_ - _AEROGEL_OUTER_WALL_THICKNESS_) /
0543       _AEROGEL_BAND_COUNT_;
0544 
0545   double agthick = 2.5; // cm
0546 
0547   double m_gzOffset = m_gas_volume_length / 2 + _BUILDING_BLOCK_CLEARANCE_ + agthick / 2;
0548 
0549   string aerogel_name = "a1040";
0550 
0551   int kkcounter = 0;
0552 
0553   for (unsigned ir = 0; ir < _AEROGEL_BAND_COUNT_; ir++) {
0554     int counter       = ir ? -1 : 0;
0555     double apitch     = 360 * degree / adim[ir];
0556     double aerogel_r0 = m_r0min + _AEROGEL_INNER_WALL_THICKNESS_ +
0557                         ir * (_AEROGEL_SEPARATOR_WALL_THICKNESS_ + rheight);
0558     double aerogel_r1 = aerogel_r0 + rheight;
0559     double rm         = (aerogel_r0 + aerogel_r1) / 2;
0560 
0561     // Calculate angular space occupied by the spacers and by the tiles; no gas gaps for now;
0562     // assume that a wegde shape is good enough (GEANT visualization does not like boolean objects),
0563     // rather than creating constant thicjkess azimuthal spacers; just assume that spacer thickness is
0564     // _AEROGEL_FRAME_WALL_THICKNESS_ at r=rm;
0565     double l0   = 2 * M_PI * rm / adim[ir];
0566     double l1   = _AEROGEL_SEPARATOR_WALL_THICKNESS_;
0567     double lsum = l0 + l1;
0568 
0569     // FIXME: names overlap in several places!;
0570     double wd0      = (l0 / lsum) * (360 * degree / adim[ir]);
0571     double wd1      = (l1 / lsum) * (360 * degree / adim[ir]);
0572     TString ag_name = "Tmp", sp_name = "Tmp";
0573 
0574     if (ir)
0575       ag_name.Form("%s-%d-00", aerogel_name.c_str(), ir);
0576     if (ir)
0577       sp_name.Form("A-Spacer--%d-00", ir);
0578 
0579     Tube agtube(aerogel_r0, aerogel_r1, agthick / 2, 0 * degree, wd0);
0580     Tube sptube(aerogel_r0, aerogel_r1, agthick / 2, wd0, wd0 + wd1);
0581 
0582     for (unsigned ia = 0; ia < adim[ir]; ia++) {
0583 
0584       Rotation3D r_aerogel_Z(RotationZYX(ia * apitch, 0.0, 0.0));
0585       Rotation3D r_aerogel_Zinv(RotationZYX(-1. * ia * apitch, 0.0, 0.0));
0586 
0587       if (ir) {
0588         ag_name.Form("%s-%d-%02d", "aerogel", ir, ia);
0589 
0590         Volume agtubeVol(ag_name.Data(), agtube, gasvolMat);
0591         auto aerogelTilePlacement = Transform3D(r_aerogel_Z, Position(0.0, 0.0, -m_gzOffset));
0592         auto aerogelTilePV        = pfRICH_volume.placeVolume(agtubeVol, aerogelTilePlacement);
0593         DetElement aerogelDE(sdet, "aerogel_de_" + std::to_string(kkcounter), 0);
0594         aerogelDE.setPlacement(aerogelTilePV);
0595 
0596         Volume sptubeVol(detName + "_sptube", sptube, mirrorMat);
0597         auto sptubePlacement = Transform3D(r_aerogel_Z, Position(0.0, 0.0, -m_gzOffset));
0598         auto sptubePV        = pfRICH_volume.placeVolume(sptubeVol, sptubePlacement);
0599         DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(kkcounter), 0);
0600         sptubeDE.setPlacement(sptubePV);
0601 
0602       } else {
0603 
0604         ag_name.Form("%s-%d-%02d", "aerogel_inner", ir, ia);
0605 
0606         Tube agtube_inner(aerogel_r0, aerogel_r1, agthick / 2, 0 * degree + ia * apitch,
0607                           wd0 + ia * apitch);
0608         SubtractionSolid agsub(agtube_inner, flange_final_shape);
0609         Volume agsubtubeVol(ag_name.Data(), agsub, gasvolMat);
0610         auto aerogelTilePlacement =
0611             Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0612         auto agsubTilePV = pfRICH_volume.placeVolume(agsubtubeVol, aerogelTilePlacement);
0613 
0614         DetElement aerogelDE(sdet, "agsubTile_de_" + std::to_string(counter), 0);
0615         aerogelDE.setPlacement(agsubTilePV);
0616 
0617         sp_name.Form("%s-%d-%02d", "sp_inner", ir, ia);
0618         Tube sptube_inner(aerogel_r0, aerogel_r1, agthick / 2, wd0 + ia * apitch,
0619                           wd0 + wd1 + ia * apitch);
0620 
0621         SubtractionSolid spsub(sptube_inner, flange_final_shape);
0622         Volume spsubtubeVol(sp_name.Data(), spsub, mirrorMat);
0623         auto spTilePlacement =
0624             Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0625         auto spsubTilePV = pfRICH_volume.placeVolume(spsubtubeVol, spTilePlacement);
0626 
0627         DetElement sptubeTileDE(sdet, "sptubeTile_de_" + std::to_string(counter), 0);
0628         sptubeTileDE.setPlacement(spsubTilePV);
0629 
0630         sp_name.Form("A-Spacer--%d-%02d", ir, ia);
0631 
0632       } //if
0633 
0634       counter++;
0635       kkcounter++;
0636 
0637     } //for ia
0638   } // for ir
0639 
0640   // Placing radial spacer
0641 
0642   double sp_accu   = m_r0min;
0643   int tube_counter = 0;
0644 
0645   for (unsigned ir = 0; ir < _AEROGEL_BAND_COUNT_ + 1; ir++) {
0646     double thickness = ir ? (ir == _AEROGEL_BAND_COUNT_ ? _AEROGEL_OUTER_WALL_THICKNESS_
0647                                                         : _AEROGEL_SEPARATOR_WALL_THICKNESS_)
0648                           : _AEROGEL_INNER_WALL_THICKNESS_;
0649     double sp_r0     = sp_accu;
0650     double sp_r1     = sp_r0 + thickness;
0651 
0652     TString sp_name = "Tmp";
0653     if (ir)
0654       sp_name.Form("R-Spacer--%d-00", ir);
0655 
0656     Tube sptube(sp_r0, sp_r1, agthick / 2, 0 * degree, 360 * degree);
0657     Volume sptubeVol(detName + "_radial_sptube", sptube, sensorMat);
0658     auto sptubePlacement = Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0659 
0660     if (ir) {
0661 
0662       auto sptubePV = pfRICH_volume.placeVolume(sptubeVol, sptubePlacement);
0663 
0664       DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(tube_counter), 0);
0665       sptubeDE.setPlacement(sptubePV);
0666 
0667     }
0668 
0669     else {
0670 
0671       SubtractionSolid spsub(sptube, flange_final_shape);
0672       Volume agsubtubeVol(detName + "_radial_sptube_inner", spsub, gasvolMat);
0673 
0674       auto sptubePV = pfRICH_volume.placeVolume(agsubtubeVol, sptubePlacement);
0675 
0676       DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(tube_counter), 0);
0677       sptubeDE.setPlacement(sptubePV);
0678 
0679     } //if
0680 
0681     sp_accu += thickness + rheight;
0682 
0683     tube_counter++;
0684 
0685   } //for ir
0686 
0687   /// Mirror construction
0688 
0689   double mlen = m_gas_volume_length - _BUILDING_BLOCK_CLEARANCE_;
0690 
0691   mlen -= _BUILDING_BLOCK_CLEARANCE_ + _HRPPD_SUPPORT_GRID_BAR_HEIGHT_;
0692 
0693   double mirror_r0[2] = {m_r0min, m_r0max};
0694   double mirror_r1[2] = {_CONICAL_MIRROR_INNER_RADIUS_, _CONICAL_MIRROR_OUTER_RADIUS_};
0695 
0696   for (unsigned im = 0; im < 2; im++) {
0697 
0698     double mirror_thickness = im ? _OUTER_MIRROR_THICKNESS_ : _INNER_MIRROR_THICKNESS_;
0699 
0700     if (im) {
0701 
0702       Cone mirror_outer_cone_shape(mlen / 2.0, mirror_r0[im], mirror_r0[im] + mirror_thickness,
0703                                    mirror_r1[im], mirror_r1[im] + mirror_thickness);
0704 
0705       Volume outer_mirrorVol(detName + "_outer_mirror", mirror_outer_cone_shape, mirrorMat);
0706 
0707       PlacedVolume mirror_outerPV = pfRICH_volume.placeVolume(outer_mirrorVol, Position(0, 0, 0));
0708 
0709       DetElement mirror_outerDE(sdet, "_outer_mirror_de", 0);
0710       mirror_outerDE.setPlacement(mirror_outerPV);
0711 
0712     } else {
0713 
0714       Cone mirror_inner_cone_shape(mlen / 2., mirror_r0[im], mirror_r0[im] + mirror_thickness,
0715                                    mirror_r1[im], mirror_r1[im] + mirror_thickness);
0716 
0717       SubtractionSolid mirror_inner_sub(mirror_inner_cone_shape, flange_final_shape);
0718 
0719       Volume inner_mirrorVol(detName + "_inner_mirror", mirror_inner_sub, mirrorMat);
0720 
0721       PlacedVolume mirror_innerPV = pfRICH_volume.placeVolume(inner_mirrorVol, Position(0, 0, 0));
0722 
0723       DetElement mirror_innerDE(sdet, "_inner_mirror_de", 0);
0724       mirror_innerDE.setPlacement(mirror_innerPV);
0725     }
0726 
0727   } //for im
0728 
0729   // Acrylic filter
0730 
0731   double acthick = _ACRYLIC_THICKNESS_;
0732   // m_gzOffset += acthick/2;
0733 
0734   Tube ac_tube(m_r0min + 3, m_r0max - 1, acthick / 2, 0 * degree, 360 * degree);
0735   SubtractionSolid ac_shape(ac_tube, flange_final_shape);
0736 
0737   Volume acVol(detName + "_ac", ac_shape, gasvolMat);
0738 
0739   PlacedVolume ac_PV = pfRICH_volume.placeVolume(acVol, Position(0, 0, -21.3));
0740 
0741   DetElement acDE(sdet, "ac_de", 0);
0742   acDE.setPlacement(ac_PV);
0743 
0744   return sdet;
0745 }
0746 
0747 // clang-format off
0748 DECLARE_DETELEMENT(epic_PFRICH, createDetector)