Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RootNuclearInteractionParametersWriter.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file RootNuclearInteractionParametersWriter.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 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <iterator>
19 #include <memory>
20 #include <stdexcept>
21 #include <tuple>
22 #include <unordered_map>
23 #include <utility>
24 
25 #include <TAxis.h>
26 #include <TDirectory.h>
27 #include <TFile.h>
28 #include <TH1.h>
29 #include <TVectorT.h>
30 
31 namespace ActsExamples {
32 struct AlgorithmContext;
33 } // namespace ActsExamples
34 
35 namespace {
36 
41 void labelEvents(
42  std::vector<
44  events) {
45  namespace Parametrisation =
47  // Search for the highest momentum particles per event
48  for (Parametrisation::EventFraction& event : events) {
49  double maxMom = 0.;
50  double maxMomOthers = 0.;
51  // Walk over all final state particles
52  for (const ActsExamples::SimParticle& p : event.finalParticles) {
53  // Search for the maximum in particles with the same PDG ID as the
54  // interacting one
55  if (p.pdg() == event.initialParticle.pdg()) {
56  maxMom = std::max(p.absoluteMomentum(), maxMom);
57  // And the maximum among the others
58  } else {
59  maxMomOthers = std::max(p.absoluteMomentum(), maxMomOthers);
60  }
61  }
62 
63  // Label the initial momentum
64  event.initialMomentum = event.initialParticle.absoluteMomentum();
65 
66  // Label the indication that the interacting particle carries most of the
67  // momentum
68  event.soft = (maxMom > maxMomOthers);
69 
70  // Get the final state p_T
71  double pt = 0.;
72  Acts::Vector2 ptVec(0., 0.);
73  for (const ActsExamples::SimParticle& p : event.finalParticles) {
74  Acts::Vector2 particlePt =
75  p.fourMomentum().template segment<2>(Acts::eMom0);
76  ptVec[0] += particlePt[0];
77  ptVec[1] += particlePt[1];
78  }
79  pt = ptVec.norm();
80 
81  // Use the final state p_T as veto for the soft label
82  if (event.soft && pt <= event.interactingParticle.transverseMomentum()) {
83  event.soft = false;
84  }
85 
86  // Store the multiplicity
87  event.multiplicity = event.finalParticles.size();
88  }
89 }
90 
99 std::tuple<std::vector<float>, std::vector<double>, double>
100 buildNotNormalisedMap(TH1F const* hist) {
101  // Retrieve the number of bins & borders
102  const int nBins = hist->GetNbinsX();
103  std::vector<float> histoBorders(nBins + 1);
104 
105  // Fill the cumulative histogram
106  double integral = 0.;
107  std::vector<double> temp_HistoContents(nBins);
108  for (int iBin = 0; iBin < nBins; iBin++) {
109  float binval = hist->GetBinContent(iBin + 1);
110  // Avoid negative bin values
111  if (binval < 0) {
112  binval = 0.;
113  }
114  // Store the value
115  integral += binval;
116  temp_HistoContents[iBin] = integral;
117  }
118 
119  // Ensure that content is available
120  if (integral == 0.) {
121  histoBorders.clear();
122  temp_HistoContents.clear();
123  return std::make_tuple(histoBorders, temp_HistoContents, integral);
124  }
125 
126  // Set the bin borders
127  for (int iBin = 1; iBin <= nBins; iBin++) {
128  histoBorders[iBin - 1] = hist->GetXaxis()->GetBinLowEdge(iBin);
129  }
130  histoBorders[nBins] = hist->GetXaxis()->GetXmax();
131 
132  return std::make_tuple(histoBorders, temp_HistoContents, integral);
133 }
134 
139 void reduceMap(std::vector<float>& histoBorders,
140  std::vector<uint32_t>& histoContents) {
141  for (auto cit = histoContents.cbegin(); cit != histoContents.cend(); cit++) {
142  while (std::next(cit, 1) != histoContents.end() &&
143  *cit == *std::next(cit, 1)) {
144  const auto distance =
145  std::distance(histoContents.cbegin(), std::next(cit, 1));
146  // Remove the bin
147  histoBorders.erase(histoBorders.begin() + distance);
148  histoContents.erase(histoContents.begin() + distance);
149  }
150  }
151 }
152 
161 std::pair<std::vector<float>, std::vector<uint32_t>> buildMap(
162  TH1F const* hist) {
163  // Build the components
164  std::tuple<std::vector<float>, std::vector<double>, double> map =
165  buildNotNormalisedMap(hist);
166  const int nBins = hist->GetNbinsX();
167  std::vector<double>& histoContents = std::get<1>(map);
168 
169  // Fast exit if the histogram is empty
170  if (histoContents.empty()) {
171  return std::make_pair(std::get<0>(map), std::vector<uint32_t>());
172  }
173 
174  // Set the bin content
175  std::vector<uint32_t> normalisedHistoContents(nBins);
176  const double invIntegral = 1. / std::get<2>(map);
177  for (int iBin = 0; iBin < nBins; ++iBin) {
178  normalisedHistoContents[iBin] = static_cast<unsigned int>(
179  UINT32_MAX * (histoContents[iBin] * invIntegral));
180  }
181 
182  auto histoBorders = std::get<0>(map);
183  reduceMap(histoBorders, normalisedHistoContents);
184  return std::make_pair(histoBorders, normalisedHistoContents);
185 }
186 
198 std::pair<std::vector<float>, std::vector<uint32_t>> buildMap(TH1F const* hist,
199  double integral) {
200  // Build the components
201  std::tuple<std::vector<float>, std::vector<double>, double> map =
202  buildNotNormalisedMap(hist);
203 
204  const int nBins = hist->GetNbinsX();
205  std::vector<double>& histoContents = std::get<1>(map);
206 
207  // Fast exit if the histogram is empty
208  if (histoContents.empty()) {
209  return std::make_pair(std::get<0>(map), std::vector<uint32_t>());
210  }
211 
212  // Set the bin content
213  std::vector<uint32_t> normalisedHistoContents(nBins);
214  const double invIntegral = 1. / std::max(integral, std::get<2>(map));
215  for (int iBin = 0; iBin < nBins; ++iBin) {
216  normalisedHistoContents[iBin] = static_cast<unsigned int>(
217  UINT32_MAX * (histoContents[iBin] * invIntegral));
218  }
219 
220  std::vector<float> histoBorders = std::get<0>(map);
221  reduceMap(histoBorders, normalisedHistoContents);
222 
223  return std::make_pair(histoBorders, normalisedHistoContents);
224 }
225 
233 std::vector<std::pair<std::vector<float>, std::vector<uint32_t>>> buildMaps(
234  const std::vector<TH1F*>& histos) {
235  std::vector<std::pair<std::vector<float>, std::vector<uint32_t>>> maps;
236  for (auto& h : histos) {
237  maps.push_back(buildMap(h));
238  }
239  return maps;
240 }
241 
248 inline void recordKinematicParametrisation(
249  const std::vector<
251  eventFractionCollection,
252  bool interactionType, unsigned int multiplicity,
254  namespace Parametrisation =
256  gDirectory->mkdir(std::to_string(multiplicity).c_str());
257  gDirectory->cd(std::to_string(multiplicity).c_str());
258 
259  // Parametrise the momentum und invarian mass distributions
260  const auto momentumParameters = Parametrisation::buildMomentumParameters(
261  eventFractionCollection, multiplicity, interactionType, cfg.momentumBins);
262  std::vector<Parametrisation::CumulativeDistribution> distributionsMom =
263  momentumParameters.second;
264 
265  const auto invariantMassParameters =
267  eventFractionCollection, multiplicity, interactionType,
268  cfg.invariantMassBins);
269  std::vector<Parametrisation::CumulativeDistribution> distributionsInvMass =
270  invariantMassParameters.second;
271 
272  // Fast exit in case of no events
273  if (!distributionsMom.empty() && !distributionsInvMass.empty()) {
274  if (multiplicity > 1) {
275  // Write the eigenspace components for the momenta
276  Parametrisation::EigenspaceComponents esComponentsMom =
277  momentumParameters.first;
278 
279  auto momEigenVal = std::get<0>(esComponentsMom);
280  auto momEigenVec = std::get<1>(esComponentsMom);
281  auto momMean = std::get<2>(esComponentsMom);
282  std::vector<float> momVecVal(momEigenVal.data(),
283  momEigenVal.data() + momEigenVal.size());
284  std::vector<float> momVecVec(momEigenVec.data(),
285  momEigenVec.data() + momEigenVec.size());
286  std::vector<float> momVecMean(momMean.data(),
287  momMean.data() + momMean.size());
288  gDirectory->WriteObject(&momVecVal, "MomentumEigenvalues");
289  gDirectory->WriteObject(&momVecVec, "MomentumEigenvectors");
290  gDirectory->WriteObject(&momVecMean, "MomentumMean");
291 
292  // Write the eigenspace components for the invariant masses
293  Parametrisation::EigenspaceComponents esComponentsInvMass =
294  invariantMassParameters.first;
295 
296  auto invMassEigenVal = std::get<0>(esComponentsInvMass);
297  auto invMassEigenVec = std::get<1>(esComponentsInvMass);
298  auto invMassMean = std::get<2>(esComponentsInvMass);
299  std::vector<float> invMassVecVal(
300  invMassEigenVal.data(),
301  invMassEigenVal.data() + invMassEigenVal.size());
302  std::vector<float> invMassVecVec(
303  invMassEigenVec.data(),
304  invMassEigenVec.data() + invMassEigenVec.size());
305  std::vector<float> invMassVecMean(
306  invMassMean.data(), invMassMean.data() + invMassMean.size());
307  gDirectory->WriteObject(&invMassVecVal, "InvariantMassEigenvalues");
308  gDirectory->WriteObject(&invMassVecVec, "InvariantMassEigenvectors");
309  gDirectory->WriteObject(&invMassVecMean, "InvariantMassMean");
310  }
311 
312  const auto momDistributions = buildMaps(distributionsMom);
313  const auto invMassDistributions = buildMaps(distributionsInvMass);
314 
315  // Write the distributions
316  for (unsigned int i = 0; i <= multiplicity; i++) {
317  if (cfg.writeOptionalHistograms) {
318  gDirectory->WriteObject(
319  distributionsMom[i],
320  ("MomentumDistributionHistogram_" + std::to_string(i)).c_str());
321  }
322  gDirectory->WriteObject(
323  &momDistributions[i].first,
324  ("MomentumDistributionBinBorders_" + std::to_string(i)).c_str());
325  gDirectory->WriteObject(
326  &momDistributions[i].second,
327  ("MomentumDistributionBinContents_" + std::to_string(i)).c_str());
328  }
329  for (unsigned int i = 0; i < multiplicity; i++) {
330  if (cfg.writeOptionalHistograms) {
331  gDirectory->WriteObject(
332  distributionsInvMass[i],
333  ("InvariantMassDistributionHistogram_" + std::to_string(i))
334  .c_str());
335  }
336  gDirectory->WriteObject(
337  &invMassDistributions[i].first,
338  ("InvariantMassDistributionBinBorders_" + std::to_string(i)).c_str());
339  gDirectory->WriteObject(
340  &invMassDistributions[i].second,
341  ("InvariantMassDistributionBinContents_" + std::to_string(i))
342  .c_str());
343  }
344  }
345  gDirectory->cd("..");
346 }
347 } // namespace
348 
352  config,
354  : WriterT(config.inputSimulationProcesses,
355  "RootNuclearInteractionParametersWriter", level),
356  m_cfg(config) {
357  if (m_cfg.inputSimulationProcesses.empty()) {
358  throw std::invalid_argument("Missing input collection");
359  }
360  if (m_cfg.filePath.empty()) {
361  throw std::invalid_argument("Missing output filename for parameters");
362  }
363 }
364 
367 
371  if (m_eventFractionCollection.empty()) {
372  return ProcessCode::ABORT;
373  }
374 
375  // Exclusive access to the file while writing
376  std::lock_guard<std::mutex> lock(m_writeMutex);
377 
378  // The file
379  TFile* tf = TFile::Open(m_cfg.filePath.c_str(), m_cfg.fileMode.c_str());
380  gDirectory->cd();
381  gDirectory->mkdir(
382  std::to_string(m_eventFractionCollection[0].initialParticle.pdg())
383  .c_str());
384  gDirectory->cd(
385  std::to_string(m_eventFractionCollection[0].initialParticle.pdg())
386  .c_str());
387  gDirectory->mkdir(
388  std::to_string(m_eventFractionCollection[0].initialMomentum).c_str());
389  gDirectory->cd(
390  std::to_string(m_eventFractionCollection[0].initialMomentum).c_str());
391  gDirectory->mkdir("soft");
392  gDirectory->mkdir("hard");
393 
394  // Write the nuclear interaction probability
395  ACTS_DEBUG("Starting parametrisation of nuclear interaction probability");
396  const auto nuclearInteractionProbability =
398  m_eventFractionCollection, m_cfg.interactionProbabilityBins);
399 
400  if (m_cfg.writeOptionalHistograms) {
401  gDirectory->WriteObject(nuclearInteractionProbability,
402  "NuclearInteractionHistogram");
403  }
404  const auto mapNIprob =
405  buildMap(nuclearInteractionProbability, m_cfg.nSimulatedEvents);
406  gDirectory->WriteObject(&mapNIprob.first, "NuclearInteractionBinBorders");
407  gDirectory->WriteObject(&mapNIprob.second, "NuclearInteractionBinContents");
408  ACTS_DEBUG("Nuclear interaction probability parametrised");
409 
410  ACTS_DEBUG("Starting calulcation of probability of interaction type");
411  // Write the interaction type proability
412  const auto softProbability =
413  Parametrisation::softProbability(m_eventFractionCollection);
414 
415  gDirectory->WriteObject(&softProbability, "SoftInteraction");
416  ACTS_DEBUG("Calulcation of probability of interaction type finished");
417 
418  // Write the PDG id production distribution
419  ACTS_DEBUG(
420  "Starting calulcation of transition probabilities between PDG IDs");
421  const auto pdgIdMap =
422  Parametrisation::cumulativePDGprobability(m_eventFractionCollection);
423  std::vector<int> branchingPdgIds;
424  std::vector<int> targetPdgIds;
425  std::vector<float> targetPdgProbability;
426  for (const auto& targetPdgIdMap : pdgIdMap) {
427  for (const auto& producedPdgIdMap : targetPdgIdMap.second) {
428  branchingPdgIds.push_back(targetPdgIdMap.first);
429  targetPdgIds.push_back(producedPdgIdMap.first);
430  targetPdgProbability.push_back(producedPdgIdMap.second);
431  }
432  }
433 
434  gDirectory->WriteObject(&branchingPdgIds, "BranchingPdgIds");
435  gDirectory->WriteObject(&targetPdgIds, "TargetPdgIds");
436  gDirectory->WriteObject(&targetPdgProbability, "TargetPdgProbability");
437  ACTS_DEBUG(
438  "Calulcation of transition probabilities between PDG IDs finished");
439 
440  // Write the multiplicity and kinematics distribution
441  ACTS_DEBUG("Starting parametrisation of multiplicity probabilities");
442  const auto multiplicity = Parametrisation::cumulativeMultiplicityProbability(
443  m_eventFractionCollection, m_cfg.multiplicityMax);
444  ACTS_DEBUG("Parametrisation of multiplicity probabilities finished");
445 
446  gDirectory->cd("soft");
447  if (m_cfg.writeOptionalHistograms) {
448  gDirectory->WriteObject(multiplicity.first, "MultiplicityHistogram");
449  }
450  const auto multProbSoft = buildMap(multiplicity.first);
451  gDirectory->WriteObject(&multProbSoft.first, "MultiplicityBinBorders");
452  gDirectory->WriteObject(&multProbSoft.second, "MultiplicityBinContents");
453  for (unsigned int i = 1; i <= m_cfg.multiplicityMax; i++) {
454  ACTS_DEBUG("Starting parametrisation of final state kinematics for soft " +
455  std::to_string(i) + " particle(s) final state");
456  recordKinematicParametrisation(m_eventFractionCollection, true, i, m_cfg);
457  ACTS_DEBUG("Parametrisation of final state kinematics for soft " +
458  std::to_string(i) + " particle(s) final state finished");
459  }
460  gDirectory->cd("../hard");
461  if (m_cfg.writeOptionalHistograms) {
462  gDirectory->WriteObject(multiplicity.second, "MultiplicityHistogram");
463  }
464  const auto multProbHard = buildMap(multiplicity.second);
465  gDirectory->WriteObject(&multProbHard.first, "MultiplicityBinBorders");
466  gDirectory->WriteObject(&multProbHard.second, "MultiplicityBinContents");
467 
468  for (unsigned int i = 1; i <= m_cfg.multiplicityMax; i++) {
469  ACTS_DEBUG("Starting parametrisation of final state kinematics for hard " +
470  std::to_string(i) + " particle(s) final state");
471  recordKinematicParametrisation(m_eventFractionCollection, false, i, m_cfg);
472  ACTS_DEBUG("Parametrisation of final state kinematics for hard " +
473  std::to_string(i) + " particle(s) final state finished");
474  }
475  gDirectory->cd();
476  tf->Write();
477  tf->Close();
478  return ProcessCode::SUCCESS;
479 }
480 
483  const AlgorithmContext& /*ctx*/,
485  // Convert the tuple to use additional categorisation variables
486  std::vector<detail::NuclearInteractionParametrisation::EventFraction>
487  eventFractions;
488  eventFractions.reserve(event.size());
489  for (const auto& e : event) {
490  eventFractions.emplace_back(e);
491  }
492  // Categorise the event
493  labelEvents(eventFractions);
494  // Store the event
495  m_eventFractionCollection.insert(m_eventFractionCollection.end(),
496  eventFractions.begin(),
497  eventFractions.end());
498 
499  return ProcessCode::SUCCESS;
500 }