Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:22:08

0001 #include "PHG4MvtxDetector.h"
0002 
0003 #include "PHG4MvtxMisalignment.h"
0004 
0005 #include "PHG4MvtxDefs.h"
0006 #include "PHG4MvtxDisplayAction.h"
0007 #include "PHG4MvtxSupport.h"
0008 
0009 #include <mvtx/CylinderGeom_Mvtx.h>
0010 #include <trackbase/MvtxDefs.h>
0011 #include <trackbase/TrkrDefs.h>
0012 
0013 #include <g4detectors/PHG4CylinderGeomContainer.h>
0014 
0015 #include <phparameter/PHParameters.h>
0016 #include <phparameter/PHParametersContainer.h>
0017 
0018 #include <g4main/PHG4Detector.h>       // for PHG4Detector
0019 #include <g4main/PHG4DisplayAction.h>  // for PHG4DisplayAction
0020 #include <g4main/PHG4Subsystem.h>      // for PHG4Subsystem
0021 
0022 #include <phool/PHCompositeNode.h>
0023 #include <phool/PHIODataNode.h>
0024 #include <phool/PHNode.h>          // for PHNode
0025 #include <phool/PHNodeIterator.h>  // for PHNodeIterator
0026 #include <phool/PHObject.h>        // for PHObject
0027 #include <phool/getClass.h>
0028 
0029 #include <Geant4/G4AssemblyVolume.hh>
0030 #include <Geant4/G4LogicalVolume.hh>
0031 #include <Geant4/G4Material.hh>
0032 #include <Geant4/G4PVPlacement.hh>
0033 #include <Geant4/G4Polycone.hh>
0034 #include <Geant4/G4RotationMatrix.hh>  // for G4RotationMatrix
0035 #include <Geant4/G4String.hh>          // for G4String
0036 #include <Geant4/G4SystemOfUnits.hh>
0037 #include <Geant4/G4ThreeVector.hh>  // for G4ThreeVector
0038 #include <Geant4/G4Transform3D.hh>  // for G4Transform3D
0039 #include <Geant4/G4Tubs.hh>
0040 #include <Geant4/G4Types.hh>            // for G4double
0041 #include <Geant4/G4VPhysicalVolume.hh>  // for G4VPhysicalVolume
0042 
0043 #include <Geant4/G4GDMLParser.hh>
0044 #include <Geant4/G4GDMLReadStructure.hh>  // for G4GDMLReadStructure
0045 
0046 #include <cmath>
0047 #include <cstdio>    // for sprintf
0048 #include <iostream>  // for operator<<, basic...
0049 #include <memory>
0050 #include <sstream>
0051 #include <utility>  // for pair, make_pair
0052 #include <vector>   // for vector, vector<>:...
0053 
0054 namespace mvtxGeomDef
0055 {
0056   const double wrap_rmax = (107.7 + 0.3) * mm;  // 300 um Marging from SB Flange
0057   const double wrap_rmin = 22 * mm;
0058   const double wrap_smallCylR = 55.00 * mm;  // CYSS Ext Nose + 905 um
0059   const double wrap_CYSSFlgN_Z = (177.5 + 12.5) * mm;
0060   const double wrap_CYSSNose_Z = -245 * mm;
0061   const double wrap_CYSSHead_Z = -315 * mm;
0062   const double wrap_SBCyl_Z = -1800 * mm;  // SB Cyl (1650 mm + 15 cm Margin)
0063 }  // namespace mvtxGeomDef
0064 
0065 PHG4MvtxDetector::PHG4MvtxDetector(PHG4Subsystem *subsys, PHCompositeNode *Node, const PHParametersContainer *_paramsContainer, const std::string &dnam, const bool applyMisalignment, const std::string& misalignmentfile)
0066   : PHG4Detector(subsys, Node, dnam)
0067   , m_DisplayAction(dynamic_cast<PHG4MvtxDisplayAction *>(subsys->GetDisplayAction()))
0068   , m_ParamsContainer(_paramsContainer)
0069   , m_StaveGeometryFile(_paramsContainer->GetParameters(PHG4MvtxDefs::GLOBAL)->get_string_param("stave_geometry_file"))
0070   , apply_misalignment(applyMisalignment)
0071   , m_misalignmentFile(misalignmentfile)
0072 {
0073   if (Verbosity() > 0)
0074   {
0075     std::cout << "PHG4MvtxDetector constructor called" << std::endl;
0076   }
0077 
0078   for (int ilayer = 0; ilayer < n_Layers; ++ilayer)
0079   {
0080     const PHParameters *params = m_ParamsContainer->GetParameters(ilayer);
0081     m_IsLayerActive[ilayer] = params->get_int_param("active");
0082     m_IsLayerSupportActive[ilayer] = params->get_int_param("supportactive");
0083     m_IsBlackHole[ilayer] = params->get_int_param("blackhole");
0084     m_N_staves[ilayer] = params->get_int_param("N_staves");
0085     m_nominal_radius[ilayer] = params->get_double_param("layer_nominal_radius");  
0086     m_nominal_phitilt[ilayer] = params->get_double_param("phitilt");
0087     m_nominal_phi0[ilayer] = params->get_double_param("phi0");
0088     m_SupportActiveFlag += m_IsLayerSupportActive[ilayer];
0089   }
0090   if (apply_misalignment)
0091   {
0092     std::cout << "PHG4MvtxDetector constructor: Apply Misalignment, get global displacement" << std::endl;
0093     PHG4MvtxMisalignment *m_MvtxMisalignment = new PHG4MvtxMisalignment();
0094     if(!m_misalignmentFile.empty())
0095     {
0096       std::cout << "loading mvtx survey geometry from " << m_misalignmentFile << std::endl;
0097       m_MvtxMisalignment->setAlignmentFile(m_misalignmentFile);
0098     }
0099     m_MvtxMisalignment->LoadMvtxStaveAlignmentParameters();
0100 
0101     std::vector<double> v_globaldisplacement = m_MvtxMisalignment->get_GlobalDisplacement();
0102     m_GlobalDisplacementX = v_globaldisplacement[0];
0103     m_GlobalDisplacementY = v_globaldisplacement[1];
0104     m_GlobalDisplacementZ = v_globaldisplacement[2];
0105     delete m_MvtxMisalignment;
0106   }
0107 
0108   if (Verbosity() > 0)
0109   {
0110     std::cout << "PHG4MvtxDetector constructor: making Mvtx detector. " << std::endl;
0111   }
0112 }
0113 
0114 int PHG4MvtxDetector::IsSensor(G4VPhysicalVolume *volume) const
0115 {
0116   // Is this volume one of the sensors?
0117   // Checks if pointer matches one of our stored sensors for this layer
0118   if (m_SensorPV.contains(volume))
0119   {
0120     if (Verbosity() > 0)
0121     {
0122       std::cout << " -- PHG4MvtxTDetector::IsSensor --" << std::endl;
0123       std::cout << " volume Name : " << volume->GetName() << std::endl;
0124       std::cout << " -----------------------------------------" << std::endl;
0125     }
0126     return 1;
0127   }
0128   if (m_SupportActiveFlag)
0129   {
0130     if (m_SupportLV.contains(volume->GetLogicalVolume()))
0131     {
0132       return -1;
0133     }
0134   }
0135   return 0;
0136 }
0137 
0138 int PHG4MvtxDetector::IsInMvtx(G4VPhysicalVolume *volume, int &layer, int &stave) const
0139 {
0140   // Does this stave belong to this layer?
0141   // Since the Assembly volume read from GDML does not give unique pointers
0142   // to sensors, we need to check the stave, which is unique
0143   auto iter = m_StavePV.find(volume);
0144   if (iter != m_StavePV.end())
0145   {
0146     std::tie(layer, stave) = iter->second;
0147     if (Verbosity() > 0)
0148     {
0149       std::cout << " -- PHG4MvtxDetector::IsInMvtx --" << std::endl;
0150       std::cout << " layer: " << layer << std::endl;
0151       std::cout << " stave: " << stave << std::endl;
0152       std::cout << " volume Name : " << volume->GetName() << std::endl;
0153       std::cout << " stave Name  : " << iter->first->GetName() << std::endl;
0154       std::cout << " -----------------------------------------" << std::endl;
0155     }
0156     return 1;
0157   }
0158 
0159   return 0;
0160 }
0161 
0162 int PHG4MvtxDetector::get_layer(int index) const
0163 {
0164   // Get Mvtx layer from stave index in the Mvtx
0165   // Mvtx stave index start from 0, i.e index = 0 for stave 0 in layer 0
0166   int lay = 0;
0167   while (!(index < m_N_staves[lay]))
0168   {
0169     index -= m_N_staves[lay];
0170     lay++;
0171   }
0172   return lay;
0173 }
0174 
0175 int PHG4MvtxDetector::get_stave(int index) const
0176 {
0177   // Get stave index in the layer from stave index in the Mvtx
0178   int lay = 0;
0179   while (!(index < m_N_staves[lay]))
0180   {
0181     index -= m_N_staves[lay];
0182     lay++;
0183   }
0184   return index;
0185 }
0186 
0187 void PHG4MvtxDetector::ConstructMe(G4LogicalVolume *logicWorld)
0188 {
0189   // This is called from PHG4PhenixDetector::Construct()
0190   if (Verbosity() > 0)
0191   {
0192     std::cout << std::endl
0193               << "PHG4MvtxDetector::Construct called for Mvtx " << std::endl;
0194   }
0195 
0196   const G4int numZPlanes = 4;
0197   const G4double zPlane[numZPlanes] = {mvtxGeomDef::wrap_SBCyl_Z, mvtxGeomDef::wrap_CYSSHead_Z, mvtxGeomDef::wrap_CYSSNose_Z, mvtxGeomDef::wrap_CYSSFlgN_Z};
0198 
0199   const G4double rInner[numZPlanes] = {
0200     apply_misalignment ? mvtxGeomDef::wrap_rmin + 0.822 * mm : mvtxGeomDef::wrap_rmin,
0201     apply_misalignment ? mvtxGeomDef::wrap_rmin + 0.822 * mm : mvtxGeomDef::wrap_rmin,
0202     apply_misalignment ? mvtxGeomDef::wrap_rmin + 0.822 * mm : mvtxGeomDef::wrap_rmin,
0203     apply_misalignment ? mvtxGeomDef::wrap_rmin + 0.822 * mm : mvtxGeomDef::wrap_rmin
0204   };
0205 
0206   const G4double rOuter[numZPlanes] = {mvtxGeomDef::wrap_rmax, mvtxGeomDef::wrap_rmax, mvtxGeomDef::wrap_smallCylR, mvtxGeomDef::wrap_smallCylR};
0207 
0208   auto *mvtxWrapSol = new G4Polycone("sol_MVTX_Wrapper", 0, 2.0 * M_PI, numZPlanes, zPlane, rInner, rOuter);
0209 
0210   auto *world_mat = logicWorld->GetMaterial();
0211 
0212   auto *logicMVTX = new G4LogicalVolume(mvtxWrapSol, world_mat, "log_MVTX_Wrapper");
0213 
0214   G4RotationMatrix Ra;
0215   G4ThreeVector Ta;
0216 
0217   if (apply_misalignment)
0218   {
0219     if (Verbosity() > 1) {
0220       std::cout << "PHG4MvtxDetector::Apply Global Displacement to the MVTX_Wrapper: " << m_GlobalDisplacementX << " " << m_GlobalDisplacementY << " " << m_GlobalDisplacementZ << std::endl;
0221 }
0222 
0223     Ta.setX(m_GlobalDisplacementX);
0224     Ta.setY(m_GlobalDisplacementY);
0225     Ta.setZ(m_GlobalDisplacementZ);
0226   }
0227 
0228   G4Transform3D Tr(Ra, Ta);
0229   new G4PVPlacement(Tr, logicMVTX, "MVTX_Wrapper", logicWorld, false, 0, false);
0230 
0231   // the tracking layers are placed directly in the world volume,
0232   // since some layers are (touching) double layers
0233   // this reads in the ITS stave geometry from a file and constructs the layer from it
0234   ConstructMvtx(logicMVTX);
0235   ConstructMvtxPassiveVol(logicMVTX);
0236 
0237   AddGeometryNode();
0238   return;
0239 }
0240 
0241 int PHG4MvtxDetector::ConstructMvtx(G4LogicalVolume *trackerenvelope)
0242 {
0243   if (Verbosity() > 0)
0244   {
0245     std::cout << " PHG4MvtxDetector::ConstructMvtx:" << std::endl;
0246     std::cout << std::endl;
0247   }
0248 
0249   //===================================
0250   // Import the stave physical volume here
0251   //===================================
0252 
0253   // import the staves from the gemetry file
0254   std::unique_ptr<G4GDMLReadStructure> reader(new G4GDMLReadStructure());
0255   G4GDMLParser gdmlParser(reader.get());
0256   gdmlParser.Read(m_StaveGeometryFile, false);
0257 
0258   // figure out which assembly we want
0259   std::string assemblyname = "MVTXStave";
0260   if (Verbosity() > 0)
0261   {
0262     std::cout << "Geting the stave assembly named " << assemblyname << std::endl;
0263   }
0264   G4AssemblyVolume *av_ITSUStave = reader->GetAssembly(assemblyname);
0265 
0266   for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
0267   {
0268     if (m_IsLayerActive[ilayer])
0269     {
0270       if (Verbosity() > 0)
0271       {
0272         std::cout << std::endl;
0273         std::cout << " Constructing Layer " << ilayer << std::endl;
0274       }
0275       ConstructMvtx_Layer(ilayer, av_ITSUStave, trackerenvelope);
0276     }
0277   }
0278   FillPVArray(av_ITSUStave);
0279   SetDisplayProperty(av_ITSUStave);
0280 
0281   return 0;
0282 }
0283 
0284 int PHG4MvtxDetector::ConstructMvtx_Layer(int layer, G4AssemblyVolume *av_ITSUStave, G4LogicalVolume *&trackerenvelope)
0285 {
0286   //=========================================
0287   // Now we populate the whole layer with the staves
0288   //==========================================
0289 
0290   int N_staves = m_N_staves[layer];
0291   G4double layer_nominal_radius = m_nominal_radius[layer];
0292   G4double phitilt = m_nominal_phitilt[layer];
0293   G4double phi0 = m_nominal_phi0[layer];  // YCM: azimuthal offset for the first stave
0294 
0295   if (N_staves < 0)
0296   {
0297     // The user did not specify how many staves to use for this layer, so we calculate the best value
0298     // We choose a phistep that fits N_staves into this radius, but with an arclength separation of AT LEAST arcstep
0299     // ideally, the radius would be chosen so that numstaves = N_staves exactly, to get the closest spacing of staves possible without overlaps
0300     double arcstep = 12.25;
0301     double numstaves = 2.0 * M_PI * layer_nominal_radius / arcstep;  // this is just to print out
0302     N_staves = int(2.0 * M_PI * layer_nominal_radius / arcstep);     // this is the number of staves used
0303 
0304     // Also suggest the ideal radius for this layer
0305     if (Verbosity() > 0)
0306     {
0307       std::cout << " Calculated N_staves for layer " /*<< layer*/
0308                 << " layer_nominal_radius " << layer_nominal_radius << " ITS arcstep " << arcstep << " circumference divided by arcstep  " << numstaves << " N_staves " << N_staves << std::endl;
0309       std::cout << "A radius for this layer of " << (double) N_staves * arcstep / (2.0 * M_PI) + 0.01 << " or " << (double) (N_staves + 1) * arcstep / (2.0 * M_PI) + 0.01 << " would produce  perfect stave spacing" << std::endl;
0310     }
0311   }
0312 
0313   G4double phistep = get_phistep(layer);  // this produces even stave spacing
0314   double z_location = 0.0;
0315 
0316   if (Verbosity() > 0)
0317   {
0318     std::cout << " layer " /*<< layer*/
0319               << " layer_nominal_radius " << layer_nominal_radius << " N_staves " << N_staves << " phistep " << phistep << " phitilt " << phitilt << " phi0    " << phi0 << std::endl;
0320   }
0321 
0322   // The stave starts out at (0,0,0) and oriented so that the sensors face upward in y
0323   // So we need to rotate the sensor 90 degrees before placing it using phi_offset
0324   // note that the gdml file uses a negative phi_offset - different coord system, apparently - the following works
0325   double phi_offset = M_PI / 2.0;
0326 
0327   for (int iphi = 0; iphi < N_staves; iphi++)
0328   {
0329     // Place the ladder segment envelopes at the correct z and phi
0330     // This is the azimuthal angle at which we place the stave
0331     // phi0 is the azimuthal offset for the first stave
0332     G4double phi_rotation = phi0 + (double) iphi * phistep;
0333 
0334     G4RotationMatrix Ra;
0335     G4ThreeVector Ta;
0336 
0337     if (Verbosity() > 0)
0338     {
0339       std::cout << "phi_offset = " << phi_offset << " iphi " << iphi << " phi_rotation = " << phi_rotation << " phitilt " << phitilt << std::endl;
0340     }
0341     // It  is first rotated in phi by the azimuthal angle phi_rotation, plus the 90 degrees needed to point the face of the sensor  at the origin,  plus the tilt (if a tilt is appropriate)
0342 
0343     // note - if this is layer 0-2, phitilt is the additional tilt for clearance. Otherwise it is zero
0344     Ra.rotateZ(phi_rotation + phi_offset + phitilt);
0345     // Then translated as follows
0346 
0347     Ta.setX(layer_nominal_radius * cos(phi_rotation));
0348     Ta.setY(layer_nominal_radius * sin(phi_rotation));
0349     Ta.setZ(z_location);
0350 
0351     if (Verbosity() > 0)
0352     {
0353       std::cout << " iphi " << iphi << " phi_rotation " << phi_rotation << " x " << layer_nominal_radius * cos(phi_rotation) << " y " << layer_nominal_radius * sin(phi_rotation) << " z " << z_location << std::endl;
0354     }
0355     G4Transform3D Tr(Ra, Ta);
0356 
0357     av_ITSUStave->MakeImprint(trackerenvelope, Tr, 0, OverlapCheck());
0358   }
0359 
0360   if (Verbosity() > 0)
0361   {
0362     std::cout << "This layer has a total of " << N_staves << " staves" << std::endl;
0363   }
0364   return 0;
0365 }
0366 
0367 int PHG4MvtxDetector::ConstructMvtxPassiveVol(G4LogicalVolume *&lv)
0368 {
0369   if (Verbosity() > 0)
0370   {
0371     std::cout << " PHG4MvtxDetector::ConstructMvtxServices:" << std::endl;
0372     std::cout << std::endl;
0373   }
0374 
0375   // Now construct EWs, service barrel, CYSS, cones and cables
0376   PHG4MvtxSupport *mvtxSupportSystem = new PHG4MvtxSupport(this, m_DisplayAction, OverlapCheck());
0377   mvtxSupportSystem->ConstructMvtxSupport(lv);
0378 
0379   delete mvtxSupportSystem;
0380 
0381   return 0;
0382 }
0383 
0384 // NOLINTNEXTLINE(misc-no-recursion)
0385 void PHG4MvtxDetector::SetDisplayProperty(G4AssemblyVolume *av)
0386 {
0387   //  std::cout <<"SetDisplayProperty - G4AssemblyVolume w/ TotalImprintedVolumes "<<av->TotalImprintedVolumes()
0388   //   <<"/"<<av->GetImprintsCount()<<std::endl;
0389 
0390   std::vector<G4VPhysicalVolume *>::iterator it = av->GetVolumesIterator();
0391 
0392   int nDaughters = av->TotalImprintedVolumes();
0393   for (int i = 0; i < nDaughters; ++i, ++it)
0394   {
0395     if (Verbosity() >= 50)
0396     {
0397       std::cout << "SetDisplayProperty - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0398     }
0399     G4VPhysicalVolume *pv = (*it);
0400 
0401     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0402     SetDisplayProperty(worldLogical);
0403   }
0404 }
0405 
0406 // NOLINTNEXTLINE(misc-no-recursion)
0407 void PHG4MvtxDetector::SetDisplayProperty(G4LogicalVolume *lv)
0408 {
0409   std::string material_name(lv->GetMaterial()->GetName());
0410 
0411   if (Verbosity() >= 50)
0412   {
0413     std::cout << "SetDisplayProperty - LV " << lv->GetName() << " built with " << material_name << std::endl;
0414   }
0415   std::vector<std::string> matname = {"SI", "KAPTON", "ALUMINUM", "Carbon", "M60J3K", "WATER"};
0416   bool found = false;
0417   for (const std::string &nam : matname)
0418   {
0419     if (material_name.find(nam) != std::string::npos)
0420     {
0421       m_DisplayAction->AddVolume(lv, nam);
0422       if (Verbosity() >= 50)
0423       {
0424         std::cout << "SetDisplayProperty - LV " << lv->GetName() << " display with " << nam << std::endl;
0425       }
0426       found = true;
0427       break;
0428     }
0429   }
0430   if (!found)
0431   {
0432     m_DisplayAction->AddVolume(lv, "ANYTHING_ELSE");
0433   }
0434   int nDaughters = lv->GetNoDaughters();
0435   for (int i = 0; i < nDaughters; ++i)
0436   {
0437     G4VPhysicalVolume *pv = lv->GetDaughter(i);
0438 
0439     // std::cout <<"SetDisplayProperty - PV["<<i<<"] = "<<pv->GetName()<<std::endl;
0440 
0441     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0442     SetDisplayProperty(worldLogical);
0443   }
0444 }
0445 
0446 void PHG4MvtxDetector::AddGeometryNode()
0447 {
0448   int active = 0;
0449   // for (auto &isAct : m_IsLayerActive)
0450   // {
0451   //   active |= isAct;
0452   // }
0453   if (std::any_of(m_IsLayerActive.begin(), m_IsLayerActive.end(), [](int isAct) { return isAct != 0; }))
0454   {
0455     active = 1;
0456   }
0457   
0458   if (active)  // At least one layer is active
0459   {
0460     //    ostringstream geonode;
0461     std::string geonode = "CYLINDERGEOM_" + ((m_SuperDetector != "NONE") ? m_SuperDetector : m_Detector);
0462     PHG4CylinderGeomContainer *geo = findNode::getClass<PHG4CylinderGeomContainer>(topNode(), geonode);
0463     if (!geo)
0464     {
0465       geo = new PHG4CylinderGeomContainer();
0466       PHNodeIterator iter(topNode());
0467       PHCompositeNode *runNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "RUN"));
0468       PHNodeIterator runiter(runNode);
0469       PHCompositeNode *geomNode = dynamic_cast<PHCompositeNode *>(runiter.findFirst("PHCompositeNode", "RECO_TRACKING_GEOMETRY"));
0470       if(!geomNode)
0471       {
0472         geomNode = new PHCompositeNode("RECO_TRACKING_GEOMETRY");
0473         runNode->addNode(geomNode);
0474       }
0475       PHIODataNode<PHObject> *newNode = new PHIODataNode<PHObject>(geo, geonode, "PHObject");
0476       geomNode->addNode(newNode);
0477     }
0478     // here in the detector class we have internal units(mm), convert to cm
0479     // before putting into the geom object
0480     for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
0481     {
0482       CylinderGeom_Mvtx *mygeom = new CylinderGeom_Mvtx(ilayer, m_N_staves[ilayer], m_nominal_radius[ilayer] / cm, get_phistep(ilayer) / rad, m_nominal_phitilt[ilayer] / rad, m_nominal_phi0[ilayer] / rad);
0483       geo->AddLayerGeom(ilayer, mygeom);
0484     }  // loop per layers
0485     if (Verbosity())
0486     {
0487       geo->identify();
0488     }
0489   }  // is active
0490 }  // AddGeometryNode
0491 
0492 void PHG4MvtxDetector::FillPVArray(G4AssemblyVolume *av)
0493 {
0494   if (Verbosity() > 0)
0495   {
0496     std::cout << "-- FillPVArray --" << std::endl;
0497   }
0498   std::vector<G4VPhysicalVolume *>::iterator it = av->GetVolumesIterator();
0499 
0500   int nDaughters = av->TotalImprintedVolumes();
0501   for (int i = 0; i < nDaughters; ++i, ++it)
0502   {
0503     G4VPhysicalVolume *pv = (*it);
0504 
0505     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0506     // we only care about the staves, which contain the sensors, not the structures
0507     if (pv->GetName().find("MVTXHalfStave_pv") != std::string::npos)
0508     {
0509       int layer = get_layer(m_StavePV.size());
0510       int stave = get_stave(m_StavePV.size());
0511 
0512       m_StavePV.insert(std::make_pair(pv, std::make_tuple(layer, stave)));
0513 
0514       if (Verbosity() > 0)
0515       {
0516         std::cout << "Mvtx layer id " << layer << std::endl;
0517         std::cout << "Stave in layer id " << stave << std::endl;
0518         std::cout << "Mvtx stave count " << m_StavePV.size() << std::endl;
0519         std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0520         std::cout << "              LV[" << i << "] = " << worldLogical->GetName() << std::endl;
0521       }
0522 
0523       FindSensor(worldLogical);
0524     }
0525     else  // in case of stave structure
0526     {
0527       if (Verbosity() > 0)
0528       {
0529         std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0530         std::cout << "              LV[" << i << "] = " << worldLogical->GetName() << std::endl;
0531       }
0532     }
0533   }
0534 }
0535 
0536 // NOLINTNEXTLINE(misc-no-recursion)
0537 void PHG4MvtxDetector::FindSensor(G4LogicalVolume *lv)
0538 {
0539   int nDaughters = lv->GetNoDaughters();
0540   for (int i = 0; i < nDaughters; ++i)
0541   {
0542     G4VPhysicalVolume *pv = lv->GetDaughter(i);
0543     if (Verbosity() > 0)
0544     {
0545       std::cout << "                 PV[" << i << "]: " << pv->GetName() << std::endl;
0546     }
0547     if (pv->GetName().find("MVTXSensor_") != std::string::npos)
0548     {
0549       m_SensorPV.insert(pv);
0550 
0551       if (Verbosity() > 0)
0552       {
0553         std::cout << "                      Adding Sensor Vol <" << pv->GetName() << " (" << m_SensorPV.size() << ")>" << std::endl;
0554       }
0555     }
0556 
0557     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0558 
0559     if (Verbosity() > 0)
0560     {
0561       std::cout << "                 LV[" << i << "]: " << worldLogical->GetName() << std::endl;
0562     }
0563     FindSensor(worldLogical);
0564   }
0565 }