cepgen is hosted by Hepforge, IPPP Durham
CepGen 1.2.5
Central exclusive processes event generator
Loading...
Searching...
No Matches
Pythia8Hadroniser.cpp
Go to the documentation of this file.
1/*
2 * CepGen: a central exclusive processes event generator
3 * Copyright (C) 2016-2024 Laurent Forthomme
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <unordered_map>
20
23#include "CepGen/Event/Event.h"
27#include "CepGen/Physics/PDG.h"
28#include "CepGen/Utils/Value.h"
30
31namespace cepgen {
32 namespace hadr {
36 public:
37 explicit Pythia8Hadroniser(const ParametersList& plist)
38 : Hadroniser(plist),
39 pythia_(new Pythia8::Pythia),
40 cg_evt_(new Pythia8::CepGenEvent),
41 correct_central_(steer<bool>("correctCentralSystem")),
42 debug_lhef_(steer<bool>("debugLHEF")),
43 output_config_(steer<std::string>("outputConfig")) {}
44
46 if (!output_config_.empty())
47 pythia_->settings.writeFile(output_config_, false);
48 if (debug_lhef_)
49 cg_evt_->closeLHEF(true);
50 }
51
53
54 void readString(const std::string& param) override {
55 if (!pythia_->readString(param))
56 throw CG_FATAL("Pythia8Hadroniser") << "The Pythia8 core failed to parse the following setting:\n\t" << param;
57 }
58 void initialise() override;
59 bool run(Event& ev, double& weight, bool fast) override;
60
61 void setCrossSection(const Value& cross_section) override {
62 cg_evt_->setCrossSection(0, cross_section, cross_section.uncertainty());
63 }
64
65 private:
66 void* enginePtr() override { return (void*)pythia_.get(); }
67
68 static constexpr unsigned short PYTHIA_STATUS_IN_BEAM = 12;
69 static constexpr unsigned short PYTHIA_STATUS_IN_PARTON_KT = 61;
70
71 pdgids_t min_ids_;
72 std::unordered_map<short, short> py_cg_corresp_;
73 unsigned short findRole(const Event& ev, const Pythia8::Particle& p) const;
74 void updateEvent(Event& ev, double& weight) const;
75 Particle& addParticle(Event& ev, const Pythia8::Particle&, const Pythia8::Vec4& mom, unsigned short) const;
76
77 const std::unique_ptr<Pythia8::Pythia> pythia_;
78 const std::shared_ptr<Pythia8::CepGenEvent> cg_evt_;
79
80 const bool correct_central_;
81 const bool debug_lhef_;
82 const std::string output_config_;
83 bool res_decay_{true};
84 bool enable_hadr_{false};
85 unsigned short offset_{0};
86 bool first_evt_{true};
87 };
88
90 cg_evt_->initialise(runParameters());
91#if defined(PYTHIA_VERSION_INTEGER) && PYTHIA_VERSION_INTEGER < 8300
92 pythia_->setLHAupPtr(cg_evt_.get());
93#else
94 pythia_->setLHAupPtr(cg_evt_);
95#endif
96 const auto& kin = runParameters().kinematics();
97
98 pythia_->settings.parm("Beams:idA", (long)kin.incomingBeams().positive().integerPdgId());
99 pythia_->settings.parm("Beams:idB", (long)kin.incomingBeams().negative().integerPdgId());
100 // specify we will be using a LHA input
101 pythia_->settings.mode("Beams:frameType", 5);
102 pythia_->settings.parm("Beams:eCM", kin.incomingBeams().sqrtS());
103 min_ids_ = kin.minimumFinalState();
104 if (debug_lhef_)
105 cg_evt_->openLHEF("debug.lhe");
106 pythia_->settings.flag("ProcessLevel:resonanceDecays", res_decay_);
107 if (pythia_->settings.flag("ProcessLevel:all") != enable_hadr_)
108 pythia_->settings.flag("ProcessLevel:all", enable_hadr_);
109
110 if (seed_ == -1ll)
111 pythia_->settings.flag("Random:setSeed", false);
112 else {
113 pythia_->settings.flag("Random:setSeed", true);
114 pythia_->settings.mode("Random:seed", seed_);
115 }
116
117#if defined(PYTHIA_VERSION_INTEGER) && PYTHIA_VERSION_INTEGER >= 8226
118 switch (kin.incomingBeams().mode()) {
120 pythia_->settings.mode("BeamRemnants:unresolvedHadron", 3);
121 pythia_->settings.flag("PartonLevel:MPI", false);
122 } break;
124 pythia_->settings.mode("BeamRemnants:unresolvedHadron", 2);
125 pythia_->settings.flag("PartonLevel:MPI", false);
126 } break;
128 pythia_->settings.mode("BeamRemnants:unresolvedHadron", 1);
129 pythia_->settings.flag("PartonLevel:MPI", false);
130 } break;
132 default: {
133 pythia_->settings.mode("BeamRemnants:unresolvedHadron", 0);
134 } break;
135 }
136#else
137 CG_WARNING("Pythia8Hadroniser") << "Beam remnants framework for this version of Pythia "
138 << "(" << utils::format("%.3f", pythia_->settings.parm("Pythia:versionNumber"))
139 << ")\n\t"
140 << "does not support mixing of unresolved hadron states.\n\t"
141 << "The proton remnants output might hence be wrong.\n\t"
142 << "Please update the Pythia version or disable this part.";
143#endif
144 if (correct_central_ && res_decay_)
145 CG_WARNING("Pythia8Hadroniser") << "Central system's kinematics correction enabled while resonances are\n\t"
146 << "expected to be decayed. Please check that this is fully intended.";
147
148 if (!pythia_->init())
149 throw CG_FATAL("Pythia8Hadroniser") << "Failed to initialise the Pythia8 core!\n\t"
150 << "See the message above for more details.";
151
152 if (debug_lhef_)
153 cg_evt_->initLHEF();
154 }
155
156 bool Pythia8Hadroniser::run(Event& ev, double& weight, bool fast) {
157 //--- initialise the event weight before running any decay algorithm
158 weight = 1.;
159
160 //--- only launch Pythia if:
161 // 1) the full event kinematics (i.e. with remnants) is to be specified,
162 // 2) the remnants are to be fragmented, or
163 // 3) the resonances are to be decayed.
164 if (!fast && !remn_fragm_ && !res_decay_)
165 return true;
166 if (fast && !res_decay_)
167 return true;
168
169 //--- switch full <-> partial event
170 if (!fast != enable_hadr_) {
171 enable_hadr_ = !fast;
172 initialise();
173 }
174
175 //===========================================================================================
176 // convert our event into a custom LHA format
177 //===========================================================================================
178
179 cg_evt_->feedEvent(
180 ev,
182 if (debug_lhef_ && !fast)
183 cg_evt_->eventLHEF();
184
185 //===========================================================================================
186 // launch the hadronisation / resonances decays, and update the event accordingly
187 //===========================================================================================
188
189 auto& num_hadr_trials = ev.metadata["pythia8:num_hadronisation_trials"];
190 num_hadr_trials = 0;
191 while (true) {
192 if (num_hadr_trials++ > max_trials_)
193 return false;
194 //--- run the hadronisation/fragmentation algorithm
195 if (pythia_->next()) {
196 //--- hadronisation successful
197 if (first_evt_ && !fast) {
198 offset_ = 0;
199 for (unsigned short i = 1; i < pythia_->event.size(); ++i)
200 if (pythia_->event[i].status() == -PYTHIA_STATUS_IN_BEAM)
201 //--- no incoming particles in further stages
202 offset_++;
203 first_evt_ = false;
204 }
205 break;
206 }
207 }
208 CG_DEBUG("Pythia8Hadroniser") << "Pythia8 hadronisation performed successfully.\n\t"
209 << "Number of trials: " << num_hadr_trials << "/" << max_trials_ << ".\n\t"
210 << "Particles multiplicity: " << ev.particles().size() << " → "
211 << pythia_->event.size() << ".\n\t"
212 << " indices offset: " << offset_ << ".";
213
214 //===========================================================================================
215 // update the event content with Pythia's output
216 //===========================================================================================
217
218 updateEvent(ev, weight);
219 return true;
220 }
221
222 Particle& Pythia8Hadroniser::addParticle(Event& ev,
223 const Pythia8::Particle& py_part,
224 const Pythia8::Vec4& mom,
225 unsigned short role) const {
227 const pdgid_t pdg_id = py_part.idAbs();
228 //--- define the particle if not already in the list of handled PDGs
229 try {
230 prop = PDG::get()(pdg_id);
231 } catch (const Exception&) {
232 prop.pdgid = pdg_id;
233 prop.name = prop.descr = py_part.name();
234 prop.colours = py_part.col(); // colour factor
235 prop.mass = py_part.m0();
236 prop.width = py_part.mWidth();
237 if (const auto ch = int(py_part.charge() * 3.); std::abs(ch) > 0)
238 prop.charges = {ch, -ch};
239 prop.fermion = py_part.isLepton();
240 PDG::get().define(prop);
241 }
242 //--- add the particle to the event content
243 Particle& op = ev.addParticle((Particle::Role)role);
244 op.setPdgId((long)py_part.id());
245 op.setStatus(py_part.isFinal() ? Particle::Status::FinalState
246 : (Particle::Role)role == Particle::Role::CentralSystem ? Particle::Status::Propagator
247 : Particle::Status::Fragmented);
248 op.setMomentum(Momentum(mom.px(), mom.py(), mom.pz(), mom.e()).setMass(mom.mCalc()));
249 cg_evt_->addCorresp(py_part.index() - offset_, op.id());
250 return op;
251 }
252
253 void Pythia8Hadroniser::updateEvent(Event& ev, double& weight) const {
254 std::vector<unsigned short> central_parts;
255
256 for (unsigned short i = 1 + offset_; i < pythia_->event.size(); ++i) {
257 const Pythia8::Particle& p = pythia_->event[i];
258 const unsigned short cg_id = cg_evt_->cepgenId(i - offset_);
260 //----- particle already in the event
261 Particle& cg_part = ev[cg_id];
262 //--- fragmentation result
263 if (cg_part.role() == Particle::OutgoingBeam1 || cg_part.role() == Particle::OutgoingBeam2) {
264 cg_part.setStatus(Particle::Status::Fragmented);
265 continue;
266 }
267 //--- resonance decayed; apply branching ratio for this decay
268 if (cg_part.role() == Particle::CentralSystem && p.status() < 0) {
269 if (res_decay_)
270 weight *= p.particleDataEntry().pickChannel().bRatio();
271 cg_part.setStatus(Particle::Status::Resonance);
272 central_parts.emplace_back(i);
273 }
274 //--- particle is not what we expect
275 if (p.idAbs() != abs(cg_part.integerPdgId())) {
276 CG_INFO("Pythia8Hadroniser:update") << "LHAEVT event content:";
277 cg_evt_->listEvent();
278 CG_INFO("Pythia8Hadroniser:update") << "Pythia event content:";
279 pythia_->event.list();
280 CG_INFO("Pythia8Hadroniser:update") << "CepGen event content:";
281 ev.dump();
282 CG_INFO("Pythia8Hadroniser:update") << "Correspondence:";
283 cg_evt_->dumpCorresp();
284
285 throw CG_FATAL("Pythia8Hadroniser:update")
286 << "Event list corruption detected for (Pythia/CepGen) particle " << i << "/" << cg_id << ":\n\t"
287 << "should be " << abs(p.id()) << ", "
288 << "got " << cg_part.integerPdgId() << "!";
289 }
290 }
291 //--- check for messed up particles parentage and discard incoming beam particles
292 /*else if ( p.mother1() > i || p.mother1() <= offset_ )
293 continue;
294 else if ( p.mother2() > i || p.mother2() <= offset_ )
295 continue;*/
296 else {
297 //----- new particle to be added
298 const unsigned short role = findRole(ev, p);
299 switch ((Particle::Role)role) {
302 break;
305 break;
306 default:
307 break;
308 }
309 // found the role ; now we can add the particle
310 Particle& cg_part = addParticle(ev, p, p.p(), role);
311 if (correct_central_ && (Particle::Role)role == Particle::CentralSystem) {
312 if (const auto ip = std::find(central_parts.begin(), central_parts.end(), p.mother1());
313 ip != central_parts.end())
314 cg_part.setMomentum(ev[cg_evt_->cepgenId(*ip - offset_)].momentum());
315 }
316 for (const auto& moth_id : p.motherList()) {
317 if (moth_id <= offset_)
318 continue;
319 if (const unsigned short moth_cg_id = cg_evt_->cepgenId(moth_id - offset_);
321 cg_part.addMother(ev[moth_cg_id]);
322 else
323 cg_part.addMother(addParticle(ev, pythia_->event[moth_id], p.p(), role));
324 if (!p.isFinal()) {
325 if (p.isResonance() || !p.daughterList().empty())
326 cg_part.setStatus(Particle::Status::Resonance);
327 else
328 cg_part.setStatus(Particle::Status::Undefined);
329 }
330 }
331 }
332 }
333 }
334
335 unsigned short Pythia8Hadroniser::findRole(const Event& ev, const Pythia8::Particle& p) const {
336 for (const auto& par_id : p.motherList()) {
337 if (par_id == 1 && offset_ > 0)
338 return (unsigned short)Particle::OutgoingBeam1;
339 if (par_id == 2 && offset_ > 0)
340 return (unsigned short)Particle::OutgoingBeam2;
341 if (const unsigned short par_cg_id = cg_evt_->cepgenId(par_id - offset_);
343 return (unsigned short)ev(par_cg_id).role();
345 return findRole(ev, pythia_->event[par_id]);
346 }
347 return (unsigned short)Particle::UnknownRole;
348 }
349
350 ParametersDescription Pythia8Hadroniser::description() {
351 auto desc = Hadroniser::description();
352 desc.setDescription("Interface to the Pythia 8 string hadronisation/fragmentation algorithm");
353 desc.add<bool>("correctCentralSystem", false)
354 .setDescription("Correct the kinematics of the central system whenever required");
355 desc.add<bool>("debugLHEF", false).setDescription("Switch on the dump of each event into a debugging LHEF file");
356 desc.add<std::string>("outputConfig", "last_pythia_config.cmd")
357 .setDescription("Output filename for a backup of the last Pythia configuration snapshot");
358 return desc;
359 }
360 } // namespace hadr
361} // namespace cepgen
362// register hadroniser
#define REGISTER_MODIFIER(name, obj)
Add a generic event modification module definition to the factory.
#define CG_FATAL(mod)
Definition Exception.h:61
#define CG_WARNING(mod)
Definition Message.h:228
#define CG_DEBUG(mod)
Definition Message.h:220
#define CG_INFO(mod)
Definition Message.h:216
@ centralAndPartons
only include initiators and central system
@ centralAndBeamRemnants
include undissociated beam remnants and central system
static constexpr unsigned short INVALID_ID
Invalid id association.
const RunParameters & runParameters() const
List of run parameters.
unsigned short max_trials_
Maximal number of trials for the algorithm.
long long seed_
Random numbers generator seed fed to the algorithm.
Container for the information on the in- and outgoing particles' kinematics.
Definition Event.h:28
ParticleRef addParticle(Particle &part, bool replace=false)
Set the information on one particle in the process.
Definition Event.cpp:323
Particles particles() const
Vector of all particles in the event.
Definition Event.cpp:353
EventMetadata metadata
List of auxiliary information.
Definition Event.h:136
void define(const ParticleProperties &)
Add a new particle definition to the library.
Definition PDG.cpp:60
static PDG & get()
Retrieve a unique instance of this particles info collection.
Definition PDG.cpp:41
A description object for parameters collection.
Kinematic information for one particle.
Definition Particle.h:33
@ Fragmented
Already fragmented outgoing beam.
@ FinalState
Stable, final state particle.
@ Resonance
Already decayed intermediate resonance.
@ Undefined
Undefined particle.
@ OutgoingBeam1
outgoing beam state/particle
Definition Particle.h:54
@ UnknownRole
Undefined role.
Definition Particle.h:51
@ OutgoingBeam2
outgoing beam state/particle
Definition Particle.h:55
@ CentralSystem
Central particles system.
Definition Particle.h:56
const Kinematics & kinematics() const
Events kinematics for phase space definition.
T steer(const std::string &key) const
Retrieve a parameters as previously steered.
Definition Steerable.h:39
A scalar value with its uncertainty.
Definition Value.h:26
double uncertainty() const
Absolute uncertainty around the central value.
Definition Value.h:35
Class template to define any hadroniser as a general object with defined methods.
Definition Hadroniser.h:32
const bool remn_fragm_
Switch on/off the remnants fragmentation where applicable.
Definition Hadroniser.h:44
Interface to the Pythia8 hadronisation algorithm.
void readString(const std::string &param) override
Parse a configuration string.
void setCrossSection(const Value &cross_section) override
Specify the cross section value, in pb.
Pythia8Hadroniser(const ParametersList &plist)
bool run(Event &ev, double &weight, bool fast) override
Modify an event.
static ParametersDescription description()
@ ElasticElastic
proton-proton elastic case
@ InelasticElastic
proton-proton single-dissociative (or elastic-inelastic) case
@ InelasticInelastic
proton-proton double-dissociative case
@ ElasticInelastic
proton-proton single-dissociative (or inelastic-elastic) case
std::string format(const std::string &fmt, Args... args)
Format a string using a printf style format descriptor.
Definition String.h:61
Common namespace for this Monte Carlo generator.
std::vector< pdgid_t > pdgids_t
unsigned long long pdgid_t
Alias for the integer-like particle PDG id.
A collection of physics constants associated to a single particle.
std::string descr
Human-readable name.
double mass
Mass, in GeV/c .
pdgid_t pdgid
PDG identifier.
std::vector< int > charges
Electric charges, in /3.
std::string name
Particle name.
double width
Decay width, in GeV/c .
bool fermion
Is the particle a fermion?