Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:20:57

0001 /*!
0002  *  \file       MakeActsGeometry.cc
0003  *  \brief      Refit SvtxTracks with PHActs.
0004  *  \details    Refit SvtxTracks with PHActs.
0005  *  \author         Tony Frawley <afrawley@fsu.edu>
0006  */
0007 
0008 #include "MakeActsGeometry.h"
0009 
0010 #include <trackbase/AlignmentTransformation.h>
0011 #include <trackbase/InttDefs.h>
0012 #include <trackbase/MvtxDefs.h>
0013 #include <trackbase/TpcDefs.h>
0014 #include <trackbase/TrkrDefs.h>
0015 #include <trackbase/alignmentTransformationContainer.h>
0016 #include <trackbase/sPHENIXActsDetectorElement.h>
0017 
0018 #include <intt/CylinderGeomIntt.h>
0019 
0020 #include <mvtx/CylinderGeom_Mvtx.h>
0021 
0022 #include <micromegas/CylinderGeomMicromegas.h>
0023 
0024 #include <micromegas/MicromegasDefs.h>
0025 
0026 #include <g4detectors/PHG4CylinderGeom.h>  // for PHG4CylinderGeom
0027 #include <g4detectors/PHG4CylinderGeomContainer.h>
0028 #include <g4detectors/PHG4TpcGeom.h>
0029 #include <g4detectors/PHG4TpcGeomContainer.h>
0030 
0031 #include <g4mvtx/PHG4MvtxMisalignment.h>
0032 
0033 #include <phgeom/PHGeomIOTGeo.h>
0034 #include <phgeom/PHGeomTGeo.h>
0035 #include <phgeom/PHGeomUtility.h>
0036 
0037 #include <fun4all/Fun4AllReturnCodes.h>
0038 #include <fun4all/Fun4AllServer.h>
0039 
0040 #include <ffamodules/CDBInterface.h>
0041 
0042 #include <phfield/PHField.h>
0043 #include <phfield/PHFieldConfig.h>
0044 #include <phfield/PHFieldConfigv1.h>
0045 #include <phfield/PHFieldConfigv2.h>
0046 #include <phfield/PHFieldUtility.h>
0047 
0048 #include <phool/PHCompositeNode.h>
0049 #include <phool/PHDataNode.h>
0050 #include <phool/PHNode.h>
0051 #include <phool/PHNodeIterator.h>
0052 #include <phool/PHObject.h>
0053 #include <phool/getClass.h>
0054 #include <phool/phool.h>
0055 
0056 #include <Acts/Geometry/GeometryContext.hpp>
0057 #include <Acts/Geometry/TrackingVolume.hpp>
0058 #include <Acts/MagneticField/MagneticFieldContext.hpp>
0059 #include <Acts/Surfaces/PerigeeSurface.hpp>
0060 #include <Acts/Surfaces/PlaneSurface.hpp>
0061 #include <Acts/Surfaces/Surface.hpp>
0062 #include <Acts/Utilities/CalibrationContext.hpp>
0063 
0064 #include <ActsExamples/Framework/IContextDecorator.hpp>
0065 
0066 #pragma GCC diagnostic push
0067 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
0068 #pragma GCC diagnostic ignored "-Wuninitialized"
0069 #include <trackbase/CommonOptions.h>
0070 #pragma GCC diagnostic pop
0071 
0072 #include <trackbase/MagneticFieldOptions.h>
0073 #include <ActsExamples/Utilities/Options.hpp>
0074 
0075 #include <ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp>
0076 
0077 #include <Acts/Material/IMaterialDecorator.hpp>
0078 #include <Acts/Plugins/Json/JsonMaterialDecorator.hpp>
0079 #include <Acts/Plugins/Json/MaterialMapJsonConverter.hpp>
0080 #include <trackbase/MaterialWiper.h>
0081 
0082 #include <TGeoManager.h>
0083 #include <TMatrixT.h>
0084 #include <TObject.h>
0085 #include <TSystem.h>
0086 #include <TVector3.h>
0087 
0088 #include <cmath>
0089 #include <cstddef>
0090 #include <cstdlib>
0091 #include <filesystem>
0092 #include <iostream>
0093 #include <map>
0094 #include <memory>
0095 #include <utility>
0096 #include <vector>
0097 
0098 namespace
0099 {
0100   // navigate Acts volumes to find one matching a given name (recursive)
0101   // NOLINTNEXTLINE(misc-no-recursion)
0102   TrackingVolumePtr find_volume_by_name(const Acts::TrackingVolume *master, const std::string &name)
0103   {
0104     // skip if name is not composite
0105     if (master->volumeName().empty() || master->volumeName()[0] != '{')
0106     {
0107       return nullptr;
0108     }
0109 
0110     // loop over children
0111     for (const auto &child : master->confinedVolumes()->arrayObjects())
0112     {
0113       if (child->volumeName() == name)
0114       {
0115         return child;
0116       }
0117       else if (auto found = find_volume_by_name(child.get(), name))
0118       {
0119         return found;
0120       }
0121     }
0122 
0123     // not found
0124     return nullptr;
0125   }
0126 
0127   template <class T>
0128   inline constexpr T square(const T &x)
0129   {
0130     return x * x;
0131   }
0132 
0133   template <class T>
0134   inline T get_r(const T &x, const T &y)
0135   {
0136     return std::sqrt(square(x) + square(y));
0137   }
0138 
0139   // returns true if magnetic field string corresponds to constant value
0140   bool isConstantField(const std::string &name, double &fieldstrength)
0141   {
0142 
0143     std::istringstream stringline(name);
0144     stringline >> fieldstrength;
0145     if (stringline.fail())
0146     {
0147       // conversion to double fails -> we have a string (means fieldmap)
0148       fieldstrength = std::numeric_limits<double>::quiet_NaN();
0149       return false;
0150     }
0151     else
0152     {
0153       std::cout << "isConstantField - name: " << name << " field strength: " << fieldstrength << std::endl;
0154       return true;
0155     }
0156   }
0157 
0158 }  // namespace
0159 
0160 MakeActsGeometry::MakeActsGeometry(const std::string &name)
0161   : SubsysReco(name)
0162 {
0163   for (int layer = 0; layer < 57; layer++)
0164   {
0165     m_misalignmentFactor.insert(std::make_pair(layer, 1.));
0166   }
0167 }
0168 
0169 int MakeActsGeometry::Init(PHCompositeNode * /*topNode*/)
0170 {
0171   return Fun4AllReturnCodes::EVENT_OK;
0172 }
0173 
0174 int MakeActsGeometry::InitRun(PHCompositeNode *topNode)
0175 {
0176   m_geomContainerTpc =
0177       findNode::getClass<PHG4TpcGeomContainer>(topNode, "TPCGEOMCONTAINER");
0178 
0179   PHG4TpcGeom *layergeom = m_geomContainerTpc->GetLayerCellGeom(20);  // z geometry is the same for all layers
0180   m_max_driftlength = layergeom->get_max_driftlength();
0181   m_CM_halfwidth = layergeom->get_CM_halfwidth();
0182   
0183   m_maxSurfZ = m_max_driftlength - 0.0001; // add clearance from physical TPC gas volume length to avoid overlaps
0184     
0185   // Alignment Transformation declaration of instance - must be here to set initial alignment flag
0186   AlignmentTransformation alignment_transformation;
0187   alignment_transformation.createAlignmentTransformContainer(topNode);
0188 
0189   // set parameter for sampling probability distribution
0190   if (mvtxParam)
0191   {
0192     alignment_transformation.setMVTXParams(m_mvtxDevs);
0193   }
0194   if (inttParam)
0195   {
0196     alignment_transformation.setINTTParams(m_inttDevs);
0197   }
0198   if (tpcParam)
0199   {
0200     alignment_transformation.setTPCParams(m_tpcDevs);
0201   }
0202   if (mmParam)
0203   {
0204     alignment_transformation.setMMParams(m_mmDevs);
0205   }
0206 
0207   if (buildAllGeometry(topNode) != Fun4AllReturnCodes::EVENT_OK)
0208   {
0209     return Fun4AllReturnCodes::ABORTEVENT;
0210   }
0211 
0212   // load relevant magnetic field map on the node tree
0213   double fieldstrength = std::numeric_limits<double>::quiet_NaN();
0214   if( isConstantField( m_magField, fieldstrength ) )
0215   {
0216 
0217     // create uniform field
0218     PHFieldConfigv2 fcfg;
0219     fcfg.set_field_config(PHFieldConfig::FieldConfigTypes::kFieldUniform);
0220     fcfg.set_field_mag_x(0);
0221     fcfg.set_field_mag_y(0);
0222     fcfg.set_field_mag_z(fieldstrength);
0223 
0224     /*
0225      * also apply rescale, consistent with magnetic field used by ACTS
0226      * as defined in MakeActsGeometry::buildActsSurfaces
0227      */
0228     fcfg.set_magfield_rescale( m_magFieldRescale );
0229 
0230     std::cout << "MakeActsGeometry::InitRun - field config: " << std::endl;
0231     fcfg.identify();
0232 
0233     // create corresponding map and store on node tree
0234     PHFieldUtility::GetFieldMapNode(&fcfg, topNode);
0235 
0236   } else {
0237 
0238     if (std::filesystem::path(m_magField).extension() != ".root")
0239     { m_magField = CDBInterface::instance()->getUrl(m_magField); }
0240 
0241     if (!std::filesystem::exists(m_magField))
0242     {
0243       if (m_magField.empty())
0244       { m_magField = "empty string"; }
0245       std::cout << "MakeActsGeometry::InitRun - Fieldmap " << m_magField << " does not exist" << std::endl;
0246       gSystem->Exit(1);
0247     }
0248 
0249     PHFieldConfigv1 fcfg;
0250     fcfg.set_field_config(PHFieldConfig::FieldConfigTypes::Field3DCartesian);
0251     fcfg.set_filename(m_magField);
0252     fcfg.set_magfield_rescale( m_magFieldRescale );
0253 
0254     // create corresponding map and store on node tree
0255     PHFieldUtility::GetFieldMapNode(&fcfg, topNode);
0256   }
0257 
0258   // Set the actsGeometry struct to be put on the node tree
0259   ActsTrackingGeometry trackingGeometry;
0260   trackingGeometry.tGeometry = m_tGeometry;
0261   trackingGeometry.magField = m_magneticField;
0262   trackingGeometry.getGeoContext() = m_geoCtxt;  // set reference as plain geocontext
0263   trackingGeometry.tpcSurfStepPhi = m_surfStepPhi;
0264   trackingGeometry.tpcSurfStepZ = m_surfStepZ;
0265 
0266   // fill ActsSurfaceMap content
0267   ActsSurfaceMaps surfMaps;
0268   surfMaps.m_siliconSurfaceMap = m_clusterSurfaceMapSilicon;
0269   surfMaps.m_tpcSurfaceMap = m_clusterSurfaceMapTpcEdit;
0270   surfMaps.m_mmSurfaceMap = m_clusterSurfaceMapMmEdit;
0271   surfMaps.m_tGeoNodeMap = m_clusterNodeMap;
0272 
0273   // fill TPC volume ids
0274   for (const auto &[hitsetid, surfaceVector] : m_clusterSurfaceMapTpcEdit)
0275   {
0276     for (const auto &surface : surfaceVector)
0277     {
0278       surfMaps.m_tpcVolumeIds.insert(surface->geometryId().volume());
0279     }
0280   }
0281 
0282   // fill Micromegas volume ids
0283   for (const auto &[hitsetid, surface] : m_clusterSurfaceMapMmEdit)
0284   {
0285     surfMaps.m_micromegasVolumeIds.insert(surface->geometryId().volume());
0286   }
0287 
0288   m_actsGeometry->setGeometry(trackingGeometry);
0289   m_actsGeometry->setSurfMaps(surfMaps);
0290   m_actsGeometry->set_drift_velocity(m_drift_velocity);
0291   m_actsGeometry->set_max_driftlength(m_max_driftlength);
0292   m_actsGeometry->set_CM_halfwidth(m_CM_halfwidth);
0293   m_actsGeometry->set_tpc_tzero(m_tpc_tzero);
0294   m_actsGeometry->set_sampa_tzero_bias(m_sampa_tzero_bias);
0295   // alignment_transformation.useInttSurveyGeometry(m_inttSurvey);
0296   if (Verbosity() > 1)
0297   {
0298     alignment_transformation.verbosity();
0299   }
0300   alignment_transformation.createMap(topNode);
0301 
0302   for (auto &[layer, factor] : m_misalignmentFactor)
0303   {
0304     alignment_transformation.misalignmentFactor(layer, factor);
0305   }
0306 
0307   // print
0308   if (Verbosity())
0309   {
0310     for (const auto &id : surfMaps.m_tpcVolumeIds)
0311     {
0312       std::cout << "MakeActsGeometry::InitRun - TPC volume id: " << id << std::endl;
0313     }
0314 
0315     for (const auto &id : surfMaps.m_micromegasVolumeIds)
0316     {
0317       std::cout << "MakeActsGeometry::InitRun - Micromegas volume id: " << id << std::endl;
0318     }
0319   }
0320 
0321   return Fun4AllReturnCodes::EVENT_OK;
0322 }
0323 
0324 int MakeActsGeometry::buildAllGeometry(PHCompositeNode *topNode)
0325 {
0326   // Add the TPC surfaces to the copy of the TGeoManager.
0327   // this also adds the micromegas surfaces
0328   // Do this before anything else, so that the geometry is finalized
0329 
0330   if (getNodes(topNode) != Fun4AllReturnCodes::EVENT_OK)
0331   {
0332     return Fun4AllReturnCodes::ABORTEVENT;
0333   }
0334 
0335   setPlanarSurfaceDivisions();  // eshulga
0336   // This should be done only on the first tracking pass, to avoid adding surfaces twice.
0337   // There is a check for existing acts fake surfaces in editTPCGeometry
0338   editTPCGeometry(topNode);
0339 
0340   // Export the new geometry to a root file for examination
0341   if (Verbosity() > 3)
0342   {
0343     PHGeomUtility::ExportGeomtry(topNode, "sPHENIXActsGeom.root");
0344     PHGeomUtility::ExportGeomtry(topNode, "sPHENIXActsGeom.gdml");
0345   }
0346 
0347   if (createNodes(topNode) != Fun4AllReturnCodes::EVENT_OK)
0348   {
0349     return Fun4AllReturnCodes::ABORTEVENT;
0350   }
0351 
0352   // Run Acts layer builder
0353   buildActsSurfaces();
0354 
0355   // Create a map of sensor TGeoNode pointers using the TrkrDefs:: hitsetkey as the key
0356   // makeTGeoNodeMap(topNode);
0357 
0358   return Fun4AllReturnCodes::EVENT_OK;
0359 }
0360 
0361 void MakeActsGeometry::editTPCGeometry(PHCompositeNode *topNode)
0362 {
0363   // Because we reset and rebuild the geomNode, we do edits of the TPC geometry in the same module
0364 
0365   PHGeomTGeo *geomNode = PHGeomUtility::GetGeomTGeoNode(topNode, true);
0366   assert(geomNode);
0367 
0368   // Reset the geometry node, which we will recreate with the TPC edits
0369   if (geomNode->isValid())
0370   {
0371     geomNode->Reset();
0372   }
0373 
0374   PHGeomIOTGeo *dstGeomIO = PHGeomUtility::GetGeomIOTGeoNode(topNode, false);
0375   assert(dstGeomIO);
0376   assert(dstGeomIO->isValid());
0377 
0378   TGeoManager *geoManager = dstGeomIO->ConstructTGeoManager();
0379   geomNode->SetGeometry(geoManager);
0380   assert(geoManager);
0381 
0382   if (TGeoManager::GetDefaultUnits() != TGeoManager::EDefaultUnits::kRootUnits)
0383   {
0384     std::cerr << "There is a potential unit mismatch in the ROOT geometry."
0385               << " It is dangerous to continue and may lead to inconsistencies in the Acts geometry. Exiting."
0386               << std::endl;
0387     gSystem->Exit(1);
0388   }
0389 
0390   TGeoVolume *World_vol = geoManager->GetTopVolume();
0391 
0392   // TPC geometry edits
0393   //===============
0394 
0395   TGeoNode *tpc_envelope_node = nullptr;
0396   TGeoNode *tpc_gas_north_node = nullptr;
0397 
0398   // find tpc north gas volume at path of World*/tpc_envelope*
0399   if (Verbosity() > 3)
0400   {
0401     std::cout << "EditTPCGeometry - searching under volume: ";
0402     World_vol->Print();
0403   }
0404   for (int i = 0; i < World_vol->GetNdaughters(); i++)
0405   {
0406     TString node_name = World_vol->GetNode(i)->GetName();
0407     if (node_name.BeginsWith("tpc_envelope"))
0408     {
0409       if (Verbosity())
0410       {
0411         std::cout << "EditTPCGeometry - found " << node_name << std::endl;
0412       }
0413 
0414       tpc_envelope_node = World_vol->GetNode(i);
0415       break;
0416     }
0417   }
0418 
0419   assert(tpc_envelope_node);
0420 
0421   // find tpc north gas volume at path of World*/tpc_envelope*/tpc_gas_north*
0422   TGeoVolume *tpc_envelope_vol = tpc_envelope_node->GetVolume();
0423   assert(tpc_envelope_vol);
0424   if (Verbosity() > 3)
0425   {
0426     std::cout << "EditTPCGeometry - searching under volume: ";
0427     tpc_envelope_vol->Print();
0428   }
0429 
0430   for (int i = 0; i < tpc_envelope_vol->GetNdaughters(); i++)
0431   {
0432     TString node_name = tpc_envelope_vol->GetNode(i)->GetName();
0433 
0434     if (node_name.BeginsWith("tpc_gas_north"))
0435     {
0436       if (Verbosity())
0437       {
0438         std::cout << "EditTPCGeometry - found " << node_name << std::endl;
0439       }
0440 
0441       tpc_gas_north_node = tpc_envelope_vol->GetNode(i);
0442       break;
0443     }
0444   }
0445 
0446   assert(tpc_gas_north_node);
0447   TGeoVolume *tpc_gas_north_vol = tpc_gas_north_node->GetVolume();
0448   assert(tpc_gas_north_vol);
0449 
0450   int nfakesurfaces = 0;
0451   for (int i = 0; i < tpc_gas_north_vol->GetNdaughters(); i++)
0452   {
0453     TString node_name = tpc_gas_north_vol->GetNode(i)->GetName();
0454     if (node_name.BeginsWith("tpc_gas_measurement_"))
0455     {
0456       nfakesurfaces++;
0457     }
0458   }
0459 
0460   // Make a check for the fake surfaces. If we have more than 0
0461   // then we've built the fake surfaces and we should not do it again
0462   if (nfakesurfaces > 0)
0463   {
0464     return;
0465   }
0466 
0467   if (Verbosity() > 3)
0468   {
0469     std::cout << "EditTPCGeometry - gas volume: ";
0470     tpc_gas_north_vol->Print();
0471   }
0472 
0473   // adds surfaces to the underlying volume, so both north and south placements get them
0474   addActsTpcSurfaces(tpc_gas_north_vol, geoManager);
0475 
0476   // done
0477   geoManager->CloseGeometry();
0478 
0479   // save the edited geometry to DST persistent IO node for downstream DST files
0480   PHGeomUtility::UpdateIONode(topNode);
0481 }
0482 
0483 void MakeActsGeometry::addActsTpcSurfaces(TGeoVolume *tpc_gas_vol,
0484                                           TGeoManager *geoManager)
0485 {
0486   TGeoMedium *tpc_gas_medium = tpc_gas_vol->GetMedium();
0487   assert(tpc_gas_medium);
0488 
0489   // printout
0490   std::cout << "MakeActsGeometry::addActsTpcSurfaces - m_nSurfPhi: " << m_nSurfPhi << std::endl;
0491 
0492   TGeoVolume *tpc_gas_measurement_vol[m_nTpcLayers];
0493   double tan_half_phi = tan(m_surfStepPhi / 2.0);
0494   int copy = 0;
0495   for (unsigned int ilayer = 0; ilayer < m_nTpcLayers; ++ilayer)
0496   {
0497     // make a box for this layer
0498     std::string bname = "tpc_gas_measurement_" + std::to_string(ilayer);
0499     // Because we use a box, not a section of a cylinder, we need this to prevent overlaps
0500     // set the nominal r*phi dimension of the box so they just touch at the inner edge when placed
0501     double box_r_phi = 2.0 * tan_half_phi *
0502                        (m_layerRadius[ilayer] - m_layerThickness[ilayer] / 2.0);
0503 
0504     tpc_gas_measurement_vol[ilayer] = geoManager->MakeBox(bname.c_str(), tpc_gas_medium,
0505                                                           m_layerThickness[ilayer] * half_width_clearance_thick,
0506                                                           box_r_phi * half_width_clearance_phi,
0507                                                           m_surfStepZ * half_width_clearance_z);
0508 
0509     tpc_gas_measurement_vol[ilayer]->SetLineColor(kBlack);
0510     tpc_gas_measurement_vol[ilayer]->SetFillColor(kYellow);
0511     tpc_gas_measurement_vol[ilayer]->SetVisibility(kTRUE);
0512 
0513     if (Verbosity() > 3)
0514     {
0515       std::cout << " Made box for layer " << ilayer
0516                 << " with dx " << m_layerThickness[ilayer] << " dy "
0517                 << box_r_phi << " ref arc "
0518                 << m_surfStepPhi * m_layerRadius[ilayer] << " dz "
0519                 << m_surfStepZ << std::endl;
0520       tpc_gas_measurement_vol[ilayer]->Print();
0521       tpc_gas_measurement_vol[ilayer]->CheckOverlaps();
0522     }
0523 
0524     for (unsigned int iz = 0; iz < m_nSurfZ; ++iz)
0525     {
0526       // The mother is tpc_gas_volume (which is placed as tpc_gas_north and tpc_gas_south)
0527       // tpc_gas_north and tpc_gas_south are offset from zero in the tpc volume by half the CM thickness
0528       // so we center the fake surfaces in tpc_gas_volume
0529       // the fake surfaces are thus included in both tpc_gas_north and tpc_gas_south
0530       double z_center = 0.0;
0531 
0532       for (unsigned int imod = 0; imod < m_nTpcModulesPerLayer; ++imod)
0533       {
0534         for (unsigned int iphi = 0; iphi < m_nSurfPhi; ++iphi)
0535         {
0536           double min_phi = m_modulePhiStart +
0537                            (double) imod * m_moduleStepPhi +
0538                            (double) iphi * m_surfStepPhi;
0539           double phi_center = min_phi + m_surfStepPhi / 2.0;
0540           double phi_center_degrees = phi_center * 180.0 / M_PI;
0541 
0542           // place copies of the gas volume to fill up the layer
0543 
0544           double x_center = m_layerRadius[ilayer] * cos(phi_center);
0545           double y_center = m_layerRadius[ilayer] * sin(phi_center);
0546 
0547           std::string rot_name = "tpc_gas_rotation_" + std::to_string(copy);
0548           TGeoCombiTrans *tpc_gas_measurement_location = new TGeoCombiTrans(x_center, y_center, z_center,
0549                                                                             new TGeoRotation(rot_name.c_str(),
0550                                                                                              phi_center_degrees,
0551                                                                                              0, 0));
0552 
0553           tpc_gas_vol->AddNode(tpc_gas_measurement_vol[ilayer],
0554                                copy, tpc_gas_measurement_location);
0555 
0556           copy++;
0557           if (Verbosity() > 3)
0558           {
0559             std::cout << "Box center : (" << x_center << ", " << y_center
0560                       << ", " << z_center << ")"
0561                       << " and in rphiz "
0562                       << sqrt(x_center * x_center + y_center * y_center)
0563                       << ", " << atan2(y_center, x_center) << ", "
0564                       << z_center << std::endl;
0565             std::cout << "Box dimensions " << m_layerThickness[ilayer] * half_width_clearance_thick
0566                       << " , " << box_r_phi / (m_layerRadius[ilayer] - m_layerThickness[ilayer] / 2.) * half_width_clearance_phi
0567                       << ", " << m_surfStepZ * half_width_clearance_z << " and in xyz "
0568                       << m_layerThickness[ilayer] * half_width_clearance_thick * cos(box_r_phi / (m_layerRadius[ilayer] - m_layerThickness[ilayer] / 2.) * half_width_clearance_phi) << ", "
0569                       << m_layerThickness[ilayer] * half_width_clearance_thick * sin(box_r_phi / (m_layerRadius[ilayer] - m_layerThickness[ilayer] / 2.) * half_width_clearance_phi) << ", "
0570                       << m_surfStepZ * half_width_clearance_z << std::endl;
0571           }
0572         }
0573       }
0574     }
0575   }
0576 }
0577 
0578 /**
0579  * Builds silicon layers and TPC geometry in the ACTS surface world
0580  */
0581 void MakeActsGeometry::buildActsSurfaces()
0582 {
0583   // define int argc and char* argv to provide options to processGeometry
0584   static constexpr int argc = 20;
0585   char *argv[argc];
0586   m_magFieldRescale = 1;
0587 
0588   // if(Verbosity() > 0)
0589   std::cout << PHWHERE << "Magnetic field " << m_magField
0590             << " with rescale " << m_magFieldRescale << std::endl;
0591 
0592   std::string responseFile, materialFile;
0593   setMaterialResponseFile(responseFile, materialFile);
0594 
0595   // arguments
0596   // material and response file contains arguments necessary for geometry building
0597   std::vector<std::string> argstr =
0598   {
0599     "-n1",
0600     "--geo-tgeo-jsonconfig", responseFile,
0601     "--mat-input-type", "file",
0602     "--mat-input-file", materialFile
0603   };
0604 
0605   double fieldstrength = std::numeric_limits<double>::quiet_NaN();
0606   if( isConstantField( m_magField, fieldstrength ) )
0607   {
0608     std::ostringstream fld;
0609     fld.str("");
0610     fld << "0:0:" << fieldstrength;
0611 
0612     argstr.insert( argstr.end(),
0613     {
0614       "--bf-constant-tesla", fld.str().c_str(),
0615       "--bf-map-fieldscale-tesla", std::to_string(m_magFieldRescale)
0616     });
0617 
0618   } else {
0619 
0620     // get field from cdb
0621     if (std::filesystem::path(m_magField).extension() != ".root")
0622     { m_magField = CDBInterface::instance()->getUrl(m_magField); }
0623 
0624     // update arguments
0625     if (std::filesystem::exists(m_magField))
0626     {
0627       argstr.insert( argstr.end(),
0628       {
0629         "--bf-map-file", m_magField,
0630         "--bf-map-tree", "fieldmap",
0631         "--bf-map-lengthscale-mm", "10",
0632         "--bf-map-fieldscale-tesla", std::to_string(m_magFieldRescale)
0633       });
0634     }
0635   }
0636 
0637   //  if(Verbosity() > 0)
0638   std::cout << "Mag field now " << m_magField << " with rescale " << m_magFieldRescale << std::endl;
0639 
0640   // convert arguments to char**
0641   assert(argstr.size()<argc);
0642 
0643   // print arguments
0644   if (Verbosity() > 1)
0645   {
0646     std::cout << "MakeActsGeometry::buildActsSurfaces - arguments: " << std::endl;
0647     for( const auto& arg:argstr ) { std::cout << arg << ", "; }
0648     std::cout << std::endl;
0649   }
0650 
0651 
0652   // Set vector of chars to arguments needed
0653   for (size_t i = 0; i < argstr.size(); ++i)
0654   {
0655     // need a copy, since .c_str() returns a const char * and process geometry will not take a const
0656     argv[i] = strdup(argstr[i].c_str());
0657   }
0658 
0659   // We replicate the relevant functionality of
0660   // acts/Examples/Run/Common/src/GeometryExampleBase::ProcessGeometry() in MakeActsGeometry()
0661   // so we get access to the results. The layer builder magically gets the TGeoManager
0662 
0663   makeGeometry(argstr.size(), argv, m_detector);
0664 
0665   for (size_t i = 0; i < argstr.size(); ++i)
0666   {
0667     // NOLINTNEXTLINE(hicpp-no-malloc)
0668     free(argv[i]);
0669   }
0670 }
0671 
0672 
0673 void MakeActsGeometry::setMaterialResponseFile(std::string &responseFile,
0674                                                std::string &materialFile)
0675 {
0676   responseFile = "tgeo-sphenix-mms.json";
0677   materialFile = "sphenix-mm-material.json";
0678   // Check to see if files exist locally - if not, use defaults
0679   std::ifstream file;
0680 
0681   file.open(responseFile);
0682   if (!file.is_open())
0683   {
0684     std::cout << responseFile
0685               << " not found locally, use CDB version"
0686               << std::endl;
0687     responseFile = CDBInterface::instance()->getUrl("ACTSGEOMETRYCONFIG");
0688   }
0689 
0690   file.open(materialFile);
0691   if (!file.is_open())
0692   {
0693     std::cout << materialFile
0694               << " not found locally, use CDB version"
0695               << std::endl;
0696     materialFile = CDBInterface::instance()->getUrl("ACTSMATERIALMAP");
0697   }
0698 
0699     std::cout << "using Acts material file : " << materialFile
0700               << std::endl;
0701     std::cout << "Using Acts TGeoResponse file : " << responseFile
0702               << std::endl;
0703 
0704   return;
0705 }
0706 void MakeActsGeometry::makeGeometry(int argc, char *argv[],
0707                                     ActsExamples::TGeoDetectorWithOptions &detector)
0708 {
0709   // setup and parse options
0710   boost::program_options::options_description desc;
0711   ActsExamples::Options::addGeometryOptions(desc);
0712   ActsExamples::Options::addMaterialOptions(desc);
0713   ActsExamples::Options::addMagneticFieldOptions(desc);
0714 
0715   // Add specific options for this geometry
0716   detector.addOptions(desc);
0717   auto vm = ActsExamples::Options::parse(desc, argc, argv);
0718 
0719   // The geometry, material and decoration
0720   auto geometry = build(vm, detector);
0721   // Geometry is a pair of (tgeoTrackingGeometry, tgeoContextDecorators)
0722 
0723   m_tGeometry = geometry.first;
0724   if (m_useField)
0725   {
0726     m_magneticField = ActsExamples::Options::readMagneticField(vm);
0727   }
0728   else
0729   {
0730     m_magneticField = nullptr;
0731   }
0732 
0733   m_geoCtxt = Acts::GeometryContext();
0734 
0735   unpackVolumes();
0736 
0737   return;
0738 }
0739 
0740 std::pair<std::shared_ptr<const Acts::TrackingGeometry>,
0741           std::vector<std::shared_ptr<ActsExamples::IContextDecorator>>>
0742 MakeActsGeometry::build(const boost::program_options::variables_map &vm,
0743                         ActsExamples::TGeoDetectorWithOptions &detector)
0744 {
0745   // Material decoration
0746   std::shared_ptr<const Acts::IMaterialDecorator> matDeco = nullptr;
0747 
0748   // Retrieve the filename
0749   auto fileName = vm["mat-input-file"].template as<std::string>();
0750   // json or root based decorator
0751   if (fileName.find(".json") != std::string::npos ||
0752       fileName.find(".cbor") != std::string::npos)
0753   {
0754     // Set up the converter first
0755     Acts::MaterialMapJsonConverter::Config jsonGeoConvConfig;
0756     // Set up the json-based decorator
0757     matDeco = std::make_shared<const Acts::JsonMaterialDecorator>(
0758         jsonGeoConvConfig, fileName, Acts::Logging::FATAL);
0759   }
0760   else
0761   {
0762     matDeco = std::make_shared<const Acts::MaterialWiper>();
0763   }
0764 
0765   ActsExamples::TGeoDetector::Config config;
0766 
0767   config.elementFactory = sPHENIXElementFactory;
0768 
0769   config.fileName = vm["geo-tgeo-filename"].as<std::string>();
0770 
0771   config.surfaceLogLevel = Acts::Logging::FATAL;
0772   config.layerLogLevel = Acts::Logging::FATAL;
0773   config.volumeLogLevel = Acts::Logging::FATAL;
0774 
0775   const auto path = vm["geo-tgeo-jsonconfig"].template as<std::string>();
0776 
0777   readTGeoLayerBuilderConfigsFile(path, config);
0778 
0779   // Return the geometry and context decorators
0780   return detector.m_detector.finalize(config, matDeco);
0781 }
0782 
0783 void MakeActsGeometry::readTGeoLayerBuilderConfigsFile(const std::string &path,
0784                                                        ActsExamples::TGeoDetector::Config &config)
0785 {
0786   if (path.empty())
0787   {
0788     std::cout << "There is no acts geometry response file loaded. Cannot build, exiting"
0789               << std::endl;
0790     exit(1);
0791   }
0792 
0793   nlohmann::json djson;
0794   std::ifstream infile(path, std::ifstream::in | std::ifstream::binary);
0795   infile >> djson;
0796 
0797   config.unitScalor = djson["geo-tgeo-unit-scalor"];
0798 
0799   config.buildBeamPipe = djson["geo-tgeo-build-beampipe"];
0800   if (config.buildBeamPipe)
0801   {
0802     const auto beamPipeParameters =
0803         djson["geo-tgeo-beampipe-parameters"].get<std::array<double, 3>>();
0804     config.beamPipeRadius = beamPipeParameters[0];
0805     config.beamPipeHalflengthZ = beamPipeParameters[1];
0806     config.beamPipeLayerThickness = beamPipeParameters[2];
0807   }
0808 
0809   // Fill nested volume configs
0810   for (const auto &volume : djson["Volumes"])
0811   {
0812     auto &vol = config.volumes.emplace_back();
0813     vol = volume;
0814   }
0815 }
0816 
0817 void MakeActsGeometry::unpackVolumes()
0818 {
0819   // m_tGeometry is a TrackingGeometry pointer
0820   // vol is a TrackingVolume pointer
0821   auto vol = m_tGeometry->highestTrackingVolume();
0822 
0823   if (Verbosity() > 3)
0824   {
0825     std::cout << "MakeActsGeometry::unpackVolumes - top volume: " << vol->volumeName() << std::endl;
0826     std::cout << "Before: Mvtx: m_clusterSurfaceMapSilicon size    " << m_clusterSurfaceMapSilicon.size() << std::endl;
0827     std::cout << "Before: m_clusterSurfaceMapTpc size    " << m_clusterSurfaceMapTpcEdit.size() << std::endl;
0828   }
0829   {
0830     // micromegas
0831     auto mmBarrel = find_volume_by_name(vol, "MICROMEGAS::Barrel");
0832     assert(mmBarrel);
0833     makeMmMapPairs(mmBarrel);
0834   }
0835   {
0836     // MVTX
0837     auto mvtxBarrel = find_volume_by_name(vol, "MVTX::Barrel");
0838     assert(mvtxBarrel);
0839     makeMvtxMapPairs(mvtxBarrel);
0840     if (Verbosity() > 3)
0841     {
0842       std::cout << "After: Mvtx: m_clusterSurfaceMapSilicon size    " << m_clusterSurfaceMapSilicon.size() << std::endl;
0843     }
0844   }
0845 
0846   {
0847     // INTT
0848     if (Verbosity() > 3)
0849     {
0850       std::cout << "Before: INTT: m_clusterSurfaceMapSilicon size    " << m_clusterSurfaceMapSilicon.size() << std::endl;
0851     }
0852     auto inttBarrel = find_volume_by_name(vol, "Silicon::Barrel");
0853     assert(inttBarrel);
0854     makeInttMapPairs(inttBarrel);
0855     if (Verbosity() > 3)
0856     {
0857       std::cout << "After: INTT: m_clusterSurfaceMapSilicon size    " << m_clusterSurfaceMapSilicon.size() << std::endl;
0858     }
0859   }
0860 
0861   {
0862     // TPC
0863     auto tpcBarrel = find_volume_by_name(vol, "TPC::Barrel");
0864     assert(tpcBarrel);
0865     makeTpcMapPairs(tpcBarrel);
0866     if (Verbosity() > 3)
0867     {
0868       std::cout << "After: m_clusterSurfaceMapTpc size    " << m_clusterSurfaceMapTpcEdit.size() << std::endl;
0869     }
0870   }
0871 
0872   return;
0873 }
0874 
0875 void MakeActsGeometry::makeTpcMapPairs(TrackingVolumePtr &tpcVolume)
0876 {
0877   if (Verbosity() > 10)
0878   {
0879     std::cout << "MakeActsGeometry::makeTpcMapPairs - tpcVolume: " << tpcVolume->volumeName() << std::endl;
0880   }
0881 
0882   auto tpcLayerArray = tpcVolume->confinedLayers();
0883   auto tpcLayerVector = tpcLayerArray->arrayObjects();
0884 
0885   // Need to unfold each layer that Acts builds
0886   for (auto &i : tpcLayerVector)
0887   {
0888     auto surfaceArray = i->surfaceArray();
0889     if (surfaceArray == nullptr)
0890     {
0891       continue;
0892     }
0893     // surfaceVector is a vector of surfaces corresponding to the tpc layer
0894     // that acts builds
0895     auto surfaceVector = surfaceArray->surfaces();
0896     for (auto &j : surfaceVector)
0897     {
0898       auto surf = j->getSharedPtr();
0899       auto vec3d = surf->center(m_geoCtxt);
0900 
0901       // convert to cm
0902       std::vector<double> world_center = {vec3d(0) / 10.0,
0903                                           vec3d(1) / 10.0,
0904                                           vec3d(2) / 10.0};
0905 
0906       TrkrDefs::hitsetkey hitsetkey = getTpcHitSetKeyFromCoords(world_center);
0907       unsigned int layer = TrkrDefs::getLayer(hitsetkey);
0908 
0909       // If there is already an entry for this hitsetkey, add the surface
0910       // to its corresponding vector
0911       // std::map<TrkrDefs::hitsetkey, std::vector<Surface>>::iterator mapIter;
0912       std::map<unsigned int, std::vector<Surface>>::iterator mapIter;
0913       // mapIter = m_clusterSurfaceMapTpcEdit.find(hitsetkey);
0914       mapIter = m_clusterSurfaceMapTpcEdit.find(layer);
0915 
0916       if (mapIter != m_clusterSurfaceMapTpcEdit.end())
0917       {
0918         mapIter->second.push_back(surf);
0919       }
0920       else
0921       {
0922         // Otherwise make a new map entry
0923         std::vector<Surface> dumvec;
0924         dumvec.push_back(surf);
0925         std::pair<unsigned int, std::vector<Surface>> tmp =
0926             std::make_pair(layer, dumvec);
0927         m_clusterSurfaceMapTpcEdit.insert(tmp);
0928       }
0929     }
0930   }
0931 }
0932 
0933 //____________________________________________________________________________________________
0934 void MakeActsGeometry::makeMmMapPairs(TrackingVolumePtr &mmVolume)
0935 {
0936   if (Verbosity())
0937   {
0938     std::cout << "MakeActsGeometry::makeMmMapPairs - mmVolume: " << mmVolume->volumeName() << std::endl;
0939   }
0940   const auto mmLayerArray = mmVolume->confinedLayers();
0941   const auto mmLayerVector = mmLayerArray->arrayObjects();
0942 
0943   // Need to unfold each layer that Acts builds
0944   for (const auto &i : mmLayerVector)
0945   {
0946     auto surfaceArray = i->surfaceArray();
0947     if (!surfaceArray)
0948     {
0949       continue;
0950     }
0951 
0952     // surfaceVector is a vector of surfaces corresponding to the micromegas layer
0953     // that acts builds
0954     const auto surfaceVector = surfaceArray->surfaces();
0955 
0956     for (auto j : surfaceVector)
0957     {
0958       auto surface = j->getSharedPtr();
0959       auto vec3d = surface->center(m_geoCtxt);
0960 
0961       // convert to cm
0962       TVector3 world_center(
0963           vec3d(0) / Acts::UnitConstants::cm,
0964           vec3d(1) / Acts::UnitConstants::cm,
0965           vec3d(2) / Acts::UnitConstants::cm);
0966 
0967       // get relevant layer
0968       int layer = -1;
0969       CylinderGeomMicromegas *layergeom = nullptr;
0970       const auto range = m_geomContainerMicromegas->get_begin_end();
0971       double delta_r_min = -1;
0972       for (auto iter = range.first; iter != range.second; ++iter)
0973       {
0974         auto this_layergeom = static_cast<CylinderGeomMicromegas *>(iter->second);
0975         const double delta_r = std::abs(this_layergeom->get_radius() - get_r(world_center.x(), world_center.y()));
0976         if (delta_r_min < 0 || delta_r < delta_r_min)
0977         {
0978           layer = iter->first;
0979           layergeom = this_layergeom;
0980           delta_r_min = delta_r;
0981         }
0982       }
0983 
0984       if (!layergeom)
0985       {
0986         std::cout << "MakeActsGeometry::makeMmMapPairs - could not file CylinderGeomMicromegas matching ACTS surface" << std::endl;
0987         continue;
0988       }
0989 
0990       // get matching tile
0991       int tileid = layergeom->find_tile_cylindrical(world_center);
0992       if (tileid < 0)
0993       {
0994         std::cout << "MakeActsGeometry::makeMmMapPairs - could not file Micromegas tile matching ACTS surface" << std::endl;
0995         continue;
0996       }
0997 
0998       if (Verbosity())
0999       {
1000         std::cout << "MakeActsGeometry::makeMmMapPairs - layer: " << layer << " tileid: " << tileid << std::endl;
1001       }
1002 
1003       // get segmentation type
1004       const auto segmentation_type = layergeom->get_segmentation_type();
1005 
1006       // create hitset key and insert surface in map
1007       const auto hitsetkey = MicromegasDefs::genHitSetKey(layer, segmentation_type, tileid);
1008       const auto [iter, inserted] = m_clusterSurfaceMapMmEdit.insert(std::make_pair(hitsetkey, surface));
1009       assert(inserted);
1010     }
1011   }
1012 }
1013 
1014 void MakeActsGeometry::makeInttMapPairs(TrackingVolumePtr &inttVolume)
1015 {
1016   if (Verbosity() > 10)
1017   {
1018     std::cout << "MakeActsGeometry::makeInttMapPairs - inttVolume: " << inttVolume->volumeName() << std::endl;
1019   }
1020 
1021   std::cout << "Use Intt survey geometry? m_inttSurvey =" << m_inttSurvey << std::endl;
1022 
1023   auto inttLayerArray = inttVolume->confinedLayers();
1024 
1025   auto inttLayerVector = inttLayerArray->arrayObjects();
1026   // inttLayerVector is a std::vector<LayerPtr>
1027   for (auto &i : inttLayerVector)
1028   {
1029     // Get the ACTS::SurfaceArray from each INTT LayerPtr being iterated over
1030     auto surfaceArray = i->surfaceArray();
1031     if (surfaceArray == nullptr)
1032     {
1033       continue;
1034     }
1035 
1036     // surfaceVector is an Acts::SurfaceVector, vector of surfaces
1037     auto surfaceVector = surfaceArray->surfaces();
1038 
1039     for (auto &j : surfaceVector)
1040     {
1041       auto surf = j->getSharedPtr();
1042       auto vec3d = surf->center(m_geoCtxt);
1043 
1044       double ref_rad[4] = {-1., -1., -1., -1.};
1045       if (m_inttSurvey)
1046       {
1047         ref_rad[0] = 7.453;
1048         ref_rad[1] = 8.046;
1049         ref_rad[2] = 9.934;
1050         ref_rad[3] = 10.569;
1051       }
1052       else
1053       {
1054         ref_rad[0] = 7.188;
1055         ref_rad[1] = 7.732;
1056         ref_rad[2] = 9.680;
1057         ref_rad[3] = 10.262;
1058       }
1059 
1060       const double tolerance = (m_inttSurvey) ? 0.15 : 0.1;
1061 
1062       std::vector<double> world_center = {vec3d(0) / 10.0, vec3d(1) / 10.0, vec3d(2) / 10.0};  // convert from mm to cm
1063 
1064       // The Acts geometry builder combines layers 4 and 5 together,
1065       // and layers 6 and 7 together. We need to use the radius to figure
1066       // out which layer to use to get the layergeom
1067       double layer_rad = (m_inttSurvey) ? sqrt(pow(world_center[0] - m_inttbarrelcenter_survey_x, 2) + pow(world_center[1] - m_inttbarrelcenter_survey_y, 2)) : sqrt(pow(world_center[0], 2) + pow(world_center[1], 2));
1068 
1069       unsigned int layer = 0;
1070       for (unsigned int i2 = 0; i2 < 4; ++i2)
1071       {
1072         if (fabs(layer_rad - ref_rad[i2]) < tolerance)
1073         {
1074           layer = i2 + 3;
1075         }
1076       }
1077 
1078       TrkrDefs::hitsetkey hitsetkey = getInttHitSetKeyFromCoords(layer, world_center);
1079 
1080       // Add this surface to the map
1081       std::pair<TrkrDefs::hitsetkey, Surface> tmp = make_pair(hitsetkey, surf);
1082       m_clusterSurfaceMapSilicon.insert(tmp);
1083 
1084       if (Verbosity() > 10)
1085       {
1086         // check it is in there
1087         unsigned int ladderPhi = InttDefs::getLadderPhiId(hitsetkey);
1088         unsigned int ladderZ = InttDefs::getLadderZId(hitsetkey);
1089 
1090         std::cout << "Layer radius " << layer_rad << " layer " << layer << " ladderPhi " << ladderPhi << " ladderZ " << ladderZ << " hitsetkey " << hitsetkey
1091                   << " recover surface from m_clusterSurfaceMapSilicon " << std::endl;
1092         std::cout << " surface type " << surf->type() << std::endl;
1093         auto assoc_layer = surf->associatedLayer();
1094         std::cout << std::endl
1095                   << " Layer type " << assoc_layer->layerType() << std::endl;
1096 
1097         auto assoc_det_element = surf->associatedDetectorElement();
1098         if (assoc_det_element != nullptr)
1099         {
1100           std::cout << " Associated detElement has non-null pointer "
1101                     << assoc_det_element << std::endl;
1102           std::cout << std::endl
1103                     << " Associated detElement found, thickness = "
1104                     << assoc_det_element->thickness() << std::endl;
1105         }
1106         else
1107         {
1108           std::cout << std::endl
1109                     << " Associated detElement is nullptr " << std::endl;
1110         }
1111       }
1112     }
1113   }
1114 }
1115 
1116 void MakeActsGeometry::makeMvtxMapPairs(TrackingVolumePtr &mvtxVolume)
1117 {
1118   if (Verbosity() > 10)
1119   {
1120     std::cout << "MakeActsGeometry::makeMvtxMapPairs - mvtxVolume: " << mvtxVolume->volumeName() << std::endl;
1121   }
1122 
1123   // Now get the LayerArrays corresponding to each volume
1124   auto mvtxBarrelLayerArray = mvtxVolume->confinedLayers();  // the barrel is all we care about
1125 
1126   // This is a BinnedArray<LayerPtr>, but we care only about the sensitive surfaces
1127   auto mvtxLayerVector1 = mvtxBarrelLayerArray->arrayObjects();  // the barrel is all we care about
1128 
1129   // mvtxLayerVector has size 3, but only index 1 has any surfaces since the clayersplits option was removed
1130   // the layer number has to be deduced from the radius
1131   for (auto &i : mvtxLayerVector1)
1132   {
1133     // Get the Acts::SurfaceArray from each MVTX LayerPtr being iterated over
1134     auto surfaceArray = i->surfaceArray();
1135     if (surfaceArray == nullptr)
1136     {
1137       continue;
1138     }
1139 
1140     if (m_mvtxapplymisalign)
1141     {
1142       std::cout << "MakeActsGeometry::makeMvtxMapPairs - apply misalignment" << std::endl;
1143       PHG4MvtxMisalignment *mvtxmisalignment = new PHG4MvtxMisalignment();
1144       mvtxmisalignment->setAlignmentFile(CDBInterface::instance()->getUrl("MVTX_ALIGNMENT"));
1145       mvtxmisalignment->LoadMvtxStaveAlignmentParameters();
1146       v_globaldisplacement = mvtxmisalignment->get_GlobalDisplacement();
1147 
1148 
1149       delete mvtxmisalignment;
1150     }
1151 
1152     std::cout << "MakeActsGeometry::makeMvtxMapPairs - Apply misalignment: " << m_mvtxapplymisalign << "; Global displacement: (" << v_globaldisplacement[0] << ", " << v_globaldisplacement[1] << ", " << v_globaldisplacement[2] << ")" << std::endl;
1153 
1154     // surfaceVector is an Acts::SurfaceVector, vector of surfaces
1155     // std::vector<const Surface*>
1156     auto surfaceVector = surfaceArray->surfaces();
1157     for (auto &j : surfaceVector)
1158     {
1159       auto surf = j->getSharedPtr();
1160 
1161       auto vec3d = surf->center(m_geoCtxt);
1162       std::vector<double> world_center = {(vec3d(0) - v_globaldisplacement[0]) / 10.0, (vec3d(1) - v_globaldisplacement[1]) / 10.0, (vec3d(2) - v_globaldisplacement[2]) / 10.0};  // convert from mm to cm
1163       double layer_rad = sqrt(pow(world_center[0], 2) + pow(world_center[1], 2));
1164       if (Verbosity() > 0)
1165       {
1166         std::cout << "[DEBUG] MVTX surface center (before misalignment): (x,y,z)=(" << vec3d(0) / 10. << "," << vec3d(1) / 10. << "," << vec3d(2) / 10. << "), layer_rad=" << sqrt(pow(vec3d(0) / 10., 2) + pow(vec3d(1) / 10., 2)) << std::endl;
1167         std::cout << "[DEBUG] MVTX surface center: (x,y,z)=(" << world_center[0] << "," << world_center[1] << "," << world_center[2] << "), layer_rad=" << layer_rad << std::endl;
1168       }
1169 
1170       auto detelement = surf->associatedDetectorElement();
1171       if(!detelement)
1172     {
1173       std::cout << PHWHERE << " Did not find associatedDetectorElement, have to quit! " << std::endl;
1174       exit(1);
1175     }
1176 
1177       Acts::GeometryIdentifier id = detelement->surface().geometryId();
1178       unsigned int volume = id.volume();
1179       unsigned int actslayer = id.layer();
1180 
1181       unsigned int sphlayer = 0;
1182       unsigned int stave = 0;
1183       unsigned int chip = 0;
1184       unsigned int sensor = id.sensitive() - 1;  // Acts sensor ID starts at 1;
1185       if (!m_mvtxapplymisalign)
1186       {
1187         sphlayer = base_layer_map.find(volume)->second + actslayer / 2 - 1;
1188         stave = sensor / 9;
1189         chip = sensor % 9;
1190       }
1191       else
1192       {
1193         stave = sensor / mvtx_chips_per_stave;
1194         chip = sensor % mvtx_chips_per_stave;
1195         if(sensor > 107 && sensor < 252)
1196           {
1197             sphlayer = 1;
1198             stave = (sensor-108) / mvtx_chips_per_stave;
1199             chip = (sensor-108) % mvtx_chips_per_stave;
1200           }
1201         else if(sensor > 251)
1202           {
1203             sphlayer = 2;
1204             stave = (sensor-252) / mvtx_chips_per_stave;
1205             chip = (sensor-252) % mvtx_chips_per_stave;
1206           }
1207       }
1208       int dummy_strobe = 0;
1209       TrkrDefs::hitsetkey hitsetkey= MvtxDefs::genHitSetKey(sphlayer, stave, chip, dummy_strobe);
1210 
1211       if(Verbosity() > 10)
1212     {
1213       std::cout << "detelement: volume " << volume << " actslayer " << actslayer << " sphlayer " << sphlayer
1214             << " sensor " << sensor << " stave " << stave << " chip " << chip << std::endl;
1215     }
1216 
1217       // Add this surface to the map
1218       std::pair<TrkrDefs::hitsetkey, Surface> tmp = make_pair(hitsetkey, surf);
1219       m_clusterSurfaceMapSilicon.insert(tmp);
1220 
1221       if (Verbosity() > 10)
1222       {
1223     unsigned int stavecheck = MvtxDefs::getStaveId(hitsetkey);
1224     unsigned int chipcheck = MvtxDefs::getChipId(hitsetkey);
1225 
1226         double surface_phi = atan2(vec3d(1), vec3d(0)); // for debugging
1227 
1228         // check it is in there
1229         std::cout << "hitsetkey " << hitsetkey << " Layer radius " << layer_rad << " Layer "
1230                   << sphlayer << " stave " << stavecheck << " chip " << chipcheck
1231                   << " surface phi " << surface_phi
1232                   << " recover surface from m_clusterSurfaceMapSilicon "
1233                   << std::endl;
1234 
1235         std::map<TrkrDefs::hitsetkey, Surface>::iterator surf_iter = m_clusterSurfaceMapSilicon.find(hitsetkey);
1236         std::cout << " surface type " << surf_iter->second->type()
1237                   << std::endl;
1238         auto assoc_layer = surf->associatedLayer();
1239         std::cout << std::endl
1240                   << " Layer type "
1241                   << assoc_layer->layerType() << std::endl;
1242 
1243         auto assoc_det_element = surf->associatedDetectorElement();
1244         if (assoc_det_element != nullptr)
1245         {
1246           std::cout << " Associated detElement has non-null pointer "
1247                     << assoc_det_element << std::endl;
1248           std::cout << std::endl
1249                     << " Associated detElement found, thickness = "
1250                     << assoc_det_element->thickness() << std::endl;
1251         }
1252         else
1253         {
1254           std::cout << std::endl
1255                     << " Associated detElement is nullptr " << std::endl;
1256         }
1257       }
1258     }
1259   }
1260 }
1261 
1262 TrkrDefs::hitsetkey MakeActsGeometry::getTpcHitSetKeyFromCoords(std::vector<double> &world)
1263 {
1264   // Look up TPC surface index values from world position of surface center
1265   // layer
1266   unsigned int layer = 999;
1267   double layer_rad = sqrt(pow(world[0], 2) + pow(world[1], 2));
1268   for (unsigned int ilayer = 0; ilayer < m_nTpcLayers; ++ilayer)
1269   {
1270     double tpc_ref_radius_low =
1271         m_layerRadius[ilayer] - m_layerThickness[ilayer] / 2.0;
1272     double tpc_ref_radius_high =
1273         m_layerRadius[ilayer] + m_layerThickness[ilayer] / 2.0;
1274     if (layer_rad >= tpc_ref_radius_low && layer_rad < tpc_ref_radius_high)
1275     {
1276       layer = ilayer;
1277       break;
1278     }
1279   }
1280 
1281   if (layer >= m_nTpcLayers)
1282   {
1283     std::cout << PHWHERE
1284               << "Error: undefined layer, do nothing world =  "
1285               << world[0] << "  " << world[1] << "  " << world[2]
1286               << " layer " << layer << std::endl;
1287     return Fun4AllReturnCodes::ABORTEVENT;
1288   }
1289 
1290   layer += 7;
1291 
1292   // side -  from world z sign
1293   unsigned int side;
1294   if (world[2] < 0)
1295   {
1296     side = 0;
1297   }
1298   else
1299   {
1300     side = 1;
1301   }
1302 
1303   // readout module
1304   unsigned int readout_mod = 999;
1305   double phi_world = atan2(world[1], world[0]);
1306   for (unsigned int imod = 0; imod < m_nTpcModulesPerLayer; ++imod)
1307   {
1308     double min_phi = m_modulePhiStart + (double) imod * m_moduleStepPhi;
1309     double max_phi = m_modulePhiStart + (double) (imod + 1) * m_moduleStepPhi;
1310     if (phi_world >= min_phi && phi_world < max_phi)
1311     {
1312       readout_mod = imod;
1313       break;
1314     }
1315   }
1316   if (readout_mod >= m_nTpcModulesPerLayer)
1317   {
1318     std::cout << PHWHERE
1319               << "Error: readout_mod is undefined, do nothing  phi_world = "
1320               << phi_world << std::endl;
1321     return Fun4AllReturnCodes::ABORTEVENT;
1322   }
1323 
1324   TrkrDefs::hitsetkey hitset_key = TpcDefs::genHitSetKey(layer, readout_mod, side);
1325   if (Verbosity() > 3)
1326   {
1327     if (layer == 30)
1328     {
1329       std::cout << "   world = " << world[0] << "  " << world[1]
1330                 << "  " << world[2] << " phi_world "
1331                 << phi_world * 180 / M_PI << " layer " << layer
1332                 << " readout_mod " << readout_mod << " side " << side
1333                 << " hitsetkey " << hitset_key << std::endl;
1334     }
1335   }
1336 
1337   return hitset_key;
1338 }
1339 
1340 TrkrDefs::hitsetkey MakeActsGeometry::getMvtxHitSetKeyFromCoords(unsigned int layer, std::vector<double> &world)
1341 {
1342   // Look up the MVTX sensor index values from the world position of the surface center
1343 
1344   CylinderGeom_Mvtx *layergeom = dynamic_cast<CylinderGeom_Mvtx *>(m_geomContainerMvtx->GetLayerGeom(layer));
1345   if (!layergeom)
1346   {
1347     std::cout << PHWHERE << "Did not get layergeom for layer "
1348               << layer << std::endl;
1349     return 0;
1350   }
1351 
1352   unsigned int stave = 0;
1353   unsigned int chip = 0;
1354   layergeom->get_sensor_indices_from_world_coords(world, stave, chip);
1355 
1356   unsigned int strobe = 0;
1357   TrkrDefs::hitsetkey mvtx_hitsetkey = MvtxDefs::genHitSetKey(layer, stave, chip, strobe);
1358 
1359   return mvtx_hitsetkey;
1360 }
1361 
1362 TrkrDefs::hitsetkey MakeActsGeometry::getInttHitSetKeyFromCoords(unsigned int layer, std::vector<double> &world)
1363 {
1364   // Look up the INTT sensor index values from the world position of the surface center
1365 
1366   CylinderGeomIntt *layergeom = dynamic_cast<CylinderGeomIntt *>(m_geomContainerIntt->GetLayerGeom(layer));
1367   if (!layergeom)
1368   {
1369     std::cout << PHWHERE << "Did not get layergeom for layer "
1370               << layer << std::endl;
1371     return 0;
1372   }
1373 
1374   double location[3] = {world[0], world[1], world[2]};
1375   int segment_z_bin = 0;
1376   int segment_phi_bin = 0;
1377   layergeom->find_indices_from_segment_center(segment_z_bin,
1378                                               segment_phi_bin, location);
1379 
1380   int crossing = 0;
1381   TrkrDefs::hitsetkey intt_hitsetkey = InttDefs::genHitSetKey(layer, segment_z_bin, segment_phi_bin, crossing);
1382 
1383   return intt_hitsetkey;
1384 }
1385 
1386 // void MakeActsGeometry::makeTGeoNodeMap(PHCompositeNode * /*topNode*/)
1387 // {
1388 //   // This is just a diagnostic method
1389 //   // it lets you list all of the nodes by setting print_sensors = true
1390 //
1391 //   if (!m_geoManager)
1392 //   {
1393 //     std::cout << PHWHERE << " Did not find TGeoManager, quit! " << std::endl;
1394 //     return;
1395 //   }
1396 //   TGeoVolume *topVol = m_geoManager->GetTopVolume();
1397 //   TObjArray *nodeArray = topVol->GetNodes();
1398 //
1399 //   TIter iObj(nodeArray);
1400 //   while (TObject *obj = iObj())
1401 //   {
1402 //     TGeoNode *node = dynamic_cast<TGeoNode *>(obj);
1403 //     std::string node_str = node->GetName();
1404 //
1405 //     std::string mvtx("MVTX_Wrapper");
1406 //     std::string intt("ladder");
1407 //     std::string intt_ext("ladderext");
1408 //     std::string tpc("tpc_envelope");
1409 //     std::string micromegas("MICROMEGAS_55");
1410 //
1411 //     if (node_str.compare(0, mvtx.length(), mvtx) == 0)  // is it in the MVTX?
1412 //     {
1413 //       if (Verbosity() > 2)
1414 //  std::cout << " node " << node->GetName() << " is the MVTX wrapper"
1415 //        << std::endl;
1416 //
1417 //       // The Mvtx has an additional wrapper that needs to be unpacked
1418 //       TObjArray *mvtxArray = node->GetNodes();
1419 //       TIter mvtxObj(mvtxArray);
1420 //       while(TObject *mvtx = mvtxObj())
1421 //  {
1422 //    TGeoNode *mvtxNode = dynamic_cast<TGeoNode *>(mvtx);
1423 //    if(Verbosity() > 2)
1424 //      std::cout << "mvtx node name is " << mvtxNode->GetName()
1425 //            << std::endl;
1426 //    std::string mvtxav1("av_1");
1427 //    std::string mvtxNodeName = mvtxNode->GetName();
1428 //
1429 //    // We only want the av_1 nodes
1430 //    if(mvtxNodeName.compare(0, mvtxav1.length(), mvtxav1) == 0)
1431 //      getMvtxKeyFromNode(mvtxNode);
1432 //  }
1433 //     }
1434 //     else if (node_str.compare(0, intt.length(), intt) == 0)  // is it in the INTT?
1435 //     {
1436 //       // We do not want the "ladderext" nodes
1437 //       if (node_str.compare(0, intt_ext.length(), intt_ext) == 0)
1438 //         continue;
1439 //
1440 //       if (Verbosity() > 2)
1441 //  std::cout << " node " << node->GetName() << " is in the INTT"
1442 //        << std::endl;
1443 //       getInttKeyFromNode(node);
1444 //     }
1445 //     // Put placeholders for the TPC and MMs. Because we modify the geometry
1446 //     // within TGeoVolume, we don't need a mapping to the TGeoNode
1447 //     else if (node_str.compare(0, tpc.length(), tpc) == 0)  // is it in the TPC?
1448 //       {
1449 //  if(Verbosity() > 2)
1450 //    std::cout << " node " << node->GetName()
1451 //          << " is in the TPC " << std::endl;
1452 //       }
1453 //     else if (node_str.compare(0, micromegas.length(), micromegas) == 0)  // is it in the Micromegas?
1454 //       {
1455 //  if(Verbosity() > 2)
1456 //    std::cout << " node " << node->GetName()
1457 //          << " is in the MMs " << std::endl;
1458 //       }
1459 //     else
1460 //       continue;
1461 //
1462 //     bool print_sensor_paths = false;  // normally false
1463 //     if (print_sensor_paths)
1464 //     {
1465 //       // Descends the node tree to find the active silicon nodes - used for information only
1466 //       std::cout << " Top Node is " << node->GetName() << " volume name is " << node->GetVolume()->GetName() << std::endl;
1467 //
1468 //       int nmax_print = 20;
1469 //       isActive(node, nmax_print);
1470 //     }
1471 //   }
1472 // }
1473 
1474 void MakeActsGeometry::getInttKeyFromNode(TGeoNode *gnode)
1475 {
1476   int layer = -1;       // sPHENIX layer number
1477   int itype = -1;       // specifies inner (0) or outer (1) sensor
1478   int ladder_phi = -1;  // copy number of ladder in phi
1479   int zposneg = -1;     // specifies positive (1) or negative (0) z
1480   int ladder_z = -1;    // 0-3, from most negative z to most positive
1481 
1482   std::string s = gnode->GetName();
1483   std::string delimiter = "_";
1484   std::string posz("posz");
1485   std::string negz("negz");
1486 
1487   size_t pos = 0;
1488   std::string token;
1489 
1490   int counter = 0;
1491   while ((pos = s.find(delimiter)) != std::string::npos)
1492   {
1493     token = s.substr(0, pos);
1494 
1495     s.erase(0, pos + delimiter.length());
1496     if (counter == 1)
1497     {
1498       layer = std::atoi(token.c_str()) + 3;
1499     }
1500     if (counter == 2)
1501     {
1502       itype = std::atoi(token.c_str());
1503     }
1504     if (counter == 3)
1505     {
1506       ladder_phi = std::atoi(token.c_str());
1507       if (s.compare(0, negz.length(), negz) == 0)
1508       {
1509         zposneg = 0;
1510       }
1511       if (s.compare(0, posz.length(), posz) == 0)
1512       {
1513         zposneg = 1;
1514       }
1515     }
1516     counter++;
1517   }
1518 
1519   ladder_z = itype + zposneg * 2;
1520 
1521   // The active sensor is a daughter of gnode
1522   int ndaught = gnode->GetNdaughters();
1523   if (ndaught == 0)
1524   {
1525     std::cout << PHWHERE << "OOPS: Did not find INTT sensor! Quit."
1526               << std::endl;
1527     exit(1);
1528   }
1529 
1530   std::string intt_refactive("siactive");
1531   TGeoNode *sensor_node = nullptr;
1532   for (int i = 0; i < ndaught; ++i)
1533   {
1534     std::string node_str = gnode->GetDaughter(i)->GetName();
1535 
1536     if (node_str.compare(0, intt_refactive.length(), intt_refactive) == 0)
1537     {
1538       sensor_node = gnode->GetDaughter(i);
1539       break;
1540     }
1541   }
1542 
1543   // unique key identifying this sensor
1544   int crossing = 0;
1545   TrkrDefs::hitsetkey node_key = InttDefs::genHitSetKey(layer, ladder_z, ladder_phi, crossing);
1546 
1547   std::pair<TrkrDefs::hitsetkey, TGeoNode *> tmp = std::make_pair(
1548       node_key, sensor_node);
1549   m_clusterNodeMap.insert(tmp);
1550 
1551   if (Verbosity() > 1)
1552   {
1553     std::cout << " INTT layer " << layer << " ladder_phi " << ladder_phi
1554               << " itype " << itype << " zposneg " << zposneg
1555               << " ladder_z " << ladder_z << " name "
1556               << sensor_node->GetName() << std::endl;
1557   }
1558 
1559   return;
1560 }
1561 void MakeActsGeometry::getTpcKeyFromNode(TGeoNode * /*gnode*/)
1562 {
1563 }
1564 void MakeActsGeometry::getMvtxKeyFromNode(TGeoNode *gnode)
1565 {
1566   int counter = 0;
1567   int impr = -1;  // stave number, 1-48 in TGeo
1568   int layer = -1;
1569   int stave = -1;  // derived from impr
1570   int chip = -1;   // 9 chips per stave
1571 
1572   std::string s = gnode->GetName();
1573   std::string delimiter = "_";
1574 
1575   size_t pos = 0;
1576   std::string token;
1577 
1578   while ((pos = s.find(delimiter)) != std::string::npos)
1579   {
1580     token = s.substr(0, pos);
1581     // std::cout << token << std::endl;
1582     s.erase(0, pos + delimiter.length());
1583     if (counter == 3)
1584     {
1585       impr = std::atoi(token.c_str());
1586     }
1587 
1588     counter++;
1589   }
1590 
1591   // extract layer and stave info from impr
1592   // int staves_in_layer[3] = {12, 16, 20};
1593   // note - impr stave count starts from 1, not 0, but TrkrCluster counting starts from 0, so we reduce it by 1 here
1594   impr -= 1;
1595 
1596   if (impr < 12)
1597   {
1598     layer = 0;
1599     stave = impr;
1600   }
1601   else if (impr > 11 && impr < 28)
1602   {
1603     layer = 1;
1604     stave = impr - 12;
1605   }
1606   else
1607   {
1608     layer = 2;
1609     stave = impr - 28;
1610   }
1611 
1612   // Now descend node tree to find chip ID's - there are multiple chips per stave
1613   TGeoNode *module_node = gnode->GetDaughter(0);
1614   int mnd = module_node->GetNdaughters();
1615   std::string mvtx_chip("MVTXChip");
1616   for (int i = 0; i < mnd; ++i)
1617   {
1618     std::string dstr = module_node->GetDaughter(i)->GetName();
1619     if (dstr.compare(0, mvtx_chip.length(), mvtx_chip) == 0)
1620     {
1621       if (Verbosity() > 3)
1622       {
1623         std::cout << "Found MVTX layer " << layer << " stave " << stave
1624                   << " chip  " << i << " with node name "
1625                   << module_node->GetDaughter(i)->GetName() << std::endl;
1626       }
1627 
1628       // Make key for this chip
1629       unsigned int strobe = 0;
1630       TrkrDefs::hitsetkey node_key = MvtxDefs::genHitSetKey(layer,
1631                                                             stave, i, strobe);
1632 
1633       // add sensor node to map
1634       TGeoNode *sensor_node = module_node->GetDaughter(i)->GetDaughter(0);
1635       std::pair<TrkrDefs::hitsetkey, TGeoNode *> tmp = std::make_pair(
1636           node_key, sensor_node);
1637       m_clusterNodeMap.insert(tmp);
1638 
1639       if (Verbosity() > 3)
1640       {
1641         std::cout << " MVTX layer " << layer << " stave " << stave
1642                   << " chip " << chip << " name "
1643                   << sensor_node->GetName() << std::endl;
1644       }
1645     }
1646   }
1647 
1648   return;
1649 }
1650 
1651 // void MakeActsGeometry::isActive(TGeoNode *gnode, int nmax_print)
1652 // {
1653 //   // Not used in analysis: diagnostic, for looking at the node tree only.
1654 //   // Recursively searches gnode for silicon sensors, prints out heirarchy
1655 //
1656 //   std::string node_str = gnode->GetName();
1657 //
1658 //   std::string intt_refactive("siactive");
1659 //   std::string mvtx_refactive("MVTXSensor");
1660 //   std::string tpc_refactive("tpc_gas_measurement");
1661 //   std::string micromegas_refactive("MICROMEGAS_55");
1662 //
1663 //   if (node_str.compare(0, intt_refactive.length(), intt_refactive) == 0)
1664 //   {
1665 //     std::cout << "          ******* Found INTT active volume,  node is "
1666 //        << gnode->GetName()
1667 //        << " volume name is " << gnode->GetVolume()->GetName()
1668 //        << std::endl;
1669 //
1670 //     return;
1671 //   }
1672 //   else if (node_str.compare(0, mvtx_refactive.length(), mvtx_refactive) == 0)
1673 //   {
1674 //     std::cout << "        ******* Found MVTX active volume,  node is "
1675 //        << gnode->GetName()
1676 //        << " volume name is " << gnode->GetVolume()->GetName()
1677 //        << std::endl;
1678 //
1679 //      return;
1680 //   }
1681 //   else if (node_str.compare(0, tpc_refactive.length(), tpc_refactive) == 0)
1682 //   {
1683 //     if(nprint_tpc < nmax_print)
1684 //       {
1685 //  std::cout << "     ******* Found TPC  active volume,  node is "
1686 //        << gnode->GetName()
1687 //        << " volume name is " << gnode->GetVolume()->GetName()
1688 //        << std::endl;
1689 //       }
1690 //     nprint_tpc++;
1691 //
1692 //     return;
1693 //   }
1694 //   else if (node_str.compare(0, micromegas_refactive.length(), micromegas_refactive) == 0)
1695 //   {
1696 //     std::cout << "     ******* Found Micromegas  active volume,  node is "
1697 //        << gnode->GetName()
1698 //        << " volume name is " << gnode->GetVolume()->GetName()
1699 //        << std::endl;
1700 //
1701 //     return;
1702 //   }
1703 //   else
1704 //     {
1705 //       if(nprint_tpc < nmax_print)
1706 //  {
1707 //    std::cout << "          ******* Found  node "
1708 //          << gnode->GetName()
1709 //          << " volume name is " << gnode->GetVolume()->GetName()
1710 //          << std::endl;
1711 //  }
1712 //       nprint_tpc++;
1713 //
1714 //       return;
1715 //     }
1716 //
1717 //
1718 //   int ndaught = gnode->GetNdaughters();
1719 //
1720 //   for (int i = 0; i < ndaught; ++i)
1721 //   {
1722 //     std::cout << "     " << gnode->GetVolume()->GetName()
1723 //        << "  daughter " << i << " has name "
1724 //        << gnode->GetDaughter(i)->GetVolume()->GetName() << std::endl;
1725 //
1726 //     isActive(gnode->GetDaughter(i), nmax_print);
1727 //   }
1728 // }
1729 
1730 void MakeActsGeometry::setPlanarSurfaceDivisions()
1731 {
1732   // These are arbitrary tpc subdivisions, and may change
1733   // Setup how TPC boxes will be built for Acts::Surfaces
1734   m_surfStepZ = (m_maxSurfZ - m_minSurfZ) / (double) m_nSurfZ;
1735   m_moduleStepPhi = 2.0 * M_PI / 12.0;
1736   m_modulePhiStart = -M_PI;
1737   m_surfStepPhi = 2.0 * M_PI / (double) (m_nSurfPhi * m_nTpcModulesPerLayer);
1738 
1739   int layer = 0;
1740   PHG4TpcGeomContainer::ConstRange layerrange = m_geomContainerTpc->get_begin_end();
1741   for (PHG4TpcGeomContainer::ConstIterator layeriter = layerrange.first;
1742        layeriter != layerrange.second;
1743        ++layeriter)
1744   {
1745     m_layerRadius[layer] = layeriter->second->get_radius();
1746     m_layerThickness[layer] = layeriter->second->get_thickness();
1747     layer++;
1748   }
1749 }
1750 
1751 int MakeActsGeometry::createNodes(PHCompositeNode *topNode)
1752 {
1753   PHNodeIterator iter(topNode);
1754   // Get the DST Node
1755   PHCompositeNode *parNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "PAR"));
1756 
1757   // Check that it is there
1758   if (!parNode)
1759   {
1760     std::cout << "PAR Node missing, creating it" << std::endl;
1761     parNode = new PHCompositeNode("PAR");
1762     topNode->addNode(parNode);
1763   }
1764 
1765   PHNodeIterator pariter(parNode);
1766   // Get the tracking subnode
1767   PHCompositeNode *svtxNode = dynamic_cast<PHCompositeNode *>(pariter.findFirst("PHCompositeNode", "SVTX"));
1768 
1769   // Check that it is there
1770   if (!svtxNode)
1771   {
1772     svtxNode = new PHCompositeNode("SVTX");
1773     parNode->addNode(svtxNode);
1774   }
1775 
1776   m_actsGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
1777   if (!m_actsGeometry)
1778   {
1779     m_actsGeometry = new ActsGeometry();
1780     PHDataNode<ActsGeometry> *tGeoNode = new PHDataNode<ActsGeometry>(m_actsGeometry, "ActsGeometry");
1781     svtxNode->addNode(tGeoNode);
1782   }
1783 
1784   return Fun4AllReturnCodes::EVENT_OK;
1785 }
1786 
1787 /*
1788  * GetNodes():
1789  *  Get all the all the required nodes off the node tree
1790  */
1791 int MakeActsGeometry::getNodes(PHCompositeNode *topNode)
1792 {
1793   m_geoManager = PHGeomUtility::GetTGeoManager(topNode);
1794   if (!m_geoManager)
1795   {
1796     std::cout << PHWHERE << " Did not find TGeoManager, quit! "
1797               << std::endl;
1798     return Fun4AllReturnCodes::ABORTEVENT;
1799   }
1800 
1801   m_geomContainerMvtx = findNode::getClass<
1802       PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MVTX");
1803   if (!m_geomContainerMvtx)
1804   {
1805     std::cout << PHWHERE
1806               << " CYLINDERGEOM_MVTX  node not found on node tree"
1807               << std::endl;
1808     return Fun4AllReturnCodes::ABORTEVENT;
1809   }
1810 
1811   m_geomContainerTpc =
1812       findNode::getClass<PHG4TpcGeomContainer>(topNode, "TPCGEOMCONTAINER");
1813   if (!m_geomContainerTpc)
1814   {
1815     std::cout << PHWHERE
1816               << "ERROR: Can't find node TPCGEOMCONTAINER"
1817               << std::endl;
1818     topNode->print();
1819     auto *se = Fun4AllServer::instance();
1820     se->Print();
1821     return Fun4AllReturnCodes::ABORTRUN;
1822   }
1823 
1824   m_geomContainerIntt = findNode::getClass<
1825       PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_INTT");
1826   if (!m_geomContainerIntt)
1827   {
1828     std::cout << PHWHERE
1829               << " CYLINDERGEOM_INTT  node not found on node tree"
1830               << std::endl;
1831     return Fun4AllReturnCodes::ABORTEVENT;
1832   }
1833 
1834   m_geomContainerMicromegas = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL");
1835   if (!m_geomContainerMicromegas)
1836   {
1837     std::cout << PHWHERE
1838               << " CYLINDERGEOM_MICROMEGAS_FULL  node not found on node tree"
1839               << std::endl;
1840     return Fun4AllReturnCodes::ABORTEVENT;
1841   }
1842 
1843   return Fun4AllReturnCodes::EVENT_OK;
1844 }