File indexing completed on 2025-08-07 08:10:49
0001
0002
0003
0004
0005
0006
0007
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
0029
0030
0031
0032
0033
0034
0035 float gaussianValue(TH1F const* histo, const float mom) {
0036
0037 TH1F* normalised = (TH1F*)histo->DrawNormalized();
0038 TH1F* cumulative = (TH1F*)normalised->GetCumulative();
0039
0040 const float binContent = cumulative->GetBinContent(cumulative->FindBin(mom));
0041
0042 const float value = TMath::ErfInverse(2. * binContent - 1.);
0043
0044 delete (normalised);
0045 delete (cumulative);
0046 return value;
0047 }
0048
0049
0050
0051
0052
0053
0054
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 }
0064
0065 std::pair<Vector, Matrix> calculateMeanAndCovariance(
0066 unsigned int multiplicity, const EventProperties& events) {
0067
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
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
0093 Eigen::EigenSolver<Matrix> es(covariance);
0094 Vector eigenvalues = es.eigenvalues().real();
0095 Matrix eigenvectors = es.eigenvectors().real();
0096
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
0106 auto momenta = prepareMomenta(events, multiplicity, soft);
0107 if (momenta.empty()) {
0108 return Parametrisation();
0109 }
0110
0111
0112 ProbabilityDistributions histos = buildMomPerMult(momenta, nBins);
0113
0114
0115 auto momentaGaussian = convertEventToGaussian(histos, momenta);
0116 auto meanAndCovariance =
0117 calculateMeanAndCovariance(multiplicity + 1, momentaGaussian);
0118
0119 EigenspaceComponents eigenspaceElements =
0120 calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
0121
0122 return std::make_pair(eigenspaceElements, histos);
0123 }
0124
0125 EventProperties prepareMomenta(const EventCollection& events,
0126 unsigned int multiplicity,
0127 bool soft)
0128 {
0129 EventProperties result;
0130
0131 for (const EventFraction& event : events) {
0132
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
0139 for (const ActsExamples::SimParticle& p : event.finalParticles) {
0140 sum += p.absoluteMomentum();
0141 momenta.push_back(p.absoluteMomentum() / initialMomentum);
0142 }
0143
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
0154 if (events.empty()) {
0155 return {};
0156 }
0157 const unsigned int multMax = events[0].size();
0158
0159
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
0170
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
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
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
0194 if (events.empty()) {
0195 return {};
0196 }
0197 const unsigned int multMax = events[0].size();
0198
0199
0200 EventProperties gaussianEvents;
0201 for (const std::vector<float>& event : events) {
0202
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
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
0217 for (const EventFraction& event : events) {
0218
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
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
0238 auto invariantMasses = prepareInvariantMasses(events, multiplicity, soft);
0239 if (invariantMasses.empty()) {
0240 return Parametrisation();
0241 }
0242
0243
0244 ProbabilityDistributions histos = buildMomPerMult(invariantMasses, nBins);
0245
0246
0247 auto invariantMassesGaussian =
0248 convertEventToGaussian(histos, invariantMasses);
0249 auto meanAndCovariance =
0250 calculateMeanAndCovariance(multiplicity, invariantMassesGaussian);
0251
0252 EigenspaceComponents eigenspaceElements =
0253 calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
0254
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
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
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
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
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
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
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
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
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
0369 TH1F* histo = new TH1F("", "", interactionProbabilityBins, min, max);
0370 for (const EventFraction& event : events) {
0371 histo->Fill(event.interactingParticle.pathInL0());
0372 }
0373
0374
0375 return histo;
0376
0377 }
0378 }