Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-18 08:20:49

0001 // RivetHooks.h is a part of the PYTHIA event generator.
0002 // Copyright (C) 2025 Philip Ilten, Torbjorn Sjostrand.
0003 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
0004 // Please respect the MCnet Guidelines, see GUIDELINES for details.
0005 
0006 // Author: Philip Ilten.
0007 
0008 // This class implements an interface to Rivet 4 that can be loaded
0009 // via the plugin structure. It can be run with PythiaParallel and merges
0010 // the separate analyses into a single one at the end of the run.
0011 
0012 #ifndef Pythia8_RivetHooks_H
0013 #define Pythia8_RivetHooks_H
0014 
0015 // Pythia includes.
0016 #include "Pythia8/Pythia.h"
0017 #include "Pythia8/Plugins.h"
0018 #include "Pythia8Plugins/HepMC3.h"
0019 
0020 // Rivet includes.
0021 #include "Rivet/AnalysisHandler.hh"
0022 
0023 // Directory creation for POSIX.
0024 #include <sys/stat.h>
0025 
0026 namespace Pythia8 {
0027 
0028 //==========================================================================
0029 
0030 // Create a directory, with nesting if necessary. This is not compatible
0031 // outside POSIX systems, and should be removed after migration to C++17.
0032 
0033 bool makeDir(vector<string> path, mode_t mode = 0777) {
0034 
0035   // Create the structure needed for stat.
0036   struct stat info;
0037 
0038   // Loop over the directories.
0039   string pathNow = "";
0040   bool first = true;
0041   for (string& dir : path) {
0042 
0043     // Check if the directory exists.
0044     pathNow += (first ? "" : "/") + dir;
0045     first = false;
0046     if (stat(pathNow.c_str(), &info) == 0) {
0047       if ((info.st_mode & S_IFDIR) == 0) return false;
0048       else continue;
0049     }
0050 
0051     // Create the directory and check it exists.
0052     if (mkdir(pathNow.c_str(), mode) != 0) return false;
0053   }
0054   return true;
0055 
0056 }
0057 
0058 //==========================================================================
0059 
0060 // UserHook to run Rivet analyses.
0061 
0062 class RivetHooks : public UserHooks {
0063 
0064 public:
0065 
0066   // Constructors and destructor.
0067   RivetHooks() {}
0068   RivetHooks(Pythia* pythiaPtrIn, Settings*, Logger*) :
0069     pythiaPtr(pythiaPtrIn) {}
0070   ~RivetHooks() {if (rivetPtr != nullptr) delete rivetPtr;}
0071 
0072   //--------------------------------------------------------------------------
0073 
0074   // Perform the Rivet analysis.
0075   void onEndEvent(Status) override {
0076 
0077     // Optionally skip zero-weight events.
0078     if (flag("Rivet:skipZeroWeights") && pythiaPtr->info.weight() == 0) {
0079       loggerPtr->INFO_MSG("skipping zero-weight event");
0080       return;
0081     }
0082 
0083     // Convert the event to HepMC.
0084     hepmc.fillNextEvent(*pythiaPtr);
0085 
0086     // Create the Rivet analysis handler if it does not exist.
0087     // The analysis handler constructor is not thread safe, so the
0088     // global mutex must be locked.
0089     if (rivetPtr == nullptr) {
0090       mutexPtr->lock();
0091 
0092       // Create the handler.
0093       rivetPtr = new Rivet::AnalysisHandler();
0094 
0095       // Set whether beams should be checked.
0096       rivetPtr->setCheckBeams(flag("Rivet:checkBeams"));
0097 
0098       // Set file dumping if requested.
0099       if (mode("Rivet:dumpPeriod") > 0) {
0100         string dumpName = word("Rivet:dumpName");
0101         if (dumpName == "") dumpName = word("Rivet:fileName");
0102         rivetPtr->setFinalizePeriod(dumpName, mode("Rivet:dumpPeriod"));
0103       }
0104 
0105       // Preload requested data.
0106       for (string& preload : wvec("Rivet:preloads"))
0107         rivetPtr->readData(preload);
0108 
0109       // Set the analyses.
0110       for (string& analysis : wvec("Rivet:analyses"))
0111         rivetPtr->addAnalysis(analysis);
0112 
0113       // Initialize Rivet and unlock the global mutex.
0114       rivetPtr->init(hepmc.event());
0115       mutexPtr->unlock();
0116     }
0117 
0118     // Run the analysis.
0119     rivetPtr->analyze(hepmc.event());
0120 
0121   }
0122 
0123   //--------------------------------------------------------------------------
0124 
0125   // Write the Rivet analyses.
0126   void onStat() override {
0127 
0128     // Create the directory structure if needed.
0129     if (rivetPtr == nullptr) return;
0130     string filename = word("Rivet:fileName");
0131     vector<string> path = splitString(filename, "/");
0132     if (path.size() > 1) {
0133       mutexPtr->lock();
0134       makeDir(vector<string>(path.begin(), path.end() - 1));
0135       mutexPtr->unlock();
0136     }
0137 
0138     // Write the Rivet output.
0139     rivetPtr->finalize();
0140     rivetPtr->writeData(filename);
0141 
0142   }
0143 
0144   //--------------------------------------------------------------------------
0145 
0146   // Merge the Rivet analysis handlers.
0147   void onStat(vector<PhysicsBase*> hookPtrs, Pythia*) override {
0148 
0149     // Get the main Rivet hook.
0150     Rivet::AnalysisHandler* rivetMain = rivetPtr;
0151     if (rivetMain == nullptr) {
0152       loggerPtr->ERROR_MSG("could not retrieve first RivetHooks");
0153       return;
0154     }
0155 
0156     // Get the Rivet pointer for each thread.
0157     int iPtr = 0;
0158     for (PhysicsBase* hookPtr : hookPtrs) {
0159       if (hookPtr == this) continue;
0160       RivetHooks* hookNow = dynamic_cast<RivetHooks*>(hookPtr);
0161       if (hookNow == nullptr) {
0162         loggerPtr->ERROR_MSG("could not retrieve RivetHooks for thread ",
0163           toString(iPtr));
0164         return;
0165       } else ++iPtr;
0166 
0167       // Get the current anaysis handler.
0168       Rivet::AnalysisHandler* rivetNow = hookNow->rivetPtr;
0169       if (rivetNow == nullptr) {
0170         loggerPtr->ERROR_MSG("could not retrieve AnalysisHandler for thread ",
0171           toString(iPtr));
0172         return;
0173       }
0174 
0175       // Merge the analysis handler with the main anaysis handler.
0176       // This should be done with AnalysisHandler::serializeContent and
0177       // AnalysisHandler::deserializeContent. However, with the most recent
0178       // Rivet versions, this appears to cause issues in a threaded
0179       // environment, and so for now we use use AnalysisHandler::merge.
0180       try {
0181         rivetMain->merge(*rivetNow);
0182       } catch (const Rivet::UserError err) {
0183         loggerPtr->ERROR_MSG("failed to merge analyses in thread ",
0184           toString(iPtr));
0185         return;
0186       }
0187       rivetMain->merge(*rivetNow);
0188     }
0189 
0190     // Write the data with the main Rivet hook.
0191     onStat();
0192 
0193   }
0194 
0195   //--------------------------------------------------------------------------
0196 
0197  private:
0198 
0199   Pythia* pythiaPtr{};
0200   Rivet::AnalysisHandler* rivetPtr{};
0201   Pythia8ToHepMC hepmc;
0202 
0203 };
0204 
0205 //--------------------------------------------------------------------------
0206 
0207 // Register Rivet settings.
0208 
0209 void rivetSettings(Settings *settingsPtr) {
0210   settingsPtr->addWord("Rivet:fileName", "rivet.yoda");
0211   settingsPtr->addWVec("Rivet:analyses", {});
0212   settingsPtr->addWVec("Rivet:preloads", {});
0213   settingsPtr->addFlag("Rivet:checkBeams", true);
0214   settingsPtr->addWord("Rivet:dumpName", "");
0215   settingsPtr->addMode("Rivet:dumpPeriod", -1, true, false, -1, 0);
0216   settingsPtr->addFlag("Rivet:skipZeroWeights", false);
0217 }
0218 
0219 //--------------------------------------------------------------------------
0220 
0221 // Declare the plugin.
0222 
0223 PYTHIA8_PLUGIN_CLASS(UserHooks, RivetHooks, true, false, false)
0224 PYTHIA8_PLUGIN_SETTINGS(rivetSettings)
0225 PYTHIA8_PLUGIN_PARALLEL(true);
0226 PYTHIA8_PLUGIN_VERSIONS(PYTHIA_VERSION_INTEGER)
0227 
0228 //==========================================================================
0229 
0230 } // end namespace Pythia8
0231 
0232 #endif // end Pythia8_RivetHooks_H