aegir
Phlex-based simulation framework for the SHiP experiment.
Loading...
Searching...
No Matches
pythia_common.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 CERN for the benefit of the SHiP Collaboration
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5// pythia_common.hpp — helpers shared by the Pythia8-based generators
6//
7// Header-only and free of Phlex/data-model dependencies so the standalone
8// benchmark can share the same code. extract_particles() is templated on the
9// output particle type (any struct exposing the MCParticle fields), letting
10// the plugins emit SHiP::MCParticle while the benchmark keeps its local
11// stand-in.
12
13#pragma once
14
15#include <Pythia8/Pythia.h>
16
17#include <string>
18#include <vector>
19
20namespace aegir {
21
22// Configure a fixed-target beam: beam A on a stationary target B (frameType 2,
23// eB = 0). Templated so it works for both Pythia8::Pythia and PythiaParallel.
24template <typename Pythia>
25void configure_beams(Pythia& pythia, int idA, int idB, double beam_energy) {
26 pythia.readString("Beams:idA = " + std::to_string(idA));
27 pythia.readString("Beams:idB = " + std::to_string(idB));
28 pythia.readString("Beams:frameType = 2");
29 pythia.readString("Beams:eA = " + std::to_string(beam_energy));
30 pythia.readString("Beams:eB = 0.");
31}
32
33// Make long-lived particles (tau0 above threshold, in mm/c) stable so a
34// downstream simulation (e.g. Geant4) handles their decay. Guards against null
35// particleData entries.
36template <typename Pythia>
37void stabilise_long_lived(Pythia& pythia, double tau0_threshold) {
38 for (auto it = pythia.particleData.begin(); it != pythia.particleData.end();
39 ++it) {
40 auto& entry = it->second; // ParticleDataEntryPtr (shared_ptr-like)
41 if (entry && entry->tau0() > tau0_threshold) entry->setMayDecay(false);
42 }
43}
44
45// Extract final-state particles from a Pythia event record into a vector of
46// MCParticle (any type exposing pdgCode/vertex/momentum/energy/time/motherId/
47// status). vertex z is shifted by z_offset (mm).
48//
49// motherId is remapped from the full Pythia-record index to the index within
50// the returned vector, or -1 when the mother was not itself written out — the
51// common case, since only final-state particles are kept and their mothers
52// generally are not. This makes motherId a valid index into the emitted
53// collection rather than a dangling reference into the discarded record.
54template <typename MCParticle>
55std::vector<MCParticle> extract_particles(Pythia8::Event const& event,
56 double z_offset = 0.0) {
57 std::vector<MCParticle> particles;
58 particles.reserve(event.size());
59
60 // Pythia-record index -> output index for written (final-state) particles.
61 std::vector<int> out_index(static_cast<std::size_t>(event.size()), -1);
62
63 for (int i = 0; i < event.size(); ++i) {
64 auto const& p = event[i];
65 if (!p.isFinal()) continue;
66
67 out_index[static_cast<std::size_t>(i)] = static_cast<int>(particles.size());
68
69 MCParticle mc;
70 mc.pdgCode = p.id();
71 mc.vertex = {p.xProd(), p.yProd(), p.zProd() + z_offset}; // mm
72 mc.momentum = {p.px(), p.py(), p.pz()}; // GeV
73 mc.energy = p.e();
74 mc.time = p.tProd() / 299.792458; // mm/c -> ns
75 mc.motherId = p.mother1(); // record index, remapped below
76 mc.status = p.statusHepMC();
77 particles.push_back(mc);
78 }
79
80 for (auto& mc : particles) {
81 int m = mc.motherId;
82 mc.motherId = (m >= 0 && m < static_cast<int>(out_index.size()))
83 ? out_index[static_cast<std::size_t>(m)]
84 : -1;
85 }
86 return particles;
87}
88
89} // namespace aegir
Definition math_utils.hpp:12
void stabilise_long_lived(Pythia &pythia, double tau0_threshold)
Definition pythia_common.hpp:37
void configure_beams(Pythia &pythia, int idA, int idB, double beam_energy)
Definition pythia_common.hpp:25
std::vector< MCParticle > extract_particles(Pythia8::Event const &event, double z_offset=0.0)
Definition pythia_common.hpp:55