PYTHIA  8.316
RivetHooks.h
1 // RivetHooks.h is a part of the PYTHIA event generator.
2 // Copyright (C) 2025 Philip Ilten, Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 // Author: Philip Ilten.
7 
8 // This class implements an interface to Rivet 4 that can be loaded
9 // via the plugin structure. It can be run with PythiaParallel and merges
10 // the separate analyses into a single one at the end of the run.
11 
12 #ifndef Pythia8_RivetHooks_H
13 #define Pythia8_RivetHooks_H
14 
15 // Pythia includes.
16 #include "Pythia8/Pythia.h"
17 #include "Pythia8/Plugins.h"
18 #include "Pythia8Plugins/HepMC3.h"
19 
20 // Rivet includes.
21 #include "Rivet/AnalysisHandler.hh"
22 
23 // Directory creation for POSIX.
24 #include <sys/stat.h>
25 
26 namespace Pythia8 {
27 
28 //==========================================================================
29 
30 // Create a directory, with nesting if necessary. This is not compatible
31 // outside POSIX systems, and should be removed after migration to C++17.
32 
33 bool makeDir(vector<string> path, mode_t mode = 0777) {
34 
35  // Create the structure needed for stat.
36  struct stat info;
37 
38  // Loop over the directories.
39  string pathNow = "";
40  bool first = true;
41  for (string& dir : path) {
42 
43  // Check if the directory exists.
44  pathNow += (first ? "" : "/") + dir;
45  first = false;
46  if (stat(pathNow.c_str(), &info) == 0) {
47  if ((info.st_mode & S_IFDIR) == 0) return false;
48  else continue;
49  }
50 
51  // Create the directory and check it exists.
52  if (mkdir(pathNow.c_str(), mode) != 0) return false;
53  }
54  return true;
55 
56 }
57 
58 //==========================================================================
59 
60 // UserHook to run Rivet analyses.
61 
62 class RivetHooks : public UserHooks {
63 
64 public:
65 
66  // Constructors and destructor.
68  RivetHooks(Pythia* pythiaPtrIn, Settings*, Logger*) :
69  pythiaPtr(pythiaPtrIn) {}
70  ~RivetHooks() {if (rivetPtr != nullptr) delete rivetPtr;}
71 
72  //--------------------------------------------------------------------------
73 
74  // Perform the Rivet analysis.
75  void onEndEvent(Status) override {
76 
77  // Optionally skip zero-weight events.
78  if (flag("Rivet:skipZeroWeights") && pythiaPtr->info.weight() == 0) {
79  loggerPtr->INFO_MSG("skipping zero-weight event");
80  return;
81  }
82 
83  // Convert the event to HepMC.
84  hepmc.fillNextEvent(*pythiaPtr);
85 
86  // Create the Rivet analysis handler if it does not exist.
87  // The analysis handler constructor is not thread safe, so the
88  // global mutex must be locked.
89  if (rivetPtr == nullptr) {
90  mutexPtr->lock();
91 
92  // Create the handler.
93  rivetPtr = new Rivet::AnalysisHandler();
94 
95  // Set whether beams should be checked.
96  rivetPtr->setCheckBeams(flag("Rivet:checkBeams"));
97 
98  // Set file dumping if requested.
99  if (mode("Rivet:dumpPeriod") > 0) {
100  string dumpName = word("Rivet:dumpName");
101  if (dumpName == "") dumpName = word("Rivet:fileName");
102  rivetPtr->setFinalizePeriod(dumpName, mode("Rivet:dumpPeriod"));
103  }
104 
105  // Preload requested data.
106  for (string& preload : wvec("Rivet:preloads"))
107  rivetPtr->readData(preload);
108 
109  // Set the analyses.
110  for (string& analysis : wvec("Rivet:analyses"))
111  rivetPtr->addAnalysis(analysis);
112 
113  // Initialize Rivet and unlock the global mutex.
114  rivetPtr->init(hepmc.event());
115  mutexPtr->unlock();
116  }
117 
118  // Run the analysis.
119  rivetPtr->analyze(hepmc.event());
120 
121  }
122 
123  //--------------------------------------------------------------------------
124 
125  // Write the Rivet analyses.
126  void onStat() override {
127 
128  // Create the directory structure if needed.
129  if (rivetPtr == nullptr) return;
130  string filename = word("Rivet:fileName");
131  vector<string> path = splitString(filename, "/");
132  if (path.size() > 1) {
133  mutexPtr->lock();
134  makeDir(vector<string>(path.begin(), path.end() - 1));
135  mutexPtr->unlock();
136  }
137 
138  // Write the Rivet output.
139  rivetPtr->finalize();
140  rivetPtr->writeData(filename);
141 
142  }
143 
144  //--------------------------------------------------------------------------
145 
146  // Merge the Rivet analysis handlers.
147  void onStat(vector<PhysicsBase*> hookPtrs, Pythia*) override {
148 
149  // Get the main Rivet hook.
150  Rivet::AnalysisHandler* rivetMain = rivetPtr;
151  if (rivetMain == nullptr) {
152  loggerPtr->ERROR_MSG("could not retrieve first RivetHooks");
153  return;
154  }
155 
156  // Get the Rivet pointer for each thread.
157  int iPtr = 0;
158  for (PhysicsBase* hookPtr : hookPtrs) {
159  if (hookPtr == this) continue;
160  RivetHooks* hookNow = dynamic_cast<RivetHooks*>(hookPtr);
161  if (hookNow == nullptr) {
162  loggerPtr->ERROR_MSG("could not retrieve RivetHooks for thread ",
163  toString(iPtr));
164  return;
165  } else ++iPtr;
166 
167  // Get the current anaysis handler.
168  Rivet::AnalysisHandler* rivetNow = hookNow->rivetPtr;
169  if (rivetNow == nullptr) {
170  loggerPtr->ERROR_MSG("could not retrieve AnalysisHandler for thread ",
171  toString(iPtr));
172  return;
173  }
174 
175  // Merge the analysis handler with the main anaysis handler.
176  // This should be done with AnalysisHandler::serializeContent and
177  // AnalysisHandler::deserializeContent. However, with the most recent
178  // Rivet versions, this appears to cause issues in a threaded
179  // environment, and so for now we use use AnalysisHandler::merge.
180  try {
181  rivetMain->merge(*rivetNow);
182  } catch (const Rivet::UserError err) {
183  loggerPtr->ERROR_MSG("failed to merge analyses in thread ",
184  toString(iPtr));
185  return;
186  }
187  rivetMain->merge(*rivetNow);
188  }
189 
190  // Write the data with the main Rivet hook.
191  onStat();
192 
193  }
194 
195  //--------------------------------------------------------------------------
196 
197  private:
198 
199  Pythia* pythiaPtr{};
200  Rivet::AnalysisHandler* rivetPtr{};
201  Pythia8ToHepMC hepmc;
202 
203 };
204 
205 //--------------------------------------------------------------------------
206 
207 // Register Rivet settings.
208 
210  settingsPtr->addWord("Rivet:fileName", "rivet.yoda");
211  settingsPtr->addWVec("Rivet:analyses", {});
212  settingsPtr->addWVec("Rivet:preloads", {});
213  settingsPtr->addFlag("Rivet:checkBeams", true);
214  settingsPtr->addWord("Rivet:dumpName", "");
215  settingsPtr->addMode("Rivet:dumpPeriod", -1, true, false, -1, 0);
216  settingsPtr->addFlag("Rivet:skipZeroWeights", false);
217 }
218 
219 //--------------------------------------------------------------------------
220 
221 // Declare the plugin.
222 
223 PYTHIA8_PLUGIN_CLASS(UserHooks, RivetHooks, true, false, false)
224 PYTHIA8_PLUGIN_SETTINGS(rivetSettings)
226 PYTHIA8_PLUGIN_VERSIONS(PYTHIA_VERSION_INTEGER)
227 
228 //==========================================================================
229 
230 } // end namespace Pythia8
231 
232 #endif // end Pythia8_RivetHooks_H
void onEndEvent(Status) override
Perform the Rivet analysis.
Definition: RivetHooks.h:75
vector< string > splitString(string val, string delim)
Split a string by a delimiter.
Definition: PythiaStdlib.cc:72
GenEvent & event()
Get a reference to the current GenEvent.
Definition: HepMC2.h:465
Settings * settingsPtr
Pointer to the settings database.
Definition: PhysicsBase.h:85
Definition: PhysicsBase.h:26
mutex * mutexPtr
Mutex that should be locked for thread-unsafe code.
Definition: PhysicsBase.h:131
PYTHIA8_PLUGIN_PARALLEL(true)
Declare the plugin.
void onStat() override
Write the Rivet analyses.
Definition: RivetHooks.h:126
void onStat(vector< PhysicsBase * > hookPtrs, Pythia *) override
Merge the Rivet analysis handlers.
Definition: RivetHooks.h:147
Definition: Logger.h:23
UserHook to run Rivet analyses.
Definition: RivetHooks.h:62
void rivetSettings(Settings *settingsPtr)
Register Rivet settings.
Definition: RivetHooks.h:209
Status
Enumerate the different status codes the event generation can have.
Definition: PhysicsBase.h:31
Definition: HepMC2.h:409
string toString(bool val)
Convert a boolean to a string.
Definition: PythiaStdlib.h:208
bool makeDir(vector< string > path, mode_t mode=0777)
Definition: HepMC3Hooks.h:29
Logger * loggerPtr
Pointer to logger.
Definition: PhysicsBase.h:91
Header for classes to set beam momentum and interaction vertex spread.
Definition: Analysis.h:20
The Pythia class contains the top-level routines to generate an event.
Definition: Pythia.h:72
bool fillNextEvent(Pythia &pythia)
Definition: HepMC2.h:445
const Info & info
Public information and statistic on the generation.
Definition: Pythia.h:378
UserHooks is base class for user access to program execution.
Definition: UserHooks.h:32
RivetHooks()
Constructors and destructor.
Definition: RivetHooks.h:67
bool flag(string key) const
Shorthand to read settings values.
Definition: PhysicsBase.h:44
double weight(int i=0) const
Event weights and accumulated weight.
Definition: Info.cc:162
Definition: Settings.h:196
void addFlag(string keyIn, bool defaultIn)
Add new entry.
Definition: Settings.h:280