Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:18:24

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