9#include <generators/modules/herwigfragmentation/HerwigHepMCFragmentationModule.h>
10#include <generators/modules/herwigfragmentation/HerwigFragHelper.h>
12#include <framework/logging/Logger.h>
13#include <framework/datastore/StoreArray.h>
14#include <framework/datastore/StoreObjPtr.h>
15#include <framework/dataobjects/EventMetaData.h>
16#include <framework/utilities/FileSystem.h>
17#include <mdst/dataobjects/MCParticle.h>
18#include <framework/particledb/EvtGenDatabasePDG.h>
20#include <HepMC/IO_GenEvent.h>
21#include <HepMC/GenEvent.h>
23#include <EvtGenBase/EvtPDL.hh>
24#include <EvtGenBase/EvtDecayTable.hh>
39HerwigHepMCFragmentationModule::HerwigHepMCFragmentationModule() :
Module()
41 setDescription(
"Stage 3 of the Herwig multistep pipeline. "
42 "Reads pre-generated HepMC events and KKMC sidecar files, reconstructs "
43 "the full MCParticle truth tree, and applies EvtGen + PHOTOS decays. "
44 "No subprocess calls in event() (thus parallelizable with c_ParallelProcessingCertified).");
46 setPropertyFlags(c_ParallelProcessingCertified);
48 addParam(
"WorkDir", m_workDir,
"Working directory written by HerwigLHEWriterModule.", std::string(
""));
49 addParam(
"UseEvtGen", m_useEvtGen,
"Use EvtGen for particle decays",
true);
50 addParam(
"DecFile", m_decFile,
"EvtGen decay file",
52 addParam(
"UserDecFile", m_userDecFile,
"EvtGen user decay file path", std::string(
""));
53 addParam(
"CoherentMixing", m_coherentMixing,
"Coherent B0-B0bar mixing",
true);
54 addParam(
"KeepTempFiles", m_keepTempFiles,
55 "Keep WorkDir and all pipeline files after terminate(). "
56 "False (default): delete WorkDir recursively after all events are processed. "
57 "True: keep intermediate files.",
false);
60HerwigHepMCFragmentationModule::~HerwigHepMCFragmentationModule()
74 B2FATAL(
"HerwigHepMCFragmentation: WorkDir parameter must be set to the directory "
75 "written by HerwigLHEWriterModule.");
79 struct stat hepmcStat;
81 B2FATAL(
"HerwigHepMCFragmentation: HepMC file not found: " <<
m_hepmcPath);
84 B2FATAL(
"HerwigHepMCFragmentation: failed to read manifest from " <<
m_workDir);
87 B2FATAL(
"HerwigHepMCFragmentation: failed to load HepMC events from " <<
m_hepmcPath);
93 B2INFO(
"HerwigHepMCFragmentation: EvtGen initialized");
94 }
catch (std::exception& e) {
95 B2ERROR(
"EvtGen initialization failed: " << e.what());
100 m_helper = std::make_unique<HerwigFragHelper>();
104 B2INFO(
"HerwigHepMCFragmentation: loaded " <<
m_hepmcEvents.size()
116 <<
" events (" << std::fixed << std::setprecision(2) << rate <<
"%)");
117 if (rate < 97.0 && m_eventCounter > 100)
118 B2WARNING(
"Success rate below 97% threshold");
123 B2INFO(
"HerwigHepMCFragmentation: removed working directory " <<
m_workDir);
124 }
catch (std::exception& e) {
125 B2WARNING(
"HerwigHepMCFragmentation: could not remove working directory "
139 const int globalEvtNum =
static_cast<int>(eventMetaData->getEvent());
147 HepMC::GenEvent* hepmc = evtIt->second;
152 std::vector<int> quarkIndices;
154 B2ERROR(
"HerwigHepMCFragmentation: failed to load sidecar for event " << globalEvtNum);
162 B2ERROR(
"HerwigHepMCFragmentation: addHepMCToGraph failed for event " << globalEvtNum);
179 B2ERROR(
"HerwigHepMCFragmentation: EvtGen decays failed for event "
180 << globalEvtNum <<
" - event skipped (incomplete MCParticle tree)");
194 const std::string manifestPath =
m_workDir +
"/manifest.txt";
195 std::ifstream mf(manifestPath);
197 B2ERROR(
"HerwigHepMCFragmentation: cannot open " << manifestPath);
202 while (std::getline(mf, line)) {
203 if (line.empty() || line[0] ==
'#')
continue;
204 std::istringstream ss(line);
207 if (key ==
"work_dir" || key ==
"batch_seed" || key ==
"total_events")
continue;
210 int evtNum = std::stoi(key);
211 std::string sidecarFile;
213 if (!sidecarFile.empty())
216 B2WARNING(
"HerwigHepMCFragmentation: skipping manifest line: " << line);
219 B2DEBUG(10,
"HerwigHepMCFragmentation: manifest loaded, " <<
m_sidecarMap.size() <<
" events");
228 HepMC::IO_GenEvent ascii_in(
m_hepmcPath.c_str(), std::ios::in);
229 HepMC::GenEvent* evt;
231 bool signal_id_ok =
true;
232 std::vector<HepMC::GenEvent*> ordered;
234 while ((evt = ascii_in.read_next_event()) !=
nullptr) {
235 ordered.push_back(evt);
236 const int spid = evt->signal_process_id();
240 signal_id_ok =
false;
246 B2ERROR(
"HerwigHepMCFragmentation: no events read from " <<
m_hepmcPath);
251 if (!signal_id_ok ||
static_cast<int>(
m_hepmcEvents.size()) != count) {
252 B2WARNING(
"HerwigHepMCFragmentation: signal_process_id not unique or missing. "
253 "Herwig 7.2.0 sets signal_process_id=-1 so we use sequential manifest-to-HepMC ordering for this version.");
259 if (hepmc_idx >= count)
break;
263 if (hepmc_idx < count) {
264 B2WARNING(
"HerwigHepMCFragmentation: " << count - hepmc_idx
265 <<
" HepMC events could not be mapped to manifest entries.");
267 for (; hepmc_idx < count; ++hepmc_idx)
268 delete ordered[hepmc_idx];
271 B2INFO(
"HerwigHepMCFragmentation: signal_process_id mapping verified for "
272 << count <<
" events.");
282 std::vector<int>& quarkIndices)
284 quarkIndices.clear();
288 B2ERROR(
"HerwigHepMCFragmentation: no sidecar entry for event " << globalEvtNum);
292 std::ifstream sf(it->second);
294 B2ERROR(
"HerwigHepMCFragmentation: cannot open sidecar " << it->second);
300 std::getline(sf, line);
304 if (n <= 0) { B2ERROR(
"HerwigHepMCFragmentation: empty sidecar for event " << globalEvtNum);
return false; }
309 struct SidecarEntry {
310 int pdg, status, motherIdx;
311 double e, px, py, pz, vx, vy, vz, vt, mass;
313 std::vector<SidecarEntry> entries(n);
316 for (
int i = 0; i < n; ++i) {
317 auto& e = entries[i];
318 sf >> e.pdg >> e.e >> e.px >> e.py >> e.pz
319 >> e.vx >> e.vy >> e.vz >> e.vt
320 >> e.mass >> e.status >> e.motherIdx;
324 for (
int i = 0; i < n; ++i) {
325 const auto& entry = entries[i];
330 p.setMomentum(ROOT::Math::XYZVector(entry.px, entry.py, entry.pz));
331 p.setMass(entry.mass);
333 const double e2 = entry.px * entry.px + entry.py * entry.py
334 + entry.pz * entry.pz + entry.mass * entry.mass;
335 p.setEnergy(e2 > 0.0 ? std::sqrt(e2) : 0.0);
336 p.setProductionVertex(ROOT::Math::XYZVector(entry.vx, entry.vy, entry.vz));
337 p.setProductionTime(entry.vt);
338 p.setStatus(entry.status);
341 if (entry.motherIdx >= 0 && entry.motherIdx < n)
345 const int absPdg = std::abs(entry.pdg);
346 if (absPdg >= 1 && absPdg <= 5 && entry.motherIdx >= 0)
347 quarkIndices.push_back(graphBase + i);
363 for (
size_t i = 0; i < nBefore; ++i) {
371 if (std::abs(gp.
getPDG()) == 10433) {
372 const char* name = (gp.
getPDG() > 0) ?
"D_s1+" :
"D_s1-";
374 if (part) gp.
setPDG(part->PdgCode());
378 if (gp.
getPDG() == 10221) {
380 if (part) gp.
setPDG(part->PdgCode());
384 const EvtId
id = EvtPDL::evtIdFromStdHep(gp.
getPDG());
385 if (
id.getId() < 0)
continue;
388 if (EvtDecayTable::getInstance()->getNModes(
id) == 0)
continue;
391 if (result > 0) nDecayed++;
400 }
catch (std::exception& e) {
401 B2ERROR(
"EvtGen decay failed: " << e.what());
static EvtGenDatabasePDG * Instance()
Instance method that loads the EvtGen table.
static std::string findFile(const std::string &path, bool silent=false)
Search for given file or directory in local or central release directory, and return absolute path if...
std::string m_userDecFile
EvtGen user decay file.
int m_successCounter
Counts successfully processed events.
bool m_coherentMixing
Coherent B0/B0bar mixing in EvtGen.
std::map< int, std::string > m_sidecarMap
Mapping from global event number to sidecar filename (basename only).
virtual void initialize() override
Open and validate HepMC file; load all events and manifest; initialize EvtGen.
std::string m_decFile
EvtGen main decay file.
virtual void event() override
Load one HepMC event, reconstruct MCParticle truth tree, apply EvtGen decays.
virtual void terminate() override
Log success rate; warn if below 97% threshold.
bool applyEvtGenDecays()
Apply EvtGen filter and write decayed particles to StoreArray.
std::string m_hepmcPath
Resolved path to all_events.hepmc.
std::map< int, HepMC::GenEvent * > m_hepmcEvents
Mapping from global basf2 event number to HepMC event stored in memory.
bool m_useEvtGen
Use EvtGen for particle decays (default true).
std::string m_workDir
Working directory written by HerwigLHEWriterModule (Stage 1).
bool m_keepTempFiles
If false, delete WorkDir and all pipeline files in terminate().
int m_eventCounter
Counts event() calls.
bool loadManifest()
Load the manifest.txt written by HerwigLHEWriterModule::terminate().
bool loadKKMCSidecar(int globalEvtNum, std::vector< int > &quarkIndices)
Add KKMC particles from sidecar file to m_mcParticleGraph.
MCParticleGraph m_mcParticleGraph
Particle graph rebuilt per event.
std::unique_ptr< HerwigFragHelper > m_helper
Two-pass HepMC-to-graph helper.
bool loadHepMCEvents()
Load all HepMC events from all_events.hepmc into m_hepmcEvents map.
EvtGenInterface m_evtGenInterface
Persistent EvtGen interface; setup() called once in initialize().
Class to represent Particle data in graph.
@ c_checkCyclic
Check for cyclic dependencies.
@ c_clearParticles
Clear the particle list before adding the graph.
@ c_setDecayInfo
Set decay time and vertex.
void setPDG(int pdg)
Set PDG code of the particle.
int getPDG() const
Return PDG code of particle.
int getFirstDaughter() const
Get 1-based index of first daughter, 0 if no daughters.
void setReturnValue(int value)
Sets the return value for this module as integer.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
Accessor to arrays stored in the data store.
Type-safe access to single objects in the data store.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Abstract base class for different kinds of events.