diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh index 549cfbc..2439b75 100644 --- a/include/HEJ/LesHouchesReader.hh +++ b/include/HEJ/LesHouchesReader.hh @@ -1,80 +1,83 @@ /** \file * \brief Header file for reading events in the Les Houches Event File format. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include "LHEF/LHEF.h" #include "HEJ/Event.hh" #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): stream_{filename}, reader_{stream_} - {} + { + // always use the newest LHE version + reader_.heprup.version = LHEF::HEPRUP().version; + } //! Read an event bool read_event() override { return reader_.readEvent(); } //! 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; } private: HEJ::istream stream_; protected: //! Underlying reader LHEF::Reader reader_; }; /** * @brief Les Houches Event file reader for LHE files created by Sherpa * @details In Sherpa the cross section is given by * sum(weights)/(number of trials). This EventReader converts the * weights such that cross section=sum(weights) * @note Reading from a pipe is not possible! */ class SherpaLHEReader : public LesHouchesReader{ public: //! Inialise Reader for a Sherpa LHE file explicit SherpaLHEReader(std::string const & filename); bool read_event() override; HEJ::optional number_events() const override { return num_events; } private: double num_trials; size_t num_events; }; } // namespace HEJ diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc index 0b2693e..e96ee0e 100644 --- a/src/LesHouchesWriter.cc +++ b/src/LesHouchesWriter.cc @@ -1,124 +1,127 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #include #include #include #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/LesHouchesWriter.hh" #include "HEJ/utility.hh" namespace HEJ{ namespace{ template std::unique_ptr make_unique(Args&&... a){ return std::unique_ptr{new T{std::forward(a)...}}; } size_t to_index(event_type::EventType const type){ return type==0?0:floor(log2(type))+1; } } LesHouchesWriter::LesHouchesWriter( std::string const & file, LHEF::HEPRUP heprup ): out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc}, writer_{HEJ::make_unique(out_)} { if(! out_.is_open()){ throw std::ios_base::failure("Failed to open " + file); }; // scientific style is needed to allow rewriting the init block out_ << std::scientific; writer_->heprup = std::move(heprup); // lhe Standard: IDWTUP (negative => weights = +/-) // IDWTUP: HEJ -> SHG/Pythia/next program // 1: weighted->unweighted, xs = mean(weight), XMAXUP given // 2: weighted->unweighted, xs = XSECUP, XMAXUP given // 3: unweighted (weight=+1)->unweighted, no additional information // 4: weighted->weighted, xs = mean(weight) // // None of these codes actually match what we want: // 1 and 4 require xs = mean(weight), which is impossible until after generation // 2 tells the SHG to unweight our events, which is wasteful // 3 claims we produce unweighted events, which is both wasteful _and_ // impossible until after generation (we don't know the maximum weight before) // // For the time being, we choose -3. If the consumer (like Pythia) assumes // weight=+1, the final weights have to be corrected by multiplying with // the original weight we provided. We are also often use NLO-PDFs which can // give negative weights, hence the native IDWTUP. // writer_->heprup.IDWTUP = -3; + // always use the newest LHE version + // Pythia only saves weights (hepeup.XWGTUP) for version >=2 + writer_->heprup.version = LHEF::HEPRUP().version; const int max_number_types = to_index(event_type::last_type)+1; writer_->heprup.NPRUP = max_number_types; // ids of event types writer_->heprup.LPRUP.clear(); writer_->heprup.LPRUP.reserve(max_number_types); writer_->heprup.LPRUP.emplace_back(0); for(size_t i=event_type::first_type+1; i<=event_type::last_type; i*=2) writer_->heprup.LPRUP.emplace_back(i); // use placeholders for unknown init block values // we can overwrite them after processing all events writer_->heprup.XSECUP = std::vector(max_number_types, 0.); writer_->heprup.XERRUP = std::vector(max_number_types, 0.); writer_->heprup.XMAXUP = std::vector(max_number_types, 0.); write_init(); } void LesHouchesWriter::write(Event const & ev){ assert(writer_ && out_.is_open()); const double wt = ev.central().weight; writer_->hepeup = HEJ::to_HEPEUP(std::move(ev), &heprup()); writer_->writeEvent(); assert(heprup().XSECUP.size() > to_index(ev.type())); heprup().XSECUP[to_index(ev.type())] += wt; heprup().XERRUP[to_index(ev.type())] += wt*wt; if(wt > heprup().XMAXUP[to_index(ev.type())]){ heprup().XMAXUP[to_index(ev.type())] = wt; } } // this function is called after overwritting the Les Houches init block // assert that we have overwritten *exactly* the init block, // i.e. we are at the end of the file or an intact event block is next void assert_next_event_intact(std::istream & out){ (void) out; // suppress compiler warnings if not in debug mode #ifndef NDEBUG std::string line; getline(out, line); assert(out.eof() || line.rfind(" #include #include "HEJ/event_types.hh" #include "HEJ/EventReader.hh" #include "hej_test.hh" namespace { static constexpr double ep = 1e-3; const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4}; constexpr double min_jet_pt = 30; } int main(int argn, char** argv) { if(argn != 2){ std::cerr << "Usage: " << argv[0] << " lhe_file\n"; return EXIT_FAILURE; } auto reader{ HEJ::make_reader(argv[1]) }; + // version should be overwritten + ASSERT(reader->heprup().version==LHEF::HEPRUP().version); + std::unordered_map xsec_ref; for(int i=0; i < reader->heprup().NPRUP; ++i) xsec_ref[reader->heprup().LPRUP[i]] = 0.; while(reader->read_event()){ auto const & hepeup = reader->hepeup(); ASSERT(hepeup.NUP > 2); // at least 3 particles (2 in + 1 out) // first incoming has positive pz ASSERT(hepeup.PUP[0][2] > hepeup.PUP[1][2]); // test that we can trasform IDPRUP to event type (void) name(static_cast(hepeup.IDPRUP)); xsec_ref[hepeup.IDPRUP] += hepeup.weight(); // test that a HEJ event can be transformed back to the original HEPEUP auto hej_event = HEJ::Event::EventData(hepeup).cluster(jet_def, min_jet_pt); // there are two different weight infos, which should be the same ASSERT(hej_event.central().weight == hepeup.weight()); ASSERT(hej_event.central().weight == hepeup.XWGTUP); // reader->heprup() is const, we can't use it to create a hepeup auto cp_heprup = reader->heprup(); auto new_event = HEJ::to_HEPEUP(hej_event, &cp_heprup); ASSERT_PROPERTY(new_event, hepeup, weight()); ASSERT_PROPERTY(new_event, hepeup, XWGTUP); ASSERT_PROPERTY(new_event, hepeup, SCALUP); ASSERT_PROPERTY(new_event, hepeup, NUP); } for(size_t i = 0; i < xsec_ref.size(); ++i){ double const ref = xsec_ref[reader->heprup().LPRUP[i]]; double const calc = reader->heprup().XSECUP[i]; std::cout << ref << '\t' << calc << '\n'; if(std::abs(calc-ref) > ep*calc){ std::cerr << "Cross sections deviate substantially"; return EXIT_FAILURE; } } return EXIT_SUCCESS; } diff --git a/t/check_lhe_sherpa.cc b/t/check_lhe_sherpa.cc index 31561d6..7f82d07 100644 --- a/t/check_lhe_sherpa.cc +++ b/t/check_lhe_sherpa.cc @@ -1,50 +1,52 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #include #include #include "HEJ/LesHouchesReader.hh" #include "hej_test.hh" static constexpr double ep = 1e-5; int main(int argn, char** argv) { if(argn != 3){ std::cerr << "Usage: " << argv[0] << " lhe_file xs\n"; return EXIT_FAILURE; } auto reader{ HEJ::make_reader(argv[1])}; const double ref_xs = std::stod(argv[2]); if(std::abs(reader->heprup().IDWTUP) != 3){ std::cerr << "Sherpa Events should always be neutral/unweighted\n"; return EXIT_FAILURE; } + // version should be overwritten + ASSERT(reader->heprup().version==LHEF::HEPRUP().version); double xs { 0. }; size_t n_evts { 0 }; ASSERT(std::abs(reader->heprup().XSECUP.front()-ref_xs) < ep*ref_xs); while(reader->read_event()){ ++n_evts; xs += reader->hepeup().weight(); ASSERT(reader->hepeup().weight() == reader->hepeup().XWGTUP); } if(std::abs(xs-ref_xs) > ep*xs){ std::cerr << "Cross sections deviate substantially!\n" <<"Found "<< xs <<" but expected "<< ref_xs <<" -> "<< xs/ref_xs <<"\n"; return EXIT_FAILURE; } if(!reader->number_events() || *(reader->number_events()) != n_evts){ std::cerr << "Number of Event not correctly set for Sherpa LHE reader\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; }