Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NuclearInteractionParametrisation.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file NuclearInteractionParametrisation.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020-2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
13 
14 #include <cmath>
15 #include <complex>
16 #include <iterator>
17 #include <limits>
18 #include <memory>
19 
20 #include <Eigen/Eigenvalues>
21 #include <TMath.h>
22 #include <TVectorF.h>
23 #include <TVectorT.h>
24 
25 namespace ActsExamples {
26 namespace detail {
28 namespace {
29 
37 float gaussianValue(TH1F const* histo, const float mom) {
38  // Get the cumulative probability distribution
39  TH1F* normalised = (TH1F*)histo->DrawNormalized();
40  TH1F* cumulative = (TH1F*)normalised->GetCumulative();
41  // Find the cumulative probability
42  const float binContent = cumulative->GetBinContent(cumulative->FindBin(mom));
43  // Transform the probability to an entry in a standard normal distribution
44  const float value = TMath::ErfInverse(2. * binContent - 1.);
45 
46  delete (normalised);
47  delete (cumulative);
48  return value;
49 }
50 
57 float invariantMass(const ActsExamples::SimParticle::Vector4& fourVector1,
58  const ActsExamples::SimParticle::Vector4& fourVector2) {
59  ActsExamples::SimParticle::Vector4 sum = fourVector1 + fourVector2;
62  sum.template segment<3>(Acts::eMom0).norm();
63  return std::sqrt(energy * energy - momentum * momentum);
64 }
65 } // namespace
66 
67 std::pair<Vector, Matrix> calculateMeanAndCovariance(
68  unsigned int multiplicity, const EventProperties& events) {
69  // Calculate the mean
70  Vector mean = Vector::Zero(multiplicity);
71  for (const std::vector<float>& event : events) {
72  for (unsigned int j = 0; j < multiplicity; j++) {
73  mean[j] += event[j];
74  }
75  }
76  mean /= (float)events.size();
77 
78  // Calculate the covariance matrix
79  Matrix covariance = Matrix::Zero(multiplicity, multiplicity);
80  for (unsigned int i = 0; i < multiplicity; i++) {
81  for (unsigned int j = 0; j < multiplicity; j++) {
82  for (unsigned int k = 0; k < events.size(); k++) {
83  covariance(i, j) += (events[k][i] - mean[i]) * (events[k][j] - mean[j]);
84  }
85  }
86  }
87  covariance /= (float)events.size();
88 
89  return std::make_pair(mean, covariance);
90 }
91 
93  const Matrix& covariance) {
94  // Calculate eigenvalues and eigenvectors
95  Eigen::EigenSolver<Matrix> es(covariance);
96  Vector eigenvalues = es.eigenvalues().real();
97  Matrix eigenvectors = es.eigenvectors().real();
98  // Transform the mean vector into eigenspace
99  Vector meanEigenspace = eigenvectors * mean;
100 
101  return std::make_tuple(eigenvalues, eigenvectors, meanEigenspace);
102 }
103 
105  unsigned int multiplicity, bool soft,
106  unsigned int nBins) {
107  // Strip off data
108  auto momenta = prepareMomenta(events, multiplicity, soft);
109  if (momenta.empty()) {
110  return Parametrisation();
111  }
112 
113  // Build histos
115 
116  // Build normal distribution
117  auto momentaGaussian = convertEventToGaussian(histos, momenta);
118  auto meanAndCovariance =
119  calculateMeanAndCovariance(multiplicity + 1, momentaGaussian);
120  // Calculate the transformation into the eigenspace of the covariance matrix
121  EigenspaceComponents eigenspaceElements =
122  calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
123  // Calculate the cumulative distributions
124  return std::make_pair(eigenspaceElements, histos);
125 }
126 
128  unsigned int multiplicity,
129  bool soft) // TODO: build enum instead of bool
130 {
131  EventProperties result;
132  // Loop over all events
133  for (const EventFraction& event : events) {
134  // Test the multiplicity and type of the event
135  if (event.multiplicity == multiplicity && event.soft == soft) {
136  const float initialMomentum = event.initialParticle.absoluteMomentum();
137  float sum = 0.;
138  std::vector<float> momenta;
139  momenta.reserve(multiplicity + 1);
140  // Fill the vector with the scaled momenta
141  for (const ActsExamples::SimParticle& p : event.finalParticles) {
142  sum += p.absoluteMomentum();
143  momenta.push_back(p.absoluteMomentum() / initialMomentum);
144  }
145  // Add the scaled sum of momenta
146  momenta.push_back(sum / initialMomentum);
147  result.push_back(std::move(momenta));
148  }
149  }
150  return result;
151 }
152 
154  unsigned int nBins) {
155  // Fast exit
156  if (events.empty()) {
157  return {};
158  }
159  const unsigned int multMax = events[0].size();
160 
161  // Find the range of each histogram
162  std::vector<float> min(multMax, std::numeric_limits<float>::max());
163  std::vector<float> max(multMax, 0);
164  for (const std::vector<float>& event : events) {
165  for (unsigned int i = 0; i < multMax; i++) {
166  min[i] = std::min(event[i], min[i]);
167  max[i] = std::max(event[i], max[i]);
168  }
169  }
170 
171  // Evaluate the range of the histograms
172  // This is used to avoid entries in over-/underflow bins
173  std::vector<float> diff(multMax);
174  for (unsigned int i = 0; i < multMax; i++) {
175  diff[i] = (max[i] - min[i]) * 0.1;
176  }
177 
178  // Build the histograms
180  for (unsigned int i = 0; i < multMax; i++) {
181  histos[i] = new TH1F("", "", nBins, min[i] - diff[i], max[i] + diff[i]);
182  }
183 
184  // Fill the histograms
185  for (const std::vector<float>& event : events) {
186  for (unsigned int i = 0; i < multMax; i++) {
187  histos[i]->Fill(event[i]);
188  }
189  }
190  return histos;
191 }
192 
194  const EventProperties& events) {
195  // Fast exit
196  if (events.empty()) {
197  return {};
198  }
199  const unsigned int multMax = events[0].size();
200 
201  // Loop over the events
202  EventProperties gaussianEvents;
203  for (const std::vector<float>& event : events) {
204  // Transform the properties in the events
205  std::vector<float> gaussianEvent;
206  for (unsigned int i = 0; i < multMax; i++) {
207  gaussianEvent.push_back(gaussianValue(histos[i], event[i]));
208  }
209  // Store the transformed event
210  gaussianEvents.push_back(gaussianEvent);
211  }
212  return gaussianEvents;
213 }
214 
216  unsigned int multiplicity, bool soft) {
217  EventProperties result;
218  // Loop over all events
219  for (const EventFraction& event : events) {
220  // Test the multiplicity and type of the event
221  if (event.multiplicity == multiplicity && event.soft == soft) {
222  const auto fourVectorBefore = event.interactingParticle.fourMomentum();
223  std::vector<float> invariantMasses;
224  invariantMasses.reserve(multiplicity);
225  // Fill the vector with the invariant masses
226  for (const ActsExamples::SimParticle& p : event.finalParticles) {
227  const auto fourVector = p.fourMomentum();
228  invariantMasses.push_back(invariantMass(fourVectorBefore, fourVector));
229  }
230  result.push_back(invariantMasses);
231  }
232  }
233  return result;
234 }
235 
237  unsigned int multiplicity,
238  bool soft, unsigned int nBins) {
239  // Strip off data
240  auto invariantMasses = prepareInvariantMasses(events, multiplicity, soft);
241  if (invariantMasses.empty()) {
242  return Parametrisation();
243  }
244 
245  // Build histos
246  ProbabilityDistributions histos = buildMomPerMult(invariantMasses, nBins);
247 
248  // Build normal distribution
249  auto invariantMassesGaussian =
250  convertEventToGaussian(histos, invariantMasses);
251  auto meanAndCovariance =
252  calculateMeanAndCovariance(multiplicity, invariantMassesGaussian);
253  // Calculate the transformation into the eigenspace of the covariance matrix
254  EigenspaceComponents eigenspaceElements =
255  calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second);
256  // Calculate the cumulative distributions
257  return std::make_pair(eigenspaceElements, histos);
258 }
259 
260 std::unordered_map<int, std::unordered_map<int, float>>
262  std::unordered_map<int, std::unordered_map<int, float>> counter;
263 
264  // Count how many and which particles were created by which particle
265  for (const EventFraction& event : events) {
266  if (event.finalParticles.empty()) {
267  continue;
268  }
269  if (!event.soft) {
270  counter[event.initialParticle.pdg()][event.finalParticles[0].pdg()]++;
271  }
272  for (unsigned int i = 1; i < event.multiplicity; i++) {
273  counter[event.finalParticles[i - 1].pdg()]
274  [event.finalParticles[i].pdg()]++;
275  }
276  }
277 
278  // Build a cumulative distribution
279  for (const auto& element : counter) {
280  float sum = 0;
281  auto prevIt = counter[element.first].begin();
282  for (auto it1 = counter[element.first].begin();
283  it1 != counter[element.first].end(); it1++) {
284  float binEntry = 0;
285  if (it1 == counter[element.first].begin()) {
286  binEntry = it1->second;
287  prevIt = it1;
288  } else {
289  binEntry = it1->second - prevIt->second;
290  prevIt = it1;
291  }
292  // Add content to next bins
293  for (auto it2 = std::next(it1, 1); it2 != counter[element.first].end();
294  it2++) {
295  it2->second += binEntry;
296  sum = it2->second;
297  }
298  }
299  // Normalise the entry
300  for (auto it1 = counter[element.first].begin();
301  it1 != counter[element.first].end(); it1++) {
302  it1->second /= sum;
303  }
304  }
305  return counter;
306 }
307 
308 std::pair<CumulativeDistribution, CumulativeDistribution>
310  unsigned int multiplicityMax) {
311  // Find the range of both histogram
312  unsigned int minSoft = std::numeric_limits<unsigned int>::max();
313  unsigned int maxSoft = 0;
314  unsigned int minHard = std::numeric_limits<unsigned int>::max();
315  unsigned int maxHard = 0;
316  for (const EventFraction& event : events) {
317  if (event.soft) {
318  minSoft = std::min(event.multiplicity, minSoft);
319  maxSoft = std::max(event.multiplicity, maxSoft);
320  } else {
321  minHard = std::min(event.multiplicity, minHard);
322  maxHard = std::max(event.multiplicity, maxHard);
323  }
324  }
325 
326  // Build and fill the histograms
327  TH1F* softHisto =
328  new TH1F("", "", std::min(maxSoft, multiplicityMax) + 1 - minSoft,
329  minSoft, std::min(maxSoft, multiplicityMax) + 1);
330  TH1F* hardHisto =
331  new TH1F("", "", std::min(maxHard, multiplicityMax) + 1 - minHard,
332  minHard, std::min(maxHard, multiplicityMax) + 1);
333  for (const EventFraction& event : events) {
334  if (event.multiplicity <= multiplicityMax) {
335  if (event.soft) {
336  softHisto->Fill(event.multiplicity);
337  } else {
338  hardHisto->Fill(event.multiplicity);
339  }
340  }
341  }
342 
343  return std::make_pair(softHisto, hardHisto);
344 }
345 
347  float counter = 0.;
348  // Count the soft events
349  for (const EventFraction& event : events) {
350  if (event.soft) {
351  counter++;
352  }
353  }
354 
355  TVectorF result(1);
356  result[0] = counter / (float)events.size();
357  return result;
358 }
359 
361  const EventCollection& events, unsigned int interactionProbabilityBins) {
362  // Find the limits of the histogram
363  float min = std::numeric_limits<float>::max();
364  float max = 0.;
365  for (const EventFraction& event : events) {
366  min = std::min((float)event.interactingParticle.pathInL0(), min);
367  max = std::max((float)event.interactingParticle.pathInL0(), max);
368  }
369 
370  // Fill the histogram
371  TH1F* histo = new TH1F("", "", interactionProbabilityBins, min, max);
372  for (const EventFraction& event : events) {
373  histo->Fill(event.interactingParticle.pathInL0());
374  }
375 
376  // Build the distributions
377  return histo; // TODO: in this case the normalisation is not taking into
378  // account
379 }
380 } // namespace NuclearInteractionParametrisation
381 } // namespace detail
382 } // namespace ActsExamples