main144
Back to index.
// main144.cc is a part of the PYTHIA event generator.
// Copyright (C) 2025 Torbjorn Sjostrand.
// PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
// Please respect the MCnet Guidelines, see GUIDELINES for details.
// Authors:
//            Christian Bierlich
// Keywords:
//            Analysis
//            Hepmc
//            Command file
//            Command line option
//            Root
//            Rivet
//            Tuning
// Streamlined event generation with possibility to output ROOT files,
// output HepMC files and run RIVET analyses, all by specifying output modes
// in a cmnd file, where also the event generator settings are specified.
// The example is run with command line options, run ./main144 -h to see a
// full list. See ROOT Usage for information about ROOT output, RIVET Usage
// for information about RIVET and HepMC Interface for information about HepMC.
#include "Pythia8/Pythia.h"
#include "Pythia8/HeavyIons.h"
#include "Pythia8Plugins/InputParser.h"
#include <chrono>
#ifdef RIVET
#include "Pythia8Plugins/Pythia8Rivet.h"
#endif
#ifdef PY8ROOT
#include "TSystem.h"
#include "TTree.h"
#include "TFile.h"
#endif
// Use the Pythia namespace.
using namespace Pythia8;
//==========================================================================
// Define filling ROOT particle and events if using ROOT.
#ifdef PY8ROOT
#include "main144Dct.h"
// Fill a ROOT particle.
RootParticle::RootParticle(Pythia8::Particle &prt) {
  phi = prt.phi();
  eta = prt.eta();
  y   = prt.y();
  pT  = prt.pT();
  pid = prt.id();
}
// Fill a ROOT event to a TTree.
void RootEvent::fill(const Pythia8::Info &infoIn, vector<RootParticle> &prtsIn,
  TTree *treeIn) {
  weight = infoIn.weight();
  particles = prtsIn;
  treeIn->Fill();
}
#endif
//==========================================================================
// Implement a user supplied UserHooks derived class inside this
// wrapper, which will allow you to give settings that can be supplied
// in command files.
class UserHooksWrapper : public UserHooks {
public:
  // Add the settings you want available in the run card in this method.
  void additionalSettings(Settings* settingsIn) {
    settings = settingsIn;
    settings->addFlag("UserHooks:doMPICut", false);
    settings->addMode("UserHooks:nMPICut", 0, true, false, 0, 0);
  }
  // Check if parton level can be vetoed.
  bool canVetoPartonLevel() final {
    return settings->flag("UserHooks:doMPICut");}
  // Check if parton level should be vetoed.
  bool doVetoPartonLevel(const Event&) final {
    return infoPtr->nMPI() < settings->mode("UserHooks:nMPICut");}
private:
  Settings* settings{};
};
//==========================================================================
// Main example execution.
int main(int argc, char* argv[]) {
  // Parser object for command line input.
  InputParser ip("Run Pythia with cmnd file input, and get Rivet, HepMC or"
    " standard Pythia output.",
    {"./main144 [options]", "./main144 -c main144.cmnd -n 1000 -o myoutput"},
    "Additional options in cmnd file:\n"
    "\tMain:writeLog = on\n\t\tRedirect output to <-o prefix>.log.\n"
    "\tMain:writeHepMC = on \n\t\tWrite HepMC output, requires HepMC linked.\n"
    "\tMain:writeRoot = on \n\t\tWrite a ROOT tree declared in "
    "RootEvent.h, requires ROOT linked.\n"
    "\tMain:runRivet = on \n\t\tRun Rivet analyses, requires Rivet linked.\n"
    "\tMain:rivetAnalyses = {ANALYSIS1,ANALYSIS2,...}\n "
    "\t\tComma separated list of Rivet analyses to run.\n"
    "\t\tAnalysis names can be post-fixed with analysis parameters.\n"
    "\t\tANALYSIS:parm=value:parm2=value2:...\n"
    "\tMain:rivetRunName = STRING \n\t\tAdd an optional run name to "
    "the Rivet analysis.\n"
    "\tMain:rivetIgnoreBeams = on\n\t\tIgnore beams in Rivet. \n"
    "\tMain:rivetDumpPeriod = NUMBER\n\t\tDump Rivet histograms "
    "to file evert NUMBER of events.\n"
    "\tMain:rivetDumpFile = STRING\n\t\t Specify alternative "
    "name for Rivet dump file. Default = OUT.\n");
  // Set up command line options.
  ip.require("c", "User-written command file, can use multiple times.",
    {"-cmnd"});
  ip.add("s", "-1", "Specify seed for the random number generator.",
    {"-seed"});
  ip.add("o", "main144", "Output prefix for log file, Rivet, HepMC, and ROOT.",
    {"-out"});
  ip.add("n", "-1", "Number of events. Overrides the command files.",
    {"-nevents"});
  ip.add("l", "false", "Silence the splash screen.");
  ip.add("t", "false", "Time event generation.", {"-time"});
  ip.add("v", "false", "Print Pythia version number and exit.", {"-version"});
  // Initialize the parser and exit if necessary.
  InputParser::Status status = ip.init(argc, argv);
  if (status != InputParser::Valid) return status;
  // Print version number and exit.
  if (ip.get<bool>("v")) {
    cout << "PYTHIA version: " << PYTHIA_VERSION << endl;
    return 0;
  }
  // Get the command files.
  vector<string> cmnds = ip.getVector<string>("c");
  if (cmnds.size() == 0) {
    cout << "Please provide one or more command files with the -c option."
         << endl;
    return 1;
  }
  // Random number seed.
  string seed = ip.get<string>("s");
  // Output filename.
  string out = ip.get<string>("o");
  // Time event generation.
  bool writeTime = ip.get<bool>("t");
  // Command line number of event, overrides the one set in input .cmnd file.
  int nev = ip.get<int>("n");
  // Catch the splash screen in a buffer.
  stringstream splashBuf;
  std::streambuf* sBuf = cout.rdbuf();
  cout.rdbuf(splashBuf.rdbuf());
  // The Pythia object.
  Pythia pythia;
  // Direct cout back.
  cout.rdbuf(sBuf);
  // UserHooks wrapper.
  shared_ptr<UserHooksWrapper> userHooksWrapper =
    make_shared<UserHooksWrapper>();
  userHooksWrapper->additionalSettings(&pythia.settings);
  pythia.setUserHooksPtr(userHooksWrapper);
  // Some extra parameters.
  pythia.settings.addFlag("Main:writeLog", false);
  pythia.settings.addFlag("Main:writeHepMC", false);
  pythia.settings.addFlag("Main:writeRoot", false);
  pythia.settings.addFlag("Main:runRivet", false);
  pythia.settings.addFlag("Main:rivetIgnoreBeams", false);
  pythia.settings.addMode("Main:rivetDumpPeriod", -1, true, false, -1, 0);
  pythia.settings.addWord("Main:rivetDumpFile", "");
  pythia.settings.addWord("Main:rivetRunName", "");
  pythia.settings.addWVec("Main:rivetAnalyses", {});
  pythia.settings.addWVec("Main:rivetPreload", {});
  // Read the command files.
  for (int iCmnd = 0; iCmnd < (int)cmnds.size(); ++iCmnd)
    if (!cmnds[iCmnd].empty()) pythia.readFile(cmnds[iCmnd]);
  // Set seed after reading input.
  if(seed != "-1") {
    pythia.readString("Random:setSeed = on");
    pythia.readString("Random:seed = "+seed);
  }
  // Read the extra parameters.
  if (nev > -1) pythia.settings.mode("Main:numberOfEvents", nev);
  int nEvent                   = pythia.mode("Main:numberOfEvents");;
  int nError                   = pythia.mode("Main:timesAllowErrors");
  bool writeLog                = pythia.flag("Main:writeLog");
  bool writeHepmc              = pythia.flag("Main:writeHepMC");
  bool writeRoot               = pythia.flag("Main:writeRoot");
  bool runRivet                = pythia.flag("Main:runRivet");
  bool countErrors             = nError > 0;
  // Check if Rivet, HepMC, and ROOT are requested and available.
  bool valid = true;
#ifndef RIVET
  valid = valid && !runRivet && !writeHepmc;
  if (runRivet)
    cout << "Option Main::runRivet = on requires the Rivet library.\n";
  if (writeHepmc)
    cout << "Option Main::writeHepMC = on requires the HepMC library.\n";
#endif
#ifndef PY8ROOT
  valid = valid && !writeRoot;
  if (writeRoot)
    cout << "Option Main::writeRoot = on requires the ROOT library.\n";
#endif
  if (!valid) return 1;
  // Rivet and HepMC initialization.
#ifdef RIVET
  // Initialize HepMC.
  Pythia8ToHepMC hepmc;
  if (writeHepmc) hepmc.setNewFile(out + ".hepmc");
  // Initialize Rivet.
  Pythia8Rivet rivet(pythia, out + ".yoda");
  rivet.ignoreBeams(pythia.flag("Main:rivetIgnoreBeams"));
  rivet.dump(pythia.settings.mode("Main:rivetDumpPeriod"),
    pythia.settings.word("Main:rivetDumpFile"));
  // Load the analyses.
  vector<string> rivetAnalyses = pythia.settings.wvec("Main:rivetAnalyses");
  for (int iAna = 0; iAna < (int)rivetAnalyses.size(); ++iAna)
    rivet.addAnalysis(rivetAnalyses[iAna]);
  // Pre-load the YODA histograms.
  vector<string> rivetPreload = pythia.settings.wvec("Main:rivetPreload");
  for (int iYoda = 0; iYoda < (int)rivetPreload.size(); ++iYoda)
    rivet.addPreload(rivetPreload[iYoda]);
  // Add the run name.
  rivet.addRunName(pythia.settings.word("Main:rivetRunName"));
#endif
  // ROOT initialization.
#ifdef PY8ROOT
  // Create the ROOT TFile and TTree.
  TFile *file;
  TTree *tree;
  RootEvent *evt;
  if (writeRoot) {
    // Open the ROOT file.
    file = TFile::Open((out + ".root").c_str(), "recreate" );
    tree = new TTree("t", "Pythia8 event tree");
    evt  = new RootEvent();
    // Set the TTree branch to the ROOT event.
    tree->Branch("events", &evt);
  }
#endif
  // Logfile initialization.
  ofstream logBuf;
  streambuf *oldCout;
  if (writeLog) {
    oldCout = cout.rdbuf(logBuf.rdbuf());
    logBuf.open(out + ".log");
  }
  // Remove splash screen, if requested.
  ostream cnull(NULL);
  if (ip.get<bool>("l")) cnull << splashBuf.str();
  else cout << splashBuf.str();
  // If Pythia fails to initialize, exit with error.
  if (!pythia.init()) return 1;
  // Make a sanity check of initialized Rivet analyses.
#ifdef RIVET
  if (!runRivet && rivetAnalyses.size() > 0 )
    cout << "Rivet analyses are set with Main:rivetAnalyses, "
         << "but Main:runRivet = off.\n";
#endif
  // Loop over events.
  auto startAllEvents = std::chrono::high_resolution_clock::now();
  for ( int iEvent = 0; iEvent < nEvent; ++iEvent ) {
    auto startThisEvent = std::chrono::high_resolution_clock::now();
    // Exit if too many failures.
    if (!pythia.next()) {
      if (countErrors && --nError < 0) {
        pythia.stat();
        cout << " \n *-------  PYTHIA STOPPED!  -----------------------*\n"
             << " | Event generation failed due to too many errors. |\n"
             << " *-------------------------------------------------*\n";
        return 1;
      }
      continue;
    }
    // Calculate the event time.
    auto stopThisEvent = std::chrono::high_resolution_clock::now();
    auto eventTime = std::chrono::duration_cast<std::chrono::milliseconds>
      (stopThisEvent - startThisEvent);
    double tt = eventTime.count();
    // Run the Rivet analyses.
#ifdef RIVET
    if (runRivet) {
      if (writeTime) rivet.addAttribute("EventTime", tt);
      rivet();
    }
    if (writeHepmc) hepmc.writeNextEvent(pythia);
#endif
    // Write to ROOT file output.
#ifdef PY8ROOT
    if (writeRoot) {
      vector<RootParticle> prts;
      for (int iPrt = 0; iPrt < pythia.event.size(); ++iPrt) {
        Particle& prt = pythia.event[iPrt];
        // Any particle cuts can be placed here. Here, only final
        // state particles are kept.
        if (!prt.isFinal()) continue;
        // Push back the ROOT particle.
        prts.push_back(RootParticle(prt));
      }
      // Fill the ROOT event and tree.
      evt->fill(pythia.info, prts, tree);
    }
#endif
  }
  // Finalize.
  pythia.stat();
#ifdef PY8ROOT
  if (writeRoot) {
    tree->Print();
    tree->Write();
    delete file, tree, evt;
  }
#endif
  // Print timing.
  auto stopAllEvents = std::chrono::high_resolution_clock::now();
  auto durationAll = std::chrono::duration_cast<std::chrono::milliseconds>
    (stopAllEvents - startAllEvents);
  if (writeTime) {
    cout << " \n *-------  Generation time  -----------------------*\n"
         << " | Event generation, analysis and writing to files  |\n"
         << " | took: " << double(durationAll.count()) << " ms or "
         << double(durationAll.count())/double(nEvent)
         << " ms per event     |\n"
         << " *-------------------------------------------------*\n";
  }
  // Put cout back in its place.
  if (writeLog) cout.rdbuf(oldCout);
  return 0;
}