diff --git a/include/HEJ/LesHouchesWriter.hh b/include/HEJ/LesHouchesWriter.hh index 60c7652..70c4d82 100644 --- a/include/HEJ/LesHouchesWriter.hh +++ b/include/HEJ/LesHouchesWriter.hh @@ -1,65 +1,67 @@ /** \file * \brief Contains the writer for LesHouches output * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2023 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "LHEF/LHEF.h" #include "HEJ/EventWriter.hh" namespace HEJ { class Event; //! Class for writing events to a file in the Les Houches Event File format class LesHouchesWriter : public EventWriter{ public: //! Constructor /** * @param file Name of output file * @param heprup General process information */ LesHouchesWriter(std::string const & file, LHEF::HEPRUP heprup); LesHouchesWriter(LesHouchesWriter const & other) = delete; LesHouchesWriter & operator=(LesHouchesWriter const & other) = delete; /** @TODO in principle, this class should be movable * but that somehow(?) breaks the write member function */ LesHouchesWriter(LesHouchesWriter && other) = delete; LesHouchesWriter & operator=(LesHouchesWriter && other) = delete; ~LesHouchesWriter() override; //! Write an event to the file specified in the constructor void write(Event const & ev) override; //! Set the ratio (cross section) / (sum of event weights) void set_xs_scale(double scale) override; //! Dump general information & finish writing void finish() override; private: void write_init(){ writer_->init(); } + void rewrite_init_and_close(); + LHEF::HEPRUP & heprup(){ return writer_->heprup; } LHEF::HEPEUP & hepeup(){ return writer_->hepeup; } std::fstream out_; std::unique_ptr writer_; double xs_scale_ = 1.; }; } // namespace HEJ diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc index a9b08f7..5631e41 100644 --- a/src/LesHouchesWriter.cc +++ b/src/LesHouchesWriter.cc @@ -1,134 +1,154 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/LesHouchesWriter.hh" #include #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/Parameters.hh" #include "HEJ/event_types.hh" #include "HEJ/exceptions.hh" #include "HEJ/utility.hh" namespace HEJ { namespace { std::size_t to_index(event_type::EventType const type){ return type==0?0:1+std::floor(std::log2(static_cast(type))); } } LesHouchesWriter::LesHouchesWriter( std::string const & file, LHEF::HEPRUP heprup ): out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc}, writer_{std::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 std::size_t 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(auto 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){ if(finished()) { throw std::logic_error{ "Tried to write a Les Houches event after calling `finish`" }; } assert(writer_ && out_.is_open()); const double wt = ev.central().weight; writer_->hepeup = to_HEPEUP(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; } } void LesHouchesWriter::set_xs_scale(const double scale) { xs_scale_ = scale; } // 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){ ignore(out); // suppress compiler warnings if not in debug mode #ifndef NDEBUG std::string line; getline(out, line); assert(out.eof() || line.rfind("" << std::endl; + rewrite_init_and_close(); } LesHouchesWriter::~LesHouchesWriter(){ finish_or_abort(this, "LesHouchesWriter"); } } // namespace HEJ