Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:18:13

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.find(volume) != m_SensorPV.end())
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.find(volume->GetLogicalVolume()) != m_SupportLV.end())
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   char assemblyname[500];
0260   sprintf(assemblyname, "MVTXStave");
0261 
0262   if (Verbosity() > 0)
0263   {
0264     std::cout << "Geting the stave assembly named " << assemblyname << std::endl;
0265   }
0266   G4AssemblyVolume *av_ITSUStave = reader->GetAssembly(assemblyname);
0267 
0268   for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
0269   {
0270     if (m_IsLayerActive[ilayer])
0271     {
0272       if (Verbosity() > 0)
0273       {
0274         std::cout << std::endl;
0275         std::cout << " Constructing Layer " << ilayer << std::endl;
0276       }
0277       ConstructMvtx_Layer(ilayer, av_ITSUStave, trackerenvelope);
0278     }
0279   }
0280   FillPVArray(av_ITSUStave);
0281   SetDisplayProperty(av_ITSUStave);
0282 
0283   return 0;
0284 }
0285 
0286 int PHG4MvtxDetector::ConstructMvtx_Layer(int layer, G4AssemblyVolume *av_ITSUStave, G4LogicalVolume *&trackerenvelope)
0287 {
0288   //=========================================
0289   // Now we populate the whole layer with the staves
0290   //==========================================
0291 
0292   int N_staves = m_N_staves[layer];
0293   G4double layer_nominal_radius = m_nominal_radius[layer];
0294   G4double phitilt = m_nominal_phitilt[layer];
0295   G4double phi0 = m_nominal_phi0[layer];  // YCM: azimuthal offset for the first stave
0296 
0297   if (N_staves < 0)
0298   {
0299     // The user did not specify how many staves to use for this layer, so we calculate the best value
0300     // We choose a phistep that fits N_staves into this radius, but with an arclength separation of AT LEAST arcstep
0301     // ideally, the radius would be chosen so that numstaves = N_staves exactly, to get the closest spacing of staves possible without overlaps
0302     double arcstep = 12.25;
0303     double numstaves = 2.0 * M_PI * layer_nominal_radius / arcstep;  // this is just to print out
0304     N_staves = int(2.0 * M_PI * layer_nominal_radius / arcstep);     // this is the number of staves used
0305 
0306     // Also suggest the ideal radius for this layer
0307     if (Verbosity() > 0)
0308     {
0309       std::cout << " Calculated N_staves for layer " /*<< layer*/
0310                 << " layer_nominal_radius " << layer_nominal_radius << " ITS arcstep " << arcstep << " circumference divided by arcstep  " << numstaves << " N_staves " << N_staves << std::endl;
0311       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;
0312     }
0313   }
0314 
0315   G4double phistep = get_phistep(layer);  // this produces even stave spacing
0316   double z_location = 0.0;
0317 
0318   if (Verbosity() > 0)
0319   {
0320     std::cout << " layer " /*<< layer*/
0321               << " layer_nominal_radius " << layer_nominal_radius << " N_staves " << N_staves << " phistep " << phistep << " phitilt " << phitilt << " phi0    " << phi0 << std::endl;
0322   }
0323 
0324   // The stave starts out at (0,0,0) and oriented so that the sensors face upward in y
0325   // So we need to rotate the sensor 90 degrees before placing it using phi_offset
0326   // note that the gdml file uses a negative phi_offset - different coord system, apparently - the following works
0327   double phi_offset = M_PI / 2.0;
0328 
0329   for (int iphi = 0; iphi < N_staves; iphi++)
0330   {
0331     // Place the ladder segment envelopes at the correct z and phi
0332     // This is the azimuthal angle at which we place the stave
0333     // phi0 is the azimuthal offset for the first stave
0334     G4double phi_rotation = phi0 + (double) iphi * phistep;
0335 
0336     G4RotationMatrix Ra;
0337     G4ThreeVector Ta;
0338 
0339     if (Verbosity() > 0)
0340     {
0341       std::cout << "phi_offset = " << phi_offset << " iphi " << iphi << " phi_rotation = " << phi_rotation << " phitilt " << phitilt << std::endl;
0342     }
0343     // 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)
0344 
0345     // note - if this is layer 0-2, phitilt is the additional tilt for clearance. Otherwise it is zero
0346     Ra.rotateZ(phi_rotation + phi_offset + phitilt);
0347     // Then translated as follows
0348 
0349     Ta.setX(layer_nominal_radius * cos(phi_rotation));
0350     Ta.setY(layer_nominal_radius * sin(phi_rotation));
0351     Ta.setZ(z_location);
0352 
0353     if (Verbosity() > 0)
0354     {
0355       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;
0356     }
0357     G4Transform3D Tr(Ra, Ta);
0358 
0359     av_ITSUStave->MakeImprint(trackerenvelope, Tr, 0, OverlapCheck());
0360   }
0361 
0362   if (Verbosity() > 0)
0363   {
0364     std::cout << "This layer has a total of " << N_staves << " staves" << std::endl;
0365   }
0366   return 0;
0367 }
0368 
0369 int PHG4MvtxDetector::ConstructMvtxPassiveVol(G4LogicalVolume *&lv)
0370 {
0371   if (Verbosity() > 0)
0372   {
0373     std::cout << " PHG4MvtxDetector::ConstructMvtxServices:" << std::endl;
0374     std::cout << std::endl;
0375   }
0376 
0377   // Now construct EWs, service barrel, CYSS, cones and cables
0378   PHG4MvtxSupport *mvtxSupportSystem = new PHG4MvtxSupport(this, m_DisplayAction, OverlapCheck());
0379   mvtxSupportSystem->ConstructMvtxSupport(lv);
0380 
0381   delete mvtxSupportSystem;
0382 
0383   return 0;
0384 }
0385 
0386 // NOLINTNEXTLINE(misc-no-recursion)
0387 void PHG4MvtxDetector::SetDisplayProperty(G4AssemblyVolume *av)
0388 {
0389   //  std::cout <<"SetDisplayProperty - G4AssemblyVolume w/ TotalImprintedVolumes "<<av->TotalImprintedVolumes()
0390   //   <<"/"<<av->GetImprintsCount()<<std::endl;
0391 
0392   std::vector<G4VPhysicalVolume *>::iterator it = av->GetVolumesIterator();
0393 
0394   int nDaughters = av->TotalImprintedVolumes();
0395   for (int i = 0; i < nDaughters; ++i, ++it)
0396   {
0397     if (Verbosity() >= 50)
0398     {
0399       std::cout << "SetDisplayProperty - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0400     }
0401     G4VPhysicalVolume *pv = (*it);
0402 
0403     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0404     SetDisplayProperty(worldLogical);
0405   }
0406 }
0407 
0408 // NOLINTNEXTLINE(misc-no-recursion)
0409 void PHG4MvtxDetector::SetDisplayProperty(G4LogicalVolume *lv)
0410 {
0411   std::string material_name(lv->GetMaterial()->GetName());
0412 
0413   if (Verbosity() >= 50)
0414   {
0415     std::cout << "SetDisplayProperty - LV " << lv->GetName() << " built with " << material_name << std::endl;
0416   }
0417   std::vector<std::string> matname = {"SI", "KAPTON", "ALUMINUM", "Carbon", "M60J3K", "WATER"};
0418   bool found = false;
0419   for (const std::string &nam : matname)
0420   {
0421     if (material_name.find(nam) != std::string::npos)
0422     {
0423       m_DisplayAction->AddVolume(lv, nam);
0424       if (Verbosity() >= 50)
0425       {
0426         std::cout << "SetDisplayProperty - LV " << lv->GetName() << " display with " << nam << std::endl;
0427       }
0428       found = true;
0429       break;
0430     }
0431   }
0432   if (!found)
0433   {
0434     m_DisplayAction->AddVolume(lv, "ANYTHING_ELSE");
0435   }
0436   int nDaughters = lv->GetNoDaughters();
0437   for (int i = 0; i < nDaughters; ++i)
0438   {
0439     G4VPhysicalVolume *pv = lv->GetDaughter(i);
0440 
0441     // std::cout <<"SetDisplayProperty - PV["<<i<<"] = "<<pv->GetName()<<std::endl;
0442 
0443     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0444     SetDisplayProperty(worldLogical);
0445   }
0446 }
0447 
0448 void PHG4MvtxDetector::AddGeometryNode()
0449 {
0450   int active = 0;
0451   // for (auto &isAct : m_IsLayerActive)
0452   // {
0453   //   active |= isAct;
0454   // }
0455   if (std::any_of(m_IsLayerActive.begin(), m_IsLayerActive.end(), [](int isAct) { return isAct != 0; }))
0456   {
0457     active = 1;
0458   }
0459   
0460   if (active)  // At least one layer is active
0461   {
0462     //    ostringstream geonode;
0463     std::string geonode = "CYLINDERGEOM_" + ((m_SuperDetector != "NONE") ? m_SuperDetector : m_Detector);
0464     PHG4CylinderGeomContainer *geo = findNode::getClass<PHG4CylinderGeomContainer>(topNode(), geonode);
0465     if (!geo)
0466     {
0467       geo = new PHG4CylinderGeomContainer();
0468       PHNodeIterator iter(topNode());
0469       PHCompositeNode *runNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "RUN"));
0470       PHIODataNode<PHObject> *newNode = new PHIODataNode<PHObject>(geo, geonode, "PHObject");
0471       runNode->addNode(newNode);
0472     }
0473     // here in the detector class we have internal units(mm), convert to cm
0474     // before putting into the geom object
0475     for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
0476     {
0477       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);
0478       geo->AddLayerGeom(ilayer, mygeom);
0479     }  // loop per layers
0480     if (Verbosity())
0481     {
0482       geo->identify();
0483     }
0484   }  // is active
0485 }  // AddGeometryNode
0486 
0487 void PHG4MvtxDetector::FillPVArray(G4AssemblyVolume *av)
0488 {
0489   if (Verbosity() > 0)
0490   {
0491     std::cout << "-- FillPVArray --" << std::endl;
0492   }
0493   std::vector<G4VPhysicalVolume *>::iterator it = av->GetVolumesIterator();
0494 
0495   int nDaughters = av->TotalImprintedVolumes();
0496   for (int i = 0; i < nDaughters; ++i, ++it)
0497   {
0498     G4VPhysicalVolume *pv = (*it);
0499 
0500     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0501     // we only care about the staves, which contain the sensors, not the structures
0502     if (pv->GetName().find("MVTXHalfStave_pv") != std::string::npos)
0503     {
0504       int layer = get_layer(m_StavePV.size());
0505       int stave = get_stave(m_StavePV.size());
0506 
0507       m_StavePV.insert(std::make_pair(pv, std::make_tuple(layer, stave)));
0508 
0509       if (Verbosity() > 0)
0510       {
0511         std::cout << "Mvtx layer id " << layer << std::endl;
0512         std::cout << "Stave in layer id " << stave << std::endl;
0513         std::cout << "Mvtx stave count " << m_StavePV.size() << std::endl;
0514         std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0515         std::cout << "              LV[" << i << "] = " << worldLogical->GetName() << std::endl;
0516       }
0517 
0518       FindSensor(worldLogical);
0519     }
0520     else  // in case of stave structure
0521     {
0522       if (Verbosity() > 0)
0523       {
0524         std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
0525         std::cout << "              LV[" << i << "] = " << worldLogical->GetName() << std::endl;
0526       }
0527     }
0528   }
0529 }
0530 
0531 // NOLINTNEXTLINE(misc-no-recursion)
0532 void PHG4MvtxDetector::FindSensor(G4LogicalVolume *lv)
0533 {
0534   int nDaughters = lv->GetNoDaughters();
0535   for (int i = 0; i < nDaughters; ++i)
0536   {
0537     G4VPhysicalVolume *pv = lv->GetDaughter(i);
0538     if (Verbosity() > 0)
0539     {
0540       std::cout << "                 PV[" << i << "]: " << pv->GetName() << std::endl;
0541     }
0542     if (pv->GetName().find("MVTXSensor_") != std::string::npos)
0543     {
0544       m_SensorPV.insert(pv);
0545 
0546       if (Verbosity() > 0)
0547       {
0548         std::cout << "                      Adding Sensor Vol <" << pv->GetName() << " (" << m_SensorPV.size() << ")>" << std::endl;
0549       }
0550     }
0551 
0552     G4LogicalVolume *worldLogical = pv->GetLogicalVolume();
0553 
0554     if (Verbosity() > 0)
0555     {
0556       std::cout << "                 LV[" << i << "]: " << worldLogical->GetName() << std::endl;
0557     }
0558     FindSensor(worldLogical);
0559   }
0560 }