Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-07 08:10:49

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2020-2021 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #include "ActsExamples/Io/NuclearInteractions/detail/NuclearInteractionParametrisation.hpp"
0010 
0011 #include "Acts/Definitions/Common.hpp"
0012 #include "ActsFatras/EventData/Particle.hpp"
0013 
0014 #include <cmath>
0015 #include <complex>
0016 #include <iterator>
0017 #include <limits>
0018 #include <memory>
0019 
0020 #include <Eigen/Eigenvalues>
0021 #include <TMath.h>
0022 #include <TVectorF.h>
0023 #include <TVectorT.h>
0024 
0025 namespace ActsExamples::detail::NuclearInteractionParametrisation {
0026 namespace {
0027 
0028 /// @brief Evaluate the location in a standard normal distribution for a value
0029 /// from a probability distribution
0030 ///
0031 /// @param [in] histo The probability distribution
0032 /// @param [in] mom The abscissa value in @p histo
0033 ///
0034 /// @return The location in a standard normal distribution
0035 float gaussianValue(TH1F const* histo, const float mom) {
0036   // Get the cumulative probability distribution
0037   TH1F* normalised = (TH1F*)histo->DrawNormalized();
0038   TH1F* cumulative = (TH1F*)normalised->GetCumulative();
0039   // Find the cumulative probability
0040   const float binContent = cumulative->GetBinContent(cumulative->FindBin(mom));
0041   // Transform the probability to an entry in a standard normal distribution
0042   const float value = TMath::ErfInverse(2. * binContent - 1.);
0043 
0044   delete (normalised);
0045   delete (cumulative);
0046   return value;
0047 }
0048 
0049 /// @brief Evaluate the invariant mass of two four vectors
0050 ///
0051 /// @param [in] fourVector1 The one four vector
0052 /// @param [in] fourVector2 The other four vector
0053 ///
0054 /// @return The invariant mass
0055 float invariantMass(const ActsExamples::SimParticle::Vector4& fourVector1,
0056                     const ActsExamples::SimParticle::Vector4& fourVector2) {
0057   ActsExamples::SimParticle::Vector4 sum = fourVector1 + fourVector2;
0058   const ActsExamples::SimParticle::Scalar energy = sum[Acts::eEnergy];
0059   ActsExamples::SimParticle::Scalar momentum =
0060       sum.template segment<3>(Acts::eMom0).norm();
0061   return std::sqrt(energy * energy - momentum * momentum);
0062 }
0063 }  // namespace
0064 
0065 std::pair<Vector, Matrix> calculateMeanAndCovariance(
0066     unsigned int multiplicity, const EventProperties& events) {
0067   // Calculate the mean
0068   Vector mean = Vector::Zero(multiplicity);
0069   for (const std::vector<float>& event : events) {
0070     for (unsigned int j = 0; j < multiplicity; j++) {
0071       mean[j] += event[j];
0072     }
0073   }
0074   mean /= (float)events.size();
0075 
0076   // Calculate the covariance matrix
0077   Matrix covariance = Matrix::Zero(multiplicity, multiplicity);
0078   for (unsigned int i = 0; i < multiplicity; i++) {
0079     for (unsigned int j = 0; j < multiplicity; j++) {
0080       for (unsigned int k = 0; k < events.size(); k++) {
0081         covariance(i, j) += (events[k][i] - mean[i]) * (events[k][j] - mean[j]);
0082       }
0083     }
0084   }
0085   covariance /= (float)events.size();
0086 
0087   return std::make_pair(mean, covariance);
0088 }
0089 
0090 EigenspaceComponents calculateEigenspace(const Vector& mean,
0091                                          const Matrix& covariance) {
0092   // Calculate eigenvalues and eigenvectors
0093   Eigen::EigenSolver<Matrix> es(covariance);
0094   Vector eigenvalues = es.eigenvalues().real();
0095   Matrix eigenvectors = es.eigenvectors().real();
0096   // Transform the mean vector into eigenspace
0097   Vector meanEigenspace = eigenvectors * mean;
0098 
0099   return std::make_tuple(eigenvalues, eigenvectors, meanEigenspace);
0100 }
0101 
0102 Parametrisation buildMomentumParameters(const EventCollection& events,
0103                                         unsigned int multiplicity, bool soft,
0104                                         unsigned int nBins) {
0105   // Strip off data
0106   auto momenta = prepareMomenta(events, multiplicity, soft);
0107   if (momenta.empty()) {
0108     return Parametrisation();
0109   }
0110 
0111   // Build histos
0112   ProbabilityDistributions histos = buildMomPerMult(momenta, nBins);
0113 
0114   // Build normal distribution
0115   auto momentaGaussian = convertEventToGaussian(histos, momenta);
0116   auto meanAndCovariance =
0117       calculateMeanAndCovariance(multiplicity + 1, momentaGaussian);
0118   // Calculate the transformation into the eigenspace of the covariance matrix
0119   EigenspaceComponents eigenspaceElements =
0120       calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
0121   // Calculate the cumulative distributions
0122   return std::make_pair(eigenspaceElements, histos);
0123 }
0124 
0125 EventProperties prepareMomenta(const EventCollection& events,
0126                                unsigned int multiplicity,
0127                                bool soft)  // TODO: build enum instead of bool
0128 {
0129   EventProperties result;
0130   // Loop over all events
0131   for (const EventFraction& event : events) {
0132     // Test the multiplicity and type of the event
0133     if (event.multiplicity == multiplicity && event.soft == soft) {
0134       const float initialMomentum = event.initialParticle.absoluteMomentum();
0135       float sum = 0.;
0136       std::vector<float> momenta;
0137       momenta.reserve(multiplicity + 1);
0138       // Fill the vector with the scaled momenta
0139       for (const ActsExamples::SimParticle& p : event.finalParticles) {
0140         sum += p.absoluteMomentum();
0141         momenta.push_back(p.absoluteMomentum() / initialMomentum);
0142       }
0143       // Add the scaled sum of momenta
0144       momenta.push_back(sum / initialMomentum);
0145       result.push_back(std::move(momenta));
0146     }
0147   }
0148   return result;
0149 }
0150 
0151 ProbabilityDistributions buildMomPerMult(const EventProperties& events,
0152                                          unsigned int nBins) {
0153   // Fast exit
0154   if (events.empty()) {
0155     return {};
0156   }
0157   const unsigned int multMax = events[0].size();
0158 
0159   // Find the range of each histogram
0160   std::vector<float> min(multMax, std::numeric_limits<float>::max());
0161   std::vector<float> max(multMax, 0);
0162   for (const std::vector<float>& event : events) {
0163     for (unsigned int i = 0; i < multMax; i++) {
0164       min[i] = std::min(event[i], min[i]);
0165       max[i] = std::max(event[i], max[i]);
0166     }
0167   }
0168 
0169   // Evaluate the range of the histograms
0170   // This is used to avoid entries in over-/underflow bins
0171   std::vector<float> diff(multMax);
0172   for (unsigned int i = 0; i < multMax; i++) {
0173     diff[i] = (max[i] - min[i]) * 0.1;
0174   }
0175 
0176   // Build the histograms
0177   ProbabilityDistributions histos(multMax);
0178   for (unsigned int i = 0; i < multMax; i++) {
0179     histos[i] = new TH1F("", "", nBins, min[i] - diff[i], max[i] + diff[i]);
0180   }
0181 
0182   // Fill the histograms
0183   for (const std::vector<float>& event : events) {
0184     for (unsigned int i = 0; i < multMax; i++) {
0185       histos[i]->Fill(event[i]);
0186     }
0187   }
0188   return histos;
0189 }
0190 
0191 EventProperties convertEventToGaussian(const ProbabilityDistributions& histos,
0192                                        const EventProperties& events) {
0193   // Fast exit
0194   if (events.empty()) {
0195     return {};
0196   }
0197   const unsigned int multMax = events[0].size();
0198 
0199   // Loop over the events
0200   EventProperties gaussianEvents;
0201   for (const std::vector<float>& event : events) {
0202     // Transform the properties in the events
0203     std::vector<float> gaussianEvent;
0204     for (unsigned int i = 0; i < multMax; i++) {
0205       gaussianEvent.push_back(gaussianValue(histos[i], event[i]));
0206     }
0207     // Store the transformed event
0208     gaussianEvents.push_back(gaussianEvent);
0209   }
0210   return gaussianEvents;
0211 }
0212 
0213 EventProperties prepareInvariantMasses(const EventCollection& events,
0214                                        unsigned int multiplicity, bool soft) {
0215   EventProperties result;
0216   // Loop over all events
0217   for (const EventFraction& event : events) {
0218     // Test the multiplicity and type of the event
0219     if (event.multiplicity == multiplicity && event.soft == soft) {
0220       const auto fourVectorBefore = event.interactingParticle.fourMomentum();
0221       std::vector<float> invariantMasses;
0222       invariantMasses.reserve(multiplicity);
0223       // Fill the vector with the invariant masses
0224       for (const ActsExamples::SimParticle& p : event.finalParticles) {
0225         const auto fourVector = p.fourMomentum();
0226         invariantMasses.push_back(invariantMass(fourVectorBefore, fourVector));
0227       }
0228       result.push_back(invariantMasses);
0229     }
0230   }
0231   return result;
0232 }
0233 
0234 Parametrisation buildInvariantMassParameters(const EventCollection& events,
0235                                              unsigned int multiplicity,
0236                                              bool soft, unsigned int nBins) {
0237   // Strip off data
0238   auto invariantMasses = prepareInvariantMasses(events, multiplicity, soft);
0239   if (invariantMasses.empty()) {
0240     return Parametrisation();
0241   }
0242 
0243   // Build histos
0244   ProbabilityDistributions histos = buildMomPerMult(invariantMasses, nBins);
0245 
0246   // Build normal distribution
0247   auto invariantMassesGaussian =
0248       convertEventToGaussian(histos, invariantMasses);
0249   auto meanAndCovariance =
0250       calculateMeanAndCovariance(multiplicity, invariantMassesGaussian);
0251   // Calculate the transformation into the eigenspace of the covariance matrix
0252   EigenspaceComponents eigenspaceElements =
0253       calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
0254   // Calculate the cumulative distributions
0255   return std::make_pair(eigenspaceElements, histos);
0256 }
0257 
0258 std::unordered_map<int, std::unordered_map<int, float>>
0259 cumulativePDGprobability(const EventCollection& events) {
0260   std::unordered_map<int, std::unordered_map<int, float>> counter;
0261 
0262   // Count how many and which particles were created by which particle
0263   for (const EventFraction& event : events) {
0264     if (event.finalParticles.empty()) {
0265       continue;
0266     }
0267     if (!event.soft) {
0268       counter[event.initialParticle.pdg()][event.finalParticles[0].pdg()]++;
0269     }
0270     for (unsigned int i = 1; i < event.multiplicity; i++) {
0271       counter[event.finalParticles[i - 1].pdg()]
0272              [event.finalParticles[i].pdg()]++;
0273     }
0274   }
0275 
0276   // Build a cumulative distribution
0277   for (const auto& element : counter) {
0278     float sum = 0;
0279     auto prevIt = counter[element.first].begin();
0280     for (auto it1 = counter[element.first].begin();
0281          it1 != counter[element.first].end(); it1++) {
0282       float binEntry = 0;
0283       if (it1 == counter[element.first].begin()) {
0284         binEntry = it1->second;
0285         prevIt = it1;
0286       } else {
0287         binEntry = it1->second - prevIt->second;
0288         prevIt = it1;
0289       }
0290       // Add content to next bins
0291       for (auto it2 = std::next(it1, 1); it2 != counter[element.first].end();
0292            it2++) {
0293         it2->second += binEntry;
0294         sum = it2->second;
0295       }
0296     }
0297     // Normalise the entry
0298     for (auto it1 = counter[element.first].begin();
0299          it1 != counter[element.first].end(); it1++) {
0300       it1->second /= sum;
0301     }
0302   }
0303   return counter;
0304 }
0305 
0306 std::pair<CumulativeDistribution, CumulativeDistribution>
0307 cumulativeMultiplicityProbability(const EventCollection& events,
0308                                   unsigned int multiplicityMax) {
0309   // Find the range of both histogram
0310   unsigned int minSoft = std::numeric_limits<unsigned int>::max();
0311   unsigned int maxSoft = 0;
0312   unsigned int minHard = std::numeric_limits<unsigned int>::max();
0313   unsigned int maxHard = 0;
0314   for (const EventFraction& event : events) {
0315     if (event.soft) {
0316       minSoft = std::min(event.multiplicity, minSoft);
0317       maxSoft = std::max(event.multiplicity, maxSoft);
0318     } else {
0319       minHard = std::min(event.multiplicity, minHard);
0320       maxHard = std::max(event.multiplicity, maxHard);
0321     }
0322   }
0323 
0324   // Build and fill the histograms
0325   TH1F* softHisto =
0326       new TH1F("", "", std::min(maxSoft, multiplicityMax) + 1 - minSoft,
0327                minSoft, std::min(maxSoft, multiplicityMax) + 1);
0328   TH1F* hardHisto =
0329       new TH1F("", "", std::min(maxHard, multiplicityMax) + 1 - minHard,
0330                minHard, std::min(maxHard, multiplicityMax) + 1);
0331   for (const EventFraction& event : events) {
0332     if (event.multiplicity <= multiplicityMax) {
0333       if (event.soft) {
0334         softHisto->Fill(event.multiplicity);
0335       } else {
0336         hardHisto->Fill(event.multiplicity);
0337       }
0338     }
0339   }
0340 
0341   return std::make_pair(softHisto, hardHisto);
0342 }
0343 
0344 TVectorF softProbability(const EventCollection& events) {
0345   float counter = 0.;
0346   // Count the soft events
0347   for (const EventFraction& event : events) {
0348     if (event.soft) {
0349       counter++;
0350     }
0351   }
0352 
0353   TVectorF result(1);
0354   result[0] = counter / (float)events.size();
0355   return result;
0356 }
0357 
0358 CumulativeDistribution cumulativeNuclearInteractionProbability(
0359     const EventCollection& events, unsigned int interactionProbabilityBins) {
0360   // Find the limits of the histogram
0361   float min = std::numeric_limits<float>::max();
0362   float max = 0.;
0363   for (const EventFraction& event : events) {
0364     min = std::min((float)event.interactingParticle.pathInL0(), min);
0365     max = std::max((float)event.interactingParticle.pathInL0(), max);
0366   }
0367 
0368   // Fill the histogram
0369   TH1F* histo = new TH1F("", "", interactionProbabilityBins, min, max);
0370   for (const EventFraction& event : events) {
0371     histo->Fill(event.interactingParticle.pathInL0());
0372   }
0373 
0374   // Build the distributions
0375   return histo;  // TODO: in this case the normalisation is not taking into
0376                  // account
0377 }
0378 }  // namespace ActsExamples::detail::NuclearInteractionParametrisation