Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:15:59

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   DetElement hrppdDE(sdet, "hrppd_de", 0);
0349 
0350   // Quartz Window
0351   Box wnd_Solid(xysize / 2, xysize / 2, wndthick / 2);
0352 
0353   Volume wndVol(detName + "_wnd", wnd_Solid, HRPPD_WindowMat);
0354   wndVol.setVisAttributes(gasvolVis);
0355 
0356   double accu = -hrppd_container_volume_thickness / 2;
0357 
0358   PlacedVolume wndPV = hrppdVol_air.placeVolume(wndVol, Position(0, 0, accu + wndthick / 2));
0359 
0360   DetElement wndDE(hrppdDE, "wnd_de", 0);
0361   wndDE.setPlacement(wndPV);
0362 
0363   //  double pitch = xysize + _HRPPD_INSTALLATION_GAP_;
0364   double xyactive = _HRPPD_ACTIVE_AREA_SIZE_;
0365   double xyopen   = _HRPPD_OPEN_AREA_SIZE_;
0366   double certhick = _HRPPD_CERAMIC_BODY_THICKNESS_; //, zcer = azOffset + wndthick + certhick/2;
0367 
0368   accu += wndthick;
0369 
0370   // Ceramic body
0371   Box cerbox(xysize / 2, xysize / 2, certhick / 2);
0372   Box cut_box(xyopen / 2, xyopen / 2, certhick / 2);
0373 
0374   SubtractionSolid ceramic(cerbox, cut_box, Position(0, 0, -_HRPPD_BASEPLATE_THICKNESS_));
0375 
0376   Volume ceramicVol(detName + "_ceramic", ceramic, HRPPD_MPDMat);
0377   ceramicVol.setVisAttributes(gasvolVis);
0378 
0379   PlacedVolume ceramicPV =
0380       hrppdVol_air.placeVolume(ceramicVol, Position(0.0, 0.0, accu + certhick / 2));
0381   DetElement ceramicDE(sdet, "ceramic_de", 0);
0382   ceramicDE.setPlacement(ceramicPV);
0383 
0384   // Plating body
0385 
0386   Box plating_solid(xyopen / 2, xyopen / 2, _HRPPD_PLATING_LAYER_THICKNESS_ / 2);
0387   Volume platingVol(detName + "_plating", plating_solid, HRPPD_MPDMat);
0388 
0389   platingVol.setVisAttributes(gasvolVis);
0390   PlacedVolume platingPV =
0391       hrppdVol_air.placeVolume(platingVol, Position(0.0, 0.0, accu + certhick / 2));
0392   DetElement platingDE(sdet, "plating_de", 0);
0393   platingDE.setPlacement(platingPV);
0394 
0395   // MCP body
0396 
0397   Box mcp_solid(xyopen / 2, xyopen / 2, _EFFECTIVE_MCP_THICKNESS_ / 2);
0398   Volume mcpVol(detName + "_mcp", mcp_solid, HRPPD_MPDMat);
0399 
0400   mcpVol.setVisAttributes(gasvolVis);
0401   PlacedVolume mcpPV = hrppdVol_air.placeVolume(
0402       mcpVol, Position(0.0, 0.0,
0403                        accu + certhick / 2 + _HRPPD_PLATING_LAYER_THICKNESS_ / 2 +
0404                            _EFFECTIVE_MCP_THICKNESS_ / 2));
0405   DetElement mcpDE(sdet, "mcp_de", 0);
0406   mcpDE.setPlacement(mcpPV);
0407 
0408   double pdthick = 0.001;
0409 
0410   Box pdbox_solid(xyactive / 2, xyactive / 2, pdthick / 2);
0411   Volume pdboxVol(detName + "_pd", pdbox_solid, HRPPD_MPDMat);
0412 
0413   pdboxVol.setVisAttributes(gasvolVis);
0414   PlacedVolume pdboxPV =
0415       hrppdVol_air.placeVolume(pdboxVol, Position(0.0, 0.0, accu + pdthick + pdthick / 2));
0416 
0417   DetElement pdboxDE(sdet, "pdbox_de", 0);
0418   pdboxDE.setPlacement(pdboxPV);
0419 
0420   Box qdbox_solid(xyactive / 2, xyactive / 2, pdthick / 2);
0421   Volume qdboxVol(detName + "_qd", qdbox_solid, HRPPD_MPDMat);
0422 
0423   qdboxVol.setVisAttributes(gasvolVis);
0424   PlacedVolume qdboxPV = hrppdVol_air.placeVolume(qdboxVol, Position(0.0, 0.0, accu + pdthick / 2));
0425 
0426   DetElement qdboxDE(sdet, "qdbox_de", 0);
0427   pdboxDE.setPlacement(qdboxPV);
0428 
0429   accu += certhick + 1 * mm;
0430 
0431   /// PCB Board
0432 
0433   Box pcb_solid(_READOUT_PCB_SIZE_ / 2, _READOUT_PCB_SIZE_ / 2, _READOUT_PCB_THICKNESS_ / 2);
0434   Volume pcbVol(detName + "_pcb", pcb_solid, HRPPD_PCBMat);
0435 
0436   pcbVol.setVisAttributes(gasvolVis);
0437   PlacedVolume pcbPV =
0438       hrppdVol_air.placeVolume(pcbVol, Position(0.0, 0.0, accu + _READOUT_PCB_THICKNESS_ / 2));
0439 
0440   DetElement pcbDE(sdet, "pcb_de", 0);
0441   pcbDE.setPlacement(pcbPV);
0442 
0443   accu += _READOUT_PCB_THICKNESS_ + 0.001;
0444 
0445   // ASIC Board
0446 
0447   Box asic_solid(_ASIC_SIZE_XY_ / 2, _ASIC_SIZE_XY_ / 2, _ASIC_THICKNESS_ / 2);
0448   Volume asicVol(detName + "_asic", asic_solid, HRPPD_ASICMat);
0449   asicVol.setVisAttributes(mirrorVis);
0450 
0451   double asic_pitch = _READOUT_PCB_SIZE_ / 2;
0452 
0453   imod = 0;
0454 
0455   for (unsigned ix = 0; ix < 2; ix++) {
0456     double xOffset = asic_pitch * (ix - (2 - 1) / 2.);
0457 
0458     for (unsigned iy = 0; iy < 2; iy++) {
0459       double yOffset = asic_pitch * (iy - (2 - 1) / 2.);
0460 
0461       auto asicPV = hrppdVol_air.placeVolume(
0462           asicVol, Position(xOffset, yOffset, accu + _ASIC_THICKNESS_ / 2));
0463 
0464       DetElement asicDE(sdet, "asic_de_" + std::to_string(imod), 0);
0465       asicDE.setPlacement(asicPV);
0466 
0467       imod++;
0468 
0469     } //for iy
0470   } //for ix
0471 
0472   accu += _ASIC_THICKNESS_ + 0.01 * mm;
0473 
0474   // Loading the coordinates
0475 
0476   unsigned const hdim              = 9;
0477   const unsigned flags[hdim][hdim] = {
0478       // NB: WYSIWIG fashion; well, it is top/ bottom and left/right symmetric;
0479       {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},
0480       {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},
0481       {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}};
0482 
0483   std::vector<std::pair<TVector2, bool>> coord;
0484 
0485   for (unsigned ix = 0; ix < hdim; ix++) {
0486     double xOffset = (_HRPPD_TILE_SIZE_ + _HRPPD_INSTALLATION_GAP_) * (ix - (hdim - 1) / 2.);
0487 
0488     for (unsigned iy = 0; iy < hdim; iy++) {
0489       double yOffset = (_HRPPD_TILE_SIZE_ + _HRPPD_INSTALLATION_GAP_) * (iy - (hdim - 1) / 2.);
0490       unsigned flag  = flags[hdim - iy - 1][ix];
0491 
0492       if (!flag)
0493         continue;
0494 
0495       double qxOffset = xOffset + (flag >= 3 ? -_HRPPD_CENTRAL_ROW_OFFSET_ : 0.0);
0496       coord.push_back(std::make_pair(TVector2(qxOffset, yOffset), flag % 2));
0497     } //for iy
0498   } //for ix
0499 
0500   /// Set sensors into the coordinates
0501   ///
0502   for (auto xyptr : coord) {
0503     auto& xy = xyptr.first;
0504 
0505     double sx = xy.X();
0506     double sy = xy.Y();
0507 
0508     // placement (note: transformations are in reverse order)
0509     auto sensorPlacement =
0510         Transform3D(Translation3D(sensorPlanePos.x(), sensorPlanePos.y(),
0511                                   sensorPlanePos.z() + 34) * // move to reference position
0512                     Translation3D(sx, sy, 0.)                // move to grid position
0513         );
0514 
0515     auto sensorPV = pfRICH_volume.placeVolume(hrppdVol_air, sensorPlacement);
0516 
0517     // properties
0518     sensorPV.addPhysVolID("module", imod); // NOTE: must be consistent with `sensorIDfields`
0519     auto imodEnc = encodeSensorID(sensorPV.volIDs());
0520     DetElement sensorDE(sdet, "sensor_de_" + std::to_string(imod), imodEnc);
0521     sensorDE.setPlacement(sensorPV);
0522     if (!debug_optics) {
0523       SkinSurface sensorSkin(description, sensorDE,
0524                              "sensor_optical_surface_" + std::to_string(imod), sensorSurf,
0525                              sensorVol);
0526       sensorSkin.isValid();
0527     };
0528 
0529     // increment sensor module number
0530     imod++;
0531   }
0532 
0533   /// Aerogel
0534 
0535   const int _AEROGEL_BAND_COUNT_ = 3;
0536   float m_r0min = _FLANGE_EPIPE_DIAMETER_ / 2 + _FLANGE_CLEARANCE_ + _VESSEL_INNER_WALL_THICKNESS_ +
0537                   _BUILDING_BLOCK_CLEARANCE_;
0538   float m_r0max = m_gas_volume_radius - _BUILDING_BLOCK_CLEARANCE_;
0539 
0540   const unsigned adim[_AEROGEL_BAND_COUNT_] = {9, 14, 20};
0541   double rheight =
0542       (m_r0max - m_r0min - (_AEROGEL_BAND_COUNT_ - 1) * _AEROGEL_SEPARATOR_WALL_THICKNESS_ -
0543        _AEROGEL_INNER_WALL_THICKNESS_ - _AEROGEL_OUTER_WALL_THICKNESS_) /
0544       _AEROGEL_BAND_COUNT_;
0545 
0546   double agthick = 2.5; // cm
0547 
0548   double m_gzOffset = m_gas_volume_length / 2 + _BUILDING_BLOCK_CLEARANCE_ + agthick / 2;
0549 
0550   string aerogel_name = "a1040";
0551 
0552   int kkcounter = 0;
0553 
0554   for (unsigned ir = 0; ir < _AEROGEL_BAND_COUNT_; ir++) {
0555     int counter       = ir ? -1 : 0;
0556     double apitch     = 360 * degree / adim[ir];
0557     double aerogel_r0 = m_r0min + _AEROGEL_INNER_WALL_THICKNESS_ +
0558                         ir * (_AEROGEL_SEPARATOR_WALL_THICKNESS_ + rheight);
0559     double aerogel_r1 = aerogel_r0 + rheight;
0560     double rm         = (aerogel_r0 + aerogel_r1) / 2;
0561 
0562     // Calculate angular space occupied by the spacers and by the tiles; no gas gaps for now;
0563     // assume that a wegde shape is good enough (GEANT visualization does not like boolean objects),
0564     // rather than creating constant thicjkess azimuthal spacers; just assume that spacer thickness is
0565     // _AEROGEL_FRAME_WALL_THICKNESS_ at r=rm;
0566     double l0   = 2 * M_PI * rm / adim[ir];
0567     double l1   = _AEROGEL_SEPARATOR_WALL_THICKNESS_;
0568     double lsum = l0 + l1;
0569 
0570     // FIXME: names overlap in several places!;
0571     double wd0      = (l0 / lsum) * (360 * degree / adim[ir]);
0572     double wd1      = (l1 / lsum) * (360 * degree / adim[ir]);
0573     TString ag_name = "Tmp", sp_name = "Tmp";
0574 
0575     if (ir)
0576       ag_name.Form("%s-%d-00", aerogel_name.c_str(), ir);
0577     if (ir)
0578       sp_name.Form("A-Spacer--%d-00", ir);
0579 
0580     Tube agtube(aerogel_r0, aerogel_r1, agthick / 2, 0 * degree, wd0);
0581     Tube sptube(aerogel_r0, aerogel_r1, agthick / 2, wd0, wd0 + wd1);
0582 
0583     for (unsigned ia = 0; ia < adim[ir]; ia++) {
0584 
0585       Rotation3D r_aerogel_Z(RotationZYX(ia * apitch, 0.0, 0.0));
0586       Rotation3D r_aerogel_Zinv(RotationZYX(-1. * ia * apitch, 0.0, 0.0));
0587 
0588       if (ir) {
0589         ag_name.Form("%s-%d-%02d", "aerogel", ir, ia);
0590 
0591         Volume agtubeVol(ag_name.Data(), agtube, gasvolMat);
0592         auto aerogelTilePlacement = Transform3D(r_aerogel_Z, Position(0.0, 0.0, -m_gzOffset));
0593         auto aerogelTilePV        = pfRICH_volume.placeVolume(agtubeVol, aerogelTilePlacement);
0594         DetElement aerogelDE(sdet, "aerogel_de_" + std::to_string(kkcounter), 0);
0595         aerogelDE.setPlacement(aerogelTilePV);
0596 
0597         Volume sptubeVol(detName + "_sptube", sptube, mirrorMat);
0598         auto sptubePlacement = Transform3D(r_aerogel_Z, Position(0.0, 0.0, -m_gzOffset));
0599         auto sptubePV        = pfRICH_volume.placeVolume(sptubeVol, sptubePlacement);
0600         DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(kkcounter), 0);
0601         sptubeDE.setPlacement(sptubePV);
0602 
0603       } else {
0604 
0605         ag_name.Form("%s-%d-%02d", "aerogel_inner", ir, ia);
0606 
0607         Tube agtube_inner(aerogel_r0, aerogel_r1, agthick / 2, 0 * degree + ia * apitch,
0608                           wd0 + ia * apitch);
0609         SubtractionSolid agsub(agtube_inner, flange_final_shape);
0610         Volume agsubtubeVol(ag_name.Data(), agsub, gasvolMat);
0611         auto aerogelTilePlacement =
0612             Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0613         auto agsubTilePV = pfRICH_volume.placeVolume(agsubtubeVol, aerogelTilePlacement);
0614 
0615         DetElement aerogelDE(sdet, "agsubTile_de_" + std::to_string(counter), 0);
0616         aerogelDE.setPlacement(agsubTilePV);
0617 
0618         sp_name.Form("%s-%d-%02d", "sp_inner", ir, ia);
0619         Tube sptube_inner(aerogel_r0, aerogel_r1, agthick / 2, wd0 + ia * apitch,
0620                           wd0 + wd1 + ia * apitch);
0621 
0622         SubtractionSolid spsub(sptube_inner, flange_final_shape);
0623         Volume spsubtubeVol(sp_name.Data(), spsub, mirrorMat);
0624         auto spTilePlacement =
0625             Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0626         auto spsubTilePV = pfRICH_volume.placeVolume(spsubtubeVol, spTilePlacement);
0627 
0628         DetElement sptubeTileDE(sdet, "sptubeTile_de_" + std::to_string(counter), 0);
0629         sptubeTileDE.setPlacement(spsubTilePV);
0630 
0631         sp_name.Form("A-Spacer--%d-%02d", ir, ia);
0632 
0633       } //if
0634 
0635       counter++;
0636       kkcounter++;
0637 
0638     } //for ia
0639   } // for ir
0640 
0641   // Placing radial spacer
0642 
0643   double sp_accu   = m_r0min;
0644   int tube_counter = 0;
0645 
0646   for (unsigned ir = 0; ir < _AEROGEL_BAND_COUNT_ + 1; ir++) {
0647     double thickness = ir ? (ir == _AEROGEL_BAND_COUNT_ ? _AEROGEL_OUTER_WALL_THICKNESS_
0648                                                         : _AEROGEL_SEPARATOR_WALL_THICKNESS_)
0649                           : _AEROGEL_INNER_WALL_THICKNESS_;
0650     double sp_r0     = sp_accu;
0651     double sp_r1     = sp_r0 + thickness;
0652 
0653     TString sp_name = "Tmp";
0654     if (ir)
0655       sp_name.Form("R-Spacer--%d-00", ir);
0656 
0657     Tube sptube(sp_r0, sp_r1, agthick / 2, 0 * degree, 360 * degree);
0658     Volume sptubeVol(detName + "_radial_sptube", sptube, sensorMat);
0659     auto sptubePlacement = Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(0.0, 0.0, -m_gzOffset));
0660 
0661     if (ir) {
0662 
0663       auto sptubePV = pfRICH_volume.placeVolume(sptubeVol, sptubePlacement);
0664 
0665       DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(tube_counter), 0);
0666       sptubeDE.setPlacement(sptubePV);
0667 
0668     }
0669 
0670     else {
0671 
0672       SubtractionSolid spsub(sptube, flange_final_shape);
0673       Volume agsubtubeVol(detName + "_radial_sptube_inner", spsub, gasvolMat);
0674 
0675       auto sptubePV = pfRICH_volume.placeVolume(agsubtubeVol, sptubePlacement);
0676 
0677       DetElement sptubeDE(sdet, "sptube_de_" + std::to_string(tube_counter), 0);
0678       sptubeDE.setPlacement(sptubePV);
0679 
0680     } //if
0681 
0682     sp_accu += thickness + rheight;
0683 
0684     tube_counter++;
0685 
0686   } //for ir
0687 
0688   /// Mirror construction
0689 
0690   double mlen = m_gas_volume_length - _BUILDING_BLOCK_CLEARANCE_;
0691 
0692   mlen -= _BUILDING_BLOCK_CLEARANCE_ + _HRPPD_SUPPORT_GRID_BAR_HEIGHT_;
0693 
0694   double mirror_r0[2] = {m_r0min, m_r0max};
0695   double mirror_r1[2] = {_CONICAL_MIRROR_INNER_RADIUS_, _CONICAL_MIRROR_OUTER_RADIUS_};
0696 
0697   for (unsigned im = 0; im < 2; im++) {
0698 
0699     double mirror_thickness = im ? _OUTER_MIRROR_THICKNESS_ : _INNER_MIRROR_THICKNESS_;
0700 
0701     if (im) {
0702 
0703       Cone mirror_outer_cone_shape(mlen / 2.0, mirror_r0[im], mirror_r0[im] + mirror_thickness,
0704                                    mirror_r1[im], mirror_r1[im] + mirror_thickness);
0705 
0706       Volume outer_mirrorVol(detName + "_outer_mirror", mirror_outer_cone_shape, mirrorMat);
0707 
0708       PlacedVolume mirror_outerPV = pfRICH_volume.placeVolume(outer_mirrorVol, Position(0, 0, 0));
0709 
0710       DetElement mirror_outerDE(sdet, "_outer_mirror_de", 0);
0711       mirror_outerDE.setPlacement(mirror_outerPV);
0712 
0713     } else {
0714 
0715       Cone mirror_inner_cone_shape(mlen / 2., mirror_r0[im], mirror_r0[im] + mirror_thickness,
0716                                    mirror_r1[im], mirror_r1[im] + mirror_thickness);
0717 
0718       SubtractionSolid mirror_inner_sub(mirror_inner_cone_shape, flange_final_shape);
0719 
0720       Volume inner_mirrorVol(detName + "_inner_mirror", mirror_inner_sub, mirrorMat);
0721 
0722       PlacedVolume mirror_innerPV = pfRICH_volume.placeVolume(inner_mirrorVol, Position(0, 0, 0));
0723 
0724       DetElement mirror_innerDE(sdet, "_inner_mirror_de", 0);
0725       mirror_innerDE.setPlacement(mirror_innerPV);
0726     }
0727 
0728   } //for im
0729 
0730   // Acrylic filter
0731 
0732   double acthick = _ACRYLIC_THICKNESS_;
0733   // m_gzOffset += acthick/2;
0734 
0735   Tube ac_tube(m_r0min + 3, m_r0max - 1, acthick / 2, 0 * degree, 360 * degree);
0736   SubtractionSolid ac_shape(ac_tube, flange_final_shape);
0737 
0738   Volume acVol(detName + "_ac", ac_shape, gasvolMat);
0739 
0740   PlacedVolume ac_PV = pfRICH_volume.placeVolume(acVol, Position(0, 0, -21.3));
0741 
0742   DetElement acDE(sdet, "ac_de", 0);
0743   acDE.setPlacement(ac_PV);
0744 
0745   return sdet;
0746 }
0747 
0748 // clang-format off
0749 DECLARE_DETELEMENT(epic_PFRICH, createDetector)