diff --git a/include/HEJ/EventReader.hh b/include/HEJ/EventReader.hh index 43c861f..86f644a 100644 --- a/include/HEJ/EventReader.hh +++ b/include/HEJ/EventReader.hh @@ -1,55 +1,59 @@ /** \file * \brief Header file for event reader interface * * This header defines an abstract base class for reading events from files. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2022 * \copyright GPLv2 or later */ #pragma once #include #include #include #include namespace LHEF { class HEPEUP; class HEPRUP; } namespace HEJ { //! Abstract base class for reading events from files struct EventReader { //! Read an event virtual bool read_event() = 0; //! Access header text virtual std::string const & header() const = 0; //! Access run information virtual LHEF::HEPRUP const & heprup() const = 0; //! Access last read event virtual LHEF::HEPEUP const & hepeup() const = 0; //! Guess number of events from header virtual std::optional number_events() const = 0; virtual double scalefactor() const { return 1.; }; + virtual bool is_fifo() const{ + return false; + }; + virtual ~EventReader() = default; }; //! Factory function for event readers /** * @param filename The name of the input file * @returns A pointer to an instance of an EventReader * for the input file */ std::unique_ptr make_reader(std::string const & filename); } // namespace HEJ diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh index 9f7cc31..d9316b5 100644 --- a/include/HEJ/LesHouchesReader.hh +++ b/include/HEJ/LesHouchesReader.hh @@ -1,88 +1,93 @@ /** \file * \brief Header file for reading events in the Les Houches Event File format. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2022 * \copyright GPLv2 or later */ #pragma once #include #include #include "LHEF/LHEF.h" #include "HEJ/EventReader.hh" #include "HEJ/stream.hh" namespace HEJ { //! Class for reading events from a file in the Les Houches Event File format class LesHouchesReader : public EventReader{ public: //! Contruct object reading from the given file explicit LesHouchesReader(std::string const & filename); //! Read an event bool read_event() override; //! Access header text std::string const & header() const override { return reader_.headerBlock; } //! Access run information LHEF::HEPRUP const & heprup() const override { return reader_.heprup; } //! Access last read event LHEF::HEPEUP const & hepeup() const override { return reader_.hepeup; } std::optional number_events() const override { std::size_t start = header().rfind("Number of Events"); start = header().find_first_of("123456789", start); if(start == std::string::npos) { return {}; } const std::size_t end = header().find_first_not_of("0123456789", start); return std::stoi(header().substr(start, end - start)); } double scalefactor() const override { if (generator_==Generator::Sherpa) { const std::size_t num_trials = std::accumulate( num_trials_.begin(), num_trials_.end(), 0 ); return 1./static_cast(num_trials); } else return 1.; } + bool is_fifo() const override { + return is_fifo_; + } + private: enum class Generator{ HEJ, HEJFOG, Sherpa, MG, unknown }; bool calculate_XSECUP() { return calculate_XSECUP_; } Generator get_generator( LHEF::HEPRUP const & heprup, std::string const & header ); istream stream_; LHEF::Reader reader_; std::vector num_trials_; Generator generator_; bool calculate_XSECUP_; + bool is_fifo_; }; } // namespace HEJ diff --git a/include/HEJ/ProgressBar.hh b/include/HEJ/ProgressBar.hh index 5f38051..bf895aa 100644 --- a/include/HEJ/ProgressBar.hh +++ b/include/HEJ/ProgressBar.hh @@ -1,94 +1,96 @@ /** \file * \brief Contains the ProgressBar class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include #include #include namespace HEJ { //! Class representing (and printing) a progress bar template class ProgressBar { public: //! Constructor /** * @param out Output stream * @param max Maximum value of the progress parameter * * This will print a fixed-width progress bar, which is initially at 0%. */ ProgressBar(std::ostream & out, T max) : out_{out}, max_{max} { - if(max <= 0) { + if(max < 0) { throw std::invalid_argument{ "Maximum in progress bar has to be positive" }; } - print_bar(); + if (max != 0) { + print_bar(); + } } //! Increment progress /** * @param count Value to add to the current progress parameter * * After updating the progess parameter, the progress bar is updated * to a percentage that corresponds to the ratio of the current and * maximum progress parameters. */ ProgressBar & increment(T count) { counter_ += count; update_progress(); return *this; } //! Increase progress by one unit /** * After updating the progess parameter, the progress bar is updated * to a percentage that corresponds to the ratio of the current and * maximum progress parameters. */ ProgressBar & operator++() { ++counter_; update_progress(); return *this; } private: void update_progress() { counter_ = std::min(counter_, max_); const int ndots = (100*counter_)/max_; const int new_dots = ndots - ndots_; if(new_dots > 0) { for(int dot = 0; dot < new_dots; ++dot) out_.get() << '.'; out_.get().flush(); ndots_ = ndots; } } void print_bar() const { out_.get() << "0% "; for(int i = 10; i <= 100; i+= 10){ out_.get() << " " + std::to_string(i) + "%"; } out_.get() << "\n|"; for(int i = 10; i <= 100; i+= 10){ out_.get() << "---------|"; } out_.get() << '\n'; } std::reference_wrapper out_; T counter_ = 0; T ndots_ = 0; T max_; }; } // namespace HEJ diff --git a/src/LesHouchesReader.cc b/src/LesHouchesReader.cc index 992a68d..872e918 100644 --- a/src/LesHouchesReader.cc +++ b/src/LesHouchesReader.cc @@ -1,80 +1,83 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2022 * \copyright GPLv2 or later */ #include "HEJ/LesHouchesReader.hh" #include "HEJ/Version.hh" +#include #include namespace HEJ { LesHouchesReader::Generator LesHouchesReader::get_generator( LHEF::HEPRUP const & heprup, std::string const & header ){ // try getting generator name from specific tag if(!heprup.generators.empty()){ std::string const & gen_name = heprup.generators.back().name; if(gen_name == "HEJ" || gen_name == HEJ::Version::String()) return Generator::HEJ; if(gen_name == "HEJ Fixed Order Generation") return Generator::HEJFOG; if(gen_name == "MadGraph5_aMC@NLO") return Generator::MG; std::cerr << "Unknown LHE Generator " << gen_name << " using default LHE interface.\n"; return Generator::unknown; } // The generator tag is not always used -> check by hand if(header.find("generated with HEJ")!=std::string::npos) return Generator::HEJ; if(header.find("# created by SHERPA")!=std::string::npos) return Generator::Sherpa; if(header.find("")!=std::string::npos) return Generator::MG; std::cerr<<"Could not determine LHE Generator using default LHE interface.\n"; return Generator::unknown; } LesHouchesReader::LesHouchesReader( std::string const & filename ): stream_{filename}, reader_{stream_}, num_trials_(reader_.heprup.NPRUP, 0), generator_{get_generator(reader_.heprup, reader_.headerBlock)}, // calculate cross section if it was not set to > 0. - calculate_XSECUP_{reader_.heprup.XSECUP.front() <= 0.} + calculate_XSECUP_{reader_.heprup.XSECUP.front() <= 0.}, + is_fifo_{std::filesystem::is_fifo(filename)} { // always use the newest LHE version reader_.heprup.version = LHEF::HEPRUP().version; reader_.heprup.XSECUP.resize(reader_.heprup.NPRUP); // For IDWTUP == 1 or 4 we assume avg(weight)=xs // With the modified weights we have in Sherpa sum(weight)=xs // -> overwrite IDWTUP to "something neutral" if(generator_ == Generator::Sherpa) { reader_.heprup.IDWTUP = reader_.heprup.IDWTUP>0?3:-3; } + } bool LesHouchesReader::read_event() { if(!reader_.readEvent()) return false; int nprup=reader_.heprup.NPRUP; int position=std::max(1,std::min(hepeup().IDPRUP,nprup))-1; // 0..nprup-1 if (generator_ == Generator::Sherpa) { reader_.heprup.XSECUP.at(position) *= num_trials_.at(position); num_trials_.at(position) += std::stod(hepeup().attributes.at("trials")); reader_.heprup.XSECUP.at(position) += hepeup().XWGTUP; reader_.heprup.XSECUP.at(position) /= num_trials_.at(position); } else if (LesHouchesReader::calculate_XSECUP()) { reader_.heprup.XSECUP.at(position) += hepeup().XWGTUP; } return true; } } // namespace HEJ diff --git a/src/bin/HEJ.cc b/src/bin/HEJ.cc index 399664c..37cd7ff 100644 --- a/src/bin/HEJ.cc +++ b/src/bin/HEJ.cc @@ -1,434 +1,440 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2022 * \copyright GPLv2 or later */ #include #include #include #include #include #include #include #include "yaml-cpp/yaml.h" #include "fastjet/ClusterSequence.hh" #include "HEJ/CombinedEventWriter.hh" #include "HEJ/Config.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/BufferedEventReader.hh" #include "HEJ/EventReweighter.hh" #include "HEJ/get_analysis.hh" #include "HEJ/make_RNG.hh" #include "HEJ/ProgressBar.hh" #include "HEJ/stream.hh" #include "HEJ/Unweighter.hh" #include "HEJ/Version.hh" #include "HEJ/YAMLreader.hh" HEJ::Config load_config(char const * filename){ try{ return HEJ::load_config(filename); } catch(std::exception const & exc){ std::cerr << "Error: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } std::vector> get_analyses( std::vector const & parameters, LHEF::HEPRUP const & heprup ){ try{ return HEJ::get_analyses(parameters, heprup); } catch(std::exception const & exc){ std::cerr << "Failed to load analysis: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } -// unique_ptr is a workaround: -// std::optional is a better fit, but gives spurious errors with g++ 7.3.0 -std::unique_ptr> make_progress_bar( - std::vector const & xs -) { - if(xs.empty()) return {}; - const double Born_xs = std::accumulate(begin(xs), end(xs), 0.); - if(Born_xs == 0.) return {}; - return std::make_unique>(std::cout, Born_xs); -} - std::string time_to_string(const time_t time){ char s[30]; struct tm * p = localtime(&time); strftime(s, 30, "%a %b %d %Y %H:%M:%S", p); return s; } void check_one_parton_per_jet(HEJ::Event const & ev) { const std::size_t npartons = std::count_if( ev.outgoing().begin(), ev.outgoing().end(), [](HEJ::Particle const & p) { return is_parton(p); } ); if(ev.jets().size() < npartons) { throw std::invalid_argument{ "Number of partons (" + std::to_string(npartons) + ") in input event" " does not match number of jets (" + std::to_string(ev.jets().size())+ ")" }; } } HEJ::Event to_event( LHEF::HEPEUP const & hepeup, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters, const double off_shell_tolerance ) { HEJ::Event::EventData event_data{hepeup}; event_data.reconstruct_intermediate(ew_parameters); event_data.repair_momenta(off_shell_tolerance); HEJ::Event ev{ std::move(event_data).cluster( fixed_order_jets.def, fixed_order_jets.min_pt ) }; check_one_parton_per_jet(ev); return ev; } void unweight( HEJ::Unweighter & unweighter, HEJ::WeightType weight_type, std::vector & events, HEJ::RNG & ran ) { if(weight_type == HEJ::WeightType::unweighted_resum){ unweighter.set_cut_to_maxwt(events); } events.erase( unweighter.unweight(begin(events), end(events), ran), end(events) ); } // peek up to nevents events from reader std::vector peek_events( HEJ::BufferedEventReader & reader, const int nevents ) { std::vector events; while( static_cast(events.size()) < nevents && reader.read_event() ) { events.emplace_back(reader.hepeup()); } // put everything back into the reader for(auto it = rbegin(events); it != rend(events); ++it) { reader.emplace(*it); } return events; } void append_resummed_events( std::vector & resummation_events, HEJ::EventReweighter & reweighter, LHEF::HEPEUP const & hepeup, const size_t trials, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters, const double off_shell_tolerance ) { const HEJ::Event FO_event = to_event( hepeup, fixed_order_jets, ew_parameters, off_shell_tolerance ); if(reweighter.treatment(FO_event.type()) != HEJ::EventTreatment::reweight) { return; } const auto resummed = reweighter.reweight(FO_event, trials); resummation_events.insert( end(resummation_events), begin(resummed), end(resummed) ); } void train( HEJ::Unweighter & unweighter, HEJ::BufferedEventReader & reader, HEJ::EventReweighter & reweighter, const size_t total_trials, const double max_dev, double reweight_factor, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters, const double off_shell_tolerance ) { std::cout << "Reading up to " << total_trials << " training events...\n"; auto FO_events = peek_events(reader, total_trials); if(FO_events.empty()) { throw std::runtime_error{ "No events generated to calibrate the unweighting weight!" "Please increase the number \"trials\" or deactivate the unweighting." }; } const size_t trials = total_trials/FO_events.size(); // adjust reweight factor so that the overall normalisation // is the same as in the full run reweight_factor *= trials; for(auto & hepeup: FO_events) { hepeup.XWGTUP *= reweight_factor; } std::cout << "Training unweighter with " << trials << '*' << FO_events.size() << " events\n"; auto progress = HEJ::ProgressBar{ std::cout, static_cast(FO_events.size()) }; std::vector resummation_events; for(auto const & hepeup: FO_events) { append_resummed_events( resummation_events, reweighter, hepeup, trials, fixed_order_jets, ew_parameters, off_shell_tolerance ); ++progress; } unweighter.set_cut_to_peakwt(resummation_events, max_dev); std::cout << "\nUnweighting events with weight up to " << unweighter.get_cut() << '\n'; } int main(int argn, char** argv) { using clock = std::chrono::system_clock; if (argn != 3) { std::cerr << "\n# Usage:\n."<< argv[0] <<" config_file input_file\n\n"; return EXIT_FAILURE; } const auto start_time = clock::now(); { std::cout << "Starting " << HEJ::Version::package_name_full() << ", revision " << HEJ::Version::revision() << " (" << time_to_string(clock::to_time_t(start_time)) << ")" << std::endl; } fastjet::ClusterSequence::print_banner(); // read configuration const HEJ::Config config = load_config(argv[1]); auto reader = HEJ::make_reader(argv[2]); assert(reader); + if (reader->is_fifo()){ + std::cout << "Detected input from a fifo file. Disabling progress bar." + << std::endl; + } + auto heprup{ reader->heprup() }; heprup.generators.emplace_back(LHEF::XMLTag{}); heprup.generators.back().name = HEJ::Version::package_name(); heprup.generators.back().version = HEJ::Version::String(); auto analyses = get_analyses( config.analyses_parameters, heprup ); assert(analyses.empty() || analyses.front() != nullptr); HEJ::CombinedEventWriter writer{config.output, std::move(heprup)}; double global_reweight = 1.; auto const & max_events = config.max_events; + // if we need the event number: if(std::abs(heprup.IDWTUP) == 4 || std::abs(heprup.IDWTUP) == 1 || max_events){ // try to read from LHE head auto input_events{reader->number_events()}; if(!input_events) { // else count manually auto t_reader = HEJ::make_reader(argv[2]); input_events = 0; while(t_reader->read_event()) ++(*input_events); } if(std::abs(heprup.IDWTUP) == 4 || std::abs(heprup.IDWTUP) == 1){ // IDWTUP 4 or 1 assume average(weight)=xs, but we need sum(weights)=xs std::cout << "Found IDWTUP " << heprup.IDWTUP << ": " << "assuming \"cross section = average weight\".\n" << "converting to \"cross section = sum of weights\" "; global_reweight /= *input_events; } if(max_events && (*input_events > *max_events)){ // maximal number of events given global_reweight *= *input_events/static_cast(*max_events); std::cout << "Processing " << *max_events << " out of " << *input_events << " events\n"; } } HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; std::shared_ptr ran{ HEJ::make_RNG(config.rng.name, config.rng.seed)}; assert(ran != nullptr); HEJ::EventReweighter hej{ reader->heprup(), std::move(scale_gen), to_EventReweighterConfig(config), ran }; std::optional unweighter{}; if(config.weight_type != HEJ::WeightType::weighted) { unweighter = HEJ::Unweighter{}; } if(config.weight_type == HEJ::WeightType::partially_unweighted) { HEJ::BufferedEventReader buffered_reader{std::move(reader)}; assert(config.unweight_config); train( *unweighter, buffered_reader, hej, config.unweight_config->trials, config.unweight_config->max_dev, global_reweight/config.trials, config.fixed_order_jets, config.ew_parameters, config.off_shell_tolerance ); reader = std::make_unique( std::move(buffered_reader) ); } // status infos & eye candy if (config.nlo.enabled){ std::cout << "HEJ@NLO Truncation Enabled for NLO order: " << config.nlo.nj << std::endl; } size_t nevent = 0; std::array nevent_type{0}, nfailed_type{0}; - auto progress = make_progress_bar(reader->heprup().XSECUP); + size_t size_progress{0}; + if (!reader->is_fifo()){ + auto n_input_events{reader->number_events()}; + if(!n_input_events) { + auto t_reader = HEJ::make_reader(argv[2]); + n_input_events = 0; + while(t_reader->read_event()) ++(*n_input_events); + } + if (n_input_events > 0) {size_progress=*n_input_events;} + } + auto progress = HEJ::ProgressBar{ + std::cout, static_cast(size_progress)}; + HEJ::CrossSectionAccumulator xs; std::map status_counter; size_t total_trials = 0; size_t total_resum = 0; - // Loop over the events in the input file while(reader->read_event() && (!max_events || nevent < *max_events) ){ ++nevent; + if(!reader->is_fifo()) {++progress;} // reweight events so that the total cross section is conserved auto hepeup = reader->hepeup(); hepeup.XWGTUP *= global_reweight; const auto FO_event = to_event( hepeup, config.fixed_order_jets, config.ew_parameters, config.off_shell_tolerance ); if(FO_event.central().weight == 0) { static const bool warned_once = [argv,nevent](){ std::cerr << "WARNING: event number " << nevent << " in " << argv[2] << " has zero weight. " "Ignoring this and all further events with vanishing weight.\n"; return true; }(); (void) warned_once; // shut up compiler warnings continue; } auto resummed_events{ hej.reweight(FO_event, config.trials) }; // some bookkeeping for(auto const & s: hej.status()) ++status_counter[s]; total_trials+=hej.status().size(); ++nevent_type[FO_event.type()]; if(resummed_events.empty()) ++nfailed_type[FO_event.type()]; if(unweighter) { unweight(*unweighter, config.weight_type, resummed_events, *ran); } // analysis for(auto & ev: resummed_events){ //TODO: move pass_cuts to after phase space point generation bool passed = analyses.empty(); for(auto const & analysis: analyses){ if(analysis->pass_cuts(ev, FO_event)){ passed = true; analysis->fill(ev, FO_event); }; } if(passed){ writer.write(ev); } else { ev.parameters()*=0; // do not use discarded events afterwards } } xs.fill_correlated(resummed_events); total_resum += resummed_events.size(); - if(progress) progress->increment(FO_event.central().weight); } // main event loop std::cout << '\n'; // scale to account for num_trials of sherpa if(const double scalefactor = reader->scalefactor(); scalefactor != 1.) { for(auto const & analysis: analyses){ analysis->scale(scalefactor); } xs.scale(scalefactor); } for(auto const & analysis: analyses){ analysis->finalise(); } writer.finish(); using namespace HEJ::event_type; std::cout<< "Events processed: " << nevent << " (" << total_resum << " resummed)"<< '\n'; std::cout << '\t' << name(EventType::first_type) << ": " << nevent_type[EventType::first_type] << ", failed to reconstruct " << nfailed_type[EventType::first_type] << '\n'; for(auto i=EventType::first_type+1; i<=EventType::last_type; i*=2){ std::cout << '\t' << name(static_cast(i)) << ": " << nevent_type[i] << ", failed to reconstruct " << nfailed_type[i] << '\n'; } std::cout << '\n' << xs << '\n'; std::cout << "Generation statistic: " << status_counter[HEJ::StatusCode::good] << "/" << total_trials << " trials successful.\n"; for(auto && entry: status_counter){ const double fraction = static_cast(entry.second)/total_trials; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(17) << (to_string(entry.first) + ":") << " ["; for(int i = 0; i < percent/2; ++i) std::cout << '#'; for(int i = percent/2; i < 50; ++i) std::cout << ' '; std::cout << "] " < run_time = (clock::now() - start_time); std::cout << "\nFinished " << HEJ::Version::package_name() << " at " << time_to_string(clock::to_time_t(clock::now())) << "\n=> Runtime: " << run_time.count() << " sec (" << nevent/run_time.count() << " Events/sec).\n"; return EXIT_SUCCESS; }