diff --git a/FixedOrderGen/include/Unweighter.hh b/FixedOrderGen/include/Unweighter.hh deleted file mode 100644 index de8d9b5..0000000 --- a/FixedOrderGen/include/Unweighter.hh +++ /dev/null @@ -1,75 +0,0 @@ -/** - * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 - * \copyright GPLv2 or later - */ -#pragma once - -#include <cmath> - -#include "HEJ/optional.hh" -#include "HEJ/RNG.hh" - -namespace HEJ { - class Event; -} - -namespace HEJFOG { - namespace detail { - - bool has_jet_softer_than(HEJ::Event const & ev, double pt); - - template<typename Iterator> - double calc_cut( - Iterator begin, Iterator end, double max_dev, - double min_pt - ) { - double mean = 0.; - double err = 0.; - double awt_sum = 0.; - for(; begin != end; ++begin){ - if(has_jet_softer_than(*begin, min_pt)) continue; - const double awt = std::abs(begin->central().weight); - const double tmp = awt*std::log(awt); - mean += tmp; - err += tmp*tmp; - awt_sum += awt; - } - mean /= awt_sum; - err = std::sqrt(err)/awt_sum; - return std::exp(mean + max_dev*err); - } - } - - class Unweighter { - - public: - template<typename Iterator> - Unweighter( - Iterator begin, Iterator end, double max_dev, - HEJ::RNG & ran, - /* minimum pt of jets for an event to be considered for unweighting - * - * If the 'jets: peak pt' option is set to the *resummation* jet - * threshold, events with softer jets will have a spurious - * large weight, although they hardly contribute after resummation. - * This destroys the unweighting efficiency. - * By setting min_unweight_pt to the same threshold, we can exclude - * these events from unweighting. - */ - double min_unweight_pt = 0. - ): - cut_{detail::calc_cut(begin, end, max_dev, min_unweight_pt)}, - min_unweight_pt_{min_unweight_pt}, - ran_{ran} - {} - - HEJ::optional<HEJ::Event> unweight(HEJ::Event ev) const; - - private: - double cut_; - double min_unweight_pt_; - std::reference_wrapper<HEJ::RNG> ran_; - }; - -} diff --git a/FixedOrderGen/src/Unweighter.cc b/FixedOrderGen/src/Unweighter.cc deleted file mode 100644 index 16996e5..0000000 --- a/FixedOrderGen/src/Unweighter.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 - * \copyright GPLv2 or later - */ -#include "Unweighter.hh" - -#include <cassert> - -#include "HEJ/Event.hh" - -namespace HEJFOG { - - namespace detail { - bool has_jet_softer_than(HEJ::Event const & ev, double pt) { - assert(! ev.jets().empty()); - const auto softest_jet = fastjet::sorted_by_pt(ev.jets()).back(); - return softest_jet.pt() < pt; - } - } - - HEJ::optional<HEJ::Event> Unweighter::unweight(HEJ::Event ev) const { - if(detail::has_jet_softer_than(ev, min_unweight_pt_)) return ev; - const double awt = std::abs(ev.central().weight); - if(ran_.get().flat() < awt/cut_) { - if(awt < cut_) ev.parameters() *= cut_/awt; - return ev; - } - return {}; - } -} diff --git a/FixedOrderGen/src/main.cc b/FixedOrderGen/src/main.cc index d0b83a3..4138fec 100644 --- a/FixedOrderGen/src/main.cc +++ b/FixedOrderGen/src/main.cc @@ -1,234 +1,235 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #include <algorithm> #include <chrono> #include <fstream> #include <iostream> #include <map> #include <memory> #include "yaml-cpp/yaml.h" #include "LHEF/LHEF.h" #include "HEJ/CombinedEventWriter.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/get_analysis.hh" #include "HEJ/LesHouchesWriter.hh" #include "HEJ/make_RNG.hh" #include "HEJ/ProgressBar.hh" #include "HEJ/stream.hh" +#include "HEJ/Unweighter.hh" #include "config.hh" #include "EventGenerator.hh" #include "PhaseSpacePoint.hh" -#include "Unweighter.hh" #include "Version.hh" namespace{ constexpr auto banner = " __ ___ __ ______ __ " " __ \n / / / (_)___ _/ /_ / ____/___ " " ___ _________ ___ __ / /__ / /______ \n " " / /_/ / / __ `/ __ \\ / __/ / __ \\/ _ \\/ ___/ __ `/ / / / __ / / _" " \\/ __/ ___/ \n / __ / / /_/ / / / / / /___/ /" " / / __/ / / /_/ / /_/ / / /_/ / __/ /_(__ ) " " \n /_/ /_/_/\\__, /_/ /_/ /_____/_/ /_/\\___/_/ \\__, /\\__, / \\___" "_/\\___/\\__/____/ \n ____///__/ " "__ ____ ///__//____/ ______ __ " " \n / ____(_) _____ ____/ / / __ \\_________/ /__ _____ / " "____/__ ____ ___ _________ _/ /_____ _____\n / /_ / / |/_/ _ \\/ __" " / / / / / ___/ __ / _ \\/ ___/ / / __/ _ \\/ __ \\/ _ \\/ ___/ __ `/ " "__/ __ \\/ ___/\n / __/ / /> </ __/ /_/ / / /_/ / / / /_/ / __/ / " " / /_/ / __/ / / / __/ / / /_/ / /_/ /_/ / / \n /_/ /_/_/|_|\\___" "/\\__,_/ \\____/_/ \\__,_/\\___/_/ \\____/\\___/_/ /_/\\___/_/ " "\\__,_/\\__/\\____/_/ \n"; constexpr double invGeV2_to_pb = 389379292.; constexpr long long max_warmup_events = 10000; } HEJFOG::Config load_config(char const * filename){ try{ return HEJFOG::load_config(filename); } catch(std::exception const & exc){ std::cerr << "Error: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } std::unique_ptr<HEJ::Analysis> get_analysis( YAML::Node const & parameters ){ try{ return HEJ::get_analysis(parameters); } catch(std::exception const & exc){ std::cerr << "Failed to load analysis: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } int main(int argn, char** argv) { using namespace std::string_literals; if (argn < 2) { std::cerr << "\n# Usage:\n." << argv[0] << " config_file\n"; return EXIT_FAILURE; } std::cout << banner; std::cout << "Version " << HEJFOG::Version::String() << ", revision " << HEJFOG::Version::revision() << std::endl; fastjet::ClusterSequence::print_banner(); using clock = std::chrono::system_clock; const auto start_time = clock::now(); // read configuration auto config = load_config(argv[1]); std::unique_ptr<HEJ::Analysis> analysis = get_analysis( config.analysis_parameters ); assert(analysis != nullptr); auto ran = HEJ::make_RNG(config.rng.name, config.rng.seed); assert(ran != nullptr); HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; HEJFOG::EventGenerator generator{ config.process, config.beam, std::move(scale_gen), config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, *ran }; LHEF::HEPRUP heprup; heprup.IDBMUP=std::pair<long,long>(config.beam.particles[0], config.beam.particles[1]); heprup.EBMUP=std::make_pair(config.beam.energy, config.beam.energy); heprup.PDFGUP=std::make_pair(0,0); heprup.PDFSUP=std::make_pair(config.pdf_id,config.pdf_id); heprup.NPRUP=1; heprup.XSECUP=std::vector<double>(1.); heprup.XERRUP=std::vector<double>(1.); heprup.LPRUP=std::vector<int>{1}; heprup.generators.emplace_back(LHEF::XMLTag{}); heprup.generators.back().name = HEJFOG::Version::package_name(); heprup.generators.back().version = HEJFOG::Version::String(); HEJ::CombinedEventWriter writer{config.output, heprup}; - HEJ::optional<HEJFOG::Unweighter> unweighter{}; + HEJ::optional<HEJ::Unweighter> unweighter{}; std::map<HEJFOG::Status, int> status_counter; std::vector<HEJ::Event> events; int trials = 0; // warm-up phase to train unweighter if(config.unweight) { std::cout << "Calibrating unweighting ...\n"; const auto warmup_start = clock::now(); const size_t warmup_events = config.unweight->sample_size; HEJ::ProgressBar<size_t> warmup_progress{std::cout, warmup_events}; for(; events.size() < warmup_events; ++trials){ auto ev = generator.gen_event(); ++status_counter[generator.status()]; assert( (generator.status() == HEJFOG::good) == bool(ev) ); if(generator.status() == HEJFOG::good && analysis->pass_cuts(*ev, *ev)) { events.emplace_back(std::move(*ev)); ++warmup_progress; } } std::cout << std::endl; - unweighter = HEJFOG::Unweighter{ - begin(events), end(events), config.unweight->max_dev, *ran, + unweighter = HEJ::Unweighter(); + unweighter->set_middle( + events, config.unweight->max_dev, config.jets.peak_pt?(*config.jets.peak_pt):0. - }; + ); std::vector<HEJ::Event> unweighted_events; for(auto && ev: events) { - auto unweighted = unweighter->unweight(std::move(ev)); + auto unweighted = unweighter->unweight(std::move(ev), *ran); if(unweighted) { unweighted_events.emplace_back(std::move(*unweighted)); } } events = std::move(unweighted_events); if(events.empty()) { std::cerr << "Failed to generate events. Please increase \"unweight: sample size\"" " or reduce \"unweight: max deviation\"\n"; return EXIT_FAILURE; } const auto warmup_end = clock::now(); const double completion = static_cast<double>(events.size())/config.events; const std::chrono::duration<double> remaining_time = (warmup_end- warmup_start)*(1./completion - 1); const auto finish = clock::to_time_t( std::chrono::time_point_cast<std::chrono::seconds>(warmup_end + remaining_time) ); std::cout << "Generated " << events.size() << "/" << config.events << " events (" << static_cast<int>(std::round(100*completion)) << "%)\n" << "Estimated remaining generation time: " << remaining_time.count() << " seconds (" << std::put_time(std::localtime(&finish), "%c") << ")\n\n"; } HEJ::ProgressBar<long long> progress{std::cout, config.events}; progress.increment(events.size()); events.reserve(config.events); for(; events.size() < static_cast<size_t>(config.events); ++trials){ auto ev = generator.gen_event(); ++status_counter[generator.status()]; assert( (generator.status() == HEJFOG::good) == bool(ev) ); if(generator.status() == HEJFOG::good && analysis->pass_cuts(*ev, *ev)) { if(unweighter) { - auto unweighted = unweighter->unweight(std::move(*ev)); + auto unweighted = unweighter->unweight(std::move(*ev), *ran); if(! unweighted) continue; ev = std::move(unweighted); } events.emplace_back(std::move(*ev)); ++progress; } } std::cout << std::endl; HEJ::CrossSectionAccumulator xs; for(auto & ev: events){ ev.parameters() *= invGeV2_to_pb/trials; analysis->fill(ev, ev); writer.write(ev); xs.fill(ev); } analysis->finalise(); const std::chrono::duration<double> run_time = (clock::now() - start_time); std::cout << "\nTask Runtime: " << run_time.count() << " seconds.\n\n"; std::cout << xs << '\n'; for(auto && entry: status_counter){ const double fraction = static_cast<double>(entry.second)/trials; const int percent = std::round(100*fraction); std::cout << "status " << std::left << std::setw(16) << (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 << "] " << percent << "%\n"; } return EXIT_SUCCESS; } diff --git a/include/HEJ/Unweighter.hh b/include/HEJ/Unweighter.hh index 6850364..b6c3a0e 100644 --- a/include/HEJ/Unweighter.hh +++ b/include/HEJ/Unweighter.hh @@ -1,46 +1,59 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include <functional> #include <vector> #include "HEJ/optional.hh" #include "HEJ/RNG.hh" namespace HEJ { class Event; class Unweighter { public: Unweighter(double cut = -1.): cut_{cut}{} //! explicitly set cut void set_cut(double max_weight){ cut_ = max_weight; } //! set cut as max(weight) of events void set_max(std::vector<Event> const & events); - //! @TODO estimate some reasonable cut for partial unweighting - // void set_middle(std::vector<Event> const & evts) + //! estimate some reasonable cut for partial unweighting + /** + * @param max_dev standard derivation to include above mean weight + * @param min_unweight_pt minimum pt of jets for an event to be considered + * + * @note Events with softer jets than the *resummation* jet threshold will + * have a spurious large weight, although they hardly contribute after + * resummation. This destroys the unweighting efficiency. By setting + * min_unweight_pt to the same threshold, we can exclude these events + * from unweighting. + */ + void set_middle( + std::vector<Event> const & events, double max_dev, + double min_unweight_pt = 0 + ); //! return current value of the cut double get_cut() const { return cut_; } //! unweight one event, returns original event if weight > get_cut() optional<Event> unweight(Event ev, RNG & ran) const; //! unweight for multiple events at once std::vector<Event> unweight( std::vector<Event> events, RNG & ran ) const; private: double cut_; }; } diff --git a/src/Unweighter.cc b/src/Unweighter.cc index 0cde3d0..1baec4b 100644 --- a/src/Unweighter.cc +++ b/src/Unweighter.cc @@ -1,50 +1,74 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #include "HEJ/Unweighter.hh" #include "HEJ/Event.hh" namespace HEJ { namespace { - // returns true if element can be removed/gets discarded // directly corrects weight if is accepted (not removed) bool correct_weight(double cut, RNG & ran, Event & ev){ const double awt = std::abs(ev.central().weight); if(awt >= cut ) return false; if(ran.flat() > awt/cut) return true; ev.parameters() *= cut/awt; return false; } + + bool has_jet_softer_than(HEJ::Event const & ev, double pt) { + assert(! ev.jets().empty()); + const auto softest_jet = fastjet::sorted_by_pt(ev.jets()).back(); + return softest_jet.pt() < pt; + } } optional<Event> Unweighter::unweight(Event ev, RNG & ran) const { if(correct_weight(get_cut(), ran, ev)) return {}; return ev; } std::vector<Event> Unweighter::unweight( std::vector<Event> events, RNG & ran ) const { events.erase( std::remove_if(events.begin(), events.end(), [&](auto & ev){ return correct_weight(get_cut(), ran, ev); }), events.end()); return events; } void Unweighter::set_max(std::vector<Event> const & events){ set_cut(-1); for(auto const & ev: events){ const double awt = std::abs(ev.central().weight); if( awt > get_cut()) set_cut(awt); } } + + void Unweighter::set_middle( + std::vector<Event> const & events, const double max_dev, const double min_pt + ){ + double mean = 0.; + double err = 0.; + double awt_sum = 0.; + for(auto const & ev: events){ + if(has_jet_softer_than(ev, min_pt)) continue; + const double awt = std::abs(ev.central().weight); + const double tmp = awt*std::log(awt); + mean += tmp; + err += tmp*tmp; + awt_sum += awt; + } + mean /= awt_sum; + err = std::sqrt(err)/awt_sum; + set_cut(std::exp(mean + max_dev*err)); + } }