From fb483162a1db7fc139a0f7a68010a3a17b0414a4 Mon Sep 17 00:00:00 2001 From: Laurent Forthomme Date: Sat, 7 Oct 2023 09:50:06 +0300 Subject: [PATCH] Core+Pythia8: added a safer caller method for OS interaction --- CepGen/Utils/Caller.cpp | 51 ++++++ CepGen/Utils/Caller.h | 41 +++++ .../Pythia8Wrapper/LHEFPythiaHandler.cpp | 148 ++++++++---------- .../Pythia8Wrapper/Pythia8CollinearFlux.cpp | 18 ++- .../Pythia8Wrapper/Pythia8Hadroniser.cpp | 74 ++++----- 5 files changed, 202 insertions(+), 130 deletions(-) create mode 100644 CepGen/Utils/Caller.cpp create mode 100644 CepGen/Utils/Caller.h diff --git a/CepGen/Utils/Caller.cpp b/CepGen/Utils/Caller.cpp new file mode 100644 index 000000000..7795c98ae --- /dev/null +++ b/CepGen/Utils/Caller.cpp @@ -0,0 +1,51 @@ +/* + * CepGen: a central exclusive processes event generator + * Copyright (C) 2023 Laurent Forthomme + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "CepGen/Core/Exception.h" +#include "CepGen/Utils/Caller.h" +#include "CepGen/Utils/String.h" + +namespace cepgen { + namespace utils { + std::string Caller::call(const std::vector& commands) { return call(utils::merge(commands, " ")); } + + std::string Caller::call(const std::string& command) { + auto pipe = popen(command.c_str(), "r"); + if (!pipe) + throw CG_FATAL("Caller") << "Failed to call the command '" << command << "'."; + std::array buffer; + std::string result; + while (!feof(pipe)) + if (fgets(buffer.data(), buffer.size(), pipe) != nullptr) + result += buffer.data(); + auto rc = pclose(pipe); + CG_DEBUG("Caller").log([&](auto& log) { + log << "Command '" << command << "' returned code '" << rc << "'."; + if (!result.empty()) + log << " Message: '" << result << "'."; + }); + if (rc != EXIT_SUCCESS) + throw CG_FATAL("Caller") << "Command '" << command << "' failed with return code '" << rc << "'."; + return result; + } + } // namespace utils +} // namespace cepgen diff --git a/CepGen/Utils/Caller.h b/CepGen/Utils/Caller.h new file mode 100644 index 000000000..c17f7c75a --- /dev/null +++ b/CepGen/Utils/Caller.h @@ -0,0 +1,41 @@ +/* + * CepGen: a central exclusive processes event generator + * Copyright (C) 2023 Laurent Forthomme + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CepGen_Utils_Caller_h +#define CepGen_Utils_Caller_h + +#include +#include + +namespace cepgen { + namespace utils { + /// External command piping utility + class Caller { + public: + Caller() {} + /// Start a logged call command + /// \param[in] commands Command path for the session + static std::string call(const std::vector& commands); + /// Start a logged call command + /// \param[in] command Command path for the session + static std::string call(const std::string& command); + }; + } // namespace utils +} // namespace cepgen + +#endif diff --git a/CepGenAddOns/Pythia8Wrapper/LHEFPythiaHandler.cpp b/CepGenAddOns/Pythia8Wrapper/LHEFPythiaHandler.cpp index 34f3f4842..07e5a276b 100644 --- a/CepGenAddOns/Pythia8Wrapper/LHEFPythiaHandler.cpp +++ b/CepGenAddOns/Pythia8Wrapper/LHEFPythiaHandler.cpp @@ -1,6 +1,6 @@ /* * CepGen: a central exclusive processes event generator - * Copyright (C) 2013-2023 Laurent Forthomme + * Copyright (C) 2016-2023 Laurent Forthomme * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include "CepGen/Event/Event.h" #include "CepGen/EventFilter/EventExporter.h" #include "CepGen/Modules/EventExporterFactory.h" +#include "CepGen/Utils/Caller.h" #include "CepGen/Utils/Filesystem.h" #include "CepGen/Utils/String.h" #include "CepGen/Utils/Value.h" @@ -36,97 +37,82 @@ namespace cepgen { class LHEFPythiaHandler : public EventExporter { public: /// Class constructor - explicit LHEFPythiaHandler(const ParametersList&); - ~LHEFPythiaHandler(); - - static ParametersDescription description(); - - void initialise() override; - /// Writer operator - void operator<<(const Event&) override; - void setCrossSection(const Value&) override; - - private: - std::unique_ptr pythia_; - std::shared_ptr lhaevt_; - const bool compress_event_; - std::string filename_; - bool gzip_; - }; - - LHEFPythiaHandler::LHEFPythiaHandler(const ParametersList& params) - : EventExporter(params), - pythia_(new Pythia8::Pythia), - lhaevt_(new Pythia8::CepGenEvent), - compress_event_(steer("compress")), - filename_(steer("filename")), - gzip_(false) { + explicit LHEFPythiaHandler(const ParametersList& params) + : EventExporter(params), + pythia_(new Pythia8::Pythia), + lhaevt_(new Pythia8::CepGenEvent), + compress_event_(steer("compress")), + filename_(steer("filename")) { + if (utils::fileExtension(filename_) == ".gz") { #ifdef GZIP_BIN - if (utils::fileExtension(filename_) == ".gz") { - utils::replace_all(filename_, ".gz", ""); - gzip_ = true; + utils::replace_all(filename_, ".gz", ""); +#else + CG_WARNING("LHEFPythiaHandler") + << "gzip compression requested, but the executable was not linked at Pythia8 wrapper compile time."; +#endif + gzip_ = true; + } + { + auto file_tmp = std::ofstream(filename_); + if (!file_tmp.is_open()) + throw CG_FATAL("LHEFPythiaHandler") << "Failed to open output filename \"" << filename_ << "\" for writing!"; + } + lhaevt_->openLHEF(filename_); } + inline ~LHEFPythiaHandler() { + if (lhaevt_) + lhaevt_->closeLHEF(false); // we do not want to rewrite the init block + if (gzip_) +#ifdef GZIP_BIN + utils::Caller::call({GZIP_BIN, "-f", filename_}); #endif - { - auto file_tmp = std::ofstream(filename_); - if (!file_tmp.is_open()) - throw CG_FATAL("LHEFPythiaHandler") << "Failed to open output filename \"" << filename_ << "\" for writing!"; } - lhaevt_->openLHEF(filename_); - } - LHEFPythiaHandler::~LHEFPythiaHandler() { - if (lhaevt_) - lhaevt_->closeLHEF(false); // we do not want to rewrite the init block -#ifdef GZIP_BIN - if (gzip_) { - std::string cmnd(GZIP_BIN); - cmnd += " -f " + filename_; - if (system(cmnd.c_str()) != 0) - CG_WARNING("LHEFPythiaHandler") << "Failed to zip the output file with command \"" << cmnd << "\"."; - CG_DEBUG("LHEFPythiaHandler") << cmnd; + inline static ParametersDescription description() { + auto desc = EventExporter::description(); + desc.setDescription("Pythia 8-based LHEF output module"); + desc.add("compress", true); + desc.add("filename", "output.lhe").setDescription("Output filename"); + return desc; } -#endif - } - void LHEFPythiaHandler::initialise() { - std::ostringstream oss_init; - oss_init << ""; - oss_init << std::endl; // LHEF is usually not as beautifully parsed as a standard XML... - // we're physicists, what do you expect? - lhaevt_->addComments(oss_init.str()); - lhaevt_->initialise(runParameters()); + inline void initialise() override { + std::ostringstream oss_init; + oss_init << ""; + oss_init << std::endl; // LHEF is usually not as beautifully parsed as a standard XML... + // we're physicists, what do you expect? + lhaevt_->addComments(oss_init.str()); + lhaevt_->initialise(runParameters()); #if PYTHIA_VERSION_INTEGER < 8300 - pythia_->setLHAupPtr(lhaevt_.get()); + pythia_->setLHAupPtr(lhaevt_.get()); #else - pythia_->setLHAupPtr(lhaevt_); + pythia_->setLHAupPtr(lhaevt_); #endif - pythia_->settings.flag("ProcessLevel:all", false); // we do not want Pythia to interfere... - pythia_->settings.flag("PartonLevel:all", false); // we do not want Pythia to interfere... - pythia_->settings.flag("HadronLevel:all", false); // we do not want Pythia to interfere... - pythia_->settings.mode("Beams:frameType", 5); // LHEF event readout - pythia_->settings.mode("Next:numberCount", 0); // remove some of the Pythia output - pythia_->init(); - lhaevt_->initLHEF(); - } - - void LHEFPythiaHandler::operator<<(const Event& ev) { - lhaevt_->feedEvent(compress_event_ ? ev : ev.compress(), Pythia8::CepGenEvent::Type::centralAndFullBeamRemnants); - pythia_->next(); - lhaevt_->eventLHEF(); - } - - void LHEFPythiaHandler::setCrossSection(const Value& cross_section) { - lhaevt_->setCrossSection(0, cross_section, cross_section.uncertainty()); - } + pythia_->settings.flag("ProcessLevel:all", false); // we do not want Pythia to interfere... + pythia_->settings.flag("PartonLevel:all", false); // we do not want Pythia to interfere... + pythia_->settings.flag("HadronLevel:all", false); // we do not want Pythia to interfere... + pythia_->settings.mode("Beams:frameType", 5); // LHEF event readout + pythia_->settings.mode("Next:numberCount", 0); // remove some of the Pythia output + pythia_->init(); + lhaevt_->initLHEF(); + } + /// Writer operator + inline void operator<<(const Event& ev) override { + lhaevt_->feedEvent(compress_event_ ? ev : ev.compress(), Pythia8::CepGenEvent::Type::centralAndFullBeamRemnants); + pythia_->next(); + lhaevt_->eventLHEF(); + } + inline void setCrossSection(const Value& cross_section) override { + lhaevt_->setCrossSection(0, cross_section, cross_section.uncertainty()); + } - ParametersDescription LHEFPythiaHandler::description() { - auto desc = EventExporter::description(); - desc.setDescription("Pythia 8-based LHEF output module"); - desc.add("compress", true); - desc.add("filename", "output.lhe").setDescription("Output filename"); - return desc; - } + private: + const std::unique_ptr pythia_; + const std::shared_ptr lhaevt_; + const bool compress_event_; + std::string filename_; + bool gzip_{false}; + }; } // namespace cepgen REGISTER_EXPORTER("lhef", LHEFPythiaHandler); diff --git a/CepGenAddOns/Pythia8Wrapper/Pythia8CollinearFlux.cpp b/CepGenAddOns/Pythia8Wrapper/Pythia8CollinearFlux.cpp index 62203dc19..fb5513e7d 100644 --- a/CepGenAddOns/Pythia8Wrapper/Pythia8CollinearFlux.cpp +++ b/CepGenAddOns/Pythia8Wrapper/Pythia8CollinearFlux.cpp @@ -31,13 +31,15 @@ namespace cepgen { explicit Pythia8CollinearFlux(const ParametersList& params) : CollinearFlux(params), type_(steer("type")), pdgid_(steer("partonPdgId")) { if (type_ == "Lepton") { + auto lepton_params = steer("leptonParams"); info_.reset(new Pythia8::Info); - if (const auto dil_sqrt_s = steer("leptonBeamsSqrtS"); dil_sqrt_s > 0.) + if (const auto dil_sqrt_s = lepton_params.get("sqrtS"); dil_sqrt_s > 0.) info_->setECM(dil_sqrt_s); else - CG_WARNING("Pythia8CollinearFlux") << "Beam-beam centre-of-mass energy is required (through the " - "'leptonBeamsSqrtS' parameter) for the 'Lepton' collinear flux mode."; - pdf_.reset(new Pythia8::Lepton(steer("leptonBeamPdgId"), steer("Q2max"), info_.get())); + CG_WARNING("Pythia8CollinearFlux") << "Beam-beam centre-of-mass energy is required (through the 'sqrtS' " + "parameter) for the 'Lepton' collinear flux mode."; + pdf_.reset(new Pythia8::Lepton( + lepton_params.get("beamPdgId"), lepton_params.get("Q2max"), info_.get())); } else if (type_ == "LHAGrid1") pdf_.reset(new Pythia8::LHAGrid1); else if (type_ == "MSTWpdf") @@ -59,9 +61,11 @@ namespace cepgen { desc.setDescription("Pythia 8 coll.flux"); desc.add("type", "Proton2gammaDZ").setDescription("type of PDF evaluator to use"); desc.add("partonPdgId", PDG::photon).setDescription("parton PDG identifier"); - desc.add("leptonBeamPdgId", PDG::electron).setDescription("beam particle PDG identifier (lepton case)"); - desc.add("leptonBeamsSqrtS", -1.); - desc.add("Q2max", 50.); + auto lepton_desc = ParametersDescription(); + lepton_desc.add("beamPdgId", PDG::electron).setDescription("beam particle PDG identifier"); + lepton_desc.add("sqrtS", -1.); + lepton_desc.add("Q2max", 50.); + desc.add("leptonParams", lepton_desc); return desc; } diff --git a/CepGenAddOns/Pythia8Wrapper/Pythia8Hadroniser.cpp b/CepGenAddOns/Pythia8Wrapper/Pythia8Hadroniser.cpp index db0e615a6..8cb191333 100644 --- a/CepGenAddOns/Pythia8Wrapper/Pythia8Hadroniser.cpp +++ b/CepGenAddOns/Pythia8Wrapper/Pythia8Hadroniser.cpp @@ -16,14 +16,10 @@ * along with this program. If not, see . */ -#include #include -#include #include "CepGen/Core/Exception.h" -#include "CepGen/Core/ParametersList.h" #include "CepGen/Event/Event.h" -#include "CepGen/Event/Particle.h" #include "CepGen/Modules/EventModifierFactory.h" #include "CepGen/Parameters.h" #include "CepGen/Physics/Hadroniser.h" @@ -34,22 +30,37 @@ namespace cepgen { namespace hadr { - /** - * Full interface to the Pythia8 hadronisation algorithm. It can be used in a single particle decay mode as well as a full event hadronisation using the string model, as in Jetset. - * \brief Pythia8 hadronisation algorithm - */ + /// Interface to the Pythia8 hadronisation algorithm + /// \note It can be used in a single particle decay mode as well as a full event hadronisation using the string model, as in Jetset. class Pythia8Hadroniser : public Hadroniser { public: - explicit Pythia8Hadroniser(const ParametersList&); - ~Pythia8Hadroniser(); + explicit Pythia8Hadroniser(const ParametersList& plist) + : Hadroniser(plist), + pythia_(new Pythia8::Pythia), + cg_evt_(new Pythia8::CepGenEvent), + correct_central_(steer("correctCentralSystem")), + debug_lhef_(steer("debugLHEF")), + output_config_(steer("outputConfig")) {} + + virtual ~Pythia8Hadroniser() { + if (!output_config_.empty()) + pythia_->settings.writeFile(output_config_, false); + if (debug_lhef_) + cg_evt_->closeLHEF(true); + } static ParametersDescription description(); - void readString(const char* param) override; + void readString(const char* param) override { + if (!pythia_->readString(param)) + throw CG_FATAL("Pythia8Hadroniser") << "The Pythia8 core failed to parse the following setting:\n\t" << param; + } void initialise() override; bool run(Event& ev, double& weight, bool full) override; - void setCrossSection(const Value&) override; + void setCrossSection(const Value& cross_section) override { + cg_evt_->setCrossSection(0, cross_section, cross_section.uncertainty()); + } private: void* enginePtr() override { return (void*)pythia_.get(); } @@ -62,9 +73,10 @@ namespace cepgen { unsigned short findRole(const Event& ev, const Pythia8::Particle& p) const; void updateEvent(Event& ev, double& weight) const; Particle& addParticle(Event& ev, const Pythia8::Particle&, const Pythia8::Vec4& mom, unsigned short) const; - /// A Pythia8 core to be wrapped - std::unique_ptr pythia_; - std::shared_ptr cg_evt_; + + const std::unique_ptr pythia_; ///< Pythia 8 core to be wrapped + const std::shared_ptr cg_evt_; ///< Event interface between CepGen and Pythia + const bool correct_central_; const bool debug_lhef_; const std::string output_config_; @@ -74,29 +86,9 @@ namespace cepgen { bool first_evt_{true}; }; - Pythia8Hadroniser::Pythia8Hadroniser(const ParametersList& plist) - : Hadroniser(plist), - pythia_(new Pythia8::Pythia), - cg_evt_(new Pythia8::CepGenEvent), - correct_central_(steer("correctCentralSystem")), - debug_lhef_(steer("debugLHEF")), - output_config_(steer("outputConfig")) {} - - Pythia8Hadroniser::~Pythia8Hadroniser() { - if (!output_config_.empty()) - pythia_->settings.writeFile(output_config_, false); - if (debug_lhef_) - cg_evt_->closeLHEF(true); - } - - void Pythia8Hadroniser::readString(const char* param) { - if (!pythia_->readString(param)) - throw CG_FATAL("Pythia8Hadroniser") << "The Pythia8 core failed to parse the following setting:\n\t" << param; - } - void Pythia8Hadroniser::initialise() { cg_evt_->initialise(runParameters()); -#if PYTHIA_VERSION_INTEGER < 8300 +#if defined(PYTHIA_VERSION_INTEGER) && PYTHIA_VERSION_INTEGER < 8300 pythia_->setLHAupPtr(cg_evt_.get()); #else pythia_->setLHAupPtr(cg_evt_); @@ -126,13 +118,15 @@ namespace cepgen { switch (kin.incomingBeams().mode()) { case mode::Kinematics::ElasticElastic: { pythia_->settings.mode("BeamRemnants:unresolvedHadron", 3); - pythia_->settings.flag("PartonLevel:all", false); + pythia_->settings.flag("PartonLevel:MPI", false); } break; case mode::Kinematics::InelasticElastic: { pythia_->settings.mode("BeamRemnants:unresolvedHadron", 2); + pythia_->settings.flag("PartonLevel:MPI", false); } break; case mode::Kinematics::ElasticInelastic: { pythia_->settings.mode("BeamRemnants:unresolvedHadron", 1); + pythia_->settings.flag("PartonLevel:MPI", false); } break; case mode::Kinematics::InelasticInelastic: default: { @@ -159,10 +153,6 @@ namespace cepgen { cg_evt_->initLHEF(); } - void Pythia8Hadroniser::setCrossSection(const Value& cross_section) { - cg_evt_->setCrossSection(0, cross_section, cross_section.uncertainty()); - } - bool Pythia8Hadroniser::run(Event& ev, double& weight, bool full) { //--- initialise the event weight before running any decay algorithm weight = 1.; @@ -370,5 +360,5 @@ namespace cepgen { } // namespace hadr } // namespace cepgen // register hadroniser -typedef cepgen::hadr::Pythia8Hadroniser Pythia8Hadroniser; +using cepgen::hadr::Pythia8Hadroniser; REGISTER_MODIFIER("pythia8", Pythia8Hadroniser);