Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:18:07

0001 //----------------------------------
0002 //  pfRICH: Proximity Focusing RICH
0003 //  Author: C. Dilks
0004 //----------------------------------
0005 
0006 #include "DD4hep/DetFactoryHelper.h"
0007 #include "DD4hep/OpticalSurfaces.h"
0008 #include "DD4hep/Printout.h"
0009 #include "DDRec/DetectorData.h"
0010 #include <XML/Helper.h>
0011 
0012 using namespace dd4hep;
0013 using namespace dd4hep::rec;
0014 
0015 // create the detector
0016 static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
0017 {
0018   xml::DetElement       detElem = handle;
0019   std::string           detName = detElem.nameStr();
0020   int                   detID   = detElem.id();
0021   xml::Component        dims    = detElem.dimensions();
0022   OpticalSurfaceManager surfMgr = desc.surfaceManager();
0023   DetElement            det(detName, detID);
0024 
0025   // constant attributes -----------------------------------------------------------
0026   // - vessel
0027   double vesselLength       = dims.attr<double>(_Unicode(length));
0028   double vesselZmin         = dims.attr<double>(_Unicode(zmin));
0029   double vesselRmin0        = dims.attr<double>(_Unicode(rmin0));
0030   double vesselRmin1        = dims.attr<double>(_Unicode(rmin1));
0031   double vesselRmax0        = dims.attr<double>(_Unicode(rmax0));
0032   double vesselRmax1        = dims.attr<double>(_Unicode(rmax1));
0033   double wallThickness      = dims.attr<double>(_Unicode(wall_thickness));
0034   double windowThickness    = dims.attr<double>(_Unicode(window_thickness));
0035   auto   vesselMat          = desc.material(detElem.attr<std::string>(_Unicode(material)));
0036   auto   gasvolMat          = desc.material(detElem.attr<std::string>(_Unicode(gas)));
0037   auto   vesselVis          = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_vessel)));
0038   auto   gasvolVis          = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_gas)));
0039   // - radiator (applies to aerogel and filter)
0040   auto   radiatorElem       = detElem.child(_Unicode(radiator));
0041   double radiatorRmin       = radiatorElem.attr<double>(_Unicode(rmin));
0042   double radiatorRmax       = radiatorElem.attr<double>(_Unicode(rmax));
0043   double radiatorFrontplane = radiatorElem.attr<double>(_Unicode(frontplane));
0044   // - aerogel
0045   auto   aerogelElem        = radiatorElem.child(_Unicode(aerogel));
0046   auto   aerogelMat         = desc.material(aerogelElem.attr<std::string>(_Unicode(material)));
0047   auto   aerogelVis         = desc.visAttributes(aerogelElem.attr<std::string>(_Unicode(vis)));
0048   double aerogelThickness   = aerogelElem.attr<double>(_Unicode(thickness));
0049   // - filter
0050   auto   filterElem         = radiatorElem.child(_Unicode(filter));
0051   auto   filterMat          = desc.material(filterElem.attr<std::string>(_Unicode(material)));
0052   auto   filterVis          = desc.visAttributes(filterElem.attr<std::string>(_Unicode(vis)));
0053   double filterThickness    = filterElem.attr<double>(_Unicode(thickness));
0054   // - sensor module
0055   auto   sensorElem         = detElem.child(_Unicode(sensors)).child(_Unicode(module));
0056   auto   sensorMat          = desc.material(sensorElem.attr<std::string>(_Unicode(material)));
0057   auto   sensorVis          = desc.visAttributes(sensorElem.attr<std::string>(_Unicode(vis)));
0058   auto   sensorSurf         = surfMgr.opticalSurface(sensorElem.attr<std::string>(_Unicode(surface)));
0059   double sensorSide         = sensorElem.attr<double>(_Unicode(side));
0060   double sensorGap          = sensorElem.attr<double>(_Unicode(gap));
0061   double sensorThickness    = sensorElem.attr<double>(_Unicode(thickness));
0062   // - sensor plane
0063   auto   sensorPlaneElem    = detElem.child(_Unicode(sensors)).child(_Unicode(plane));
0064   double sensorPlaneDist    = sensorPlaneElem.attr<double>(_Unicode(sensordist));
0065   double sensorPlaneRmin    = sensorPlaneElem.attr<double>(_Unicode(rmin));
0066   double sensorPlaneRmax    = sensorPlaneElem.attr<double>(_Unicode(rmax));
0067 
0068   // BUILD VESSEL //////////////////////////////////////
0069   /* - `vessel`: aluminum enclosure, the mother volume of the pfRICH
0070    * - `gasvol`: gas volume, which fills `vessel`; all other volumes defined below
0071    *   are children of `gasvol`
0072    */
0073 
0074   // tank solids
0075   double boreDelta = vesselRmin1 - vesselRmin0;
0076   Cone vesselSolid(
0077       vesselLength / 2.0,
0078       vesselRmin1,
0079       vesselRmax1,
0080       vesselRmin0,
0081       vesselRmax0
0082       );
0083   Cone gasvolSolid(
0084       vesselLength / 2.0 - windowThickness,
0085       vesselRmin1 + wallThickness,
0086       vesselRmax1 - wallThickness,
0087       vesselRmin0 + wallThickness,
0088       vesselRmax0 - wallThickness
0089       );
0090 
0091   // volumes
0092   Volume vesselVol(detName,        vesselSolid, vesselMat);
0093   Volume gasvolVol(detName+"_gas", gasvolSolid, gasvolMat);
0094   vesselVol.setVisAttributes(vesselVis);
0095   gasvolVol.setVisAttributes(gasvolVis);
0096 
0097   // reference positions
0098   /* - the vessel is created such that the center of the cylindrical tank volume
0099    *   coincides with the origin; this is called the "origin position" of the vessel
0100    * - when the vessel (and its children volumes) is placed, it is translated in
0101    *   the z-direction to be in the proper full-detector integration location
0102    * - these reference positions are for the frontplane and backplane of the vessel,
0103    *   with respect to the vessel origin position
0104    */
0105   auto originFront = Position(0., 0., vesselLength / 2.0);
0106 
0107   // sensitive detector type
0108   sens.setType("tracker");
0109 
0110   // BUILD RADIATOR //////////////////////////////////////
0111 
0112   // attributes
0113   double airGap = 0.01 * mm; // air gap between aerogel and filter (FIXME? actually it's currently a gas gap)
0114 
0115   // solid and volume: create aerogel and filter
0116   Cone aerogelSolid(
0117       aerogelThickness / 2,
0118       radiatorRmin + boreDelta * aerogelThickness / vesselLength, // at backplane
0119       radiatorRmax,
0120       radiatorRmin, // at frontplane
0121       radiatorRmax
0122       );
0123   Cone filterSolid(
0124       filterThickness / 2,
0125       radiatorRmin + boreDelta * (aerogelThickness + airGap + filterThickness) / vesselLength, // at backplane
0126       radiatorRmax,
0127       radiatorRmin + boreDelta * (aerogelThickness + airGap) / vesselLength, // at frontplane
0128       radiatorRmax
0129       );
0130   Volume aerogelVol(detName + "_aerogel", aerogelSolid, aerogelMat);
0131   Volume filterVol(detName  + "_filter",  filterSolid,  filterMat);
0132   aerogelVol.setVisAttributes(aerogelVis);
0133   filterVol.setVisAttributes(filterVis);
0134 
0135   // aerogel placement and surface properties
0136   // FIXME: define skin properties for aerogel and filter
0137   auto radiatorPos = Position(0., 0., radiatorFrontplane - 0.5 * aerogelThickness) + originFront;
0138   auto aerogelPV = gasvolVol.placeVolume(
0139       aerogelVol,
0140       Transform3D(Translation3D(radiatorPos.x(), radiatorPos.y(), radiatorPos.z())) // re-center to originFront
0141       );
0142   DetElement aerogelDE(det, "aerogel_de", 0);
0143   aerogelDE.setPlacement(aerogelPV);
0144 
0145   // filter placement and surface properties
0146   auto filterPV = gasvolVol.placeVolume(
0147       filterVol,
0148       Transform3D(
0149         Translation3D(0., 0., -airGap) // add an airgap (FIXME: actually a gas gap)
0150         *
0151         Translation3D(radiatorPos.x(), radiatorPos.y(), radiatorPos.z())  // re-center to originFront
0152         *
0153         Translation3D(0., 0., -(aerogelThickness + filterThickness) / 2.) // move to aerogel backplane
0154         )
0155       );
0156   DetElement filterDE(det, "filter_de", 0);
0157   filterDE.setPlacement(filterPV);
0158 
0159   // BUILD SENSORS ///////////////////////
0160 
0161   // solid and volume: single sensor module
0162   Box    sensorSolid(sensorSide / 2., sensorSide / 2., sensorThickness / 2.);
0163   Volume sensorVol(detName + "_sensor", sensorSolid, sensorMat);
0164   sensorVol.setVisAttributes(sensorVis);
0165 
0166   // sensitivity
0167   sensorVol.setSensitiveDetector(sens);
0168 
0169   // sensor plane positioning: we want `sensorPlaneDist` to be the distance between the
0170   // aerogel backplane (i.e., aerogel/filter boundary) and the sensor active surface (e.g, photocathode)
0171   double sensorZpos     = radiatorFrontplane - aerogelThickness - sensorPlaneDist - 0.5 * sensorThickness;
0172   auto   sensorPlanePos = Position(0., 0., sensorZpos) + originFront; // reference position
0173   // miscellaneous
0174   int    imod    = 0;           // module number
0175   double tBoxMax = vesselRmax1; // sensors will be tiled in tBox, within annular limits
0176 
0177   // SENSOR MODULE LOOP ------------------------
0178   /* cartesian tiling loop
0179    * - start at (x=0,y=0), to center the grid
0180    * - loop over positive-x positions; for each, place the corresponding negative-x sensor too
0181    * - nested similar loop over y positions
0182    */
0183   double sx, sy;
0184   for (double usx = 0; usx <= tBoxMax; usx += sensorSide + sensorGap) {
0185     for (int sgnx = 1; sgnx >= (usx > 0 ? -1 : 1); sgnx -= 2) {
0186       for (double usy = 0; usy <= tBoxMax; usy += sensorSide + sensorGap) {
0187         for (int sgny = 1; sgny >= (usy > 0 ? -1 : 1); sgny -= 2) {
0188 
0189           // sensor (x,y) center
0190           sx = sgnx * usx;
0191           sy = sgny * usy;
0192 
0193           // annular cut
0194           if (std::hypot(sx, sy) < sensorPlaneRmin || std::hypot(sx, sy) > sensorPlaneRmax)
0195             continue;
0196 
0197           // placement (note: transformations are in reverse order)
0198           auto sensorPV = gasvolVol.placeVolume(
0199               sensorVol,
0200               Transform3D(
0201                 Translation3D(sensorPlanePos.x(), sensorPlanePos.y(), sensorPlanePos.z()) // move to reference position
0202                 *
0203                 Translation3D(sx, sy, 0.) // move to grid position
0204                 )
0205               );
0206 
0207           // generate LUT for module number -> sensor position, for readout mapping tests
0208           // printf("%d %f %f\n",imod,sensorPV.position().x(),sensorPV.position().y());
0209 
0210           // properties
0211           sensorPV.addPhysVolID("module", imod);
0212           DetElement sensorDE(det, Form("sensor_de_%d", imod), imod);
0213           sensorDE.setPlacement(sensorPV);
0214           SkinSurface sensorSkin(desc, sensorDE, "sensor_optical_surface", sensorSurf, sensorVol); // FIXME: 3rd arg needs `imod`?
0215           sensorSkin.isValid();
0216 
0217           // increment sensor module number
0218           imod++;
0219         }
0220       }
0221     }
0222   }
0223   // END SENSOR MODULE LOOP ------------------------
0224 
0225   // Add service material if desired (added by Sylvester Joosten) ////////////////
0226   if (detElem.child("sensors").hasChild(_Unicode(services))) {
0227     xml_comp_t x_service = detElem.child("sensors").child(_Unicode(services));
0228     Assembly   service_vol("services");
0229     service_vol.setVisAttributes(desc, x_service.visStr());
0230 
0231     // Compute service total thickness from components
0232     double total_thickness = 0;
0233     for (xml_coll_t ci(x_service, _Unicode(component)); ci; ++ci) {
0234       total_thickness += xml_comp_t(ci).thickness();
0235     }
0236 
0237     int    ncomponents   = 0;
0238     double thickness_sum = -total_thickness / 2.0;
0239     for (xml_coll_t ci(x_service, _Unicode(component)); ci; ++ci, ncomponents++) {
0240       xml_comp_t x_comp    = ci;
0241       double     thickness = x_comp.thickness();
0242       Tube       c_tube{sensorPlaneRmin, sensorPlaneRmax, thickness / 2};
0243       Volume     c_vol{_toString(ncomponents, "component%d"), c_tube, desc.material(x_comp.materialStr())};
0244       c_vol.setVisAttributes(desc, x_comp.visStr());
0245       service_vol.placeVolume(c_vol, Position(0, 0, thickness_sum + thickness / 2.0));
0246       thickness_sum += thickness;
0247     }
0248     gasvolVol.placeVolume(service_vol,
0249         Transform3D(Translation3D(sensorPlanePos.x(), sensorPlanePos.y(),
0250             sensorPlanePos.z() - sensorThickness - total_thickness)));
0251   }
0252 
0253   // VESSEL PLACEMENT /////////////////////////////////////////////////////////////
0254 
0255   // place gas volume
0256   PlacedVolume gasvolPV = vesselVol.placeVolume(gasvolVol, Position(0, 0, 0));
0257   DetElement   gasvolDE(det, "gasvol_de", 0);
0258   gasvolDE.setPlacement(gasvolPV);
0259 
0260   // place mother volume (vessel)
0261   Volume       motherVol = desc.pickMotherVolume(det);
0262   PlacedVolume vesselPV  = motherVol.placeVolume(vesselVol, Position(0, 0, vesselZmin) - originFront);
0263   vesselPV.addPhysVolID("system", detID);
0264   det.setPlacement(vesselPV);
0265 
0266   return det;
0267 }
0268 
0269 // clang-format off
0270 DECLARE_DETELEMENT(PFRICH, createDetector)