Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-08-31 08:14:23

0001 #include "SiliconSeedGeoAcceptance.h"
0002 
0003 #include <fun4all/Fun4AllReturnCodes.h>
0004 
0005 #include <phool/getClass.h>
0006 
0007 #include <trackbase/ActsGeometry.h>
0008 #include <trackbase/TrkrDefs.h>
0009 
0010 #include <Acts/Definitions/Algebra.hpp>
0011 #include <Acts/Definitions/Units.hpp>
0012 #include <Acts/Surfaces/Surface.hpp>
0013 
0014 #include <TFile.h>
0015 #include <TH2D.h>
0016 #include <TRandom3.h>
0017 
0018 #include <algorithm>
0019 #include <array>
0020 #include <cmath>
0021 #include <numeric>
0022 #include <set>
0023 #include <string>
0024 #include <utility>
0025 #include <vector>
0026 
0027 namespace
0028 {
0029 constexpr double pi = 3.14159265358979323846;
0030 constexpr double twoPi = 2.0 * pi;
0031 constexpr double surfacePhiWindow = 0.3;
0032 constexpr double parallelTolerance = 1.0e-6;
0033 constexpr double surfaceTolerance = 1.0 * Acts::UnitConstants::mm;
0034 constexpr unsigned int lastMvtxLayer = 2;
0035 } // namespace
0036 
0037 SiliconSeedGeoAcceptance::SiliconSeedGeoAcceptance(const std::string &name) : SubsysReco(name) {}
0038 
0039 int SiliconSeedGeoAcceptance::InitRun(PHCompositeNode *topNode)
0040 {
0041     // MakeActsGeometry creates this PAR/SVTX node; tracking modules retrieve it the same way (for example trackreco/PHActsSiliconSeeding.cc::getNodes)
0042     m_tGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
0043     const auto &gctx = m_tGeometry->geometry().getGeoContext();
0044 
0045     m_outputFile = new TFile(m_outputFilename.c_str(), "RECREATE");
0046     for (int nMvtx = 0; nMvtx < m_nMvtxLayerCounts; ++nMvtx)
0047     {
0048         for (int nIntt = 0; nIntt < m_nInttLayerCounts; ++nIntt)
0049         {
0050             const std::string name = "h_A_" + std::to_string(nMvtx) + "_" + std::to_string(nIntt);
0051             m_hA[nMvtx][nIntt] = new TH2D(name.c_str(), ";#eta;vtx z [cm]", m_etaBins, m_etaLo, m_etaHi, m_vtxZBins, m_vtxZLo, m_vtxZHi);
0052         }
0053     }
0054     m_hN = new TH2D("h_N", ";#eta;vtx z [cm]", m_etaBins, m_etaLo, m_etaHi, m_vtxZBins, m_vtxZLo, m_vtxZHi);
0055 
0056     // trackreco/MakeActsGeometry.cc fills this map, which is declared in trackbase/ActsSurfaceMaps.h. Its keys identify MVTX/INTT hit sets
0057     const auto &surfaceMap = m_tGeometry->maps().m_siliconSurfaceMap;
0058     m_surfaceLayers.reserve(surfaceMap.size());
0059     m_surfacePhis.reserve(surfaceMap.size());
0060     m_surfaces.reserve(surfaceMap.size());
0061     for (const auto &[hitsetkey, surface] : surfaceMap)
0062     {
0063         const auto center = surface->center(gctx);
0064         // The hit-set-key to tracker-layer decoding is defined in trackbase/TrkrDefs.cc
0065         m_surfaceLayers.push_back(TrkrDefs::getLayer(hitsetkey));
0066         m_surfacePhis.push_back(std::atan2(center.y(), center.x()));
0067         m_surfaces.push_back(surface);
0068     }
0069 
0070     // Sorting once makes the module-specific phi-window lookup below cheap
0071     std::vector<std::size_t> order(m_surfaces.size());
0072     std::iota(order.begin(), order.end(), 0);
0073     std::sort(order.begin(), order.end(), [this](std::size_t lhs, std::size_t rhs) { return m_surfacePhis[lhs] < m_surfacePhis[rhs]; });
0074 
0075     std::vector<unsigned int> sortedLayers;
0076     std::vector<double> sortedPhis;
0077     std::vector<std::shared_ptr<const Acts::Surface>> sortedSurfaces;
0078     sortedLayers.reserve(order.size());
0079     sortedPhis.reserve(order.size());
0080     sortedSurfaces.reserve(order.size());
0081     for (const auto index : order)
0082     {
0083         sortedLayers.push_back(m_surfaceLayers[index]);
0084         sortedPhis.push_back(m_surfacePhis[index]);
0085         sortedSurfaces.push_back(m_surfaces[index]);
0086     }
0087     m_surfaceLayers = std::move(sortedLayers);
0088     m_surfacePhis = std::move(sortedPhis);
0089     m_surfaces = std::move(sortedSurfaces);
0090 
0091     // Throw rays uniformly within each (eta, vertex-z) bin and average over phi
0092     TRandom3 rng(m_randomSeed);
0093     for (int etaBin = 1; etaBin <= m_etaBins; ++etaBin)
0094     {
0095         const double etaLo = m_hN->GetXaxis()->GetBinLowEdge(etaBin);
0096         const double etaHi = m_hN->GetXaxis()->GetBinUpEdge(etaBin);
0097         for (int vtxZBin = 1; vtxZBin <= m_vtxZBins; ++vtxZBin)
0098         {
0099             const double vtxZLo = m_hN->GetYaxis()->GetBinLowEdge(vtxZBin);
0100             const double vtxZHi = m_hN->GetYaxis()->GetBinUpEdge(vtxZBin);
0101             int count[m_nMvtxLayerCounts][m_nInttLayerCounts] = {};
0102 
0103             for (int ray = 0; ray < m_raysPerBin; ++ray)
0104             {
0105                 // within the (eta, vtx-z) bin, throw a ray with uniform eta, vtx-z, and phi
0106                 const double eta = rng.Uniform(etaLo, etaHi);
0107                 const double vtxZ = rng.Uniform(vtxZLo, vtxZHi);
0108                 const double phi = rng.Uniform(-pi, pi);
0109 
0110                 int nMvtx = 0;
0111                 int nIntt = 0;
0112                 countLayers(eta, phi, vtxZ, nMvtx, nIntt);
0113                 ++count[nMvtx][nIntt];
0114             }
0115 
0116             for (int nMvtx = 0; nMvtx < m_nMvtxLayerCounts; ++nMvtx)
0117             {
0118                 for (int nIntt = 0; nIntt < m_nInttLayerCounts; ++nIntt)
0119                 {
0120                     m_hA[nMvtx][nIntt]->SetBinContent(etaBin, vtxZBin, static_cast<double>(count[nMvtx][nIntt]) / m_raysPerBin);
0121                 }
0122             }
0123             m_hN->SetBinContent(etaBin, vtxZBin, m_raysPerBin);
0124         }
0125     }
0126 
0127     return Fun4AllReturnCodes::EVENT_OK;
0128 }
0129 
0130 int SiliconSeedGeoAcceptance::process_event(PHCompositeNode * /*topNode*/) { return Fun4AllReturnCodes::ABORTEVENT; }
0131 
0132 int SiliconSeedGeoAcceptance::End(PHCompositeNode * /*topNode*/)
0133 {
0134     m_outputFile->Write();
0135     m_outputFile->Close();
0136     delete m_outputFile;
0137     m_outputFile = nullptr;
0138     return Fun4AllReturnCodes::EVENT_OK;
0139 }
0140 
0141 void SiliconSeedGeoAcceptance::countLayers(double eta, double phi, double vtxZ, int &nMvtx, int &nIntt) const
0142 {
0143     // sPHENIX positions are in cm, while ACTS surface coordinates are in mm
0144     // The same Acts::UnitConstants conversion convention is used in TrackingDiagnostics/TrackResiduals.cc
0145     const double theta = 2.0 * std::atan(std::exp(-eta));
0146     const Acts::Vector3 origin(m_beamSpotX, m_beamSpotY, vtxZ); // where the ray originates P0 = (m_beamSpotX, m_beamSpotY, vtxZ)
0147     // The ray direction is a unit vector P1 - P0 = (sin(theta) cos(phi), sin(theta) sin(phi), cos(theta)); theta = 2 arctan(exp(-eta)) is the polar angle
0148     const Acts::Vector3 direction(std::sin(theta) * std::cos(phi), std::sin(theta) * std::sin(phi), std::cos(theta));
0149 
0150     // keeps only surfaces near the ray phi; two index ranges handle a window crossing the -pi/pi boundary
0151     std::array<std::size_t, 2> firstIndex = {};
0152     std::array<std::size_t, 2> lastIndex = {};
0153     int nRanges = 1;
0154     const double phiLo = phi - surfacePhiWindow;
0155     const double phiHi = phi + surfacePhiWindow;
0156     if (phiLo < -pi)
0157     {
0158         firstIndex[0] = 0;
0159         lastIndex[0] = std::upper_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiHi) - m_surfacePhis.begin();
0160         firstIndex[1] = std::lower_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiLo + twoPi) - m_surfacePhis.begin();
0161         lastIndex[1] = m_surfacePhis.size();
0162         nRanges = 2;
0163     }
0164     else if (phiHi > pi)
0165     {
0166         firstIndex[0] = std::lower_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiLo) - m_surfacePhis.begin();
0167         lastIndex[0] = m_surfacePhis.size();
0168         firstIndex[1] = 0;
0169         lastIndex[1] = std::upper_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiHi - twoPi) - m_surfacePhis.begin();
0170         nRanges = 2;
0171     }
0172     else
0173     {
0174         firstIndex[0] = std::lower_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiLo) - m_surfacePhis.begin();
0175         lastIndex[0] = std::upper_bound(m_surfacePhis.begin(), m_surfacePhis.end(), phiHi) - m_surfacePhis.begin();
0176     }
0177 
0178     const auto &gctx = m_tGeometry->geometry().getGeoContext();
0179     std::set<unsigned int> layersHit;
0180     for (int range = 0; range < nRanges; ++range)
0181     {
0182         for (std::size_t index = firstIndex[range]; index < lastIndex[range]; ++index)
0183         {
0184             const auto &surface = m_surfaces[index];
0185             const Acts::Vector3 centerMm = surface->center(gctx);
0186             const Acts::Vector3 center = centerMm / Acts::UnitConstants::cm; // surface center
0187             const Acts::Vector3 normal = surface->normal(gctx, centerMm, direction);
0188             const double denominator = normal.dot(direction);
0189             if (std::abs(denominator) < parallelTolerance)
0190             {
0191                 continue;
0192             }
0193 
0194             // Intersect the forward ray with the plane tangent to the surface.
0195             const double pathLength = normal.dot(center - origin) / denominator;
0196             if (pathLength <= 0.0)
0197             {
0198                 continue;
0199             }
0200 
0201             const Acts::Vector3 intersection = origin + pathLength * direction;
0202             // Convert the intersection to bound surface coordinates. This is the coresoftware pattern used in TrackingDiagnostics/TrackResiduals.cc
0203             // trackreco/MakeSourceLinks.cc; bounds().inside checks if points intersect the plane outside the physical sensor
0204             // If outside, the ray does not cross the sensor (not accepted)
0205             const auto local = surface->globalToLocal(gctx, intersection * Acts::UnitConstants::cm, direction, surfaceTolerance);
0206             if (!local.ok() || !surface->bounds().inside(local.value()))
0207             {
0208                 continue;
0209             }
0210             // Count a tracker layer only once even if the ray crosses overlapping or neighboring sensor surfaces in that layer
0211             layersHit.insert(m_surfaceLayers[index]);
0212         }
0213     }
0214 
0215     nMvtx = 0;
0216     nIntt = 0;
0217     for (const auto layer : layersHit)
0218     {
0219         if (layer <= lastMvtxLayer)
0220         {
0221             ++nMvtx;
0222         }
0223         else
0224         {
0225             ++nIntt;
0226         }
0227     }
0228 }