diff --git a/FixedOrderGen/include/EventGenerator.hh b/FixedOrderGen/include/EventGenerator.hh index b89d640..899a72c 100644 --- a/FixedOrderGen/include/EventGenerator.hh +++ b/FixedOrderGen/include/EventGenerator.hh @@ -1,67 +1,67 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include "HEJ/MatrixElement.hh" #include "HEJ/optional.hh" #include "HEJ/PDF.hh" #include "HEJ/RNG.hh" #include "Beam.hh" #include "Decay.hh" #include "JetParameters.hh" #include "Process.hh" #include "Status.hh" namespace HEJ{ class Event; class HiggsCouplingSettings; class ScaleGenerator; class EWConstants; } //! Namespace for HEJ Fixed Order Generator namespace HEJFOG{ class EventGenerator{ public: EventGenerator( Process process, Beam beam, HEJ::ScaleGenerator scale_gen, JetParameters jets, int pdf_id, double subl_change, unsigned int subl_channels, ParticlesDecayMap particle_decays, HEJ::HiggsCouplingSettings Higgs_coupling, HEJ::EWConstants ew_parameters, std::shared_ptr<HEJ::RNG> ran ); HEJ::optional<HEJ::Event> gen_event(); Status status() const { return status_; } private: HEJ::PDF pdf_; HEJ::MatrixElement ME_; HEJ::ScaleGenerator scale_gen_; Process process_; JetParameters jets_; Beam beam_; Status status_; double subl_change_; unsigned int subl_channels_; ParticlesDecayMap particle_decays_; HEJ::EWConstants ew_parameters_; std::shared_ptr<HEJ::RNG> ran_; }; } diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh index 66642e0..a6eef97 100644 --- a/FixedOrderGen/include/PhaseSpacePoint.hh +++ b/FixedOrderGen/include/PhaseSpacePoint.hh @@ -1,233 +1,233 @@ /** \file PhaseSpacePoint.hh * \brief Contains the PhaseSpacePoint Class * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <bitset> #include <vector> #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/RNG.hh" #include "JetParameters.hh" #include "Decay.hh" #include "Status.hh" namespace HEJ{ class EWConstants; } namespace HEJFOG{ class Process; using HEJ::Particle; //! A point in resummation phase space class PhaseSpacePoint{ public: //! Default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param proc The process to generate * @param jet_properties Jet defintion & cuts * @param pdf The pdf set (used for sampling) * @param E_beam Energie of the beam * @param subl_chance Chance to turn a potentially unordered * emission into an actual one * @param subl_channels Possible subleading channels. * see HEJFOG::Subleading * @param particle_properties Properties of producted boson * * Initially, only FKL phase space points are generated. subl_chance gives * the change of turning one emissions into a subleading configuration, * i.e. either unordered or central quark/anti-quark pair. Unordered * emissions require that the most extremal emission in any direction is * a quark or anti-quark and the next emission is a gluon. Quark/anti-quark * pairs are only generated for W processes. At most one subleading * emission will be generated in this way. */ PhaseSpacePoint( Process const & proc, JetParameters const & jet_properties, HEJ::PDF & pdf, double E_beam, double subl_chance, unsigned int subl_channels, ParticlesDecayMap const & particle_decays, HEJ::EWConstants const & ew_parameters, HEJ::RNG & ran ); //! Get Weight Function /** * @returns Weight of Event */ double weight() const{ return weight_; } Status status() const{ return status_; } //! Get Incoming Function /** * @returns Incoming Particles */ std::array<Particle, 2> const & incoming() const{ return incoming_; } //! Get Outgoing Function /** * @returns Outgoing Particles */ std::vector<Particle> const & outgoing() const{ return outgoing_; } std::unordered_map<size_t, std::vector<Particle>> const & decays() const{ return decays_; } private: friend HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); /** * @internal * @brief Generate LO parton momentum * * @param count Number of partons to generate * @param is_pure_jets If true ensures momentum conservation in x and y * @param jet_param Jet properties to fulfil * @param max_pt max allowed pt for a parton (typically E_CMS) * @param ran Random Number Generator * * @returns Momentum of partons * * Ensures that each parton is in its own jet. * Generation is independent of parton flavour. Output is sorted in rapidity. */ std::vector<fastjet::PseudoJet> gen_LO_partons( int count, bool is_pure_jets, JetParameters const & jet_param, double max_pt, HEJ::RNG & ran ); Particle gen_boson( HEJ::ParticleID bosonid, double mass, double width, HEJ::RNG & ran ); template<class ParticleMomenta> fastjet::PseudoJet gen_last_momentum( ParticleMomenta const & other_momenta, double mass_square, double y ) const; bool jets_ok( std::vector<fastjet::PseudoJet> const & Born_jets, std::vector<fastjet::PseudoJet> const & partons ) const; /** * @internal * @brief Generate incoming partons according to the PDF * * @param uf Scale used in the PDF */ void reconstruct_incoming( Process const & proc, unsigned int subl_channels, HEJ::PDF & pdf, double E_beam, double uf, HEJ::RNG & ran ); /** * @internal * @brief Returns list of all allowed initial states partons */ std::array<std::bitset<11>,2> filter_partons( Process const & proc, unsigned int const subl_channels, HEJ::RNG & ran ); HEJ::ParticleID generate_incoming_id( size_t beam_idx, double x, double uf, HEJ::PDF & pdf, std::bitset<11> allowed_partons, HEJ::RNG & ran ); bool momentum_conserved(double ep) const; HEJ::Particle const & most_backward_FKL( std::vector<HEJ::Particle> const & partons ) const; HEJ::Particle const & most_forward_FKL( std::vector<HEJ::Particle> const & partons ) const; HEJ::Particle & most_backward_FKL(std::vector<HEJ::Particle> & partons) const; HEJ::Particle & most_forward_FKL(std::vector<HEJ::Particle> & partons) const; bool extremal_FKL_ok( std::vector<fastjet::PseudoJet> const & partons ) const; double random_normal(double stddev, HEJ::RNG & ran); /** * @internal * @brief Turns a FKL configuration into a subleading one * * @param chance Change to switch to subleading configuration * @param channels Allowed channels for subleading process * @param proc Process to decide which subleading * configurations are allowed * * With a chance of "chance" the FKL configuration is either turned into * a unordered configuration or, for A/W/Z bosons, a configuration with * a central quark/anti-quark pair. */ void maybe_turn_to_subl(double chance, unsigned int channels, Process const & proc, HEJ::RNG & ran); void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, HEJ::RNG & ran); void turn_to_qqx(bool allow_strange, HEJ::RNG & ran); //! decay where we select the decay channel std::vector<Particle> decay_boson( HEJ::Particle const & parent, std::vector<Decay> const & decays, HEJ::RNG & ran ); //! generate decay products of a boson std::vector<Particle> decay_boson( HEJ::Particle const & parent, std::vector<HEJ::ParticleID> const & decays, HEJ::RNG & ran ); /// @brief setup outgoing partons to ensure correct coupling to boson void couple_boson(HEJ::ParticleID boson, HEJ::RNG & ran); Decay select_decay_channel( std::vector<Decay> const & decays, HEJ::RNG & ran ); double gen_hard_pt( int np, double ptmin, double ptmax, double y, HEJ::RNG & ran ); double gen_soft_pt(int np, double ptmax, HEJ::RNG & ran); double gen_parton_pt( int count, JetParameters const & jet_param, double ptmax, double y, HEJ::RNG & ran ); double weight_; Status status_; std::array<Particle, 2> incoming_; std::vector<Particle> outgoing_; //! Particle decays in the format {outgoing index, decay products} std::unordered_map<size_t, std::vector<Particle>> decays_; }; //! Extract HEJ::Event::EventData from PhaseSpacePoint HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); } diff --git a/FixedOrderGen/include/config.hh b/FixedOrderGen/include/config.hh index 7a55cb7..df5b36b 100644 --- a/FixedOrderGen/include/config.hh +++ b/FixedOrderGen/include/config.hh @@ -1,46 +1,46 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include "yaml-cpp/yaml.h" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/optional.hh" #include "HEJ/Config.hh" #include "HEJ/output_formats.hh" #include "HEJ/exceptions.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Fraction.hh" #include "Beam.hh" #include "Decay.hh" #include "JetParameters.hh" #include "Process.hh" #include "UnweightSettings.hh" namespace HEJFOG{ struct Config{ Process process; size_t events; JetParameters jets; Beam beam; int pdf_id; HEJ::Fraction<double> subleading_fraction; unsigned int subleading_channels; //! < see HEJFOG::Subleading ParticlesDecayMap particle_decays; std::vector<YAML::Node> analyses_parameters; HEJ::ScaleConfig scales; std::vector<HEJ::OutputFile> output; HEJ::RNGConfig rng; HEJ::HiggsCouplingSettings Higgs_coupling; HEJ::EWConstants ew_parameters; HEJ::optional<UnweightSettings> unweight; }; Config load_config(std::string const & config_file); } diff --git a/FixedOrderGen/src/EventGenerator.cc b/FixedOrderGen/src/EventGenerator.cc index 58a5d62..852d462 100644 --- a/FixedOrderGen/src/EventGenerator.cc +++ b/FixedOrderGen/src/EventGenerator.cc @@ -1,95 +1,95 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "EventGenerator.hh" #include <utility> #include "Process.hh" #include "Beam.hh" #include "JetParameters.hh" #include "PhaseSpacePoint.hh" #include "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/EWConstants.hh" namespace HEJFOG{ EventGenerator::EventGenerator( Process process, Beam beam, HEJ::ScaleGenerator scale_gen, JetParameters jets, int pdf_id, double subl_change, unsigned int subl_channels, ParticlesDecayMap particle_decays, HEJ::HiggsCouplingSettings Higgs_coupling, HEJ::EWConstants ew_parameters, std::shared_ptr<HEJ::RNG> ran ): pdf_{pdf_id, beam.particles[0], beam.particles[1]}, ME_{ [this](double mu){ return pdf_.Halphas(mu); }, HEJ::MatrixElementConfig{ false, std::move(Higgs_coupling), ew_parameters } }, scale_gen_{std::move(scale_gen)}, process_{std::move(process)}, jets_{std::move(jets)}, beam_{std::move(beam)}, subl_change_{subl_change}, subl_channels_{subl_channels}, particle_decays_{std::move(particle_decays)}, ew_parameters_{ew_parameters}, ran_{std::move(ran)} { } HEJ::optional<HEJ::Event> EventGenerator::gen_event(){ HEJFOG::PhaseSpacePoint psp{ process_, jets_, pdf_, beam_.energy, subl_change_, subl_channels_, particle_decays_, ew_parameters_, *ran_ }; status_ = psp.status(); if(status_ != good) return {}; HEJ::Event ev = scale_gen_( HEJ::Event{ to_EventData( std::move(psp) ).cluster( jets_.def, jets_.min_pt) } ); if(!is_resummable(ev.type())) throw HEJ::not_implemented("Tried to generate a event type, " "which is not yet implemented in HEJ."); ev.generate_colours(*ran_); const double shat = HEJ::shat(ev); const double xa = (ev.incoming()[0].E()-ev.incoming()[0].pz())/(2.*beam_.energy); const double xb = (ev.incoming()[1].E()+ev.incoming()[1].pz())/(2.*beam_.energy); // evaluate matrix element ev.parameters() *= ME_.tree(ev)/(shat*shat); // and PDFs ev.central().weight *= pdf_.pdfpt(0,xa,ev.central().muf, ev.incoming()[0].type); ev.central().weight *= pdf_.pdfpt(0,xb,ev.central().muf, ev.incoming()[1].type); for(size_t i = 0; i < ev.variations().size(); ++i){ auto & var = ev.variations(i); var.weight *= pdf_.pdfpt(0,xa,var.muf, ev.incoming()[0].type); var.weight *= pdf_.pdfpt(0,xb,var.muf, ev.incoming()[1].type); } return ev; } } diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc index 64ce55a..bd85e39 100644 --- a/FixedOrderGen/src/PhaseSpacePoint.cc +++ b/FixedOrderGen/src/PhaseSpacePoint.cc @@ -1,695 +1,695 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "PhaseSpacePoint.hh" #include <algorithm> #include "CLHEP/Vector/LorentzVector.h" #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/exceptions.hh" #include "HEJ/kinematics.hh" #include "HEJ/Particle.hh" #include "HEJ/utility.hh" #include "Process.hh" #include "Subleading.hh" using namespace HEJ; namespace HEJFOG{ namespace { static_assert( std::numeric_limits<double>::has_quiet_NaN, "no quiet NaN for double" ); constexpr double NaN = std::numeric_limits<double>::quiet_NaN(); } // namespace anonymous HEJ::Event::EventData to_EventData(PhaseSpacePoint psp){ //! @TODO Same function already in HEJ HEJ::Event::EventData result; result.incoming = std::move(psp).incoming_; assert(result.incoming.size() == 2); result.outgoing = std::move(psp).outgoing_; // technically Event::EventData doesn't have to be sorted, // but PhaseSpacePoint should be anyway assert( std::is_sorted( begin(result.outgoing), end(result.outgoing), HEJ::rapidity_less{} ) ); assert(result.outgoing.size() >= 2); result.decays = std::move(psp).decays_; result.parameters.central = {NaN, NaN, psp.weight()}; return result; } namespace{ bool can_swap_to_uno( HEJ::Particle const & p1, HEJ::Particle const & p2 ){ return is_parton(p1) && p1.type != pid::gluon && p2.type == pid::gluon; } size_t count_gluons(std::vector<Particle>::const_iterator first, std::vector<Particle>::const_iterator last){ return std::count_if(first, last, [](Particle const & p) {return p.type == pid::gluon;}); } /** assumes FKL configurations between first and last, * else there can be a quark in a non-extreme position * e.g. uno configuration gqg would pass */ bool can_change_to_qqx( std::vector<Particle>::const_iterator first, std::vector<Particle>::const_iterator last){ return 1 < count_gluons(first,last); } bool is_AWZ_proccess(Process const & proc){ return proc.boson && is_AWZ_boson(*proc.boson); } bool is_up_type(Particle const & part){ return HEJ::is_anyquark(part) && !(abs(part.type)%2); } bool is_down_type(Particle const & part){ return HEJ::is_anyquark(part) && abs(part.type)%2; } bool can_couple_to_W(Particle const & part, pid::ParticleID const W_id){ const int W_charge = W_id>0?1:-1; return abs(part.type)<5 && ( (W_charge*part.type > 0 && is_up_type(part)) || (W_charge*part.type < 0 && is_down_type(part)) ); } } void PhaseSpacePoint::maybe_turn_to_subl( double chance, unsigned int const channels, Process const & proc, HEJ::RNG & ran ){ if(proc.njets <= 2) return; assert(outgoing_.size() >= 2); // decide what kind of subleading process is allowed bool allow_uno = false; bool allow_strange = true; const size_t nout = outgoing_.size(); const bool can_be_uno_backward = (channels&Subleading::uno) && can_swap_to_uno(outgoing_[0], outgoing_[1]); const bool can_be_uno_forward = (channels&Subleading::uno) && can_swap_to_uno(outgoing_[nout-1], outgoing_[nout-2]); allow_uno = can_be_uno_backward || can_be_uno_forward; bool allow_qqx = (channels&Subleading::qqx) && can_change_to_qqx(outgoing_.cbegin(), outgoing_.cend()); if(is_AWZ_proccess(proc)) { if(std::none_of(outgoing_.cbegin(), outgoing_.cend(), [&proc](Particle const & p){ return can_couple_to_W(p, *proc.boson);})) { // enforce qqx if A/W/Z can't couple somewhere else assert(allow_qqx); allow_uno = false; chance = 1.; // strange not allowed for W if(abs(*proc.boson)== pid::Wp) allow_strange = false; } } if(!allow_uno && !allow_qqx) return; if(ran.flat() < chance){ weight_ /= chance; if(allow_uno && !allow_qqx){ turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran); } else if (!allow_uno && allow_qqx) { turn_to_qqx(allow_strange, ran); } else { assert( allow_uno && allow_qqx); if(ran.flat() < 0.5) turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran); else turn_to_qqx(allow_strange, ran); weight_ *= 2.; } } else weight_ /= 1 - chance; } void PhaseSpacePoint::turn_to_uno( const bool can_be_uno_backward, const bool can_be_uno_forward, HEJ::RNG & ran ){ if(!can_be_uno_backward && !can_be_uno_forward) return; const size_t nout = outgoing_.size(); if(can_be_uno_backward && can_be_uno_forward){ if(ran.flat() < 0.5){ std::swap(outgoing_[0].type, outgoing_[1].type); } else { std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type); } weight_ *= 2.; } else if(can_be_uno_backward){ std::swap(outgoing_[0].type, outgoing_[1].type); } else { assert(can_be_uno_forward); std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type); } } void PhaseSpacePoint::turn_to_qqx(const bool allow_strange, HEJ::RNG & ran){ /// find first and last gluon in FKL chain auto first = std::find_if(outgoing_.begin(), outgoing_.end(), [](Particle const & p){return p.type == pid::gluon;}); std::vector<Particle*> FKL_gluons; for(auto p = first; p!=outgoing_.end(); ++p){ if(p->type == pid::gluon) FKL_gluons.push_back(&*p); else if(is_anyquark(*p)) break; } const size_t ng = FKL_gluons.size(); if(ng < 2) throw std::logic_error("not enough gluons to create qqx"); // select flavour of quark const double r1 = 2.*ran.flat()-1.; const double max_flavour = allow_strange?n_f:n_f-1; weight_ *= max_flavour*2; int flavour = pid::down + std::floor(std::abs(r1)*max_flavour); flavour*=r1<0.?-1:1; // select gluon for switch const size_t idx = floor((ng-1) * ran.flat()); weight_ *= (ng-1); FKL_gluons[idx]->type = ParticleID(flavour); FKL_gluons[idx+1]->type = ParticleID(-flavour); } template<class ParticleMomenta> fastjet::PseudoJet PhaseSpacePoint::gen_last_momentum( ParticleMomenta const & other_momenta, const double mass_square, const double y ) const { std::array<double,2> pt{0.,0.}; for (auto const & p: other_momenta) { pt[0]-= p.px(); pt[1]-= p.py(); } const double mperp = sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square); const double pz=mperp*sinh(y); const double E=mperp*cosh(y); return {pt[0], pt[1], pz, E}; } namespace { //! adds a particle to target (in correct rapidity ordering) //! @returns positon of insertion auto insert_particle(std::vector<HEJ::Particle> & target, HEJ::Particle && particle ){ const auto pos = std::upper_bound( begin(target),end(target),particle,rapidity_less{} ); target.insert(pos, std::move(particle)); return pos; } } PhaseSpacePoint::PhaseSpacePoint( Process const & proc, JetParameters const & jet_param, HEJ::PDF & pdf, double E_beam, double const subl_chance, unsigned int const subl_channels, ParticlesDecayMap const & particle_decays, HEJ::EWConstants const & ew_parameters, HEJ::RNG & ran ) { assert(proc.njets >= 2); status_ = good; weight_ = 1; const int nout = proc.njets + (proc.boson?1:0) + proc.boson_decay.size(); outgoing_.reserve(nout); // generate parton momenta const bool is_pure_jets = (nout == proc.njets); auto partons = gen_LO_partons( proc.njets, is_pure_jets, jet_param, E_beam, ran ); // pre fill flavour with gluons for(auto&& p_out: partons) { outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out), {}}); } if(status_ != good) return; if(proc.boson){ // decay boson const auto & boson_prop = ew_parameters.prop(*proc.boson) ; auto boson{ gen_boson(*proc.boson, boson_prop.mass, boson_prop.width, ran) }; const auto pos{insert_particle(outgoing_, std::move(boson))}; const size_t boson_idx = std::distance(begin(outgoing_), pos); const auto & boson_decay = particle_decays.find(*proc.boson); if( !proc.boson_decay.empty() ){ // decay given in proc decays_.emplace( boson_idx, decay_boson(outgoing_[boson_idx], proc.boson_decay, ran) ); } else if( boson_decay != particle_decays.end() && !boson_decay->second.empty() ){ // decay given explicitly decays_.emplace( boson_idx, decay_boson(outgoing_[boson_idx], boson_decay->second, ran) ); } } // normalisation of momentum-conserving delta function weight_ *= pow(2*M_PI, 4); /** @TODO * uf (jet_param.min_pt) doesn't correspond to our final scale choice. * The HEJ scale generators currently expect a full event as input, * so fixing this is not completely trivial */ reconstruct_incoming(proc, subl_channels, pdf, E_beam, jet_param.min_pt, ran); if(status_ != good) return; // set outgoing states most_backward_FKL(outgoing_).type = incoming_[0].type; most_forward_FKL(outgoing_).type = incoming_[1].type; maybe_turn_to_subl(subl_chance, subl_channels, proc, ran); if(proc.boson) couple_boson(*proc.boson, ran); } double PhaseSpacePoint::gen_hard_pt( int np , double ptmin, double ptmax, double y, HEJ::RNG & ran ) { // heuristic parameters for pt sampling const double ptpar = ptmin + np/5.; const double arg_small_y = atan((ptmax - ptmin)/ptpar); const double y_cut = 3.; const double r1 = ran.flat(); if(y < y_cut){ const double pt = ptmin + ptpar*tan(r1*arg_small_y); const double temp = cos(r1*arg_small_y); weight_ *= pt*ptpar*arg_small_y/(temp*temp); return pt; } const double ptpar2 = ptpar/(1 + 5*(y-y_cut)); const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2); const double pt = ptmin - ptpar2*std::log(1-r1*temp); weight_ *= pt*ptpar2*temp/(1-r1*temp); return pt; } double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, HEJ::RNG & ran) { constexpr double ptpar = 4.; const double r = ran.flat(); const double pt = max_pt + ptpar/np*std::log(r); weight_ *= pt*ptpar/(np*r); return pt; } double PhaseSpacePoint::gen_parton_pt( int count, JetParameters const & jet_param, double max_pt, double y, HEJ::RNG & ran ) { constexpr double p_small_pt = 0.02; if(! jet_param.peak_pt) { return gen_hard_pt(count, jet_param.min_pt, max_pt, y, ran); } const double r = ran.flat(); if(r > p_small_pt) { weight_ /= 1. - p_small_pt; return gen_hard_pt(count, *jet_param.peak_pt, max_pt, y, ran); } weight_ /= p_small_pt; const double pt = gen_soft_pt(count, *jet_param.peak_pt, ran); if(pt < jet_param.min_pt) { weight_=0.0; status_ = not_enough_jets; return jet_param.min_pt; } return pt; } std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_LO_partons( int np, bool is_pure_jets, JetParameters const & jet_param, double max_pt, HEJ::RNG & ran ){ if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"}; weight_ /= pow(16.*pow(M_PI,3),np); weight_ /= std::tgamma(np+1); //remove rapidity ordering std::vector<fastjet::PseudoJet> partons; partons.reserve(np); for(int i = 0; i < np; ++i){ const double y = -jet_param.max_y + 2*jet_param.max_y*ran.flat(); weight_ *= 2*jet_param.max_y; const bool is_last_parton = i+1 == np; if(is_pure_jets && is_last_parton) { constexpr double parton_mass_sq = 0.; partons.emplace_back(gen_last_momentum(partons, parton_mass_sq, y)); break; } const double phi = 2*M_PI*ran.flat(); weight_ *= 2.0*M_PI; const double pt = gen_parton_pt(np, jet_param, max_pt, y, ran); if(weight_ == 0.0) return {}; partons.emplace_back(fastjet::PtYPhiM(pt, y, phi)); assert(jet_param.min_pt <= partons[i].pt()); assert(partons[i].pt() <= max_pt+1e-5); } // Need to check that at LO, the number of jets = number of partons; fastjet::ClusterSequence cs(partons, jet_param.def); auto cluster_jets=cs.inclusive_jets(jet_param.min_pt); if (cluster_jets.size()!=unsigned(np)){ weight_=0.0; status_ = not_enough_jets; return {}; } std::sort(begin(partons), end(partons), rapidity_less{}); return partons; } Particle PhaseSpacePoint::gen_boson( HEJ::ParticleID bosonid, double mass, double width, HEJ::RNG & ran ){ // Usual phase space measure weight_ /= 16.*pow(M_PI, 3); // Generate a y Gaussian distributed around 0 /// @TODO check magic numbers for different boson Higgs /// @TODO better sampling for W const double stddev_y = 1.6; const double y = random_normal(stddev_y, ran); const double r1 = ran.flat(); const double s_boson = mass*( mass + width*tan(M_PI/2.*r1 + (r1-1.)*atan(mass/width)) ); // off-shell s_boson sampling, compensates for Breit-Wigner /// @TODO use a flag instead if(abs(bosonid) == pid::Wp){ weight_/=M_PI*M_PI*8.; weight_*= mass*width*( M_PI+2.*atan(mass/width) ) / ( 1. + cos( M_PI*r1 + 2.*(r1-1.)*atan(mass/width) ) ); } auto p = gen_last_momentum(outgoing_, s_boson, y); return Particle{bosonid, std::move(p), {}}; } Particle const & PhaseSpacePoint::most_backward_FKL( std::vector<Particle> const & partons ) const{ if(!HEJ::is_parton(partons[0])) return partons[1]; return partons[0]; } Particle const & PhaseSpacePoint::most_forward_FKL( std::vector<Particle> const & partons ) const{ const size_t last_idx = partons.size() - 1; if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1]; return partons[last_idx]; } Particle & PhaseSpacePoint::most_backward_FKL( std::vector<Particle> & partons ) const{ if(!HEJ::is_parton(partons[0])) return partons[1]; return partons[0]; } Particle & PhaseSpacePoint::most_forward_FKL( std::vector<Particle> & partons ) const{ const size_t last_idx = partons.size() - 1; if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1]; return partons[last_idx]; } namespace { /// partons are ordered: even = anti, 0 = gluon ParticleID index_to_pid(size_t i){ if(!i) return pid::gluon; return static_cast<ParticleID>( i%2 ? (i+1)/2 : -i/2 ); } /// partons are ordered: even = anti, 0 = gluon size_t pid_to_index(ParticleID id){ if(id==pid::gluon) return 0; return id>0 ? id*2-1 : abs(id)*2; } std::bitset<11> init_allowed(ParticleID const id){ if(abs(id) == pid::proton) return ~0; std::bitset<11> out{0}; if(is_parton(id)) out[pid_to_index(id)] = 1; return out; } /// decides which "index" (see index_to_pid) are allowed for process std::bitset<11> allowed_quarks(ParticleID const boson){ std::bitset<11> allowed = ~0; if(abs(boson) == pid::Wp){ // special case W: // Wp: anti-down or up-type quark, no b/t // Wm: down or anti-up-type quark, no b/t allowed = boson>0? 0b00011001101 :0b00100110011; } return allowed; } } /** * checks which partons are allowed as initial state: * 1. only allow what is given in the Runcard (p -> all) * 2. A/W/Z require something to couple to * a) no qqx => no incoming gluon * b) 2j => no incoming gluon * c) 3j => can couple OR is gluon => 2 gluons become qqx later */ std::array<std::bitset<11>,2> PhaseSpacePoint::filter_partons( Process const & proc, unsigned int const subl_channels, HEJ::RNG & ran ){ std::array<std::bitset<11>,2> allowed_partons{ init_allowed(proc.incoming[0]), init_allowed(proc.incoming[1]) }; bool const allow_qqx = subl_channels&Subleading::qqx; // special case A/W/Z if(is_AWZ_proccess(proc) && ((proc.njets < 4) || !allow_qqx)){ // all possible incoming states auto allowed(allowed_quarks(*proc.boson)); if(proc.njets == 2 || !allow_qqx) allowed[0]=0; // possible states per leg std::array<std::bitset<11>,2> const maybe_partons{ allowed_partons[0]&allowed, allowed_partons[1]&allowed}; if(maybe_partons[0].any() && maybe_partons[1].any()){ // two options to get allowed initial state => choose one at random const size_t idx = ran.flat() < 0.5; allowed_partons[idx] = maybe_partons[idx]; // else choose the possible } else if(maybe_partons[0].any()) { allowed_partons[0] = maybe_partons[0]; } else if(maybe_partons[1].any()) { allowed_partons[1] = maybe_partons[1]; } else{ throw std::invalid_argument{"Incoming state not allowed."}; } } return allowed_partons; } void PhaseSpacePoint::reconstruct_incoming( Process const & proc, unsigned int const subl_channels, HEJ::PDF & pdf, double E_beam, double uf, HEJ::RNG & ran ){ std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_); // calculate xa, xb const double sqrts=2*E_beam; const double xa=(incoming_[0].E()-incoming_[0].pz())/sqrts; const double xb=(incoming_[1].E()+incoming_[1].pz())/sqrts; // abort if phase space point is outside of collider energy reach if (xa>1. || xb>1.){ weight_=0; status_ = too_much_energy; return; } auto const & ids = proc.incoming; std::array<std::bitset<11>,2> allowed_partons( filter_partons(proc, subl_channels, ran)); for(size_t i = 0; i < 2; ++i){ if(ids[i] == pid::proton || ids[i] == pid::p_bar){ // pick ids according to pdfs incoming_[i].type = generate_incoming_id(i, i?xb:xa, uf, pdf, allowed_partons[i], ran); } else { assert(allowed_partons[i][pid_to_index(ids[i])]); incoming_[i].type = ids[i]; } } assert(momentum_conserved(1e-7)); } HEJ::ParticleID PhaseSpacePoint::generate_incoming_id( size_t const beam_idx, double const x, double const uf, HEJ::PDF & pdf, std::bitset<11> allowed_partons, HEJ::RNG & ran ){ std::array<double,11> pdf_wt; pdf_wt[0] = allowed_partons[0]?fabs(pdf.pdfpt(beam_idx,x,uf,pid::gluon)):0.; double pdftot = pdf_wt[0]; for(size_t i = 1; i < pdf_wt.size(); ++i){ pdf_wt[i] = allowed_partons[i]?4./9.*fabs(pdf.pdfpt(beam_idx,x,uf,index_to_pid(i))):0; pdftot += pdf_wt[i]; } const double r1 = pdftot * ran.flat(); double sum = 0; for(size_t i=0; i < pdf_wt.size(); ++i){ if (r1 < (sum+=pdf_wt[i])){ weight_*= pdftot/pdf_wt[i]; return index_to_pid(i); } } std::cerr << "Error in choosing incoming parton: "<<x<<" "<<uf<<" " <<sum<<" "<<pdftot<<" "<<r1<<std::endl; throw std::logic_error{"Failed to choose parton flavour"}; } void PhaseSpacePoint::couple_boson( HEJ::ParticleID const boson, HEJ::RNG & ran ){ if(abs(boson) != pid::Wp) return; // only matters for W /// @TODO this could be use to sanity check gamma and Z // find all possible quarks std::vector<Particle*> allowed_parts; for(auto & part: outgoing_){ // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom if ( can_couple_to_W(part, boson) ) allowed_parts.push_back(&part); } if(allowed_parts.size() == 0){ throw std::logic_error{"Found no parton for coupling with boson"}; } // select one and flip it size_t idx = 0; if(allowed_parts.size() > 1){ /// @TODO more efficient sampling /// old code: probability[i] = exp(parton[i].y - W.y) idx = floor(ran.flat()*allowed_parts.size()); weight_ *= allowed_parts.size(); } const int W_charge = boson>0?1:-1; allowed_parts[idx]->type = static_cast<ParticleID>( allowed_parts[idx]->type - W_charge ); } double PhaseSpacePoint::random_normal( double stddev, HEJ::RNG & ran ){ const double r1 = ran.flat(); const double r2 = ran.flat(); const double lninvr1 = -log(r1); const double result = stddev*sqrt(2.*lninvr1)*cos(2.*M_PI*r2); weight_ *= exp(result*result/(2*stddev*stddev))*sqrt(2.*M_PI)*stddev; return result; } bool PhaseSpacePoint::momentum_conserved(double ep) const{ fastjet::PseudoJet diff; for(auto const & in: incoming()) diff += in.p; for(auto const & out: outgoing()) diff -= out.p; return nearby_ep(diff, fastjet::PseudoJet{}, ep); } Decay PhaseSpacePoint::select_decay_channel( std::vector<Decay> const & decays, HEJ::RNG & ran ){ double br_total = 0.; for(auto const & decay: decays) br_total += decay.branching_ratio; // adjust weight // this is given by (channel branching ratio)/(chance to pick channel) // where (chance to pick channel) = // (channel branching ratio)/(total branching ratio) weight_ *= br_total; if(decays.size()==1) return decays.front(); const double r1 = br_total*ran.flat(); double br_sum = 0.; for(auto const & decay: decays){ br_sum += decay.branching_ratio; if(r1 < br_sum) return decay; } throw std::logic_error{"unreachable"}; } std::vector<Particle> PhaseSpacePoint::decay_boson( HEJ::Particle const & parent, std::vector<Decay> const & decays, HEJ::RNG & ran ){ const auto channel = select_decay_channel(decays, ran); return decay_boson(parent, channel.products, ran); } std::vector<Particle> PhaseSpacePoint::decay_boson( HEJ::Particle const & parent, std::vector<HEJ::ParticleID> const & decays, HEJ::RNG & ran ){ if(decays.size() != 2){ throw HEJ::not_implemented{ "only decays into two particles are implemented" }; } std::vector<Particle> decay_products(decays.size()); for(size_t i = 0; i < decays.size(); ++i){ decay_products[i].type = decays[i]; } // choose polar and azimuth angle in parent rest frame const double E = parent.m()/2; const double theta = 2.*M_PI*ran.flat(); const double cos_phi = 2.*ran.flat()-1.; const double sin_phi = sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi const double px = E*cos(theta)*sin_phi; const double py = E*sin(theta)*sin_phi; const double pz = E*cos_phi; decay_products[0].p.reset(px, py, pz, E); decay_products[1].p.reset(-px, -py, -pz, E); for(auto & particle: decay_products) particle.p.boost(parent.p); return decay_products; } } diff --git a/FixedOrderGen/src/config.cc b/FixedOrderGen/src/config.cc index 326bb73..0f51c49 100644 --- a/FixedOrderGen/src/config.cc +++ b/FixedOrderGen/src/config.cc @@ -1,432 +1,432 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "config.hh" #include <cctype> #include "Subleading.hh" #include "HEJ/Config.hh" #include "HEJ/YAMLreader.hh" namespace HEJFOG{ using HEJ::set_from_yaml; using HEJ::set_from_yaml_if_defined; using HEJ::pid::ParticleID; namespace{ //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "process", "events", "subleading fraction","subleading channels", "scales", "scale factors", "max scale ratio", "pdf", "vev", "event output", "analyses", "analysis", "import scales" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "peak pt", "algorithm", "R", "max rapidity"}){ supported["jets"][jet_opt] = ""; } for(auto && particle_type: {"Higgs", "W", "Z"}){ for(auto && particle_opt: {"mass", "width"}){ supported["particle properties"][particle_type][particle_opt] = ""; } } for(auto && particle_type: {"Higgs", "Wp", "W+", "Wm", "W-", "Z"}){ for(auto && particle_opt: {"into", "branching ratio"}){ supported["decays"][particle_type][particle_opt] = ""; } } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } for(auto && beam_opt: {"energy", "particles"}){ supported["beam"][beam_opt] = ""; } for(auto && unweight_opt: {"sample size", "max deviation"}){ supported["unweight"][unweight_opt] = ""; } for(auto && opt: {"name", "seed"}){ supported["random generator"][opt] = ""; } return supported; }(); return supported; } JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ const auto p = HEJ::get_jet_parameters(node, entry); JetParameters result; result.def = p.def; result.min_pt = p.min_pt; set_from_yaml(result.max_y, node, entry, "max rapidity"); set_from_yaml_if_defined(result.peak_pt, node, entry, "peak pt"); if(result.peak_pt && *result.peak_pt <= result.min_pt) throw std::invalid_argument{ "Value of option 'peak pt' has to be larger than 'min pt'." }; return result; } Beam get_Beam( YAML::Node const & node, std::string const & entry ){ Beam beam; std::vector<HEJ::ParticleID> particles; set_from_yaml(beam.energy, node, entry, "energy"); set_from_yaml_if_defined(particles, node, entry, "particles"); if(! particles.empty()){ for(HEJ::ParticleID particle: particles){ if(particle != HEJ::pid::p && particle != HEJ::pid::p_bar){ throw std::invalid_argument{ "Unsupported value in option " + entry + ": particles:" " only proton ('p') and antiproton ('p_bar') beams are supported" }; } } if(particles.size() != 2){ throw std::invalid_argument{"Not exactly two beam particles"}; } beam.particles.front() = particles.front(); beam.particles.back() = particles.back(); } return beam; } std::vector<std::string> split( std::string const & str, std::string const & delims ){ std::vector<std::string> result; for(size_t begin, end = 0; end != str.npos;){ begin = str.find_first_not_of(delims, end); if(begin == str.npos) break; end = str.find_first_of(delims, begin + 1); result.emplace_back(str.substr(begin, end - begin)); } return result; } std::invalid_argument invalid_incoming(std::string const & what){ return std::invalid_argument{ "Incoming particle type " + what + " not supported," " incoming particles have to be 'p', 'p_bar' or partons" }; } std::invalid_argument invalid_outgoing(std::string const & what){ return std::invalid_argument{ "Outgoing particle type " + what + " not supported," " outgoing particles have to be 'j', 'photon', 'H', 'Wm', 'Wp', 'e-', 'e+', 'nu_e', 'nu_e_bar'" }; } HEJ::ParticleID reconstruct_boson_id( std::vector<HEJ::ParticleID> const & ids ){ assert(ids.size()==2); const int pidsum = ids[0] + ids[1]; if(pidsum == +1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_antineutrino(ids[0])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_neutrino(ids[1])); // charged antilepton + neutrino means we had a W+ return HEJ::pid::Wp; } if(pidsum == -1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_neutrino(ids[1])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_antineutrino(ids[0])); // charged lepton + antineutrino means we had a W- return HEJ::pid::Wm; } throw HEJ::not_implemented{ "final state with leptons "+HEJ::name(ids[0])+" and "+HEJ::name(ids[1]) +" not supported" }; } Process get_process( YAML::Node const & node, std::string const & entry ){ Process result; std::string process_string; set_from_yaml(process_string, node, entry); assert(! process_string.empty()); const auto particles = split(process_string, " \n\t\v=>"); if(particles.size() < 3){ throw std::invalid_argument{ "Bad format in option process: '" + process_string + "', expected format is 'in1 in2 => out1 ...'" }; } result.incoming.front() = HEJ::to_ParticleID(particles[0]); result.incoming.back() = HEJ::to_ParticleID(particles[1]); for(size_t i = 0; i < result.incoming.size(); ++i){ const HEJ::ParticleID in = result.incoming[i]; if( in != HEJ::pid::proton && in != HEJ::pid::p_bar && !HEJ::is_parton(in) ){ throw invalid_incoming(particles[i]); } } result.njets = 0; for(size_t i = result.incoming.size(); i < particles.size(); ++i){ assert(! particles[i].empty()); if(particles[i] == "j") ++result.njets; else if(std::isdigit(particles[i].front()) && particles[i].back() == 'j') result.njets += std::stoi(particles[i]); else{ const auto pid = HEJ::to_ParticleID(particles[i]); if(pid==HEJ::pid::Higgs || pid==HEJ::pid::Wp || pid==HEJ::pid::Wm){ if(result.boson) throw std::invalid_argument{ "More than one outgoing boson is not supported" }; if(!result.boson_decay.empty()) throw std::invalid_argument{ "Production of a boson together with a lepton is not supported" }; result.boson = pid; } else if (HEJ::is_anylepton(pid)){ // Do not accept more leptons, if two leptons are already mentioned if( result.boson_decay.size()>=2 ) throw std::invalid_argument{"Too many leptons provided"}; if(result.boson) throw std::invalid_argument{ "Production of a lepton together with a boson is not supported" }; result.boson_decay.emplace_back(pid); } else { throw invalid_outgoing(particles[i]); } } } if(result.njets < 2){ throw std::invalid_argument{ "Process has to include at least two jets ('j')" }; } if(!result.boson_decay.empty()){ std::sort(begin(result.boson_decay),end(result.boson_decay)); assert(!result.boson); result.boson = reconstruct_boson_id(result.boson_decay); } return result; } HEJFOG::Subleading to_subleading_channel(YAML::Node const & yaml){ std::string name; using namespace HEJFOG::channels; set_from_yaml(name, yaml); if(name == "none") return none; if(name == "all") return all; if(name == "unordered" || name == "uno") return uno; if(name == "qqx") return qqx; throw HEJ::unknown_option("Unknown subleading channel '"+name+"'"); } unsigned int get_subleading_channels(YAML::Node const & node){ using YAML::NodeType; using namespace HEJFOG::channels; // all channels allowed by default if(!node) return all; switch(node.Type()){ case NodeType::Undefined: return all; case NodeType::Null: return none; case NodeType::Scalar: return to_subleading_channel(node); case NodeType::Map: throw HEJ::invalid_type{"map is not a valid option for subleading channels"}; case NodeType::Sequence: unsigned int channels = HEJFOG::Subleading::none; for(auto && channel_node: node){ channels |= get_subleading_channels(channel_node); } return channels; } throw std::logic_error{"unreachable"}; } Decay get_decay(YAML::Node const & node, std::string const & entry, std::string const & boson ){ Decay decay; set_from_yaml(decay.products, node, entry, boson, "into"); decay.branching_ratio=1; set_from_yaml_if_defined(decay.branching_ratio, node, entry, boson, "branching ratio"); return decay; } std::vector<Decay> get_decays(YAML::Node const & node, std::string const & entry, std::string const & boson ){ using YAML::NodeType; if(!node[entry][boson]) return {}; switch(node[entry][boson].Type()){ case NodeType::Null: case NodeType::Undefined: return {}; case NodeType::Scalar: throw HEJ::invalid_type{"value is not a list of decays"}; case NodeType::Map: return {get_decay(node, entry, boson)}; case NodeType::Sequence: std::vector<Decay> result; for(auto && decay_str: node[entry][boson]){ result.emplace_back(get_decay(decay_str, entry, boson)); } return result; } throw std::logic_error{"unreachable"}; } ParticlesDecayMap get_all_decays(YAML::Node const & node, std::string const & entry ){ if(!node[entry]) return {}; if(!node[entry].IsMap()) throw HEJ::invalid_type{entry + " have to be a map"}; ParticlesDecayMap result; for(auto const & sub_node: node[entry]) { const auto boson = sub_node.first.as<std::string>(); const auto id = HEJ::to_ParticleID(boson); result.emplace(id, get_decays(node, entry, boson)); } return result; } HEJ::ParticleProperties get_particle_properties( YAML::Node const & node, std::string const & entry, std::string const & boson ){ HEJ::ParticleProperties result; set_from_yaml(result.mass, node, entry, boson, "mass"); set_from_yaml(result.width, node, entry, boson, "width"); return result; } HEJ::EWConstants get_ew_parameters(YAML::Node const & node){ HEJ::EWConstants result; double vev; set_from_yaml(vev, node, "vev"); result.set_vevWZH(vev, get_particle_properties(node, "particle properties", "W"), get_particle_properties(node, "particle properties", "Z"), get_particle_properties(node, "particle properties", "Higgs") ); return result; } UnweightSettings get_unweight( YAML::Node const & node, std::string const & entry ){ UnweightSettings result; set_from_yaml(result.sample_size, node, entry, "sample size"); if(result.sample_size <= 0){ throw std::invalid_argument{ "negative sample size " + std::to_string(result.sample_size) }; } set_from_yaml(result.max_dev, node, entry, "max deviation"); return result; } Config to_Config(YAML::Node const & yaml){ try{ HEJ::assert_all_options_known(yaml, get_supported_options()); } catch(HEJ::unknown_option const & ex){ throw HEJ::unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.process = get_process(yaml, "process"); set_from_yaml(config.events, yaml, "events"); config.jets = get_jet_parameters(yaml, "jets"); config.beam = get_Beam(yaml, "beam"); for(size_t i = 0; i < config.process.incoming.size(); ++i){ const auto & in = config.process.incoming[i]; using namespace HEJ::pid; if( (in == p || in == p_bar) && in != config.beam.particles[i]){ throw std::invalid_argument{ "Particle type of beam " + std::to_string(i+1) + " incompatible" + " with type of incoming particle " + std::to_string(i+1) }; } } set_from_yaml(config.pdf_id, yaml, "pdf"); set_from_yaml(config.subleading_fraction, yaml, "subleading fraction"); if(config.subleading_fraction == 0) config.subleading_channels = Subleading::none; else config.subleading_channels = get_subleading_channels(yaml["subleading channels"]); config.ew_parameters = get_ew_parameters(yaml); config.particle_decays = get_all_decays(yaml, "decays"); if(config.process.boson // check that Ws always decay && std::abs(*config.process.boson) == HEJ::ParticleID::Wp && config.process.boson_decay.empty() ){ auto const & decay = config.particle_decays.find(*config.process.boson); if(decay == config.particle_decays.end() || decay->second.empty()) throw std::invalid_argument{ "Decay for "+HEJ::name(*config.process.boson)+" is required"}; } set_from_yaml_if_defined(config.analyses_parameters, yaml, "analyses"); if(yaml["analysis"]){ std::cerr << "WARNING: Configuration entry 'analysis' is deprecated. " " Use 'analyses' instead.\n"; YAML::Node ana; set_from_yaml(ana, yaml, "analysis"); if(!ana.IsNull()){ config.analyses_parameters.push_back(ana); } } config.scales = HEJ::to_ScaleConfig(yaml); set_from_yaml_if_defined(config.output, yaml, "event output"); config.rng = HEJ::to_RNGConfig(yaml, "random generator"); config.Higgs_coupling = HEJ::get_Higgs_coupling(yaml, "Higgs coupling"); if(yaml["unweight"]) config.unweight = get_unweight(yaml, "unweight"); return config; } } // namespace anonymous Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } } diff --git a/FixedOrderGen/src/main.cc b/FixedOrderGen/src/main.cc index aab69a3..4595f0c 100644 --- a/FixedOrderGen/src/main.cc +++ b/FixedOrderGen/src/main.cc @@ -1,277 +1,277 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <algorithm> #include <chrono> #include <fstream> #include <iostream> #include <map> #include <memory> #include <cstdint> #include "LHEF/LHEF.h" #include "yaml-cpp/yaml.h" #include <boost/iterator/filter_iterator.hpp> #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 "Version.hh" namespace{ constexpr auto banner = " __ ___ __ ______ __ " " __ \n / / / (_)___ _/ /_ / ____/___ " " ___ _________ ___ __ / /__ / /______ \n " " / /_/ / / __ `/ __ \\ / __/ / __ \\/ _ \\/ ___/ __ `/ / / / __ / / _" " \\/ __/ ___/ \n / __ / / /_/ / / / / / /___/ /" " / / __/ / / /_/ / /_/ / / /_/ / __/ /_(__ ) " " \n /_/ /_/_/\\__, /_/ /_/ /_____/_/ /_/\\___/_/ \\__, /\\__, / \\___" "_/\\___/\\__/____/ \n ____///__/ " "__ ____ ///__//____/ ______ __ " " \n / ____(_) _____ ____/ / / __ \\_________/ /__ _____ / " "____/__ ____ ___ _________ _/ /_____ _____\n / /_ / / |/_/ _ \\/ __" " / / / / / ___/ __ / _ \\/ ___/ / / __/ _ \\/ __ \\/ _ \\/ ___/ __ `/ " "__/ __ \\/ ___/\n / __/ / /> </ __/ /_/ / / /_/ / / / /_/ / __/ / " " / /_/ / __/ / / / __/ / / /_/ / /_/ /_/ / / \n /_/ /_/_/|_|\\___" "/\\__,_/ \\____/_/ \\__,_/\\___/_/ \\____/\\___/_/ /_/\\___/_/ " "\\__,_/\\__/\\____/_/ \n"; constexpr double invGeV2_to_pb = 389379292.; } 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::vector<std::unique_ptr<HEJ::Analysis>> get_analyses( std::vector<YAML::Node> 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); } } template<class Iterator> auto make_lowpt_filter(Iterator begin, Iterator end, HEJ::optional<double> peak_pt){ return boost::make_filter_iterator( [peak_pt](HEJ::Event const & ev){ assert(! ev.jets().empty()); double min_pt = peak_pt?(*peak_pt):0.; const auto softest_jet = fastjet::sorted_by_pt(ev.jets()).back(); return softest_jet.pt() > min_pt; }, begin, end ); } 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::shared_ptr<HEJ::RNG> 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 }; // prepare process information for output 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}; std::vector<std::unique_ptr<HEJ::Analysis>> analyses = get_analyses( config.analyses_parameters, heprup ); assert(analyses.empty() || analyses.front() != nullptr); // warm-up phase to train unweighter HEJ::optional<HEJ::Unweighter> unweighter{}; std::map<HEJFOG::Status, std::uint64_t> status_counter; std::vector<HEJ::Event> events; std::uint64_t trials = 0; 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) continue; const bool pass_cuts = analyses.empty() || std::any_of( begin(analyses), end(analyses), [&ev](auto const & analysis) { return analysis->pass_cuts(*ev, *ev); } ); if(pass_cuts) { events.emplace_back(std::move(*ev)); ++warmup_progress; } } std::cout << std::endl; unweighter = HEJ::Unweighter(); unweighter->set_cut_to_peakwt( make_lowpt_filter(events.cbegin(), events.cend(), config.jets.peak_pt), make_lowpt_filter(events.cend(), events.cend(), config.jets.peak_pt), config.unweight->max_dev ); std::vector<HEJ::Event> unweighted_events; for(auto && ev: events) { 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"; } // end unweighting warm-up // main generation loop // event weight is wrong, need to divide by "total number of trials" afterwards HEJ::ProgressBar<size_t> progress{std::cout, config.events}; progress.increment(events.size()); events.reserve(config.events); for(; events.size() < config.events; ++trials){ auto ev = generator.gen_event(); ++status_counter[generator.status()]; assert( (generator.status() == HEJFOG::good) == bool(ev) ); if(generator.status() != HEJFOG::good) continue; const bool pass_cuts = analyses.empty() || std::any_of( begin(analyses), end(analyses), [&ev](auto const & analysis) { return analysis->pass_cuts(*ev, *ev); } ); if(pass_cuts) { if(unweighter) { 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; // final run though events with correct weight HEJ::CrossSectionAccumulator xs; for(auto & ev: events){ ev.parameters() *= invGeV2_to_pb/trials; for(auto const & analysis: analyses) { if(analysis->pass_cuts(ev, ev)) { analysis->fill(ev, ev); } } writer.write(ev); xs.fill(ev); } for(auto const & analysis: analyses) { analysis->finalise(); } // Print final informations const std::chrono::duration<double> run_time = (clock::now() - start_time); std::cout << "\nTask Runtime: " << run_time.count() << " seconds for " << events.size() << " Events (" << events.size()/run_time.count() << " evts/s)\n" << std::endl; 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 << "%" << std::endl; } return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/2j.cc b/FixedOrderGen/t/2j.cc index 498e352..a95c0f6 100644 --- a/FixedOrderGen/t/2j.cc +++ b/FixedOrderGen/t/2j.cc @@ -1,67 +1,67 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cmath> #include <cassert> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Event.hh" #include "HEJ/PDF.hh" #include "HEJ/MatrixElement.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 86.42031848*1e6; //calculated with "combined" HEJ svn r3480 auto config = load_config("config_2j.yml"); std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Mixmax>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.01*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/4j.cc b/FixedOrderGen/t/4j.cc index 5ac5493..6329d71 100644 --- a/FixedOrderGen/t/4j.cc +++ b/FixedOrderGen/t/4j.cc @@ -1,70 +1,70 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cmath> #include <cassert> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Event.hh" #include "HEJ/PDF.hh" #include "HEJ/MatrixElement.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; // calculated with 13207b5f67a5f40a2141aa7ee515b022bd4efb65 constexpr double xs_ref = 915072; //+- 7227.72 auto config = load_config("config_2j.yml"); config.process.njets = 4; config.events *= 1.5; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Mixmax>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.04*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/W_reconstruct_enu.cc b/FixedOrderGen/t/W_reconstruct_enu.cc index 150e3c3..7b655ec 100644 --- a/FixedOrderGen/t/W_reconstruct_enu.cc +++ b/FixedOrderGen/t/W_reconstruct_enu.cc @@ -1,72 +1,72 @@ /** * \brief that the reconstruction of the W works * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Event.hh" #include "HEJ/Mixmax.hh" using namespace HEJFOG; using namespace HEJ; namespace { constexpr size_t num_events = 1000; constexpr double invGeV2_to_pb = 389379292.; } double get_xs(std::string config_name){ auto config { load_config(config_name) }; config.events = num_events; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Mixmax>(11)}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; } return xs; } int main(){ double xs_W{ get_xs("config_Wp_2j.yml")}; double xs_enu{ get_xs("config_Wp_2j_decay.yml")}; if(std::abs(xs_W/xs_enu-1.)>1e-6){ std::cerr << "Reconstructing the W in the runcard gave a different results (" << xs_W << " vs. "<< xs_enu << " -> " << std::abs(xs_W/xs_enu-1.)*100 << "%)\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_2j.cc b/FixedOrderGen/t/h_2j.cc index 84829b8..1a33407 100644 --- a/FixedOrderGen/t/h_2j.cc +++ b/FixedOrderGen/t/h_2j.cc @@ -1,75 +1,75 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cmath> #include <cassert> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Event.hh" #include "HEJ/PDF.hh" #include "HEJ/MatrixElement.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 2.04928; // +- 0.00377252 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j.yml"); std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Mixmax>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; const auto the_Higgs = std::find_if( begin(ev->outgoing()), end(ev->outgoing()), [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; } ); assert(the_Higgs != end(ev->outgoing())); if(std::abs(the_Higgs->rapidity()) > 5.) continue; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.01*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_2j_decay.cc b/FixedOrderGen/t/h_2j_decay.cc index 163e74d..72be5e4 100644 --- a/FixedOrderGen/t/h_2j_decay.cc +++ b/FixedOrderGen/t/h_2j_decay.cc @@ -1,94 +1,94 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cmath> #include <cassert> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Event.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/Particle.hh" #include "HEJ/PDF.hh" #include "HEJ/Ranlux64.hh" #include "HEJ/utility.hh" using namespace HEJFOG; bool pass_dR_cut( std::vector<fastjet::PseudoJet> const & jets, std::vector<HEJ::Particle> const & photons ){ constexpr double delta_R_min = 0.7; for(auto const & jet: jets){ for(auto const & photon: photons){ if(jet.delta_R(photon.p) < delta_R_min) return false; } } return true; } int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 0.00429198; // +- 1.0488e-05 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j_decay.yml"); std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Ranlux64>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); assert(ev->decays().size() == 1); const auto decay = begin(ev->decays()); assert(ev->outgoing().size() > decay->first); const auto & the_Higgs = ev->outgoing()[decay->first]; assert(the_Higgs.type == HEJ::pid::Higgs); assert(decay->second.size() == 2); auto const & gamma = decay->second; assert(gamma[0].type == HEJ::pid::photon); assert(gamma[1].type == HEJ::pid::photon); assert(HEJ::nearby_ep(gamma[0].p + gamma[1].p, the_Higgs.p, 1e-6)); if(!pass_dR_cut(ev->jets(), gamma)) continue; ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.012*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_3j.cc b/FixedOrderGen/t/h_3j.cc index 16a6670..7d32f28 100644 --- a/FixedOrderGen/t/h_3j.cc +++ b/FixedOrderGen/t/h_3j.cc @@ -1,76 +1,76 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cmath> #include <cassert> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Ranlux64.hh" #include "HEJ/Event.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/PDF.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 1.07807; // +- 0.0071 //calculated with HEJ revision 93efdc851b02a907a6fcc63956387f9f4c1111c2 +1 auto config = load_config("config_h_2j.yml"); config.process.njets = 3; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Ranlux64>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; const auto the_Higgs = std::find_if( begin(ev->outgoing()), end(ev->outgoing()), [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; } ); assert(the_Higgs != end(ev->outgoing())); if(std::abs(the_Higgs->rapidity()) > 5.) continue; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.02*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_3j_uno1.cc b/FixedOrderGen/t/h_3j_uno1.cc index 142107d..df5aae2 100644 --- a/FixedOrderGen/t/h_3j_uno1.cc +++ b/FixedOrderGen/t/h_3j_uno1.cc @@ -1,81 +1,81 @@ /** * check that adding uno emissions doesn't change the FKL cross section * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cassert> #include <cmath> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Ranlux64.hh" #include "Subleading.hh" #include "HEJ/Event.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/PDF.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 0.0243548; // +- 0.000119862 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j.yml"); config.process.njets = 3; config.process.incoming = {HEJ::pid::u, HEJ::pid::u}; config.subleading_channels = HEJFOG::Subleading::uno; config.events *= 1.5; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Ranlux64>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; size_t uno_found = 0; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); if(ev->type() != HEJ::event_type::FKL){ ++uno_found; continue; } ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << '\n'; std::cout << uno_found << " events with unordered emission" << std::endl; assert(uno_found > 0); assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.05*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_3j_uno2.cc b/FixedOrderGen/t/h_3j_uno2.cc index b93f1ad..13e1ae9 100644 --- a/FixedOrderGen/t/h_3j_uno2.cc +++ b/FixedOrderGen/t/h_3j_uno2.cc @@ -1,75 +1,75 @@ /** * check uno cross section * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cassert> #include <cmath> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Ranlux64.hh" #include "Subleading.hh" #include "HEJ/Event.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/PDF.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 0.00347538; // +- 3.85875e-05 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j.yml"); config.process.njets = 3; config.process.incoming = {HEJ::pid::u, HEJ::pid::u}; config.subleading_fraction = 1.; config.subleading_channels = HEJFOG::Subleading::uno; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Ranlux64>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); if(ev->type() == HEJ::event_type::FKL) continue; ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.05*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_5j.cc b/FixedOrderGen/t/h_5j.cc index 0e0948f..e852111 100644 --- a/FixedOrderGen/t/h_5j.cc +++ b/FixedOrderGen/t/h_5j.cc @@ -1,72 +1,72 @@ /** * This is a regression test * the reference cross section has not been checked against any other program * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include <algorithm> #include <cassert> #include <cmath> #include <iostream> #include "config.hh" #include "EventGenerator.hh" #include "HEJ/Ranlux64.hh" #include "HEJ/Event.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/PDF.hh" using namespace HEJFOG; int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 0.252273; // +- 0.00657742 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j.yml"); config.process.njets = 5; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Ranlux64>()}; HEJFOG::EventGenerator generator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; double xs = 0., xs_err = 0.; for (size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.06*xs); return EXIT_SUCCESS; } diff --git a/current_generator/jWuno_j.frm b/current_generator/jWuno_j.frm index e2148f0..13e4198 100644 --- a/current_generator/jWuno_j.frm +++ b/current_generator/jWuno_j.frm @@ -1,101 +1,101 @@ */** * \brief Contraction of W unordered current with FKL current * * TODO: unify conventions with developer manual * the current dictionary is as follows: * * code | manual * pg | p_1 * p1 | p_2 * pa | p_a * * \authors The HEJ collaboration (see AUTHORS for details) -* \date 2019 +* \date 2019-2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm s h,s2g,sbg,taW1; v p,p1,p2,pa,pb,pg,pl,plbar,pW,pr,q1,q1g,q2; i mu,nu,rho,sigma; cf m2inv; #do h1={+,-} * eq:U1tensor in developer manual, up to factors 1/sij, 1/tij l [U1 `h1'] = ( + Current(`h1'1, p1, nu, p1+pg, mu, pa-pW, rho, pa) + Current(`h1'1, p1, nu, p1+pg, rho, p1+pg+pW, mu, pa) + Current(`h1'1, p1, rho, p1+pW, nu, p1+pg+pW, mu, pa) ); * eq:U2tensor in developer manual, up to factors 1/sij, 1/tij l [U2 `h1'] = ( + Current(`h1'1, p1, mu, pa - pW - pg, nu, pa - pW, rho, pa) + Current(`h1'1, p1, mu, pa - pW - pg, rho, pa - pg, nu, pa) + Current(`h1'1, p1, rho, p1 + pW, mu, pa - pg, nu, pa) ); * eq:Ltensor in developer manual, up to factors 1/sij, 1/tij l [L `h1'] = ( Current(`h1'1, p1, sigma, pa - pW, rho, pa) + Current(`h1'1, p1, rho, p1 + pW, sigma, pa) )*( ((pb(nu)/sbg + p2(nu)/s2g)*m2(q1g) + 2*q1(nu) - pg(nu))*d_(mu, sigma) - 2*pg(mu)*d_(nu, sigma) + (2*pg(sigma) - q1(sigma))*d_(mu, nu) )/taW1; #enddo .sort * restore kinematic factors id Current(h?, p1, mu?, q1?, nu?, q2?, rho?, pa) = ( Current(h, p1, mu, q1, nu, q2, rho, pa)*m2inv(q1)*m2inv(q2) ); id Current(h?, p1, mu?, q1?, nu?, pa) = ( Current(h, p1, mu, q1, nu, pa)*m2inv(q1) ); .sort drop; * multiply with polarisation vector and other currents #do h1={+,-} #do h2={+,-} #do hg={+,-} #do TENSOR={U1,U2,L} l [`TENSOR' `h1'`h2'`hg'] = ( [`TENSOR' `h1'] *Eps(`hg'1, nu) *Current(`h2'1, p2, mu, pb) *Current(-1, pl, rho, plbar) ); #enddo #enddo #enddo #enddo * choice of best reference vector (p2 or pb) id Eps(h?, nu?)*Current(h?, p2, mu?, pb) = Eps(h, nu, pg, p2)*Current(h, p2, mu, pb); also Eps(h?, mu?) = Eps(h, mu, pg, pb); multiply replace_(q1g,q1-pg); multiply replace_(q1,pa-p1-pW); multiply replace_(pW,pl+plbar); .sort #call ContractCurrents multiply replace_( s2g,m2(p2+pg), sbg,m2(pb+pg), taW1,m2(pa-pl-plbar-p1) ); id m2inv(q1?) = 1/m2(q1); multiply replace_(pW,pl+plbar); .sort format O4; format c; #call WriteHeader(`OUTPUT') #call WriteOptimised(`OUTPUT',U1,3,p1,p2,pa,pb,pg,pl,plbar) #call WriteOptimised(`OUTPUT',U2,3,p1,p2,pa,pb,pg,pl,plbar) #call WriteOptimised(`OUTPUT',L,3,p1,p2,pa,pb,pg,pl,plbar) #call WriteFooter(`OUTPUT') .end diff --git a/include/HEJ/Config.hh b/include/HEJ/Config.hh index f9d1a7c..84fdaaf 100644 --- a/include/HEJ/Config.hh +++ b/include/HEJ/Config.hh @@ -1,233 +1,233 @@ /** \file * \brief HEJ 2 configuration parameters * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <map> #include <string> #include <vector> #include "fastjet/JetDefinition.hh" #include "yaml-cpp/yaml.h" #include "HEJ/Constants.hh" #include "HEJ/event_types.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Fraction.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/optional.hh" #include "HEJ/output_formats.hh" #include "HEJ/ScaleFunction.hh" namespace HEJ{ //! Jet parameters struct JetParameters{ fastjet::JetDefinition def; /**< Jet Definition */ double min_pt; /**< Minimum Jet Transverse Momentum */ }; //! Settings for scale variation struct ScaleConfig{ //! Base scale choices std::vector<ScaleFunction> base; //! Factors for multiplicative scale variation std::vector<double> factors; //! Maximum ratio between renormalisation and factorisation scale double max_ratio; }; //! Settings for random number generator struct RNGConfig { //! Random number generator name std::string name; //! Optional initial seed optional<std::string> seed; }; //! Settings for partial unweighting struct PartialUnweightConfig { //! Number of trials for training size_t trials; //! Maximum distance in standard deviations from mean logarithmic weight double max_dev; }; /**! Possible treatments for fixed-order input events. * * The program will decide on how to treat an event based on * the value of this enumeration. */ enum class EventTreatment{ reweight, /**< Perform resummation */ keep, /**< Keep the event */ discard, /**< Discard the event */ }; //! Container to store the treatments for various event types using EventTreatMap = std::map<event_type::EventType, EventTreatment>; //! Possible setting for the event weight enum class WeightType{ weighted, //!< weighted events unweighted_resum, //!< unweighted only resummation part partially_unweighted //!< mixed weighted and unweighted }; /**! Input parameters. * * This struct handles stores all configuration parameters * needed in a HEJ 2 run. * * \internal To add a new option: * 1. Add a member to the Config struct. * 2. Inside "src/YAMLreader.cc": * - Add the option name to the "supported" Node in * get_supported_options. * - Initialise the new Config member in to_Config. * The functions set_from_yaml (for mandatory options) and * set_from_yaml_if_defined (non-mandatory) may be helpful. * 3. Add a new entry (with short description) to config.yaml * 4. Update the user documentation in "doc/Sphinx/" */ struct Config { //! %Parameters for scale variation ScaleConfig scales; //! Resummation jet properties JetParameters resummation_jets; //! Fixed-order jet properties JetParameters fixed_order_jets; //! Minimum transverse momentum for extremal partons //! \deprecated This will be removed in future versions. //! Use \ref max_ext_soft_pt_fraction instead. double min_extparton_pt = 0; //! Maximum transverse momentum fraction from soft radiation in extremal jets Fraction<double> max_ext_soft_pt_fraction; //! The regulator lambda for the subtraction terms double regulator_lambda = CLAMBDA; //! Number of resummation configurations to generate per fixed-order event size_t trials; //! Maximal number of events optional<size_t> max_events; //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running bool log_correction; //! Event output files names and formats std::vector<OutputFile> output; //! Parameters for random number generation RNGConfig rng; //! Map to decide what to do for different event types EventTreatMap treat; //! %Parameters for custom analysis //! @deprecated use analyses_parameters instead YAML::Node analysis_parameters; //! %Parameters for custom analyses std::vector<YAML::Node> analyses_parameters; //! Settings for effective Higgs-gluon coupling HiggsCouplingSettings Higgs_coupling; //! elector weak parameters EWConstants ew_parameters; //! Type of event weight e.g. (un)weighted WeightType weight_type; //! Settings for partial unweighting HEJ::optional<PartialUnweightConfig> unweight_config; }; //! Configuration options for the PhaseSpacePoint class struct PhaseSpacePointConfig { //! Properties of resummation jets JetParameters jet_param; //! Minimum transverse momentum for extremal partons //! \deprecated This will be removed in future versions. //! Use \ref max_ext_soft_pt_fraction instead. double min_extparton_pt = 0; //! Maximum transverse momentum fraction from soft radiation in extremal jets Fraction<double> max_ext_soft_pt_fraction; }; //! Configuration options for the MatrixElement class struct MatrixElementConfig { MatrixElementConfig() = default; MatrixElementConfig( bool log_correction, HiggsCouplingSettings Higgs_coupling, EWConstants ew_parameters, double regulator_lambda = CLAMBDA ): log_correction{log_correction}, Higgs_coupling{Higgs_coupling}, ew_parameters{ew_parameters}, regulator_lambda{regulator_lambda} {} //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running bool log_correction; //! Settings for effective Higgs-gluon coupling HiggsCouplingSettings Higgs_coupling; //! elector weak parameters EWConstants ew_parameters; //! The regulator lambda for the subtraction terms double regulator_lambda = CLAMBDA; }; //! Configuration options for the EventReweighter class struct EventReweighterConfig { //! Settings for phase space point generation PhaseSpacePointConfig psp_config; //! Settings for matrix element calculation MatrixElementConfig ME_config; //! Access properties of resummation jets JetParameters & jet_param() { return psp_config.jet_param;} //! Access properties of resummation jets (const version) JetParameters const & jet_param() const { return psp_config.jet_param;} //! Treatment of the various event types EventTreatMap treat; }; /**! Extract PhaseSpacePointConfig from Config * * \internal We do not provide a PhaseSpacePointConfig constructor from Config * so that PhaseSpacePointConfig remains an aggregate. * This faciliates writing client code (e.g. the HEJ fixed-order generator) * that creates a PhaseSpacePointConfig *without* a Config object. * * @see to_MatrixElementConfig, to_EventReweighterConfig */ inline PhaseSpacePointConfig to_PhaseSpacePointConfig(Config const & conf) { return { conf.resummation_jets, conf.min_extparton_pt, conf.max_ext_soft_pt_fraction }; } /**! Extract MatrixElementConfig from Config * * @see to_PhaseSpacePointConfig, to_EventReweighterConfig */ inline MatrixElementConfig to_MatrixElementConfig(Config const & conf) { return {conf.log_correction, conf.Higgs_coupling, conf.ew_parameters, conf.regulator_lambda}; } /**! Extract EventReweighterConfig from Config * * @see to_PhaseSpacePointConfig, to_MatrixElementConfig */ inline EventReweighterConfig to_EventReweighterConfig(Config const & conf) { return { to_PhaseSpacePointConfig(conf), to_MatrixElementConfig(conf), conf.treat }; } } // namespace HEJ diff --git a/include/HEJ/CrossSectionAccumulator.hh b/include/HEJ/CrossSectionAccumulator.hh index c6b2fb0..c5ca18f 100644 --- a/include/HEJ/CrossSectionAccumulator.hh +++ b/include/HEJ/CrossSectionAccumulator.hh @@ -1,99 +1,99 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <iterator> #include <map> #include <ostream> #include <vector> #include "HEJ/event_types.hh" namespace HEJ { class Event; //! Collection of Cross Section with its uncertainty template<typename T> struct XSWithError { T value = T{}; //!< Cross Section T error = T{}; //!< Error }; /** * @brief Sum of Cross Section for different subproccess */ class CrossSectionAccumulator { public: //! Fill with single event //! @note for multiple resummation events use fill_correlated() instead void fill(Event const & ev); //! Fill by weight and type //! @note for multiple resummation events use fill_correlated() instead void fill(double weight, event_type::EventType type); //! Fill by weight, error and type //! @note The error will be _added_ to the current error void fill(double weight, double delta_error, event_type::EventType type); /** * @brief Fill with multiple correlated weights * @details This should be used to fill multiple reweighted events, * coming from the same fixed order point. * Total error for \f$N\f$ fixed order points each giving \f$M_i\f$ * resummation events is: * \f[ * \delta^2=\sum_i \left(\sum_j w_{i,j}\right)^2 * +\sum_{i,j} \left(w_{i,j}\right)^2, * \f] * @note This is equivalent to fill() for only one reweighted event * coming from each fixed order point (\f$M_i=1\f$) */ void fill_correlated(std::vector<Event> const & evts); //! iterator implementation of fill_correlated() template<class ConstIt> void fill_correlated(ConstIt begin, ConstIt end); //! explicit version of fill_correlated() by giving sum(wt) and sum(wt^2) void fill_correlated(double sum, double sum2, event_type::EventType type); //! begin of Cross Section and error for subprocesses auto begin() const { return std::begin(xs_); } //! end of Cross Section and error for subprocesses auto end() const { return std::end(xs_); } //! total Cross Section and error XSWithError<double> const & total() const { return total_; } private: std::map<HEJ::event_type::EventType, XSWithError<double>> xs_; XSWithError<double> total_; }; //! Print CrossSectionAccumulator to stream std::ostream& operator<<(std::ostream& os, const CrossSectionAccumulator& xs); // ------------ Implementation ------------ template<class ConstIt> void CrossSectionAccumulator::fill_correlated(ConstIt begin, ConstIt end){ if(std::distance(begin, end) < 2){ // only one event fill(*begin); return; } double sum = 0.; double sum2 = 0.; const auto type = begin->type(); for(; begin != end; ++begin){ double const wt = begin->central().weight; sum += wt; sum2 += wt*wt; } fill_correlated(sum, sum2, type); } } // namespace HEJ diff --git a/include/HEJ/EWConstants.hh b/include/HEJ/EWConstants.hh index 709bac5..6533e7d 100644 --- a/include/HEJ/EWConstants.hh +++ b/include/HEJ/EWConstants.hh @@ -1,90 +1,90 @@ /** \file * \brief Defines the electro weak parameters * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <cmath> #include "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" namespace HEJ { //! collection of basic particle properties struct ParticleProperties { double mass; //!< Mass double width; //!< Decay width }; //! Collection of electro-weak constants class EWConstants { public: EWConstants() = default; //! initialise by Vacuum expectation value & boson properties EWConstants( double vev, //!< vacuum expectation value ParticleProperties const & Wprop, //!< W boson mass & width ParticleProperties const & Zprop, //!< Z boson mass & width ParticleProperties const & Hprop //!< Higgs boson mass & width ): set{true}, vev_{vev}, Wprop_{Wprop}, Zprop_{Zprop}, Hprop_{Hprop} {} //! set constants by Vacuum expectation value & boson properties void set_vevWZH( double vev, //!< vacuum expectation value ParticleProperties const & Wprop, //!< W boson mass & width ParticleProperties const & Zprop, //!< Z boson mass & width ParticleProperties const & Hprop //!< Higgs boson mass & width ){ set = true; vev_= vev; Wprop_= Wprop; Zprop_= Zprop; Hprop_= Hprop; } //! vacuum expectation value double vev() const {check_set(); return vev_;} //! Properties of the W boson ParticleProperties const & Wprop() const {check_set(); return Wprop_;} //! Properties of the Z boson ParticleProperties const & Zprop() const {check_set(); return Zprop_;} //! Properties of the Higgs boson ParticleProperties const & Hprop() const {check_set(); return Hprop_;} //! access Properties by boson id ParticleProperties const & prop(ParticleID const id) const { using namespace pid; switch(id){ case Wp: case Wm: return Wprop(); case Z: return Zprop(); case h: return Hprop(); default: throw std::invalid_argument("No properties available for particle "+name(id)); } } //! cosine of Weinberg angle double cos_tw() const {return Wprop().mass/Zprop().mass;} //! cosine square of Weinberg angle double cos2_tw() const {return cos_tw()*cos_tw();} //! sinus Weinberg angle double sin_tw() const {return sqrt(sin2_tw());} //! sinus square of Weinberg angle double sin2_tw() const {return 1. - cos2_tw();} //! elector magnetic coupling double alpha_em() const {return e2()/4./M_PI;} //! weak coupling double alpha_w() const {return gw2()/2.;} private: double gw2() const {return 4*Wprop().mass/vev()*Wprop().mass/vev();} double e2() const {return gw2()*sin2_tw();} void check_set() const { if(!set) throw std::invalid_argument("EW constants not specified"); } bool set{false}; double vev_; ParticleProperties Wprop_; ParticleProperties Zprop_; ParticleProperties Hprop_; }; } // namespace HEJ diff --git a/include/HEJ/EmptyAnalysis.hh b/include/HEJ/EmptyAnalysis.hh index cbecbdf..7715a63 100644 --- a/include/HEJ/EmptyAnalysis.hh +++ b/include/HEJ/EmptyAnalysis.hh @@ -1,49 +1,49 @@ /** \file * \brief Declaration of the trivial (empty) analysis * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include "HEJ/Analysis.hh" //! YAML Namespace namespace YAML { class Node; } namespace HEJ { /** An analysis that does nothing * * This analysis is used by default if no user analysis is specified. * The member functions don't do anything and events passed to the * analysis are simply ignored. */ struct EmptyAnalysis: Analysis{ //! Create EmptyAnalysis static std::unique_ptr<Analysis> create( YAML::Node const & parameters, LHEF::HEPRUP const &); //! Fill event into analysis (e.g. to histograms) /** * This function does nothing */ virtual void fill(Event const &, Event const &) override; //! Whether a resummation event passes all cuts /** * There are no cuts, so all events pass */ virtual bool pass_cuts(Event const &, Event const &) override; //! Finalise analysis /** * This function does nothing */ virtual void finalise() override; virtual ~EmptyAnalysis() override = default; }; } // namespace HEJ diff --git a/include/HEJ/Event.hh b/include/HEJ/Event.hh index ebafcad..a15dbe4 100644 --- a/include/HEJ/Event.hh +++ b/include/HEJ/Event.hh @@ -1,369 +1,369 @@ /** \file * \brief Declares the Event class and helpers * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <array> #include <memory> #include <string> #include <unordered_map> #include <vector> #include "boost/iterator/filter_iterator.hpp" #include "fastjet/ClusterSequence.hh" #include "HEJ/event_types.hh" #include "HEJ/Parameters.hh" #include "HEJ/Particle.hh" #include "HEJ/RNG.hh" namespace LHEF { class HEPEUP; class HEPRUP; } namespace fastjet { class JetDefinition; } namespace HEJ { struct UnclusteredEvent; /** @brief An event with clustered jets * * This is the main HEJ 2 event class. * It contains kinematic information including jet clustering, * parameter (e.g. scale) settings and the event weight. */ class Event { public: class EventData; //! Iterator over partons using ConstPartonIterator = boost::filter_iterator< bool (*)(Particle const &), std::vector<Particle>::const_iterator >; //! Reverse Iterator over partons using ConstReversePartonIterator = std::reverse_iterator< ConstPartonIterator>; //! No default Constructor Event() = delete; //! Event Constructor adding jet clustering to an unclustered event //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0 [[deprecated("UnclusteredEvent will be replaced by EventData")]] Event( UnclusteredEvent const & ev, fastjet::JetDefinition const & jet_def, double min_jet_pt ); //! @name Particle Access //! @{ //! Incoming particles std::array<Particle, 2> const & incoming() const{ return incoming_; } //! Outgoing particles std::vector<Particle> const & outgoing() const{ return outgoing_; } //! Iterator to the first outgoing parton ConstPartonIterator begin_partons() const; //! Iterator to the first outgoing parton ConstPartonIterator cbegin_partons() const; //! Iterator to the end of the outgoing partons ConstPartonIterator end_partons() const; //! Iterator to the end of the outgoing partons ConstPartonIterator cend_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator rbegin_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator crbegin_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator rend_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator crend_partons() const; //! Particle decays /** * The key in the returned map corresponds to the index in the * vector returned by outgoing() */ std::unordered_map<size_t, std::vector<Particle>> const & decays() const{ return decays_; } //! The jets formed by the outgoing partons, sorted in rapidity std::vector<fastjet::PseudoJet> const & jets() const{ return jets_; } //! @} //! @name Weight variations //! @{ //! All chosen parameter, i.e. scale choices (const version) Parameters<EventParameters> const & parameters() const{ return parameters_; } //! All chosen parameter, i.e. scale choices Parameters<EventParameters> & parameters(){ return parameters_; } //! Central parameter choice (const version) EventParameters const & central() const{ return parameters_.central; } //! Central parameter choice EventParameters & central(){ return parameters_.central; } //! Parameter (scale) variations (const version) std::vector<EventParameters> const & variations() const{ return parameters_.variations; } //! Parameter (scale) variations std::vector<EventParameters> & variations(){ return parameters_.variations; } //! Parameter (scale) variation (const version) /** * @param i Index of the requested variation */ EventParameters const & variations(size_t i) const{ return parameters_.variations.at(i); } //! Parameter (scale) variation /** * @param i Index of the requested variation */ EventParameters & variations(size_t i){ return parameters_.variations.at(i); } //! @} //! Indices of the jets the outgoing partons belong to /** * @param jets Jets to be tested * @returns A vector containing, for each outgoing parton, * the index in the vector of jets the considered parton * belongs to. If the parton is not inside any of the * passed jets, the corresponding index is set to -1. */ std::vector<int> particle_jet_indices( std::vector<fastjet::PseudoJet> const & jets ) const { return cs_.particle_jet_indices(jets); } //! particle_jet_indices() of the Event jets() std::vector<int> particle_jet_indices() const { return particle_jet_indices(jets()); } //! Jet definition used for clustering fastjet::JetDefinition const & jet_def() const{ return cs_.jet_def(); } //! Minimum jet transverse momentum double min_jet_pt() const{ return min_jet_pt_; } //! Event type event_type::EventType type() const{ return type_; } //! Give colours to each particle /** * @returns true if new colours are generated, i.e. same as is_resummable() * @details Colour ordering is done according to leading colour in the MRK * limit, see \cite Andersen:2011zd. This only affects \ref * is_resummable() "HEJ" configurations, all other \ref event_type * "EventTypes" will be ignored. * @note This overwrites all previously set colours. */ bool generate_colours(HEJ::RNG &); //! Check that current colours are leading in the high energy limit /** * @details Checks that the colour configuration can be split up in * multiple, rapidity ordered, non-overlapping ladders. Such * configurations are leading in the MRK limit, see * \cite Andersen:2011zd * * @note This is _not_ to be confused with \ref is_resummable(), however * for all resummable states it is possible to create a leading colour * configuration, see generate_colours() */ bool is_leading_colour() const; /** * @brief Check if given event could have been produced by HEJ * @details A HEJ state has to fulfil: * 1. type() has to be \ref is_resummable() "resummable" * 2. Soft radiation in the tagging jets contributes at most to * `max_ext_soft_pt_fraction` of the total jet \f$ p_\perp \f$ * * @note This is true for any resummed stated produced by the * EventReweighter or any \ref is_resummable() "resummable" Leading * Order state. * * @param max_ext_soft_pt_fraction Maximum transverse momentum fraction from * soft radiation in extremal jets * @param min_extparton_pt Absolute minimal \f$ p_\perp \f$, * \b deprecated use max_ext_soft_pt_fraction * instead * @return True if this state could have been produced by HEJ */ bool valid_hej_state( double max_ext_soft_pt_fraction, double min_extparton_pt = 0.) const; private: //! \internal //! @brief Construct Event explicitly from input. /** This is only intended to be called from EventData. * * \warning The input is taken _as is_, sorting and classification has to be * done externally, i.e. by EventData */ Event( std::array<Particle, 2> && incoming, std::vector<Particle> && outgoing, std::unordered_map<size_t, std::vector<Particle>> && decays, Parameters<EventParameters> && parameters, fastjet::JetDefinition const & jet_def, double const min_jet_pt ); //! Iterator over partons (non-const) using PartonIterator = boost::filter_iterator< bool (*)(Particle const &), std::vector<Particle>::iterator >; //! Reverse Iterator over partons (non-const) using ReversePartonIterator = std::reverse_iterator<PartonIterator>; //! Iterator to the first outgoing parton (non-const) PartonIterator begin_partons(); //! Iterator to the end of the outgoing partons (non-const) PartonIterator end_partons(); //! Reverse Iterator to the first outgoing parton (non-const) ReversePartonIterator rbegin_partons(); //! Reverse Iterator to the first outgoing parton (non-const) ReversePartonIterator rend_partons(); std::array<Particle, 2> incoming_; std::vector<Particle> outgoing_; std::unordered_map<size_t, std::vector<Particle>> decays_; std::vector<fastjet::PseudoJet> jets_; Parameters<EventParameters> parameters_; fastjet::ClusterSequence cs_; double min_jet_pt_; event_type::EventType type_; }; // end class Event //! Class to store general Event setup, i.e. Phase space and weights class Event::EventData { public: //! Default Constructor EventData() = default; //! Constructor from LesHouches event information EventData(LHEF::HEPEUP const & hepeup); //! Constructor with all values given EventData( std::array<Particle, 2> incoming, std::vector<Particle> outgoing, std::unordered_map<size_t, std::vector<Particle>> decays, Parameters<EventParameters> parameters ): incoming(std::move(incoming)), outgoing(std::move(outgoing)), decays(std::move(decays)), parameters(std::move(parameters)) {} //! Generate an Event from the stored EventData. /** * @details Do jet clustering and classification. * Use this to generate an Event. * * @note Calling this function destroys EventData * * @param jet_def Jet definition * @param min_jet_pt minimal \f$p_T\f$ for each jet * * @returns Full clustered and classified event. */ Event cluster( fastjet::JetDefinition const & jet_def, double const min_jet_pt); //! Alias for cluster() Event operator()( fastjet::JetDefinition const & jet_def, double const min_jet_pt){ return cluster(jet_def, min_jet_pt); } //! Sort particles in rapidity void sort(); //! Reconstruct intermediate particles from final-state leptons /** * Final-state leptons are created from virtual photons, W, or Z bosons. * This function tries to reconstruct such intermediate bosons if they * are not part of the event record. */ void reconstruct_intermediate(); //! Incoming particles std::array<Particle, 2> incoming; //! Outcoing particles std::vector<Particle> outgoing; //! Particle decays in the format {outgoing index, decay products} std::unordered_map<size_t, std::vector<Particle>> decays; //! Parameters, e.g. scale or inital weight Parameters<EventParameters> parameters; }; // end class EventData //! Print Event std::ostream& operator<<(std::ostream & os, Event const & ev); //! Square of the partonic centre-of-mass energy \f$\hat{s}\f$ double shat(Event const & ev); //! Convert an event to a LHEF::HEPEUP LHEF::HEPEUP to_HEPEUP(Event const & event, LHEF::HEPRUP *); // put deprecated warning at the end, so don't get the warning inside Event.hh, // additionally doxygen can not identify [[deprecated]] correctly struct [[deprecated("UnclusteredEvent will be replaced by EventData")]] UnclusteredEvent; //! An event before jet clustering //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0 struct UnclusteredEvent{ //! Default Constructor UnclusteredEvent() = default; //! Constructor from LesHouches event information UnclusteredEvent(LHEF::HEPEUP const & hepeup); std::array<Particle, 2> incoming; /**< Incoming Particles */ std::vector<Particle> outgoing; /**< Outgoing Particles */ //! Particle decays in the format {outgoing index, decay products} std::unordered_map<size_t, std::vector<Particle>> decays; //! Central parameter (e.g. scale) choice EventParameters central; std::vector<EventParameters> variations; /**< For parameter variation */ }; } // namespace HEJ diff --git a/include/HEJ/EventReader.hh b/include/HEJ/EventReader.hh index e816a02..5a81752 100644 --- a/include/HEJ/EventReader.hh +++ b/include/HEJ/EventReader.hh @@ -1,57 +1,57 @@ /** \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 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include "LHEF/LHEF.h" #include "HEJ/optional.hh" namespace HEJ { class EventData; //! 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 HEJ::optional<size_t> number_events() const { size_t start = header().rfind("Number of Events"); start = header().find_first_of("123456789", start); if(start == std::string::npos) { return {}; } const size_t end = header().find_first_not_of("0123456789", start); return std::stoi(header().substr(start, end - start)); } 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<EventReader> make_reader(std::string const & filename); } // namespace HEJ diff --git a/include/HEJ/EventReweighter.hh b/include/HEJ/EventReweighter.hh index 969311d..59a69a1 100644 --- a/include/HEJ/EventReweighter.hh +++ b/include/HEJ/EventReweighter.hh @@ -1,196 +1,196 @@ /** \file * \brief Declares the EventReweighter class * * EventReweighter is the main class used within HEJ 2. It reweights the * resummation events. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <array> #include <memory> #include <vector> #include "HEJ/Config.hh" #include "HEJ/event_types.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/Parameters.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/StatusCode.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; class RNG; //! Beam parameters /** * Currently, only symmetric beams are supported, * so there is a single beam energy. */ struct Beam{ double E; /**< Beam energy */ std::array<ParticleID, 2> type; /**< Beam particles */ }; //! Main class for reweighting events in HEJ. class EventReweighter{ using EventType = event_type::EventType; public: EventReweighter( Beam beam, /**< Beam Energy */ int pdf_id, /**< PDF ID */ ScaleGenerator scale_gen, /**< Scale settings */ EventReweighterConfig conf, /**< Configuration parameters */ std::shared_ptr<RNG> ran /**< Random number generator */ ); EventReweighter( LHEF::HEPRUP const & heprup, /**< LHEF event header */ ScaleGenerator scale_gen, /**< Scale settings */ EventReweighterConfig conf, /**< Configuration parameters */ std::shared_ptr<RNG> ran /**< Random number generator */ ); //! Get the used pdf PDF const & pdf() const; //! Get event treatment EventTreatment treatment(EventType type) const; //! Generate resummation events for a given fixed-order event /** * @param ev Fixed-order event corresponding * to the resummation events * @param num_events Number of trial resummation configurations. * @returns A vector of resummation events. * * The result vector depends on the type of the input event and the * \ref EventTreatment of different types as specified in the constructor: * * - EventTreatment::reweight: The result vector contains between 0 and * num_events resummation events. * - EventTreatment::keep: If the input event passes the resummation * jet cuts the result vector contains one * event. Otherwise it is empty. * - EventTreatment::discard: The result vector is empty */ std::vector<Event> reweight( Event const & ev, size_t num_events ); //! Gives all StatusCodes of the last reweight() /** * Each StatusCode corresponds to one tried generation. Only good * StatusCodes generated an event. */ std::vector<StatusCode> const & status() const { return status_; } private: template<typename... T> PDF const & pdf(T&& ...); /** \internal * \brief main generation/reweighting function: * generate phase space points and divide out Born factors */ std::vector<Event> gen_res_events( Event const & ev, size_t num_events ); std::vector<Event> rescale( Event const & Born_ev, std::vector<Event> events ) const; /** \internal * \brief Do the Jets pass the resummation Cuts? * * @param ev Event in Question * @returns 0 or 1 depending on if ev passes Jet Cuts */ bool jets_pass_resummation_cuts(Event const & ev) const; /** \internal * \brief pdf_factors Function * * @param ev Event in Question * @returns EventFactor due to PDFs * * Calculates the Central value and the variation due * to the PDF choice made. */ Weights pdf_factors(Event const & ev) const; /** \internal * \brief matrix_elements Function * * @param ev Event in question * @returns EventFactor due to MatrixElements * * Calculates the Central value and the variation due * to the Matrix Element. */ Weights matrix_elements(Event const & ev) const; /** \internal * \brief Scale-dependent part of fixed-order matrix element * * @param ev Event in question * @returns EventFactor scale variation due to FO-ME. * * This is only called to compute the scale variation for events where * we don't do resummation (e.g. non-FKL). * Since at tree level the scale dependence is just due to alpha_s, * it is enough to return the alpha_s(mur) factors in the matrix element. * The rest drops out in the ratio of (output event ME)/(input event ME), * so we never have to compute it. */ Weights fixed_order_scale_ME(Event const & ev) const; /** \internal * \brief Computes the tree level matrix element * * @param ev Event in Question * @returns HEJ approximation to Tree level Matrix Element * * This computes the HEJ approximation to the tree level FO * Matrix element which is used within the LO weighting process. */ double tree_matrix_element(Event const & ev) const; //! \internal General parameters EventReweighterConfig param_; //! \internal Beam energy double E_beam_; //! \internal PDF PDF pdf_; //! \internal Object to calculate the square of the matrix element MatrixElement MEt2_; //! \internal Object to calculate event renormalisation and factorisation scales ScaleGenerator scale_gen_; //! \internal random number generator std::shared_ptr<RNG> ran_; //! \internal StatusCode of each attempt std::vector<StatusCode> status_; }; template<typename... T> PDF const & EventReweighter::pdf(T&&... t){ return pdf_ = PDF{std::forward<T>(t)...}; } } // namespace HEJ diff --git a/include/HEJ/HDF5Reader.hh b/include/HEJ/HDF5Reader.hh index 66efc75..5e8a29b 100644 --- a/include/HEJ/HDF5Reader.hh +++ b/include/HEJ/HDF5Reader.hh @@ -1,50 +1,50 @@ /** \file * \brief Header file for reading events in the HDF5 event format. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <string> #include "HEJ/EventReader.hh" namespace HEJ{ //! Class for reading events from a file in the HDF5 file format /** * @details This format is specified in \cite Hoeche:2019rti. */ class HDF5Reader : public EventReader{ public: HDF5Reader() = delete; //! Contruct object reading from the given file explicit HDF5Reader(std::string const & filename); //! Read an event bool read_event() override; //! Access header text std::string const & header() const override; //! Access run information LHEF::HEPRUP const & heprup() const override; //! Access last read event LHEF::HEPEUP const & hepeup() const override; //! Get number of events HEJ::optional<size_t> number_events() const override; ~HDF5Reader() override; private: struct HDF5ReaderImpl; std::unique_ptr<HDF5ReaderImpl> impl_; }; } // namespace HEJ diff --git a/include/HEJ/HDF5Writer.hh b/include/HEJ/HDF5Writer.hh index f74464c..917eed2 100644 --- a/include/HEJ/HDF5Writer.hh +++ b/include/HEJ/HDF5Writer.hh @@ -1,54 +1,54 @@ /** \file * \brief Contains the EventWriter for HDF5 Output. * * The output format is specified in arXiv:1905.05120. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include "HEJ/EventWriter.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This is an event writer specifically for HDF5 output. /** * \internal Implementation note: This uses the pimpl ("pointer to * implementation") idiom. HDF5 support is optional. Without pimpl, * we would have to specify whether HDF5 is available via the * preprocessor whenever this header is included. We don't want to * burden users of the HEJ library (for example the HEJ fixed-order * generator) with those details */ class HDF5Writer: public EventWriter{ public: //! Constructor /** * @param file name of the output file * @param heprup general process information */ HDF5Writer(std::string const & file, LHEF::HEPRUP heprup); HDF5Writer() = delete; //! Write an event to the output file void write(Event const & ev) override; ~HDF5Writer() override; private: struct HDF5WriterImpl; std::unique_ptr<HDF5WriterImpl> impl_; }; } // namespace HEJ diff --git a/include/HEJ/HepMC2Writer.hh b/include/HEJ/HepMC2Writer.hh index 7f2deae..004c08a 100644 --- a/include/HEJ/HepMC2Writer.hh +++ b/include/HEJ/HepMC2Writer.hh @@ -1,53 +1,53 @@ /** \file * \brief Contains the EventWriter for HepMC Output. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include "HEJ/EventWriter.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This is an event writer specifically for HepMC output. /** * \internal Implementation note: * This uses the pimpl ("pointer to implementation") idiom. * HepMC support is optional and the implementation depends on the * HepMC version. Without pimpl, we would have to specify the HepMC version * via the preprocessor whenever this header is included. We don't want to * burden users of the HEJ library (for example the HEJ fixed-order generator) * with those details */ class HepMC2Writer: public EventWriter{ public: //! Constructor /** * @param file name of the output file * @param heprup general process information */ HepMC2Writer(std::string const & file, LHEF::HEPRUP heprup); HepMC2Writer() = delete; //! Write an event to the output file void write(Event const & ev) override; ~HepMC2Writer() override; private: struct HepMC2WriterImpl; std::unique_ptr<HepMC2WriterImpl> impl_; }; } // namespace HEJ diff --git a/include/HEJ/HepMC3Writer.hh b/include/HEJ/HepMC3Writer.hh index e1d15bc..9fa6b53 100644 --- a/include/HEJ/HepMC3Writer.hh +++ b/include/HEJ/HepMC3Writer.hh @@ -1,52 +1,52 @@ /** \file * \brief Contains the EventWriter for HepMC3 Output. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include "HEJ/EventWriter.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This is an event writer specifically for HepMC3 output. /** * \internal Implementation note: * This uses the pimpl ("pointer to implementation") idiom. * HepMC3 support is optional and the implementation depends on the * HepMC3 version. Without pimpl, we would have to specify the HepMC3 version * via the preprocessor whenever this header is included. We don't want to * burden users of the HEJ library (for example the HEJ fixed-order generator) * with those details */ class HepMC3Writer: public EventWriter{ public: //! Constructor /** * @param file name of the output file * @param heprup general process information */ HepMC3Writer(std::string const & file, LHEF::HEPRUP heprup); HepMC3Writer() = delete; //! Write an event to the output file void write(Event const & ev) override; ~HepMC3Writer() override; private: struct HepMC3WriterImpl; std::unique_ptr<HepMC3WriterImpl> impl_; }; } // namespace HEJ diff --git a/include/HEJ/Hjets.hh b/include/HEJ/Hjets.hh index 5be9257..0a6a3b5 100644 --- a/include/HEJ/Hjets.hh +++ b/include/HEJ/Hjets.hh @@ -1,403 +1,403 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ /** \file * \brief Functions computing the square of current contractions in H+Jets. * * This file contains all the H+Jet specific components to compute * the current contractions for valid HEJ processes, to form a full * H+Jets ME, currently one would have to use functions from the * jets.hh header also. We have FKL and also unordered components for * H+Jets. * * @TODO add a namespace */ #pragma once #include "CLHEP/Vector/LorentzVector.h" typedef CLHEP::HepLorentzVector HLV; //! Square of gg->gg Higgs+Jets Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for gg->gg Scattering * * g~p1 g~p2 * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if g is backward, q1 is forward) */ double ME_H_gg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of gq->gq Higgs+Jets Scattering Current with Higgs before Gluon /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param pH Momentum of Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contraction */ double ME_Houtside_gq(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV pH, double mt, bool include_bottom, double mb, double vev); //! Square of qg->qg Higgs+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qg->qg Scattering * * q~p1 g~p2 (i.e. ALWAYS p1 for quark, p2 for gluon) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if g is backward, q1 is forward) */ double ME_H_qg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarg->qbarg Higgs+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarg->qbarg Scattering * * qbar~p1 g~p2 (i.e. ALWAYS p1 for anti-quark, p2 for gluon) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if g is backward, q1 is forward) */ double ME_H_qbarg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQ->qQ Higgs+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQ Scattering * * q~p1 Q~p2 (i.e. ALWAYS p1 for quark, p2 for quark) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if Q is backward, q1 is forward) */ double ME_H_qQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQbar->qQbar Higgs+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQ Scattering * * q~p1 Qbar~p2 (i.e. ALWAYS p1 for quark, p2 for anti-quark) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if Qbar is backward, q1 is forward) */ double ME_H_qQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarQ->qbarQ Higgs+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarQ->qbarQ Scattering * * qbar~p1 Q~p2 (i.e. ALWAYS p1 for anti-quark, p2 for quark) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if Q is backward, q1 is forward) */ double ME_H_qbarQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarQbar->qbarQbar Higgs+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param q1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarQbar->qbarQbar Scattering * * qbar~p1 Qbar~p2 (i.e. ALWAYS p1 for anti-quark, p2 for anti-quark) * should be called with q1 meant to be contracted with p2 in first part of vertex * (i.e. if Qbar is backward, q1 is forward) */ double ME_H_qbarQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! @name Unordered backwards //! @{ //! Square of qbarQ->qbarQg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarQ->qbarQg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_qbarQ(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQ->qQg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_qQ(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQbar->qQbarg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQbar->qQbarg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_qQbar(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarQbar->qbarQbarg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarQbar->qbarQbarg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_qbarQbar(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of gQbar->gQbarg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for gQbar->gQbarg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_gQbar(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! Square of gQ->gQg Higgs+Jets Unordered b Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for gQ->gQg Scattering * * This construction is taking rapidity order: p1out >> p2out > pg */ double ME_H_unob_gQ(HLV p1out, HLV p1in, HLV pg, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool include_bottom, double mb, double vev); //! @} //! @name impact factors for Higgs + jet //! @{ //! Implements Eq. (4.22) in \cite DelDuca:2003ba with modifications to incoming plus momenta /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns Value of Eq. (4.22) in \cite DelDuca:2003ba with modifications * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite */ double C2gHgm(HLV p2, HLV p1, HLV pH, double vev); //! Implements Eq. (4.23) in \cite DelDuca:2003ba with modifications to incoming plus momenta /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns Value of Eq. (4.23) in \cite DelDuca:2003ba * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite */ double C2gHgp(HLV p2, HLV p1, HLV pH, double vev); //! Implements Eq. (4.21) in \cite DelDuca:2003ba /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns Value of Eq. (4.22) in \cite DelDuca:2003ba * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite * * @TODO remove this function is not used */ double C2qHqm(HLV p2, HLV p1, HLV pH, double vev); //! @} diff --git a/include/HEJ/JetSplitter.hh b/include/HEJ/JetSplitter.hh index 2587de5..69a2985 100644 --- a/include/HEJ/JetSplitter.hh +++ b/include/HEJ/JetSplitter.hh @@ -1,71 +1,71 @@ /** * \file * \brief Declaration of the JetSplitter class * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <vector> #include "fastjet/JetDefinition.hh" namespace fastjet { class PseudoJet; } namespace HEJ { class RNG; //! Class to split jets into their constituents class JetSplitter { public: //! Wrapper for return values struct SplitResult { std::vector<fastjet::PseudoJet> constituents; double weight; }; //! Constructor /** * @param jet_def Jet definition * @param min_pt Minimum jet transverse momentum */ JetSplitter(fastjet::JetDefinition jet_def, double min_pt); //! Split a get into constituents /** * @param j2split Jet to be split * @param ncons Number of constituents * @param ran Random number generator * @returns The constituent momenta together with the associated * weight */ SplitResult split( fastjet::PseudoJet const & j2split, int ncons, RNG & ran ) const; //! Maximum distance of constituents to jet axis static constexpr double R_factor = 5./3.; private: //! \internal split jet into two partons SplitResult Split2(fastjet::PseudoJet const & j2split, RNG & ran) const; /** \internal * @brief sample y-phi distance to jet pt axis for a jet splitting into two * partons * * @param wt Multiplied by the weight of the sampling point * @param ran Random number generator * @returns The distance in units of the jet radius */ double sample_distance_2p(double & wt, RNG & ran) const; double min_jet_pt_; fastjet::JetDefinition jet_def_; }; } // namespace HEJ diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh index 2439b75..83732d3 100644 --- a/include/HEJ/LesHouchesReader.hh +++ b/include/HEJ/LesHouchesReader.hh @@ -1,83 +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 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <string> #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<size_t> number_events() const override { return num_events; } private: double num_trials; size_t num_events; }; } // namespace HEJ diff --git a/include/HEJ/LorentzVector.hh b/include/HEJ/LorentzVector.hh index c3b1bdb..7f460f8 100644 --- a/include/HEJ/LorentzVector.hh +++ b/include/HEJ/LorentzVector.hh @@ -1,55 +1,55 @@ /** \file * \brief Auxiliary functions for Lorentz vectors * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <complex> #include "CLHEP/Vector/LorentzVector.h" namespace HEJ { //! "dot" product inline auto dot( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ) { return pi.dot(pj); } //! "angle" product angle(pi, pj) = \<i j\> std::complex<double> angle( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ); //! "square" product square(pi, pj) = [i j] std::complex<double> square( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ); //! Invariant mass inline auto m2(CLHEP::HepLorentzVector const & h1) { return h1.m2(); } //! Split a single Lorentz vector into two lightlike Lorentz vectors /** * @param P Lorentz vector to be split * @returns A pair (p, q) of Lorentz vectors with P = p + q and p^2 = q^2 = 0 * * P.perp() has to be positive. * * p.e() is guaranteed to be positive. * In addition, if either of P.plus() or P.minus() is positive, * q.e() has the same sign as P.m2() */ std::pair<CLHEP::HepLorentzVector, CLHEP::HepLorentzVector> split_into_lightlike(CLHEP::HepLorentzVector const & P); } diff --git a/include/HEJ/Mixmax.hh b/include/HEJ/Mixmax.hh index 03d229f..029677e 100644 --- a/include/HEJ/Mixmax.hh +++ b/include/HEJ/Mixmax.hh @@ -1,36 +1,36 @@ /** \file * \brief The Mixmax random number generator * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include "CLHEP/Random/MixMaxRng.h" #include "CLHEP/Random/Randomize.h" #include "HEJ/RNG.hh" namespace HEJ { //! MIXMAX random number generator /** * For details on MIXMAX, see \cite Savvidy:2014ana */ class Mixmax : public DefaultRNG { public: Mixmax() = default; //! Constructor with explicit seed Mixmax(long seed): ran_{seed} {} //! Generate pseudorandom number between 0 and 1 double flat() override { return ran_.flat(); } private: CLHEP::MixMaxRng ran_; }; } diff --git a/include/HEJ/Parameters.hh b/include/HEJ/Parameters.hh index 84a640a..d10bed3 100644 --- a/include/HEJ/Parameters.hh +++ b/include/HEJ/Parameters.hh @@ -1,164 +1,164 @@ /** \file * \brief Containers for Parameter variations, e.g. different Weights * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include <vector> #include "HEJ/exceptions.hh" namespace HEJ{ //! Collection of parameters, e.g. Weights, assigned to a single event /** * A number of member functions of the MatrixElement class return Parameters * objects containing the squares of the matrix elements for the various * scale choices. */ template<class T> struct Parameters { T central; std::vector<T> variations; template<class T_ext> Parameters<T>& operator*=(Parameters<T_ext> const & other); Parameters<T>& operator*=(double factor); template<class T_ext> Parameters<T>& operator/=(Parameters<T_ext> const & other); Parameters<T>& operator/=(double factor); }; template<class T1, class T2> inline Parameters<T1> operator*(Parameters<T1> a, Parameters<T2> const & b) { a*=b; return a; } template<class T> inline Parameters<T> operator*(Parameters<T> a, double b) { a*=b; return a; } template<class T> inline Parameters<T> operator*(double b, Parameters<T> a) { a*=b; return a; } template<class T1, class T2> inline Parameters<T1> operator/(Parameters<T1> a, Parameters<T2> const & b) { a/=b; return a; } template<class T> inline Parameters<T> operator/(Parameters<T> a, double b) { a/=b; return a; } //! Alias for weight container, e.g. used by the MatrixElement using Weights = Parameters<double>; //! Description of event parameters, see also EventParameters struct ParameterDescription { //! Name of central scale choice (e.g. "H_T/2") std::string scale_name; //! Actual renormalisation scale divided by central scale double mur_factor; //! Actual factorisation scale divided by central scale double muf_factor; ParameterDescription() = default; ParameterDescription( std::string scale_name, double mur_factor, double muf_factor ): scale_name{std::move(scale_name)}, mur_factor{mur_factor}, muf_factor{muf_factor} {} }; //! generate human readable string name std::string to_string(ParameterDescription const & p); //! generate simplified string, intended for easy parsing //! Format: Scale_SCALENAME_MuRxx_MuFyy std::string to_simple_string(ParameterDescription const & p); //! Event parameters struct EventParameters{ double mur; /**< Value of the Renormalisation Scale */ double muf; /**< Value of the Factorisation Scale */ double weight; /**< Event Weight */ //! Optional description std::shared_ptr<ParameterDescription> description = nullptr; //! multiply weight by factor EventParameters& operator*=(double factor){ weight*=factor; return *this; } //! divide weight by factor EventParameters& operator/=(double factor){ weight/=factor; return *this; } }; inline EventParameters operator*(EventParameters a, double b){ a*=b; return a; } inline EventParameters operator*(double b, EventParameters a){ a*=b; return a; } inline EventParameters operator/(EventParameters a, double b){ a/=b; return a; } //! @{ //! @internal Implementation of template functions template<class T> template<class T_ext> Parameters<T>& Parameters<T>::operator*=(Parameters<T_ext> const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central *= other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] *= other.variations[i]; } return *this; } template<class T> Parameters<T>& Parameters<T>::operator*=(double factor) { central *= factor; for(auto & wt: variations) wt *= factor; return *this; } template<class T> template<class T_ext> Parameters<T>& Parameters<T>::operator/=(Parameters<T_ext> const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central /= other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] /= other.variations[i]; } return *this; } template<class T> Parameters<T>& Parameters<T>::operator/=(double factor) { central /= factor; for(auto & wt: variations) wt /= factor; return *this; } //! @} } // namespace HEJ diff --git a/include/HEJ/PhaseSpacePoint.hh b/include/HEJ/PhaseSpacePoint.hh index fb42718..0359e2b 100644 --- a/include/HEJ/PhaseSpacePoint.hh +++ b/include/HEJ/PhaseSpacePoint.hh @@ -1,202 +1,202 @@ /** \file * \brief Contains the PhaseSpacePoint Class * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <array> #include <unordered_map> #include <vector> #include "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/StatusCode.hh" namespace HEJ{ class RNG; //! Generated point in resummation phase space class PhaseSpacePoint{ public: //! No default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param ev Clustered Jet Event * @param conf Configuration parameters * @param ran Random number generator */ PhaseSpacePoint( Event const & ev, PhaseSpacePointConfig conf, RNG & ran ); //! Get phase space point weight double weight() const{ return weight_; } //! Access incoming particles std::array<Particle, 2> const & incoming() const{ return incoming_; } //! Access outgoing particles std::vector<Particle> const & outgoing() const{ return outgoing_; } //! Particle decays /** * The key in the returned map corresponds to the index in the * vector returned by outgoing() */ std::unordered_map<size_t, std::vector<Particle>> const & decays() const{ return decays_; } //! Status code of generation StatusCode status() const{ return status_; } static constexpr int ng_max = 1000; //!< maximum number of extra gluons private: friend Event::EventData to_EventData(PhaseSpacePoint psp); //! /internal returns the clustered jets sorted in rapidity std::vector<fastjet::PseudoJet> cluster_jets( std::vector<fastjet::PseudoJet> const & partons ) const; bool pass_resummation_cuts( std::vector<fastjet::PseudoJet> const & jets ) const; bool pass_extremal_cuts( fastjet::PseudoJet const & ext_parton, fastjet::PseudoJet const & jet ) const; int sample_ng(std::vector<fastjet::PseudoJet> const & Born_jets, RNG & ran); int sample_ng_jets( int ng, std::vector<fastjet::PseudoJet> const & Born_jets, RNG & ran ); double probability_in_jet( std::vector<fastjet::PseudoJet> const & Born_jets ) const; std::vector<fastjet::PseudoJet> gen_non_jet( int ng_non_jet, double ptmin, double ptmax, RNG & ran ); void rescale_qqx_rapidities( std::vector<fastjet::PseudoJet> & out_partons, std::vector<fastjet::PseudoJet> const & jets, const double ymin1, const double ymax2, const int qqxbackjet ); void rescale_rapidities( std::vector<fastjet::PseudoJet> & partons, double ymin, double ymax ); std::vector<fastjet::PseudoJet> reshuffle( std::vector<fastjet::PseudoJet> const & Born_jets, fastjet::PseudoJet const & q ); /** \interal final jet test * - number of jets must match Born kinematics * - no partons designated as nonjet may end up inside jets * - all other outgoing partons *must* end up inside jets * - the extremal (in rapidity) partons must be inside the extremal jets * - rapidities must be the same (by construction) */ bool jets_ok( std::vector<fastjet::PseudoJet> const & Born_jets, std::vector<fastjet::PseudoJet> const & partons ) const; void reconstruct_incoming(std::array<Particle, 2> const & Born_incoming); double phase_space_normalisation( int num_Born_jets, int num_res_partons ) const; /** \interal Distribute gluon in jet * @param jets jets to distribute gluon in * @param ng_jets number of gluons * @param qqxbackjet position of first (backwards) qqx jet * * relies on JetSplitter */ std::vector<fastjet::PseudoJet> split( std::vector<fastjet::PseudoJet> const & jets, int ng_jets, size_t qqxbackjet, RNG & ran ); std::vector<int> distribute_jet_partons( int ng_jets, std::vector<fastjet::PseudoJet> const & jets, RNG & ran ); std::vector<fastjet::PseudoJet> split( std::vector<fastjet::PseudoJet> const & jets, std::vector<int> const & np_in_jet, size_t qqxbackjet, RNG & ran ); bool split_preserved_jets( std::vector<fastjet::PseudoJet> const & jets, std::vector<fastjet::PseudoJet> const & jet_partons ) const; template<class Particle> Particle const & most_backward_FKL( std::vector<Particle> const & partons ) const; template<class Particle> Particle const & most_forward_FKL( std::vector<Particle> const & partons ) const; template<class Particle> Particle & most_backward_FKL(std::vector<Particle> & partons) const; template<class Particle> Particle & most_forward_FKL(std::vector<Particle> & partons) const; bool extremal_ok( std::vector<fastjet::PseudoJet> const & partons ) const; /** \internal * Assigns PDG IDs to outgoing partons, i.e. labels them as quarks * * \note This function assumes outgoing_ to be pure partonic when called, * i.e. A/W/Z/h bosons should _not be set_ at this stage */ void label_quarks(Event const & event); /** \internal * This function will label the qqx pair in a qqx event back to their * original types from the input event. */ void label_qqx(Event const & event); void copy_AWZH_boson_from(Event const & event); bool momentum_conserved() const; bool contains_idx( fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton ) const; bool unob_, unof_, qqxb_, qqxf_, qqxmid_; double weight_; PhaseSpacePointConfig param_; std::array<Particle, 2> incoming_; std::vector<Particle> outgoing_; //! \internal Particle decays in the format {outgoing index, decay products} std::unordered_map<size_t, std::vector<Particle>> decays_; StatusCode status_; }; //! Extract Event::EventData from PhaseSpacePoint Event::EventData to_EventData(PhaseSpacePoint psp); } // namespace HEJ diff --git a/include/HEJ/RNG.hh b/include/HEJ/RNG.hh index bd19749..63e4ceb 100644 --- a/include/HEJ/RNG.hh +++ b/include/HEJ/RNG.hh @@ -1,48 +1,48 @@ /** \file * \brief Interface for pseudorandom number generators * * We select our random number generator at runtime according to the * configuration file. This interface guarantees that we can use all * generators in the same way. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <limits> namespace HEJ { //! Interface for random number generator struct RNG { //! Random number type, see std::RandomNumberDistribution using result_type = unsigned; //! Generate random number in [0,1) virtual double flat() = 0; //! Minimum number that can be generated virtual result_type min() const = 0; //! Maximum number that can be generated virtual result_type max() const = 0; //! Generate random number in [min(), max()] virtual result_type operator()() = 0; virtual ~RNG() = default; }; //! Helper struct with default implementations struct DefaultRNG : virtual RNG { result_type min() const override { return 0u; } result_type max() const override { return std::numeric_limits<result_type>::max() - 1; } result_type operator()() override { return flat()*std::numeric_limits<result_type>::max(); } }; } // namespace HEJ diff --git a/include/HEJ/Ranlux64.hh b/include/HEJ/Ranlux64.hh index 50323d9..483ab07 100644 --- a/include/HEJ/Ranlux64.hh +++ b/include/HEJ/Ranlux64.hh @@ -1,35 +1,35 @@ /** \file * \brief Contains a class for the ranlux64 random number generator * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <string> #include "CLHEP/Random/Ranlux64Engine.h" #include "HEJ/RNG.hh" namespace HEJ { //! Ranlux64 random number generator /** * For details on ranlux64, see \cite Luscher:1993dy, \cite James:1993np */ class Ranlux64 : public DefaultRNG { public: Ranlux64(); //! Constructor with a file as seed Ranlux64(std::string const & seed_file); //! Generate pseudorandom number between 0 and 1 double flat() override; private: CLHEP::Ranlux64Engine ran_; }; } // namespace HEJ diff --git a/include/HEJ/RivetAnalysis.hh b/include/HEJ/RivetAnalysis.hh index 9570f92..9f28c78 100644 --- a/include/HEJ/RivetAnalysis.hh +++ b/include/HEJ/RivetAnalysis.hh @@ -1,70 +1,70 @@ /** \file * \brief HEJ 2 interface to rivet analyses * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include <vector> #include "LHEF/LHEF.h" #include "HEJ/Analysis.hh" #include "HEJ/optional.hh" namespace YAML { class Node; } namespace HEJ { /** * @brief Class representing a Rivet analysis * * This class inherits from Analysis and can therefore be used * like any other HEJ 2 analysis. */ class RivetAnalysis: public HEJ::Analysis { public: //! Create RivetAnalysis static std::unique_ptr<Analysis> create( YAML::Node const & config, LHEF::HEPRUP const & heprup); //! Constructor /** * @param config Configuration parameters * @param heprup General run informations * * config["rivet"] should be the name of a single Rivet analysis or * a list of Rivet analyses. config["output"] is the prefix for * the .yoda output files. */ RivetAnalysis(YAML::Node const & config, LHEF::HEPRUP const & heprup); ~RivetAnalysis() override; //! Pass an event to the underlying Rivet analysis void fill(HEJ::Event const & event, HEJ::Event const &) override; bool pass_cuts(HEJ::Event const &, HEJ::Event const &) override {return true;} //!< no additional cuts are applied void finalise() override; private: std::vector<std::string> analyses_names_; std::string output_name_; LHEF::HEPRUP heprup_; /// struct to organise the infos per rivet run/scale setting struct rivet_info; std::vector<rivet_info> rivet_runs_; /** * \internal * @brief Calculates the scale variation from the first event for the output * file */ void init(HEJ::Event const & event); bool first_event_; }; } // namespace HEJ diff --git a/include/HEJ/StatusCode.hh b/include/HEJ/StatusCode.hh index 03dd94d..bfeff84 100644 --- a/include/HEJ/StatusCode.hh +++ b/include/HEJ/StatusCode.hh @@ -1,47 +1,47 @@ /** \file * \brief Header file for status codes of event generation * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <string> #include "HEJ/exceptions.hh" namespace HEJ { //! Possible status codes from the event generation enum StatusCode{ good, discard, empty_jets, failed_reshuffle, failed_resummation_cuts, failed_split, too_much_energy, gluon_in_qqx, wrong_jets, unspecified // should never appear }; //! Get name of StatusCode //! @TODO better names inline std::string to_string(StatusCode s){ switch(s){ case good: return "good"; case discard: return "discard"; case empty_jets: return "empty jets"; case failed_reshuffle: return "failed reshuffle"; case failed_resummation_cuts: return "below cuts"; case failed_split: return "failed split"; case too_much_energy: return "too much energy"; case gluon_in_qqx: return "gluon inside qqx"; case wrong_jets: return "wrong jets"; case unspecified: return "unspecified"; default:{} } throw std::logic_error{"unreachable"}; } } // namespace HEJ diff --git a/include/HEJ/Unweighter.hh b/include/HEJ/Unweighter.hh index 1fea8f6..67f5ce6 100644 --- a/include/HEJ/Unweighter.hh +++ b/include/HEJ/Unweighter.hh @@ -1,84 +1,84 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <functional> #include <vector> #include "HEJ/optional.hh" namespace HEJ { class Event; class RNG; /** * @brief Unweight events * @details Throws away events below with abs(weight)<cut with probability * wt/cut */ class Unweighter { public: //! Constructor //! @param cut Initial value of cut, negative values for no cut 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_cut_to_maxwt(std::vector<Event> const & events){ set_cut_to_maxwt(events.cbegin(), events.cend()); } //! Iterator version of set_max() template<class ConstIt> void set_cut_to_maxwt(ConstIt begin, ConstIt end); //! Estimate some reasonable cut for partial unweighting /** * @param events Events used for the estimation * @param max_dev Standard derivation to include above mean weight */ void set_cut_to_peakwt(std::vector<Event> const & events, double max_dev){ set_cut_to_peakwt(events.cbegin(), events.cend(), max_dev); } //! Iterator version of set_cut_to_peakwt() template<class ConstIt> void set_cut_to_peakwt(ConstIt begin, ConstIt end, double max_dev); //! Returns 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; //! @brief Iterator implementation of unweight(), /** * Usage similar to std::remove(), i.e. use with erase() * * @return Beginning of "discarded" range */ template<class Iterator> Iterator unweight( Iterator begin, Iterator end, RNG & ran ) const; private: double cut_; //! Returns true if element can be removed/gets discarded //! directly corrects weight if is accepted (not removed) bool discard(RNG & ran, Event & ev) const; }; } // namespace HEJ // implementation of template functions #include "HEJ/detail/Unweighter_impl.hh" diff --git a/include/HEJ/YAMLreader.hh b/include/HEJ/YAMLreader.hh index c4b5641..1bb75d8 100644 --- a/include/HEJ/YAMLreader.hh +++ b/include/HEJ/YAMLreader.hh @@ -1,267 +1,267 @@ /** \file * \brief The file which handles the configuration file parameters * * The configuration files parameters are read and then stored * within this objects. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <string> #include <utility> #include <vector> #include "yaml-cpp/yaml.h" #include "fastjet/JetDefinition.hh" #include "HEJ/Config.hh" #include "HEJ/exceptions.hh" #include "HEJ/Fraction.hh" #include "HEJ/optional.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/utility.hh" namespace HEJ{ class OutputFile; //! Load configuration from file /** * @param config_file Name of the YAML configuration file * @returns The HEJ 2 configuration */ Config load_config(std::string const & config_file); //! Set option using the corresponding YAML entry /** * @param setting Option variable to be set * @param yaml Root of the YAML configuration * @param names Name of the entry * * If the entry does not exist or has the wrong type or format * an exception is thrown. * * For example * @code * set_from_yaml(foobar, yaml, "foo", "bar") * @endcode * is equivalent to * @code * foobar = yaml["foo"]["bar"].as<decltype(foobar)>() * @endcode * with improved diagnostics on errors. * * @see set_from_yaml_if_defined */ template<typename T, typename... YamlNames> void set_from_yaml( T & setting, YAML::Node const & yaml, YamlNames const & ... names ); //! Set option using the corresponding YAML entry, if present /** * @param setting Option variable to be set * @param yaml Root of the YAML configuration * @param names Name of the entry * * This function works similar to set_from_yaml, but does not * throw any exception if the requested YAML entry does not exist. * * @see set_from_yaml */ template<typename T, typename... YamlNames> void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, YamlNames const & ... names ); //! Extract jet parameters from YAML configuration JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ); //! Extract Higgs coupling settings from YAML configuration HiggsCouplingSettings get_Higgs_coupling( YAML::Node const & node, std::string const & entry ); //! Extract scale setting parameters from YAML configuration ScaleConfig to_ScaleConfig(YAML::Node const & yaml); //! Extract random number generator settings from YAML configuration RNGConfig to_RNGConfig(YAML::Node const & node, std::string const & entry); //! Check whether all options in configuration are supported /** * @param conf Configuration to be checked * @param supported Tree of supported options * * If conf contains an entry that does not appear in supported * an unknown_option exception is thrown. Sub-entries of "analysis" * (if present) are not checked. * * @see unknown_option */ void assert_all_options_known( YAML::Node const & conf, YAML::Node const & supported ); namespace detail{ void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml); void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml); void set_from_yaml(ParticleID & setting, YAML::Node const & yaml); void set_from_yaml(OutputFile & setting, YAML::Node const & yaml); void set_from_yaml(WeightType & setting, YAML::Node const & yaml); inline void set_from_yaml(YAML::Node & setting, YAML::Node const & yaml){ setting = yaml; } template<typename Scalar> void set_from_yaml(Scalar & setting, YAML::Node const & yaml){ assert(yaml); if(!yaml.IsScalar()){ throw invalid_type{"value is not a scalar"}; } try{ setting = yaml.as<Scalar>(); } catch(...){ throw invalid_type{ "value " + yaml.as<std::string>() + " cannot be converted to a " + type_string(setting) }; } } template<typename T> void set_from_yaml(optional<T> & setting, YAML::Node const & yaml){ T tmp; set_from_yaml(tmp, yaml); setting = tmp; } template<typename T> void set_from_yaml(std::vector<T> & setting, YAML::Node const & yaml){ assert(yaml); // special case: treat a single value like a vector with one element if(yaml.IsScalar()){ setting.resize(1); return set_from_yaml(setting.front(), yaml); } if(yaml.IsSequence()){ setting.resize(yaml.size()); for(size_t i = 0; i < setting.size(); ++i){ set_from_yaml(setting[i], yaml[i]); } return; } throw invalid_type{""}; } template<typename T, typename FirstName, typename... YamlNames> void set_from_yaml( T & setting, YAML::Node const & yaml, FirstName const & name, YamlNames && ... names ){ if(!yaml[name]) throw missing_option{""}; set_from_yaml( setting, yaml[name], std::forward<YamlNames>(names)... ); } template<typename T> void set_from_yaml_if_defined(T & setting, YAML::Node const & yaml){ return set_from_yaml(setting, yaml); } template<typename T, typename FirstName, typename... YamlNames> void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, FirstName const & name, YamlNames && ... names ){ if(!yaml[name]) return; set_from_yaml_if_defined( setting, yaml[name], std::forward<YamlNames>(names)... ); } } // namespace detail template<typename T, typename... YamlNames> void set_from_yaml( T & setting, YAML::Node const & yaml, YamlNames const & ... names ){ try{ detail::set_from_yaml(setting, yaml, names...); } catch(invalid_type const & ex){ throw invalid_type{ "In option " + join(": ", names...) + ": " + ex.what() }; } catch(missing_option const &){ throw missing_option{ "No entry for mandatory option " + join(": ", names...) }; } catch(std::invalid_argument const & ex){ throw missing_option{ "In option " + join(": ", names...) + ":" " invalid value " + ex.what() }; } } template<typename T, typename... YamlNames> void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, YamlNames const & ... names ){ try{ detail::set_from_yaml_if_defined(setting, yaml, names...); } catch(invalid_type const & ex){ throw invalid_type{ "In option " + join(": ", names...) + ": " + ex.what() }; } catch(std::invalid_argument const & ex){ throw missing_option{ "In option " + join(": ", names...) + ":" " invalid value " + ex.what() }; } } } // namespace HEJ namespace YAML { template<> struct convert<HEJ::OutputFile> { static Node encode(HEJ::OutputFile const & outfile); static bool decode(Node const & node, HEJ::OutputFile & out); }; template<class Real> struct convert<HEJ::Fraction<Real>> { static Node encode(HEJ::Fraction<Real> const & f) { return encode(Real{f}); } static bool decode(Node const & node, HEJ::Fraction<Real> & f) { Real r; if(!convert<Real>::decode(node, r)) return false; f = r; return true; } }; } diff --git a/include/HEJ/exceptions.hh b/include/HEJ/exceptions.hh index 29dab25..a63cd8f 100644 --- a/include/HEJ/exceptions.hh +++ b/include/HEJ/exceptions.hh @@ -1,58 +1,58 @@ /** \file * \brief Custom exception classes * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <stdexcept> #include <string> namespace HEJ { //! Exception indicating wrong option type /** * This exception is thrown if a configuration option has * the wrong type (e.g. 'trials' is not set to a number) */ struct invalid_type: std::invalid_argument { explicit invalid_type(std::string const & what): std::invalid_argument{what} {} explicit invalid_type(char const * what): std::invalid_argument{what} {} }; //! Exception indicating unknown option /** * This exception is thrown if an unknown configuration option * is set (e.g. the 'trials' setting is misspelt as 'trails') */ struct unknown_option: std::invalid_argument { explicit unknown_option(std::string const & what): std::invalid_argument{what} {} explicit unknown_option(char const * what): std::invalid_argument{what} {} }; //! Exception indicating missing option setting /** * This exception is thrown if a mandatory configuration option * (e.g. 'trials') is not set. */ struct missing_option: std::logic_error { explicit missing_option(std::string const & what): std::logic_error{what} {} explicit missing_option(char const * what): std::logic_error{what} {} }; //! Exception indicating functionality that has not been implemented yet struct not_implemented: std::logic_error { explicit not_implemented(std::string const & what): std::logic_error{what} {} explicit not_implemented(char const * what): std::logic_error{what} {} }; } // namespace HEJ diff --git a/include/HEJ/get_analysis.hh b/include/HEJ/get_analysis.hh index 69196d9..4d0d727 100644 --- a/include/HEJ/get_analysis.hh +++ b/include/HEJ/get_analysis.hh @@ -1,43 +1,43 @@ /** \file * \brief Contains the get_analysis function * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <vector> #include "HEJ/Analysis.hh" namespace YAML { class Node; } namespace HEJ { //! Load an analysis /** * @param parameters Analysis parameters * @param heprup General run informations * @returns A pointer to an Analysis instance * * If parameters["plugin"] exists, an analysis (deriving from the * \ref Analysis class) will be loaded from the library parameters["plugin"]. * Otherwise, if parameters["rivet"] exists, the corresponding RivetAnalysis * will be loaded. If none of these parameters are specified, a pointer to * the default EmptyAnalysis is returned. */ std::unique_ptr<Analysis> get_analysis( YAML::Node const & parameters, LHEF::HEPRUP const & heprup); //! Loads multiple analysis, vector version of get_analysis() /** * @param parameters Vector of Analysis parameters * @param heprup General run informations * @returns Vector of pointers to an Analysis instance */ std::vector<std::unique_ptr<Analysis>> get_analyses( std::vector<YAML::Node> const & parameters, LHEF::HEPRUP const & heprup); } diff --git a/include/HEJ/jets.hh b/include/HEJ/jets.hh index ab361c0..62d8591 100644 --- a/include/HEJ/jets.hh +++ b/include/HEJ/jets.hh @@ -1,403 +1,403 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ /** \file * \brief Functions computing the square of current contractions in pure jets. * * This file contains all the necessary functions to compute the * current contractions for all valid pure jet HEJ processes, which * so far is FKL and unordered processes. It will also contain some * pure jet ME components used in other process ME calculations * * @TODO add a namespace */ #pragma once #include <complex> #include <ostream> #include <vector> #include "CLHEP/Vector/LorentzVector.h" typedef std::complex<double> COM; typedef COM current[4]; typedef CLHEP::HepLorentzVector HLV; //! Square of qQ->qQ Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qQ->qQ Scattering */ double ME_qQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qQbar->qQbar Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qQbar->qQbar Scattering * * @note this can be used for qbarQ->qbarQ Scattering by inputting arguments * appropriately. */ double ME_qQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qbarQbar->qbarQbar Pure Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarQbar->qbarQbar Scattering */ double ME_qbarQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qg->qg Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for qg->qg Scattering * * @note this can be used for gq->gq Scattering by inputting arguments * appropriately. */ double ME_qg(HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qbarg->qbarg Pure Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for qbarg->qbarg Scattering * * @note this can be used for gqbar->gqbar Scattering by inputting arguments * appropriately. */ double ME_qbarg(HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of gg->gg Pure Jets Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for gg->gg Scattering */ double ME_gg(HLV p1out, HLV p1in, HLV p2out, HLV p2in); // Unordered Backwards contributions: //! Square of qQ->qQ Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qQ->qQ Scattering */ double ME_unob_qQ(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qbarQ->qbarQ Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qbarQ->qbarQ Scattering * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarQ(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qQbar->qQbar Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qQbar->qQbar Scattering * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qQbar(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qbarQbar->qbarQbar Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarQbar->qbarQbar Scattering * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarQbar(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qg->qg Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qg->qg Scattering * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qg(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of qbarg->qbarg Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarg->qbarg Scattering * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarg(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in); //! Square of gQ->qbarqQ Pure Jets Extremal qqx backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg * @returns Square of the current contractions for gQ->qbarqg Scattering * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_qbarqQ(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in); //! Square of gQ->qqbarQ Pure Jets Extremal qqx backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg * @returns Square of the current contractions for gQ->qqbarg Scattering * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_qqbarQ(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in); //! Square of gg->qbarqg Pure Jets Extremal qqx backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg * @returns Square of the current contractions for gg->qbarqg Scattering * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_qbarqg(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in); //! Square of gg->qqbarg Pure Jets Extremal qqx backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg * @returns Square of the current contractions for gg->qqbarg Scattering * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_qqbarg(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in); //! Square of qq->qQQbarq Pure Jets Central qqx Scattering Current /** * @param ka Momentum of incoming leg a * @param kb Momentum of incoming leg b * @param partons std::vector<HLV> outgoing partons * @param aqlinepa Is leg a an anti-quark? * @param aqlinepb Is leg b an anti-quark? * @param qqxmarker Is anti-quark further back in rapidity than quark (qqx pair) * @param nabove Number of gluons emitted above qqx pair (back in rap) * @returns Square of the current contractions for qq->qQQxq Scattering */ double ME_Cenqqx_qq(HLV ka, HLV kb, std::vector<HLV> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove); /** \class CCurrent jets.hh "include/HEJ/jets.hh" * \brief This is the a new class structure for currents. */ class CCurrent { public: CCurrent(COM sc0, COM sc1, COM sc2, COM sc3) :c0(sc0), c1(sc1), c2(sc2), c3(sc3) {} CCurrent(const HLV p) { c0=p.e(); c1=p.px(); c2=p.py(); c3=p.pz(); } CCurrent() {} CCurrent operator+(const CCurrent& other); CCurrent operator-(const CCurrent& other); CCurrent operator*(const double x); CCurrent operator*(const COM x); CCurrent operator/(const double x); CCurrent operator/(const COM x); friend std::ostream& operator<<(std::ostream& os, const CCurrent& cur); COM dot(HLV p1); COM dot(CCurrent p1); COM c0, c1, c2, c3; }; /* std::ostream& operator <<(std::ostream& os, const CCurrent& cur); */ CCurrent operator*(double x, CCurrent& m); CCurrent operator*(COM x, CCurrent& m); CCurrent operator/(double x, CCurrent& m); CCurrent operator/(COM x, CCurrent& m); //! Current <incoming state | mu | outgoing state> /** * This is a wrapper function around \see joi() note helicity flip to * give same answer. */ void jio(HLV pin, bool helin, HLV pout, bool helout, current &cur); //! Current <outgoing state | mu | outgoing state> /** * @param pi bra state momentum * @param heli helicity of pi * @param pj ket state momentum * @param helj helicity of pj. (must be same as heli) * @param cur reference to current which is saved. * * This function is for building <i (out)| mu |j (out)> currents. It * must be called with pi as the bra, and pj as the ket. * * @TODO Remove heli/helj and just have helicity of current as argument. */ void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur); //! Current <outgoing state | mu | incoming state> /** * @param pout bra state momentum * @param helout helicity of pout * @param pin ket state momentum * @param helin helicity of pin. (must be same as helout) * @param cur reference to current which is saved. * * This function is for building <out| mu |in> currents. It must be * called with pout as the bra, and pin as the ket. jio calls this * with flipped helicity * * @TODO Remove helout/helin and just have helicity of current as argument. */ void joi(HLV pout, bool helout, HLV pin, bool helin, current &cur); //! Current <outgoing state | mu | incoming state> /** * This is a wrapper function around the void function of the same name. \see joi * * @TODO This is never used */ CCurrent joi(HLV pout, bool helout, HLV pin, bool helin); //! Current <incoming state | mu | outgoing state> /** * This is a wrapper function around the void function of the same name. \see jio */ CCurrent jio(HLV pout, bool helout, HLV pin, bool helin); //! Current <outgoing state | mu | outgoing state> /** * This is a wrapper function around the void function of the same name. \see joo */ CCurrent joo(HLV pout, bool helout, HLV pin, bool helin); inline COM cdot(const current & j1, const current & j2) { return j1[0]*j2[0]-j1[1]*j2[1]-j1[2]*j2[2]-j1[3]*j2[3]; } inline COM cdot(const HLV & p, const current & j1) { return j1[0]*p.e()-j1[1]*p.x()-j1[2]*p.y()-j1[3]*p.z(); } inline void cmult(const COM & factor, const current & j1, current &cur) { cur[0]=factor*j1[0]; cur[1]=factor*j1[1]; cur[2]=factor*j1[2]; cur[3]=factor*j1[3]; } // WHY!?! inline void cadd(const current & j1, const current & j2, const current & j3, const current & j4, const current & j5, current &sum ) { sum[0]=j1[0]+j2[0]+j3[0]+j4[0]+j5[0]; sum[1]=j1[1]+j2[1]+j3[1]+j4[1]+j5[1]; sum[2]=j1[2]+j2[2]+j3[2]+j4[2]+j5[2]; sum[3]=j1[3]+j2[3]+j3[3]+j4[3]+j5[3]; } inline void cadd(const current & j1, const current & j2, const current & j3, const current & j4, current &sum ) { sum[0] = j1[0] + j2[0] + j3[0] + j4[0]; sum[1] = j1[1] + j2[1] + j3[1] + j4[1]; sum[2] = j1[2] + j2[2] + j3[2] + j4[2]; sum[3] = j1[3] + j2[3] + j3[3] + j4[3]; } inline void cadd(const current & j1, const current & j2, const current & j3, current &sum ) { sum[0]=j1[0]+j2[0]+j3[0]; sum[1]=j1[1]+j2[1]+j3[1]; sum[2]=j1[2]+j2[2]+j3[2]; sum[3]=j1[3]+j2[3]+j3[3]; } inline void cadd(const current & j1, const current & j2, current &sum) { sum[0]=j1[0]+j2[0]; sum[1]=j1[1]+j2[1]; sum[2]=j1[2]+j2[2]; sum[3]=j1[3]+j2[3]; } inline double abs2(const COM & a) { return (a*conj(a)).real(); } inline double vabs2(const CCurrent & cur) { return abs2(cur.c0)-abs2(cur.c1)-abs2(cur.c2)-abs2(cur.c3); } inline double vre(const CCurrent & a, const CCurrent & b) { return real(a.c0*conj(b.c0)-a.c1*conj(b.c1)-a.c2*conj(b.c2)-a.c3*conj(b.c3)); } //! @TODO These are not currents and should be moved elsewhere. double K_g(double p1minus, double paminus); double K_g(HLV const & pout, HLV const & pin); diff --git a/include/HEJ/make_RNG.hh b/include/HEJ/make_RNG.hh index 814b6c2..64a6559 100644 --- a/include/HEJ/make_RNG.hh +++ b/include/HEJ/make_RNG.hh @@ -1,32 +1,32 @@ /** \file * \brief Declares a factory function for random number generators * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include <memory> #include <string> #include "HEJ/optional.hh" #include "HEJ/RNG.hh" namespace HEJ { //! Factory function for random number generators /** * @param name Name of the random number generator * @param seed Optional seed * @returns A pointer to an instance of a random number generator * * At present, name should be one of "ranlux64" or "mixmax" (case insensitive). * The interpretation of the seed depends on the random number generator. * For ranlux64, it is the name of a seed file. For mixmax it should be a * string convertible to a long integer. */ std::unique_ptr<RNG> make_RNG( std::string const & name, optional<std::string> const & seed ); } diff --git a/src/Event.cc b/src/Event.cc index ec62998..b0a569d 100644 --- a/src/Event.cc +++ b/src/Event.cc @@ -1,1130 +1,1130 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/Event.hh" #include <algorithm> #include <assert.h> #include <iterator> #include <numeric> #include <unordered_set> #include <utility> #include "LHEF/LHEF.h" #include "fastjet/JetDefinition.hh" #include "HEJ/Constants.hh" #include "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" namespace HEJ{ namespace { constexpr int status_in = -1; constexpr int status_decayed = 2; constexpr int status_out = 1; //! true if leptonic W decay bool valid_W_decay( int const w_type, // sign of W std::vector<Particle> const & decays ){ if(decays.size() != 2) // no 1->2 decay return false; const int pidsum = decays[0].type + decays[1].type; if( std::abs(pidsum) != 1 || pidsum != w_type ) // correct charge return false; // leptonic decay (only check first, second follows from pidsum) if( w_type == 1 ) // W+ return is_antilepton(decays[0]) || is_neutrino(decays[0]); // W- return is_lepton(decays[0]) || is_antineutrino(decays[0]); } /// @name helper functions to determine event type //@{ /** * \brief check if final state valid for HEJ * * check if there is at most one photon, W, H, Z in the final state * and all the rest are quarks or gluons */ bool final_state_ok(Event const & ev){ std::vector<Particle> const & outgoing = ev.outgoing(); if(ev.decays().size() > 1) // at most one decay return false; bool has_AWZH_boson = false; for( size_t i=0; i<outgoing.size(); ++i ){ auto const & out{ outgoing[i] }; if(is_AWZH_boson(out.type)){ // at most one boson if(has_AWZH_boson) return false; has_AWZH_boson = true; // valid decay for W if(std::abs(out.type) == ParticleID::Wp){ // exactly 1 decay of W if( ev.decays().size() != 1 || ev.decays().cbegin()->first != i ) return false; if( !valid_W_decay(out.type>0?+1:-1, ev.decays().cbegin()->second) ) return false; } } else if(! is_parton(out.type)) return false; } return true; } /** * returns all EventTypes implemented in HEJ */ size_t implemented_types(std::vector<Particle> const & bosons){ using namespace event_type; if(bosons.empty()) return FKL | unob | unof | qqxexb | qqxexf | qqxmid; if(bosons.size()>1) return non_resummable; // multi boson switch (bosons[0].type) { case ParticleID::Wp: case ParticleID::Wm: return FKL | unob | unof | qqxexb | qqxexf | qqxmid; case ParticleID::h: return FKL | unob | unof; default: return non_resummable; } } /** * \brief function which determines if type change is consistent with Wp emission. * @param in incoming Particle id * @param out outgoing Particle id * @param qqx Current both incoming/both outgoing? * * \see is_Wm_Change */ bool is_Wp_Change(ParticleID in, ParticleID out, bool qqx){ if(!qqx && (in==-1 || in== 2 || in==-3 || in== 4)) return out== (in-1); if( qqx && (in== 1 || in==-2 || in== 3 || in==-4)) return out==-(in+1); return false; } /** * \brief function which determines if type change is consistent with Wm emission. * @param in incoming Particle id * @param out outgoing Particle id * @param qqx Current both incoming/both outgoing? * * Ensures that change type of quark line is possible by a flavour changing * Wm emission. Allows checking of qqx currents also. */ bool is_Wm_Change(ParticleID in, ParticleID out, bool qqx){ if(!qqx && (in== 1 || in==-2 || in== 3 || in==-4)) return out== (in+1); if( qqx && (in==-1 || in== 2 || in==-3 || in== 4)) return out==-(in-1); return false; } /** * \brief checks if particle type remains same from incoming to outgoing * @param in incoming Particle * @param out outgoing Particle * @param qqx Current both incoming/outgoing? */ bool no_flavour_change(ParticleID in, ParticleID out, bool qqx){ const int qqxCurrent = qqx?-1:1; if(abs(in)<=6 || in==pid::gluon) return (in==out*qqxCurrent); else return false; } bool has_2_jets(Event const & event){ return event.jets().size() >= 2; } /** * \brief check if we have a valid Impact factor * @param in incoming Particle * @param out outgoing Particle * @param qqx Current both incoming/outgoing? * @param W_change returns +1 if Wp, -1 if Wm, else 0 */ bool is_valid_impact_factor( ParticleID in, ParticleID out, bool qqx, int & W_change ){ if( no_flavour_change(in, out, qqx) ){ return true; } if( is_Wp_Change(in, out, qqx) ) { W_change+=1; return true; } if( is_Wm_Change(in, out, qqx) ) { W_change-=1; return true; } return false; } //! Returns all possible classifications from the impact factors // the beginning points are changed s.t. after the the classification they // point to the beginning of the (potential) FKL chain // sets W_change: + if Wp change // 0 if no change // - if Wm change // This function can be used with forward & backwards iterators template<class OutIterator> size_t possible_impact_factors( ParticleID incoming_id, // incoming OutIterator & begin_out, OutIterator const & end_out, // outgoing int & W_change, std::vector<Particle> const & boson, bool const backward // backward? ){ using namespace event_type; assert(boson.size() < 2); // keep track of all states that we don't test size_t not_tested = qqxmid; if(backward) not_tested |= unof | qqxexf; else not_tested |= unob | qqxexb; // Is this LL current? if( is_valid_impact_factor(incoming_id, begin_out->type, false, W_change) ){ ++begin_out; return not_tested | FKL; } // or NLL current? // -> needs two partons in two different jets if( std::distance(begin_out, end_out)>=2 ){ auto next = std::next(begin_out); // Is this unordered emisson? if( incoming_id!=pid::gluon && begin_out->type==pid::gluon ){ if( is_valid_impact_factor( incoming_id, next->type, false, W_change ) ){ // veto Higgs inside uno assert(next!=end_out); if( !boson.empty() && boson.front().type == ParticleID::h ){ if( (backward && boson.front().rapidity() < next->rapidity()) ||(!backward && boson.front().rapidity() > next->rapidity())) return non_resummable; } begin_out = std::next(next); return not_tested | (backward?unob:unof); } } // Is this QQbar? else if( incoming_id==pid::gluon ){ if( is_valid_impact_factor( begin_out->type, next->type, true, W_change ) ){ // veto Higgs inside qqx assert(next!=end_out); if( !boson.empty() && boson.front().type == ParticleID::h ){ if( (backward && boson.front().rapidity() < next->rapidity()) ||(!backward && boson.front().rapidity() > next->rapidity())) return non_resummable; } begin_out = std::next(next); return not_tested | (backward?qqxexb:qqxexf); } } } return non_resummable; } //! Returns all possible classifications from central emissions // the beginning points are changed s.t. after the the classification they // point to the end of the emission chain // sets W_change: + if Wp change // 0 if no change // - if Wm change template<class OutIterator> size_t possible_central( OutIterator & begin_out, OutIterator const & end_out, int & W_change, std::vector<Particle> const & boson ){ using namespace event_type; assert(boson.size() < 2); // if we already passed the central chain, // then it is not a valid all-order state if(std::distance(begin_out, end_out) < 0) return non_resummable; // keep track of all states that we don't test size_t possible = unob | unof | qqxexb | qqxexf; // Find the first non-gluon/non-FKL while( (begin_out->type==pid::gluon) && (begin_out!=end_out) ){ ++begin_out; } // end of chain -> FKL if( begin_out==end_out ){ return possible | FKL; } // is this a qqbar-pair? // needs two partons in two separate jets auto next = std::next(begin_out); if( is_valid_impact_factor( begin_out->type, next->type, true, W_change ) ){ // veto Higgs inside qqx if( !boson.empty() && boson.front().type == ParticleID::h && boson.front().rapidity() > begin_out->rapidity() && boson.front().rapidity() < next->rapidity() ){ return non_resummable; } begin_out = std::next(next); // remaining chain should be pure gluon/FKL for(; begin_out!=end_out; ++begin_out){ if(begin_out->type != pid::gluon) return non_resummable; } return possible | qqxmid; } return non_resummable; } /** * \brief Checks for all event types * @param ev Event * @returns Event Type * */ event_type::EventType classify(Event const & ev){ using namespace event_type; if(! has_2_jets(ev)) return no_2_jets; // currently we can't handle multiple boson states in the ME. So they are // considered "bad_final_state" even though the "classify" could work with // them. if(! final_state_ok(ev)) return bad_final_state; // initialise variables auto const & in = ev.incoming(); // range for current checks auto begin_out{ev.cbegin_partons()}; auto end_out{ev.crbegin_partons()}; assert(std::distance(begin(in), end(in)) == 2); assert(std::distance(begin_out, end_out.base()) >= 2); assert(std::is_sorted(begin_out, end_out.base(), rapidity_less{})); auto const boson{ filter_AWZH_bosons(ev.outgoing()) }; // we only allow one boson through final_state_ok assert(boson.size()<=1); // keep track of potential W couplings, at the end the sum should be 0 int remaining_Wp = 0; int remaining_Wm = 0; if(!boson.empty() && abs(boson.front().type) == ParticleID::Wp ){ if(boson.front().type>0) ++remaining_Wp; else ++remaining_Wm; } int W_change = 0; size_t final_type = ~(no_2_jets | bad_final_state); // check forward impact factor final_type &= possible_impact_factors( in.front().type, begin_out, end_out.base(), W_change, boson, true ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; W_change = 0; // check backward impact factor final_type &= possible_impact_factors( in.back().type, end_out, std::make_reverse_iterator(begin_out), W_change, boson, false ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; W_change = 0; // check central emissions final_type &= possible_central( begin_out, end_out.base(), W_change, boson ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; // Check whether the right number of Ws are present if( remaining_Wp != 0 || remaining_Wm != 0 ) return non_resummable; // result has to be unique if( (final_type & (final_type-1)) != 0) return non_resummable; // check that each sub processes is implemented // (has to be done at the end) if( (final_type & ~implemented_types(boson)) != 0 ) return non_resummable; return static_cast<EventType>(final_type); } //@} Particle extract_particle(LHEF::HEPEUP const & hepeup, size_t i){ const ParticleID id = static_cast<ParticleID>(hepeup.IDUP[i]); const fastjet::PseudoJet momentum{ hepeup.PUP[i][0], hepeup.PUP[i][1], hepeup.PUP[i][2], hepeup.PUP[i][3] }; if(is_parton(id)) return Particle{ id, std::move(momentum), hepeup.ICOLUP[i] }; return Particle{ id, std::move(momentum), {} }; } bool is_decay_product(std::pair<int, int> const & mothers){ if(mothers.first == 0) return false; return mothers.second == 0 || mothers.first == mothers.second; } } // namespace anonymous Event::EventData::EventData(LHEF::HEPEUP const & hepeup){ parameters.central = EventParameters{ hepeup.scales.mur, hepeup.scales.muf, hepeup.XWGTUP }; size_t in_idx = 0; for (int i = 0; i < hepeup.NUP; ++i) { // skip decay products // we will add them later on, but we have to ensure that // the decayed particle is added before if(is_decay_product(hepeup.MOTHUP[i])) continue; auto particle = extract_particle(hepeup, i); // needed to identify mother particles for decay products particle.p.set_user_index(i+1); if(hepeup.ISTUP[i] == status_in){ if(in_idx > incoming.size()) { throw std::invalid_argument{ "Event has too many incoming particles" }; } incoming[in_idx++] = std::move(particle); } else outgoing.emplace_back(std::move(particle)); } // add decay products for (int i = 0; i < hepeup.NUP; ++i) { if(!is_decay_product(hepeup.MOTHUP[i])) continue; const int mother_id = hepeup.MOTHUP[i].first; const auto mother = std::find_if( begin(outgoing), end(outgoing), [mother_id](Particle const & particle){ return particle.p.user_index() == mother_id; } ); if(mother == end(outgoing)){ throw std::invalid_argument{"invalid decay product parent"}; } const int mother_idx = std::distance(begin(outgoing), mother); assert(mother_idx >= 0); decays[mother_idx].emplace_back(extract_particle(hepeup, i)); } } Event::Event( UnclusteredEvent const & ev, fastjet::JetDefinition const & jet_def, double const min_jet_pt ): Event( Event::EventData{ ev.incoming, ev.outgoing, ev.decays, Parameters<EventParameters>{ev.central, ev.variations} }.cluster(jet_def, min_jet_pt) ) {} //! @TODO remove in HEJ 2.2.0 UnclusteredEvent::UnclusteredEvent(LHEF::HEPEUP const & hepeup){ Event::EventData const evData{hepeup}; incoming = evData.incoming; outgoing = evData.outgoing; decays = evData.decays; central = evData.parameters.central; variations = evData.parameters.variations; } void Event::EventData::sort(){ // sort particles std::sort( begin(incoming), end(incoming), [](Particle o1, Particle o2){return o1.p.pz()<o2.p.pz();} ); auto old_outgoing = std::move(outgoing); std::vector<size_t> idx(old_outgoing.size()); std::iota(idx.begin(), idx.end(), 0); std::sort(idx.begin(), idx.end(), [&old_outgoing](size_t i, size_t j){ return old_outgoing[i].rapidity() < old_outgoing[j].rapidity(); }); outgoing.clear(); outgoing.reserve(old_outgoing.size()); for(size_t i: idx) { outgoing.emplace_back(std::move(old_outgoing[i])); } // find decays again if(!decays.empty()){ auto old_decays = std::move(decays); decays.clear(); for(size_t i=0; i<idx.size(); ++i) { auto decay = old_decays.find(idx[i]); if(decay != old_decays.end()) decays.emplace(i, std::move(decay->second)); } assert(old_decays.size() == decays.size()); } } namespace { Particle reconstruct_boson(std::vector<Particle> const & leptons) { Particle decayed_boson; decayed_boson.p = leptons[0].p + leptons[1].p; const int pidsum = leptons[0].type + leptons[1].type; if(pidsum == +1) { assert(is_antilepton(leptons[0])); if(is_antineutrino(leptons[0])) { throw not_implemented{"lepton-flavour violating final state"}; } assert(is_neutrino(leptons[1])); // charged antilepton + neutrino means we had a W+ decayed_boson.type = pid::Wp; } else if(pidsum == -1) { assert(is_antilepton(leptons[0])); if(is_neutrino(leptons[1])) { throw not_implemented{"lepton-flavour violating final state"}; } assert(is_antineutrino(leptons[0])); // charged lepton + antineutrino means we had a W- decayed_boson.type = pid::Wm; } else { throw not_implemented{ "final state with leptons " + name(leptons[0].type) + " and " + name(leptons[1].type) }; } return decayed_boson; } } void Event::EventData::reconstruct_intermediate() { const auto begin_leptons = std::partition( begin(outgoing), end(outgoing), [](Particle const & p) {return !is_anylepton(p);} ); if(begin_leptons == end(outgoing)) return; assert(is_anylepton(*begin_leptons)); std::vector<Particle> leptons(begin_leptons, end(outgoing)); outgoing.erase(begin_leptons, end(outgoing)); if(leptons.size() != 2) { throw not_implemented{"Final states with one or more than two leptons"}; } std::sort( begin(leptons), end(leptons), [](Particle const & p0, Particle const & p1) { return p0.type < p1.type; } ); outgoing.emplace_back(reconstruct_boson(leptons)); decays.emplace(outgoing.size()-1, std::move(leptons)); } Event Event::EventData::cluster( fastjet::JetDefinition const & jet_def, double const min_jet_pt ){ sort(); Event ev{ std::move(incoming), std::move(outgoing), std::move(decays), std::move(parameters), jet_def, min_jet_pt }; assert(std::is_sorted(begin(ev.outgoing_), end(ev.outgoing_), rapidity_less{})); ev.type_ = classify(ev); return ev; } Event::Event( std::array<Particle, 2> && incoming, std::vector<Particle> && outgoing, std::unordered_map<size_t, std::vector<Particle>> && decays, Parameters<EventParameters> && parameters, fastjet::JetDefinition const & jet_def, double const min_jet_pt ): incoming_{std::move(incoming)}, outgoing_{std::move(outgoing)}, decays_{std::move(decays)}, parameters_{std::move(parameters)}, cs_{ to_PseudoJet( filter_partons(outgoing_) ), jet_def }, min_jet_pt_{min_jet_pt} { jets_ = sorted_by_rapidity(cs_.inclusive_jets(min_jet_pt_)); } namespace { //! check that Particles have a reasonable colour bool correct_colour(Particle const & part){ ParticleID id{ part.type }; if(!is_parton(id)) return !part.colour; if(!part.colour) return false; Colour const & col{ *part.colour }; if(is_quark(id)) return col.first != 0 && col.second == 0; if(is_antiquark(id)) return col.first == 0 && col.second != 0; assert(id==ParticleID::gluon); return col.first != 0 && col.second != 0 && col.first != col.second; } //! Connect parton to t-channel colour line & update the line //! returns false if connection not possible template<class OutIterator> bool try_connect_t(OutIterator const & it_part, Colour & line_colour){ if( line_colour.first == it_part->colour->second ){ line_colour.first = it_part->colour->first; return true; } if( line_colour.second == it_part->colour->first ){ line_colour.second = it_part->colour->second; return true; } return false; } //! Connect parton to u-channel colour line & update the line //! returns false if connection not possible template<class OutIterator> bool try_connect_u(OutIterator & it_part, Colour & line_colour){ auto it_next = std::next(it_part); if( try_connect_t(it_next, line_colour) && try_connect_t(it_part, line_colour) ){ it_part=it_next; return true; } return false; } } // namespace anonymous bool Event::is_leading_colour() const { if( !correct_colour(incoming()[0]) || !correct_colour(incoming()[1]) ) return false; Colour line_colour = *incoming()[0].colour; std::swap(line_colour.first, line_colour.second); // reasonable colour if(!std::all_of(outgoing().cbegin(), outgoing().cend(), correct_colour)) return false; for(auto it_part = cbegin_partons(); it_part!=cend_partons(); ++it_part){ switch (type()) { case event_type::FKL: if( !try_connect_t(it_part, line_colour) ) return false; break; case event_type::unob: case event_type::qqxexb: { if( !try_connect_t(it_part, line_colour) // u-channel only allowed at impact factor && (std::distance(cbegin_partons(), it_part)!=0 || !try_connect_u(it_part, line_colour))) return false; break; } case event_type::unof: case event_type::qqxexf: { if( !try_connect_t(it_part, line_colour) // u-channel only allowed at impact factor && (std::distance(it_part, cend_partons())!=2 || !try_connect_u(it_part, line_colour))) return false; break; } case event_type::qqxmid:{ auto it_next = std::next(it_part); if( !try_connect_t(it_part, line_colour) // u-channel only allowed at qqx/qxq pair && ( ( !(is_quark(*it_part) && is_antiquark(*it_next)) && !(is_antiquark(*it_part) && is_quark(*it_next))) || !try_connect_u(it_part, line_colour)) ) return false; break; } default: throw std::logic_error{"unreachable"}; } // no colour singlet exchange/disconnected diagram if(line_colour.first == line_colour.second) return false; } return (incoming()[1].colour->first == line_colour.first) && (incoming()[1].colour->second == line_colour.second); } namespace { //! connect incoming Particle to colour flow void connect_incoming(Particle & in, int & colour, int & anti_colour){ in.colour = std::make_pair(anti_colour, colour); // gluon if(in.type == pid::gluon) return; if(in.type > 0){ // quark assert(is_quark(in)); in.colour->second = 0; colour*=-1; return; } // anti-quark assert(is_antiquark(in)); in.colour->first = 0; anti_colour*=-1; return; } //! connect outgoing Particle to t-channel colour flow template<class OutIterator> void connect_tchannel( OutIterator & it_part, int & colour, int & anti_colour, RNG & ran ){ assert(colour>0 || anti_colour>0); if(it_part->type == ParticleID::gluon){ // gluon if(colour>0 && anti_colour>0){ // on g line => connect to colour OR anti-colour (random) if(ran.flat() < 0.5){ it_part->colour = std::make_pair(colour+2,colour); colour+=2; } else { it_part->colour = std::make_pair(anti_colour, anti_colour+2); anti_colour+=2; } } else if(colour > 0){ // on q line => connect to available colour it_part->colour = std::make_pair(colour+2, colour); colour+=2; } else { assert(colour<0 && anti_colour>0); // on qx line => connect to available anti-colour it_part->colour = std::make_pair(anti_colour, anti_colour+2); anti_colour+=2; } } else if(is_quark(*it_part)) { // quark assert(anti_colour>0); if(colour>0){ // on g line => connect and remove anti-colour it_part->colour = std::make_pair(anti_colour, 0); anti_colour+=2; anti_colour*=-1; } else { // on qx line => new colour colour*=-1; it_part->colour = std::make_pair(colour, 0); } } else if(is_antiquark(*it_part)) { // anti-quark assert(colour>0); if(anti_colour>0){ // on g line => connect and remove colour it_part->colour = std::make_pair(0, colour); colour+=2; colour*=-1; } else { // on q line => new anti-colour anti_colour*=-1; it_part->colour = std::make_pair(0, anti_colour); } } else { // not a parton assert(!is_parton(*it_part)); it_part->colour = {}; } } //! connect to t- or u-channel colour flow template<class OutIterator> void connect_utchannel( OutIterator & it_part, int & colour, int & anti_colour, RNG & ran ){ OutIterator it_first = it_part++; if(ran.flat()<.5) {// t-channel connect_tchannel(it_first, colour, anti_colour, ran); connect_tchannel(it_part, colour, anti_colour, ran); } else { // u-channel connect_tchannel(it_part, colour, anti_colour, ran); connect_tchannel(it_first, colour, anti_colour, ran); } } } // namespace anonymous bool Event::generate_colours(RNG & ran){ // generate only for HEJ events if(!event_type::is_resummable(type())) return false; assert(std::is_sorted( begin(outgoing()), end(outgoing()), rapidity_less{})); assert(incoming()[0].pz() < incoming()[1].pz()); // positive (anti-)colour -> can connect // negative (anti-)colour -> not available/used up by (anti-)quark int colour = COLOUR_OFFSET; int anti_colour = colour+1; // initialise first connect_incoming(incoming_[0], colour, anti_colour); // reset outgoing colours std::for_each(outgoing_.begin(), outgoing_.end(), [](Particle & part){ part.colour = {};}); for(auto it_part = begin_partons(); it_part!=end_partons(); ++it_part){ switch (type()) { // subleading can connect to t- or u-channel case event_type::unob: case event_type::qqxexb: { if( std::distance(begin_partons(), it_part)==0) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } case event_type::unof: case event_type::qqxexf: { if( std::distance(it_part, end_partons())==2) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } case event_type::qqxmid:{ auto it_next = std::next(it_part); if( std::distance(begin_partons(), it_part)>0 && std::distance(it_part, end_partons())>2 && ( (is_quark(*it_part) && is_antiquark(*it_next)) || (is_antiquark(*it_part) && is_quark(*it_next)) ) ) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } default: // rest has to be t-channel connect_tchannel(it_part, colour, anti_colour, ran); } } // Connect last connect_incoming(incoming_[1], anti_colour, colour); assert(is_leading_colour()); return true; } // generate_colours namespace { bool valid_parton( std::vector<fastjet::PseudoJet> const & jets, Particle const & parton, int const idx, double const max_ext_soft_pt_fraction, double const min_extparton_pt ){ // TODO code overlap with PhaseSpacePoint::pass_extremal_cuts if(min_extparton_pt > parton.pt()) return false; if(idx<0) return false; assert(static_cast<int>(jets.size())>=idx); auto const & jet{ jets[idx] }; if( (parton.p - jet).pt()/jet.pt() > max_ext_soft_pt_fraction) return false; return true; } } // this should work with multiple types bool Event::valid_hej_state(double const max_frac, double const min_pt ) const { using namespace event_type; if(!is_resummable(type())) return false; auto const & jet_idx{ particle_jet_indices() }; auto idx_begin{ jet_idx.cbegin() }; auto idx_end{ jet_idx.crbegin() }; auto part_begin{ cbegin_partons() }; auto part_end{ crbegin_partons() }; // always seperate extremal jets if( !valid_parton(jets(), *part_begin, *idx_begin, max_frac, min_pt) ) return false; ++part_begin; ++idx_begin; if( !valid_parton(jets(), *part_end, *idx_end, max_frac, min_pt) ) return false; ++part_end; ++idx_end; // unob -> second parton in own jet if( type() & (unob | qqxexb) ){ if( !valid_parton(jets(), *part_begin, *idx_begin, max_frac, min_pt) ) return false; ++part_begin; ++idx_begin; } if( type() & (unof | qqxexf) ){ if( !valid_parton(jets(), *part_end, *idx_end, max_frac, min_pt) ) return false; ++part_end; ++idx_end; } if( type() & qqxmid ){ // find qqx pair auto begin_qqx{ std::find_if( part_begin, part_end.base(), [](Particle const & part) -> bool { return part.type != ParticleID::gluon; } )}; assert(begin_qqx != part_end.base()); long int qqx_pos{ std::distance(part_begin, begin_qqx) }; assert(qqx_pos >= 0); idx_begin+=qqx_pos; if( !( valid_parton(jets(),*begin_qqx, *idx_begin, max_frac,min_pt) && valid_parton(jets(),*(++begin_qqx),*(++idx_begin),max_frac,min_pt) )) return false; } return true; } Event::ConstPartonIterator Event::begin_partons() const { return cbegin_partons(); } Event::ConstPartonIterator Event::cbegin_partons() const { return boost::make_filter_iterator( static_cast<bool (*)(Particle const &)>(is_parton), cbegin(outgoing()), cend(outgoing()) ); } Event::ConstPartonIterator Event::end_partons() const { return cend_partons(); } Event::ConstPartonIterator Event::cend_partons() const { return boost::make_filter_iterator( static_cast<bool (*)(Particle const &)>(is_parton), cend(outgoing()), cend(outgoing()) ); } Event::ConstReversePartonIterator Event::rbegin_partons() const { return crbegin_partons(); } Event::ConstReversePartonIterator Event::crbegin_partons() const { return std::reverse_iterator<ConstPartonIterator>( cend_partons() ); } Event::ConstReversePartonIterator Event::rend_partons() const { return crend_partons(); } Event::ConstReversePartonIterator Event::crend_partons() const { return std::reverse_iterator<ConstPartonIterator>( cbegin_partons() ); } Event::PartonIterator Event::begin_partons() { return boost::make_filter_iterator( static_cast<bool (*)(Particle const &)>(is_parton), begin(outgoing_), end(outgoing_) ); } Event::PartonIterator Event::end_partons() { return boost::make_filter_iterator( static_cast<bool (*)(Particle const &)>(is_parton), end(outgoing_), end(outgoing_) ); } Event::ReversePartonIterator Event::rbegin_partons() { return std::reverse_iterator<PartonIterator>( end_partons() ); } Event::ReversePartonIterator Event::rend_partons() { return std::reverse_iterator<PartonIterator>( begin_partons() ); } namespace { void print_momentum(std::ostream & os, fastjet::PseudoJet const & part){ const std::streamsize orig_prec = os.precision(); os <<std::scientific<<std::setprecision(6) << "[" <<std::setw(13)<<std::right<< part.px() << ", " <<std::setw(13)<<std::right<< part.py() << ", " <<std::setw(13)<<std::right<< part.pz() << ", " <<std::setw(13)<<std::right<< part.E() << "]"<< std::fixed; os.precision(orig_prec); } void print_colour(std::ostream & os, optional<Colour> const & col){ if(!col) os << "(no color)"; // American spelling for better alignment else os << "(" <<std::setw(3)<<std::right<< col->first << ", " <<std::setw(3)<<std::right<< col->second << ")"; } } std::ostream& operator<<(std::ostream & os, Event const & ev){ const std::streamsize orig_prec = os.precision(); os <<std::setprecision(4)<<std::fixed; os << "########## " << event_type::name(ev.type()) << " ##########" << std::endl; os << "Incoming particles:\n"; for(auto const & in: ev.incoming()){ os <<std::setw(3)<< in.type << ": "; print_colour(os, in.colour); os << " "; print_momentum(os, in.p); os << std::endl; } os << "\nOutgoing particles: " << ev.outgoing().size() << "\n"; for(auto const & out: ev.outgoing()){ os <<std::setw(3)<< out.type << ": "; print_colour(os, out.colour); os << " "; print_momentum(os, out.p); os << " => rapidity=" <<std::setw(7)<<std::right<< out.rapidity() << std::endl; } os << "\nForming Jets: " << ev.jets().size() << "\n"; for(auto const & jet: ev.jets()){ print_momentum(os, jet); os << " => rapidity=" <<std::setw(7)<<std::right<< jet.rapidity() << std::endl; } if(ev.decays().size() > 0 ){ os << "\nDecays: " << ev.decays().size() << "\n"; for(auto const & decay: ev.decays()){ os <<std::setw(3)<< ev.outgoing()[decay.first].type << " (" << decay.first << ") to:\n"; for(auto const & out: decay.second){ os <<" "<<std::setw(3)<< out.type << ": "; print_momentum(os, out.p); os << " => rapidity=" <<std::setw(7)<<std::right<< out.rapidity() << std::endl; } } } os << std::defaultfloat; os.precision(orig_prec); return os; } double shat(Event const & ev){ return (ev.incoming()[0].p + ev.incoming()[1].p).m2(); } LHEF::HEPEUP to_HEPEUP(Event const & event, LHEF::HEPRUP * heprup){ LHEF::HEPEUP result; result.heprup = heprup; result.weights = {{event.central().weight, nullptr}}; for(auto const & var: event.variations()){ result.weights.emplace_back(var.weight, nullptr); } size_t num_particles = event.incoming().size() + event.outgoing().size(); for(auto const & decay: event.decays()) num_particles += decay.second.size(); result.NUP = num_particles; // the following entries are pretty much meaningless result.IDPRUP = event.type(); // event type result.AQEDUP = 1./128.; // alpha_EW //result.AQCDUP = 0.118 // alpha_QCD // end meaningless part result.XWGTUP = event.central().weight; result.SCALUP = event.central().muf; result.scales.muf = event.central().muf; result.scales.mur = event.central().mur; result.scales.SCALUP = event.central().muf; result.pdfinfo.p1 = event.incoming().front().type; result.pdfinfo.p2 = event.incoming().back().type; result.pdfinfo.scale = event.central().muf; result.IDUP.reserve(num_particles); // PID result.ISTUP.reserve(num_particles); // status (in, out, decay) result.PUP.reserve(num_particles); // momentum result.MOTHUP.reserve(num_particles); // index mother particle result.ICOLUP.reserve(num_particles); // colour // incoming std::array<Particle, 2> incoming{ event.incoming() }; // First incoming should be positive pz according to LHE standard // (or at least most (everyone?) do it this way, and Pythia assumes it) if(incoming[0].pz() < incoming[1].pz()) std::swap(incoming[0], incoming[1]); for(Particle const & in: incoming){ result.IDUP.emplace_back(in.type); result.ISTUP.emplace_back(status_in); result.PUP.push_back({in.p[0], in.p[1], in.p[2], in.p[3], in.p.m()}); result.MOTHUP.emplace_back(0, 0); assert(in.colour); result.ICOLUP.emplace_back(*in.colour); } // outgoing for(size_t i = 0; i < event.outgoing().size(); ++i){ Particle const & out = event.outgoing()[i]; result.IDUP.emplace_back(out.type); const int status = event.decays().count(i)?status_decayed:status_out; result.ISTUP.emplace_back(status); result.PUP.push_back({out.p[0], out.p[1], out.p[2], out.p[3], out.p.m()}); result.MOTHUP.emplace_back(1, 2); if(out.colour) result.ICOLUP.emplace_back(*out.colour); else{ result.ICOLUP.emplace_back(std::make_pair(0,0)); } } // decays for(auto const & decay: event.decays()){ for(auto const & out: decay.second){ result.IDUP.emplace_back(out.type); result.ISTUP.emplace_back(status_out); result.PUP.push_back({out.p[0], out.p[1], out.p[2], out.p[3], out.p.m()}); const size_t mother_idx = 1 + event.incoming().size() + decay.first; result.MOTHUP.emplace_back(mother_idx, mother_idx); result.ICOLUP.emplace_back(0,0); } } assert(result.ICOLUP.size() == num_particles); static constexpr double unknown_spin = 9.; //per Les Houches accord result.VTIMUP = std::vector<double>(num_particles, unknown_spin); result.SPINUP = result.VTIMUP; return result; } } diff --git a/src/EventReader.cc b/src/EventReader.cc index 8d10e6b..dccff81 100644 --- a/src/EventReader.cc +++ b/src/EventReader.cc @@ -1,73 +1,73 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/EventReader.hh" #include "HEJ/HDF5Reader.hh" #include "HEJ/ConfigFlags.hh" #include "HEJ/LesHouchesReader.hh" #include "HEJ/utility.hh" #include "HEJ/Version.hh" namespace { enum class generator{ HEJ, HEJFOG, Sherpa, MG, unknown }; generator get_generator( LHEF::HEPRUP const & heprup, std::string const & header ){ // try getting generator name from specific tag if(heprup.generators.size()>0){ 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("<MGVersion>")!=std::string::npos) return generator::MG; std::cerr<<"Could not determine LHE Generator using default LHE interface.\n"; return generator::unknown; } } namespace HEJ { std::unique_ptr<EventReader> make_reader(std::string const & filename) { try { auto reader{ std::make_unique<LesHouchesReader>(filename) }; switch( get_generator(reader->heprup(), reader->header()) ){ case generator::Sherpa: return std::make_unique<SherpaLHEReader>(filename); case generator::HEJ: case generator::HEJFOG: case generator::MG: //! @TODO we could directly fix the MG weights here similar to Sherpa default: return reader; } } catch(std::runtime_error&) { #ifdef HEJ_BUILD_WITH_HDF5 return std::make_unique<HDF5Reader>(filename); #else throw; #endif } } } diff --git a/src/EventReweighter.cc b/src/EventReweighter.cc index 4d2f302..54b8491 100644 --- a/src/EventReweighter.cc +++ b/src/EventReweighter.cc @@ -1,256 +1,256 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/EventReweighter.hh" #include <algorithm> #include <assert.h> #include <limits> #include <math.h> #include <stddef.h> #include <string> #include <unordered_map> #include <utility> #include "fastjet/ClusterSequence.hh" #include "LHEF/LHEF.h" #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/PhaseSpacePoint.hh" #include "HEJ/RNG.hh" namespace HEJ{ EventReweighter::EventReweighter( LHEF::HEPRUP const & heprup, ScaleGenerator scale_gen, EventReweighterConfig conf, std::shared_ptr<RNG> ran ): EventReweighter{ HEJ::Beam{ heprup.EBMUP.first, {{ static_cast<HEJ::ParticleID>(heprup.IDBMUP.first), static_cast<HEJ::ParticleID>(heprup.IDBMUP.second) }} }, heprup.PDFSUP.first, std::move(scale_gen), std::move(conf), std::move(ran) } { if(heprup.EBMUP.second != E_beam_){ throw std::invalid_argument( "asymmetric beam: " + std::to_string(E_beam_) + " ---> <--- " + std::to_string(heprup.EBMUP.second) ); } if(heprup.PDFSUP.second != pdf_.id()){ throw std::invalid_argument( "conflicting PDF ids: " + std::to_string(pdf_.id()) + " vs. " + std::to_string(heprup.PDFSUP.second) ); } } EventReweighter::EventReweighter( Beam beam, int pdf_id, ScaleGenerator scale_gen, EventReweighterConfig conf, std::shared_ptr<RNG> ran ): param_{std::move(conf)}, E_beam_{beam.E}, pdf_{pdf_id, beam.type.front(), beam.type.back()}, MEt2_{ [this](double mu){ return pdf_.Halphas(mu); }, param_.ME_config }, scale_gen_{std::move(scale_gen)}, ran_{std::move(ran)} { assert(ran_); } PDF const & EventReweighter::pdf() const{ return pdf_; } std::vector<Event> EventReweighter::reweight( Event const & input_ev, size_t num_events ){ auto res_events{ gen_res_events(input_ev, num_events) }; if(res_events.empty()) return {}; for(auto & event: res_events) event = scale_gen_(std::move(event)); return rescale(input_ev, std::move(res_events)); } EventTreatment EventReweighter::treatment(EventType type) const { return param_.treat.at(type); } std::vector<Event> EventReweighter::gen_res_events( Event const & ev, size_t phase_space_points ){ assert(ev.variations().empty()); status_.clear(); switch(treatment(ev.type())){ case EventTreatment::discard: { status_.emplace_back(StatusCode::discard); return {}; } case EventTreatment::keep: if(! jets_pass_resummation_cuts(ev)) { status_.emplace_back(StatusCode::failed_resummation_cuts); return {}; } else { status_.emplace_back(StatusCode::good); return {ev}; } default:; } const double Born_shat = shat(ev); std::vector<Event> resummation_events; status_.reserve(phase_space_points); for(size_t psp_number = 0; psp_number < phase_space_points; ++psp_number){ PhaseSpacePoint psp{ev, param_.psp_config, *ran_}; status_.emplace_back(psp.status()); assert(psp.status() != StatusCode::unspecified); if(psp.status() != StatusCode::good) continue; assert(psp.weight() != 0.); if(psp.incoming()[0].E() > E_beam_ || psp.incoming()[1].E() > E_beam_) { status_.back() = StatusCode::too_much_energy; continue; } resummation_events.emplace_back( to_EventData( std::move(psp) ).cluster( param_.jet_param().def, param_.jet_param().min_pt ) ); auto & new_event = resummation_events.back(); assert( new_event.valid_hej_state( param_.psp_config.max_ext_soft_pt_fraction, param_.psp_config.min_extparton_pt ) ); if( new_event.type() != ev.type() ) throw std::logic_error{"Resummation Event does not match Born event"}; new_event.generate_colours(*ran_); assert(new_event.variations().empty()); new_event.central().mur = ev.central().mur; new_event.central().muf = ev.central().muf; const double resum_shat = shat(new_event); new_event.central().weight *= ev.central().weight*Born_shat*Born_shat/ (phase_space_points*resum_shat*resum_shat); } return resummation_events; } std::vector<Event> EventReweighter::rescale( Event const & Born_ev, std::vector<Event> events ) const{ const double Born_pdf = pdf_factors(Born_ev).central; const double Born_ME = tree_matrix_element(Born_ev); for(auto & cur_event: events){ const auto pdf = pdf_factors(cur_event); assert(pdf.variations.size() == cur_event.variations().size()); const auto ME = matrix_elements(cur_event); assert(ME.variations.size() == cur_event.variations().size()); cur_event.parameters() *= pdf*ME/(Born_pdf*Born_ME); } return events; } bool EventReweighter::jets_pass_resummation_cuts( Event const & ev ) const{ const auto out_as_PseudoJet = to_PseudoJet(filter_partons(ev.outgoing())); fastjet::ClusterSequence cs{out_as_PseudoJet, param_.jet_param().def}; return cs.inclusive_jets(param_.jet_param().min_pt).size() == ev.jets().size(); } Weights EventReweighter::pdf_factors(Event const & ev) const{ auto const & a = ev.incoming().front(); auto const & b = ev.incoming().back(); const double xa = a.p.e()/E_beam_; const double xb = b.p.e()/E_beam_; Weights result; std::unordered_map<double, double> known_pdf; result.central = pdf_.pdfpt(0,xa,ev.central().muf,a.type)* pdf_.pdfpt(1,xb,ev.central().muf,b.type); known_pdf.emplace(ev.central().muf, result.central); result.variations.reserve(ev.variations().size()); for(auto const & ev_param: ev.variations()){ const double muf = ev_param.muf; auto cur_pdf = known_pdf.find(muf); if(cur_pdf == known_pdf.end()){ cur_pdf = known_pdf.emplace( muf, pdf_.pdfpt(0,xa,muf,a.type)*pdf_.pdfpt(1,xb,muf,b.type) ).first; } result.variations.emplace_back(cur_pdf->second); } assert(result.variations.size() == ev.variations().size()); return result; } Weights EventReweighter::matrix_elements(Event const & ev) const{ assert(param_.treat.count(ev.type()) > 0); if(param_.treat.find(ev.type())->second == EventTreatment::keep){ return fixed_order_scale_ME(ev); } return MEt2_(ev); } double EventReweighter::tree_matrix_element(Event const & ev) const{ assert(ev.variations().empty()); assert(param_.treat.count(ev.type()) > 0); if(param_.treat.find(ev.type())->second == EventTreatment::keep){ return fixed_order_scale_ME(ev).central; } return MEt2_.tree(ev).central; } Weights EventReweighter::fixed_order_scale_ME(Event const & ev) const{ int alpha_s_power = 0; for(auto const & part: ev.outgoing()){ if(is_parton(part)) ++alpha_s_power; else if(part.type == pid::Higgs) { alpha_s_power += 2; } // nothing to do for other uncoloured particles } Weights result; result.central = pow(pdf_.Halphas(ev.central().mur), alpha_s_power); for(auto const & var: ev.variations()){ result.variations.emplace_back( pow(pdf_.Halphas(var.mur), alpha_s_power) ); } return result; } } // namespace HEJ diff --git a/src/HDF5Reader.cc b/src/HDF5Reader.cc index c4fec3d..32c73ac 100644 --- a/src/HDF5Reader.cc +++ b/src/HDF5Reader.cc @@ -1,306 +1,306 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HDF5Reader.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HDF5 #include <numeric> #include <iterator> #include "highfive/H5File.hpp" namespace HEJ { namespace { // buffer size for reader // each "reading from disk" reads "chunk_size" many event at once constexpr std::size_t chunk_size = 10000; struct ParticleData { std::vector<int> id; std::vector<int> status; std::vector<int> mother1; std::vector<int> mother2; std::vector<int> color1; std::vector<int> color2; std::vector<double> px; std::vector<double> py; std::vector<double> pz; std::vector<double> e; std::vector<double> m; std::vector<double> lifetime; std::vector<double> spin; }; struct EventRecords { std::vector<int> particle_start; std::vector<int> nparticles; std::vector<int> pid; std::vector<double> weight; std::vector<double> scale; std::vector<double> fscale; std::vector<double> rscale; std::vector<double> aqed; std::vector<double> aqcd; double trials; ParticleData particles; }; class ConstEventIterator { public: // iterator traits using iterator_category = std::bidirectional_iterator_tag; using value_type = LHEF::HEPEUP; using difference_type = std::ptrdiff_t; using pointer = const LHEF::HEPEUP*; using reference = LHEF::HEPEUP const &; using iterator = ConstEventIterator; friend iterator cbegin(EventRecords const & records) noexcept; friend iterator cend(EventRecords const & records) noexcept; iterator& operator++() { particle_offset_ += records_.get().nparticles[idx_]; ++idx_; return *this; } iterator& operator--() { --idx_; particle_offset_ -= records_.get().nparticles[idx_]; return *this; } iterator operator--(int) { auto res = *this; --(*this); return res; } bool operator==(iterator const & other) const { return idx_ == other.idx_; } bool operator!=(iterator other) const { return !(*this == other); } value_type operator*() const { value_type hepeup{}; auto const & r = records_.get(); hepeup.NUP = r.nparticles[idx_]; hepeup.IDPRUP = r.pid[idx_]; hepeup.XWGTUP = r.weight[idx_]/r.trials; hepeup.weights.emplace_back(hepeup.XWGTUP, nullptr); hepeup.SCALUP = r.scale[idx_]; hepeup.scales.muf = r.fscale[idx_]; hepeup.scales.mur = r.rscale[idx_]; hepeup.AQEDUP = r.aqed[idx_]; hepeup.AQCDUP = r.aqcd[idx_]; const size_t start = particle_offset_; const size_t end = start + hepeup.NUP; auto const & p = r.particles; hepeup.IDUP = std::vector<long>( begin(p.id)+start, begin(p.id)+end ); hepeup.ISTUP = std::vector<int>( begin(p.status)+start, begin(p.status)+end ); hepeup.VTIMUP = std::vector<double>( begin(p.lifetime)+start, begin(p.lifetime)+end ); hepeup.SPINUP = std::vector<double>( begin(p.spin)+start, begin(p.spin)+end ); hepeup.MOTHUP.resize(hepeup.NUP); hepeup.ICOLUP.resize(hepeup.NUP); hepeup.PUP.resize(hepeup.NUP); for(size_t i = 0; i < hepeup.MOTHUP.size(); ++i) { const size_t idx = start + i; assert(idx < end); hepeup.MOTHUP[i] = std::make_pair(p.mother1[idx], p.mother2[idx]); hepeup.ICOLUP[i] = std::make_pair(p.color1[idx], p.color2[idx]); hepeup.PUP[i] = std::vector<double>{ p.px[idx], p.py[idx], p.pz[idx], p.e[idx], p.m[idx] }; } return hepeup; } private: explicit ConstEventIterator(EventRecords const & records): records_{records} {} std::reference_wrapper<const EventRecords> records_; size_t idx_ = 0; size_t particle_offset_ = 0; }; // end ConstEventIterator ConstEventIterator cbegin(EventRecords const & records) noexcept { return ConstEventIterator{records}; } ConstEventIterator cend(EventRecords const & records) noexcept { auto it =ConstEventIterator{records}; it.idx_ = records.aqcd.size(); // or size of any other records member return it; } } // end anonymous namespace struct HDF5Reader::HDF5ReaderImpl{ HighFive::File file; std::size_t event_idx; std::size_t particle_idx; std::size_t nevents; EventRecords records; ConstEventIterator cur_event; LHEF::HEPRUP heprup; LHEF::HEPEUP hepeup; explicit HDF5ReaderImpl(std::string const & filename): file{filename}, event_idx{0}, particle_idx{0}, nevents{ file.getGroup("event") .getDataSet("nparticles") // or any other dataset .getSpace().getDimensions().front() }, records{}, cur_event{cbegin(records)}, heprup{}, hepeup{} { read_heprup(); read_event_records(chunk_size); } void read_heprup() { const auto init = file.getGroup("init"); init.getDataSet( "PDFgroupA" ).read(heprup.PDFGUP.first); init.getDataSet( "PDFgroupB" ).read(heprup.PDFGUP.second); init.getDataSet( "PDFsetA" ).read(heprup.PDFSUP.first); init.getDataSet( "PDFsetB" ).read(heprup.PDFSUP.second); init.getDataSet( "beamA" ).read(heprup.IDBMUP.first); init.getDataSet( "beamB" ).read(heprup.IDBMUP.second); init.getDataSet( "energyA" ).read(heprup.EBMUP.first); init.getDataSet( "energyB" ).read(heprup.EBMUP.second); init.getDataSet( "numProcesses" ).read(heprup.NPRUP); init.getDataSet( "weightingStrategy" ).read(heprup.IDWTUP); const auto proc_info = file.getGroup("procInfo"); proc_info.getDataSet( "procId" ).read(heprup.LPRUP); proc_info.getDataSet( "xSection" ).read(heprup.XSECUP); proc_info.getDataSet( "error" ).read(heprup.XERRUP); // TODO: is this identification correct? proc_info.getDataSet( "unitWeight" ).read(heprup.XMAXUP); std::vector<double> trials; file.getGroup("event").getDataSet("trials").read(trials); records.trials = std::accumulate(begin(trials), end(trials), 0.); } std::size_t read_event_records(std::size_t count) { count = std::min(count, nevents-event_idx); auto events = file.getGroup("event"); events.getDataSet("nparticles").select({event_idx}, {count}).read(records.nparticles); assert(records.nparticles.size() == count); events.getDataSet("pid").select( {event_idx}, {count} ).read( records.pid ); events.getDataSet("weight").select( {event_idx}, {count} ).read( records.weight ); events.getDataSet("scale").select( {event_idx}, {count} ).read( records.scale ); events.getDataSet("fscale").select( {event_idx}, {count} ).read( records.fscale ); events.getDataSet("rscale").select( {event_idx}, {count} ).read( records.rscale ); events.getDataSet("aqed").select( {event_idx}, {count} ).read( records.aqed ); events.getDataSet("aqcd").select( {event_idx}, {count} ).read( records.aqcd ); const std::size_t particle_count = std::accumulate( begin(records.nparticles), end(records.nparticles), 0 ); auto pdata = file.getGroup("particle"); auto & particles = records.particles; pdata.getDataSet("id").select( {particle_idx}, {particle_count} ).read( particles.id ); pdata.getDataSet("status").select( {particle_idx}, {particle_count} ).read( particles.status ); pdata.getDataSet("mother1").select( {particle_idx}, {particle_count} ).read( particles.mother1 ); pdata.getDataSet("mother2").select( {particle_idx}, {particle_count} ).read( particles.mother2 ); pdata.getDataSet("color1").select( {particle_idx}, {particle_count} ).read( particles.color1 ); pdata.getDataSet("color2").select( {particle_idx}, {particle_count} ).read( particles.color2 ); pdata.getDataSet("px").select( {particle_idx}, {particle_count} ).read( particles.px ); pdata.getDataSet("py").select( {particle_idx}, {particle_count} ).read( particles.py ); pdata.getDataSet("pz").select( {particle_idx}, {particle_count} ).read( particles.pz ); pdata.getDataSet("e").select( {particle_idx}, {particle_count} ).read( particles.e ); pdata.getDataSet("m").select( {particle_idx}, {particle_count} ).read( particles.m ); pdata.getDataSet("lifetime").select( {particle_idx}, {particle_count} ).read( particles.lifetime ); pdata.getDataSet("spin").select( {particle_idx}, {particle_count} ).read( particles.spin ); event_idx += count; particle_idx += particle_count; return count; } }; HDF5Reader::HDF5Reader(std::string const & filename): impl_{std::make_unique<HDF5ReaderImpl>(filename)} {} bool HDF5Reader::read_event() { if(impl_->cur_event == cend(impl_->records)) { // end of active chunk, read new events from file const auto events_read = impl_->read_event_records(chunk_size); impl_->cur_event = cbegin(impl_->records); if(events_read == 0) return false; } impl_->hepeup = *impl_->cur_event; ++impl_->cur_event; return true; } namespace { static const std::string nothing = ""; } std::string const & HDF5Reader::header() const { return nothing; } LHEF::HEPRUP const & HDF5Reader::heprup() const { return impl_->heprup; } LHEF::HEPEUP const & HDF5Reader::hepeup() const { return impl_->hepeup; } HEJ::optional<size_t> HDF5Reader::number_events() const { return impl_->nevents; } } #else // no HDF5 support namespace HEJ { class HDF5Reader::HDF5ReaderImpl{}; HDF5Reader::HDF5Reader(std::string const &){ throw std::invalid_argument{ "Failed to create HDF5 reader: " "HEJ 2 was built without HDF5 support" }; } bool HDF5Reader::read_event() { throw std::logic_error{"unreachable"}; } std::string const & HDF5Reader::header() const { throw std::logic_error{"unreachable"}; } LHEF::HEPRUP const & HDF5Reader::heprup() const { throw std::logic_error{"unreachable"}; } LHEF::HEPEUP const & HDF5Reader::hepeup() const { throw std::logic_error{"unreachable"}; } HEJ::optional<size_t> HDF5Reader::number_events() const { throw std::logic_error{"unreachable"}; } } #endif namespace HEJ { HDF5Reader::~HDF5Reader() = default; } diff --git a/src/HDF5Writer.cc b/src/HDF5Writer.cc index 1ff3525..ec9640f 100644 --- a/src/HDF5Writer.cc +++ b/src/HDF5Writer.cc @@ -1,403 +1,403 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HDF5Writer.hh" #include <cassert> #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HDF5 #include <type_traits> #include <iterator> #include "HEJ/event_types.hh" #include "HEJ/Event.hh" #include "highfive/H5File.hpp" namespace HEJ{ using HighFive::File; using HighFive::DataSpace; namespace{ constexpr std::size_t chunk_size = 1000; constexpr unsigned compression_level = 3; size_t to_index(event_type::EventType const type){ return type==0?0:floor(log2(type))+1; } template<typename T> void write_dataset(HighFive::Group & group, std::string const & name, T val) { using data_t = std::decay_t<T>; group.createDataSet<data_t>(name, DataSpace::From(val)).write(val); } template<typename T> void write_dataset( HighFive::Group & group, std::string const & name, std::vector<T> const & val ) { using data_t = std::decay_t<T>; group.createDataSet<data_t>(name, DataSpace::From(val)).write(val); } struct Cache { explicit Cache(size_t capacity): capacity{capacity} { nparticles.reserve(capacity); start.reserve(capacity); pid.reserve(capacity); weight.reserve(capacity); scale.reserve(capacity); fscale.reserve(capacity); rscale.reserve(capacity); aqed.reserve(capacity); aqcd.reserve(capacity); trials.reserve(capacity); npLO.reserve(capacity); npNLO.reserve(capacity); } void fill(HEJ::Event ev) { const auto hepeup = to_HEPEUP(ev, nullptr); // HEJ event to get nice wrapper const auto num_partons = std::distance(ev.cbegin_partons(), ev.cend_partons()); assert(num_partons>0); // Number of partons for LO matching, HEJ requires at least 2 partons npLO.emplace_back(num_partons>1?num_partons-2:num_partons); // Number of real emissions in NLO, HEJ is LO -> -1 npNLO.emplace_back(-1); fill_event_params(hepeup); fill_event_particles(hepeup); } void fill_event_params(LHEF::HEPEUP const & ev) { nparticles.emplace_back(ev.NUP); start.emplace_back(particle_pos); pid.emplace_back(ev.IDPRUP); weight.emplace_back(ev.XWGTUP); scale.emplace_back(ev.SCALUP); fscale.emplace_back(ev.scales.muf); rscale.emplace_back(ev.scales.mur); aqed.emplace_back(ev.AQEDUP); aqcd.emplace_back(ev.AQCDUP); // set first trial=1 for first event // -> sum(trials) = 1 -> xs=sum(weights)/sum(trials) as in Sherpa if(particle_pos == 0){ trials.emplace_back(1.); } else { trials.emplace_back(0.); } particle_pos += ev.NUP; } void fill_event_particles(LHEF::HEPEUP const & ev) { id.insert(end(id), begin(ev.IDUP), end(ev.IDUP)); status.insert(end(status), begin(ev.ISTUP), end(ev.ISTUP)); lifetime.insert(end(lifetime), begin(ev.VTIMUP), end(ev.VTIMUP)); spin.insert(end(spin), begin(ev.SPINUP), end(ev.SPINUP)); for(int i = 0; i < ev.NUP; ++i) { mother1.emplace_back(ev.MOTHUP[i].first); mother2.emplace_back(ev.MOTHUP[i].second); color1.emplace_back(ev.ICOLUP[i].first); color2.emplace_back(ev.ICOLUP[i].second); px.emplace_back(ev.PUP[i][0]); py.emplace_back(ev.PUP[i][1]); pz.emplace_back(ev.PUP[i][2]); e.emplace_back(ev.PUP[i][3]); m.emplace_back(ev.PUP[i][4]); } } bool is_full() const { return nparticles.size() >= capacity; } void clear() { nparticles.clear(); start.clear(); pid.clear(); id.clear(); status.clear(); mother1.clear(); mother2.clear(); color1.clear(); color2.clear(); weight.clear(); scale.clear(); fscale.clear(); rscale.clear(); aqed.clear(); aqcd.clear(); trials.clear(); npLO.clear(); npNLO.clear(); px.clear(); py.clear(); pz.clear(); e.clear(); m.clear(); lifetime.clear(); spin.clear(); } size_t capacity; std::vector<int> nparticles, start, pid, id, status, mother1, mother2, color1, color2, npLO, npNLO; std::vector<double> weight, scale, fscale, rscale, aqed, aqcd, trials, px, py, pz, e, m, lifetime, spin; private: size_t particle_pos = 0; }; } struct HDF5Writer::HDF5WriterImpl{ File file; LHEF::HEPRUP heprup; Cache cache{chunk_size}; size_t event_idx = 0; size_t particle_idx = 0; HDF5WriterImpl(std::string const & filename, LHEF::HEPRUP && hepr): file{filename, File::ReadWrite | File::Create | File::Truncate}, heprup{std::move(hepr)} { // TODO: code duplication with Les Houches Writer const int max_number_types = to_index(event_type::last_type)+1; heprup.NPRUP = max_number_types; // ids of event types heprup.LPRUP.clear(); heprup.LPRUP.reserve(max_number_types); heprup.LPRUP.emplace_back(0); for(size_t i=event_type::first_type+1; i<=event_type::last_type; i*=2) { heprup.LPRUP.emplace_back(i); } heprup.XSECUP = std::vector<double>(max_number_types, 0.); heprup.XERRUP = std::vector<double>(max_number_types, 0.); heprup.XMAXUP = std::vector<double>(max_number_types, 0.); write_init(); create_event_group(); create_particle_group(); } void write_init() { auto init = file.createGroup("init"); write_dataset(init, "PDFgroupA" , heprup.PDFGUP.first); write_dataset(init, "PDFgroupB" , heprup.PDFGUP.second); write_dataset(init, "PDFsetA" , heprup.PDFSUP.first); write_dataset(init, "PDFsetB" , heprup.PDFSUP.second); write_dataset(init, "beamA" , heprup.IDBMUP.first); write_dataset(init, "beamB" , heprup.IDBMUP.second); write_dataset(init, "energyA" , heprup.EBMUP.first); write_dataset(init, "energyB" , heprup.EBMUP.second); write_dataset(init, "numProcesses" , heprup.NPRUP); write_dataset(init, "weightingStrategy", heprup.IDWTUP); auto proc_info = file.createGroup("procInfo"); write_dataset(proc_info, "procId", heprup.LPRUP); } static HighFive::DataSetCreateProps const & hdf5_chunk() { static const auto props = [](){ HighFive::DataSetCreateProps props; props.add(HighFive::Chunking({chunk_size})); props.add(HighFive::Deflate(compression_level)); return props; }(); return props; } void create_event_group() { static const auto dim = DataSpace({0}, {DataSpace::UNLIMITED}); auto events = file.createGroup("event"); events.createDataSet<int>("nparticles", dim, hdf5_chunk()); events.createDataSet<int>("start", dim, hdf5_chunk()); events.createDataSet<int>("pid", dim, hdf5_chunk()); events.createDataSet<double>("weight", dim, hdf5_chunk()); events.createDataSet<double>("scale", dim, hdf5_chunk()); events.createDataSet<double>("fscale", dim, hdf5_chunk()); events.createDataSet<double>("rscale", dim, hdf5_chunk()); events.createDataSet<double>("aqed", dim, hdf5_chunk()); events.createDataSet<double>("aqcd", dim, hdf5_chunk()); events.createDataSet<double>("trials", dim, hdf5_chunk()); events.createDataSet<int>("npLO", dim, hdf5_chunk()); events.createDataSet<int>("npNLO", dim, hdf5_chunk()); } void resize_event_group(size_t new_size) { auto events = file.getGroup("event"); events.getDataSet("nparticles").resize({new_size}); events.getDataSet("start").resize({new_size}); events.getDataSet("pid").resize({new_size}); events.getDataSet("weight").resize({new_size}); events.getDataSet("scale").resize({new_size}); events.getDataSet("fscale").resize({new_size}); events.getDataSet("rscale").resize({new_size}); events.getDataSet("aqed").resize({new_size}); events.getDataSet("aqcd").resize({new_size}); events.getDataSet("trials").resize({new_size}); events.getDataSet("npLO").resize({new_size}); events.getDataSet("npNLO").resize({new_size}); } void create_particle_group() { static const auto dim = DataSpace({0}, {DataSpace::UNLIMITED}); auto particles = file.createGroup("particle"); particles.createDataSet<int>("id", dim, hdf5_chunk()); particles.createDataSet<int>("status", dim, hdf5_chunk()); particles.createDataSet<int>("mother1", dim, hdf5_chunk()); particles.createDataSet<int>("mother2", dim, hdf5_chunk()); particles.createDataSet<int>("color1", dim, hdf5_chunk()); particles.createDataSet<int>("color2", dim, hdf5_chunk()); particles.createDataSet<double>("px", dim, hdf5_chunk()); particles.createDataSet<double>("py", dim, hdf5_chunk()); particles.createDataSet<double>("pz", dim, hdf5_chunk()); particles.createDataSet<double>("e", dim, hdf5_chunk()); particles.createDataSet<double>("m", dim, hdf5_chunk()); particles.createDataSet<double>("lifetime", dim, hdf5_chunk()); particles.createDataSet<double>("spin", dim, hdf5_chunk()); } void resize_particle_group(size_t new_size) { auto particles = file.getGroup("particle"); particles.getDataSet("id").resize({new_size}); particles.getDataSet("status").resize({new_size}); particles.getDataSet("mother1").resize({new_size}); particles.getDataSet("mother2").resize({new_size}); particles.getDataSet("color1").resize({new_size}); particles.getDataSet("color2").resize({new_size}); particles.getDataSet("px").resize({new_size}); particles.getDataSet("py").resize({new_size}); particles.getDataSet("pz").resize({new_size}); particles.getDataSet("e").resize({new_size}); particles.getDataSet("m").resize({new_size}); particles.getDataSet("lifetime").resize({new_size}); particles.getDataSet("spin").resize({new_size}); } void write(Event const & ev){ cache.fill(ev); if(cache.is_full()) { dump_cache(); } const double wt = ev.central().weight; const size_t idx = to_index(ev.type()); heprup.XSECUP[idx] += wt; heprup.XERRUP[idx] += wt*wt; if(wt > heprup.XMAXUP[idx]){ heprup.XMAXUP[idx] = wt; } } void dump_cache() { write_event_params(); write_event_particles(); cache.clear(); } void write_event_params() { auto events = file.getGroup("event"); // choose arbitrary dataset to find size const auto dataset = events.getDataSet("nparticles"); const size_t size = dataset.getSpace().getDimensions().front(); resize_event_group(size + cache.nparticles.size()); #define WRITE_FROM_CACHE(GROUP, PROPERTY) \ GROUP.getDataSet(#PROPERTY).select({size}, {cache.PROPERTY.size()}).write(cache.PROPERTY) WRITE_FROM_CACHE(events, nparticles); WRITE_FROM_CACHE(events, start); WRITE_FROM_CACHE(events, pid); WRITE_FROM_CACHE(events, weight); WRITE_FROM_CACHE(events, scale); WRITE_FROM_CACHE(events, fscale); WRITE_FROM_CACHE(events, rscale); WRITE_FROM_CACHE(events, aqed); WRITE_FROM_CACHE(events, aqcd); WRITE_FROM_CACHE(events, trials); WRITE_FROM_CACHE(events, npLO); WRITE_FROM_CACHE(events, npNLO); } void write_event_particles() { auto particles = file.getGroup("particle"); // choose arbitrary dataset to find size const auto dataset = particles.getDataSet("id"); const size_t size = dataset.getSpace().getDimensions().front(); resize_particle_group(size + cache.id.size()); WRITE_FROM_CACHE(particles, id); WRITE_FROM_CACHE(particles, status); WRITE_FROM_CACHE(particles, lifetime); WRITE_FROM_CACHE(particles, spin); WRITE_FROM_CACHE(particles, mother1); WRITE_FROM_CACHE(particles, mother2); WRITE_FROM_CACHE(particles, color1); WRITE_FROM_CACHE(particles, color2); WRITE_FROM_CACHE(particles, px); WRITE_FROM_CACHE(particles, py); WRITE_FROM_CACHE(particles, pz); WRITE_FROM_CACHE(particles, e); WRITE_FROM_CACHE(particles, m); } #undef WRITE_FROM_CACHE ~HDF5WriterImpl(){ dump_cache(); auto proc_info = file.getGroup("procInfo"); write_dataset(proc_info, "xSection", heprup.XSECUP); write_dataset(proc_info, "error", heprup.XERRUP); write_dataset(proc_info, "unitWeight", heprup.XMAXUP); } }; HDF5Writer::HDF5Writer(std::string const & filename, LHEF::HEPRUP heprup): impl_{new HDF5Writer::HDF5WriterImpl{filename, std::move(heprup)}} {} void HDF5Writer::write(Event const & ev){ impl_->write(ev); } } #else // no HDF5 support namespace HEJ{ class HDF5Writer::HDF5WriterImpl{}; HDF5Writer::HDF5Writer(std::string const &, LHEF::HEPRUP){ throw std::invalid_argument{ "Failed to create HDF5 writer: " "HEJ 2 was built without HDF5 support" }; } void HDF5Writer::write(Event const &){ assert(false); } } #endif namespace HEJ { HDF5Writer::~HDF5Writer() = default; } diff --git a/src/HepMC2Interface.cc b/src/HepMC2Interface.cc index 9e11d78..e63f1d1 100644 --- a/src/HepMC2Interface.cc +++ b/src/HepMC2Interface.cc @@ -1,156 +1,156 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC2Interface.hh" #include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HepMC2 #include <math.h> #include <utility> #include "HEJ/detail/HepMCInterface_common.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "LHEF/LHEF.h" #include "HepMC/GenCrossSection.h" #include "HepMC/GenEvent.h" #include "HepMC/GenParticle.h" #include "HepMC/GenVertex.h" namespace HEJ{ namespace detail_HepMC { template<> struct HepMCVersion<2> { using GenEvent = HepMC::GenEvent; using Beam = std::array<HepMC::GenParticle*,2>; }; template<> auto make_particle_ptr<2> ( Particle const & sp, int status ) { return new HepMC::GenParticle( to_FourVector<HepMC::FourVector>(sp), static_cast<int> (sp.type), status ); } template<> auto make_vx_ptr<2>() { return new HepMC::GenVertex(); } } HepMC2Interface::HepMC2Interface(LHEF::HEPRUP const & heprup): event_count_(0.), tot_weight_(0.), tot_weight2_(0.) { beam_particle_[0] = static_cast<ParticleID>(heprup.IDBMUP.first); beam_particle_[1] = static_cast<ParticleID>(heprup.IDBMUP.second); beam_energy_[0] = heprup.EBMUP.first; beam_energy_[1] = heprup.EBMUP.second; } HepMC::GenCrossSection HepMC2Interface::cross_section() const { HepMC::GenCrossSection xs; xs.set_cross_section(tot_weight_, sqrt(tot_weight2_)); return xs; } HepMC::GenEvent HepMC2Interface::init_event(Event const & event) const { const std::array<HepMC::GenParticle*,2> beam { new HepMC::GenParticle( HepMC::FourVector(0,0,-beam_energy_[0],beam_energy_[0]), beam_particle_[0], detail_HepMC::status_beam ), new HepMC::GenParticle( HepMC::FourVector(0,0, beam_energy_[1],beam_energy_[1]), beam_particle_[1], detail_HepMC::status_beam ) }; auto hepmc_ev{ detail_HepMC::HepMC_init_kinematics<2>( event, beam, HepMC::GenEvent{ HepMC::Units::GEV, HepMC::Units::MM } ) }; hepmc_ev.weights().push_back( event.central().weight ); for(auto const & var: event.variations()){ hepmc_ev.weights().push_back( var.weight ); // no weight name for HepMC2 since rivet3 seem to mix them up // (could be added via hepmc_ev.weights()[name]=weight) } return hepmc_ev; } void HepMC2Interface::set_central( HepMC::GenEvent & out_ev, Event const & event, ssize_t const weight_index ){ EventParameters event_param; if(weight_index < 0) event_param = event.central(); else if ( static_cast<size_t>(weight_index) < event.variations().size()) event_param = event.variations(weight_index); else throw std::invalid_argument{ "HepMC2Interface tried to access a weight outside of the variation range." }; const double wt = event_param.weight; tot_weight_ += wt; tot_weight2_ += wt * wt; ++event_count_; // central always on first assert(out_ev.weights().size() == event.variations().size()+1); out_ev.weights()[0] = wt; out_ev.set_cross_section( cross_section() ); out_ev.set_signal_process_id(event.type()); out_ev.set_event_scale(event_param.mur); out_ev.set_event_number(event_count_); /// @TODO add alphaQCD (need function) and alphaQED /// @TODO output pdf (currently not avaiable from event alone) } HepMC::GenEvent HepMC2Interface::operator()(Event const & event, ssize_t const weight_index ){ HepMC::GenEvent out_ev(init_event(event)); set_central(out_ev, event, weight_index); return out_ev; } } #else // no HepMC2 => empty class namespace HepMC { class GenEvent {}; class GenCrossSection {}; } namespace HEJ{ HepMC2Interface::HepMC2Interface(LHEF::HEPRUP const &){ throw std::invalid_argument( "Failed to create HepMC2Interface: " "HEJ 2 was built without HepMC2 support" ); } HepMC::GenEvent HepMC2Interface::operator()(Event const &, ssize_t) {return HepMC::GenEvent();} HepMC::GenEvent HepMC2Interface::init_event(Event const &) const {return HepMC::GenEvent();} void HepMC2Interface::set_central(HepMC::GenEvent &, Event const &, ssize_t){} HepMC::GenCrossSection HepMC2Interface::cross_section() const {return HepMC::GenCrossSection();} } #endif namespace HEJ{ HepMC2Interface::~HepMC2Interface() = default; } diff --git a/src/HepMC2Writer.cc b/src/HepMC2Writer.cc index 8cf5076..78824d3 100644 --- a/src/HepMC2Writer.cc +++ b/src/HepMC2Writer.cc @@ -1,82 +1,82 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC2Writer.hh" #include <cassert> #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HepMC2 #include "HepMC/IO_GenEvent.h" #include <utility> #include "HepMC/GenParticle.h" #include "HepMC/GenVertex.h" #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #include "HEJ/HepMC2Interface.hh" namespace HEJ{ struct HepMC2Writer::HepMC2WriterImpl{ HepMC2Interface hepmc_; HepMC2WriterImpl & operator=(HepMC2WriterImpl const & other) = delete; HepMC2WriterImpl(HepMC2WriterImpl const & other) = delete; HepMC2WriterImpl & operator=(HepMC2WriterImpl && other) = delete; HepMC2WriterImpl(HepMC2WriterImpl && other) = delete; HepMC::IO_GenEvent writer_; HepMC2WriterImpl( std::string const & file, LHEF::HEPRUP && heprup ): hepmc_(heprup), writer_{file} {} void write(Event const & ev){ auto out_ev = hepmc_(ev); writer_.write_event(&out_ev); } }; HepMC2Writer::HepMC2Writer(std::string const & file, LHEF::HEPRUP heprup): impl_{new HepMC2WriterImpl{file, std::move(heprup)}} {} void HepMC2Writer::write(Event const & ev){ impl_->write(ev); } } // namespace HEJ #else // no HepMC2 namespace HEJ{ class HepMC2Writer::HepMC2WriterImpl{}; HepMC2Writer::HepMC2Writer(std::string const &, LHEF::HEPRUP){ throw std::invalid_argument( "Failed to create HepMC writer: " "HEJ 2 was built without HepMC2 support" ); } void HepMC2Writer::write(Event const &){ assert(false); } } #endif namespace HEJ{ HepMC2Writer::~HepMC2Writer() = default; } diff --git a/src/HepMC3Interface.cc b/src/HepMC3Interface.cc index 91e9405..84beaee 100644 --- a/src/HepMC3Interface.cc +++ b/src/HepMC3Interface.cc @@ -1,203 +1,203 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC3Interface.hh" #include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HepMC3 #include <math.h> #include <utility> #include "HEJ/detail/HepMCInterface_common.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "LHEF/LHEF.h" #include "HepMC3/GenCrossSection.h" #include "HepMC3/GenEvent.h" #include "HepMC3/GenParticle.h" #include "HepMC3/GenRunInfo.h" #include "HepMC3/GenVertex.h" #include "HepMC3/LHEFAttributes.h" namespace HEJ{ namespace detail_HepMC { template<> struct HepMCVersion<3> { using GenEvent = HepMC3::GenEvent; using Beam = std::array<HepMC3::GenParticlePtr,2>; }; template<> auto make_particle_ptr<3> ( Particle const & sp, int status ) { return HepMC3::make_shared<HepMC3::GenParticle>( to_FourVector<HepMC3::FourVector>(sp), static_cast<int> (sp.type), status ); } template<> auto make_vx_ptr<3>() { return HepMC3::make_shared<HepMC3::GenVertex>(); } } namespace { void reset_weight_info(LHEF::HEPRUP & heprup){ heprup.IDWTUP = 2; // use placeholders for unknown init block values // we can overwrite them after processing all events heprup.XSECUP = {0.}; heprup.XERRUP = {0.}; heprup.XMAXUP = {0.}; } HepMC3::shared_ptr<HepMC3::GenRunInfo> init_runinfo(LHEF::HEPRUP heprup){ reset_weight_info(heprup); auto runinfo{ HepMC3::make_shared<HepMC3::GenRunInfo>() }; auto hepr{ HepMC3::make_shared<HepMC3::HEPRUPAttribute>() }; hepr->heprup = heprup; runinfo->add_attribute(std::string("HEPRUP"), hepr); for(auto const & gen: heprup.generators){ runinfo->tools().emplace_back( HepMC3::GenRunInfo::ToolInfo{gen.name, gen.version, gen.contents} ); } return runinfo; } std::vector<std::string> get_weight_names(Event const & ev){ std::vector<std::string> names; names.reserve(ev.variations().size()+1); // +1 from central names.emplace_back(""); // rivet assumes central band to have no name for( size_t i=0; i<ev.variations().size(); ++i ){ auto const & var{ ev.variations()[i] }; if(var.description){ names.emplace_back( to_simple_string(*var.description) ); } else { names.emplace_back( "" ); } } assert(names.size() == ev.variations().size()+1); return names; } } // namespace anonymous HepMC3Interface::HepMC3Interface(LHEF::HEPRUP const & heprup): run_info{ init_runinfo(heprup) }, event_count_(0.), tot_weight_(0.), tot_weight2_(0.), xs_{std::make_shared<HepMC3::GenCrossSection>()} { beam_particle_[0] = static_cast<ParticleID>(heprup.IDBMUP.first); beam_particle_[1] = static_cast<ParticleID>(heprup.IDBMUP.second); beam_energy_[0] = heprup.EBMUP.first; beam_energy_[1] = heprup.EBMUP.second; } HepMC3::GenEvent HepMC3Interface::init_event(Event const & event) const { const std::array<HepMC3::GenParticlePtr,2> beam { HepMC3::make_shared<HepMC3::GenParticle>( HepMC3::FourVector(0,0,-beam_energy_[0],beam_energy_[0]), beam_particle_[0], detail_HepMC::status_beam ), HepMC3::make_shared<HepMC3::GenParticle>( HepMC3::FourVector(0,0, beam_energy_[1],beam_energy_[1]), beam_particle_[1], detail_HepMC::status_beam ) }; auto hepmc_ev{ detail_HepMC::HepMC_init_kinematics<3>( event, beam, HepMC3::GenEvent{ HepMC3::Units::GEV, HepMC3::Units::MM } ) }; // set up run specific informations if( run_info->weight_names().size() != event.variations().size()+1 ){ run_info->set_weight_names( get_weight_names(event) ); } // order matters: weights in hepmc_ev initialised when registering run_info hepmc_ev.set_run_info(run_info); assert(hepmc_ev.weights().size() == event.variations().size()+1); for(size_t i=0; i<event.variations().size(); ++i){ hepmc_ev.weights()[i+1] = event.variations()[i].weight; //! @TODO set variation specific cross section //! the problem is that set_cross_section overwrites everything } return hepmc_ev; } void HepMC3Interface::set_central(HepMC3::GenEvent & out_ev, Event const & event, ssize_t const weight_index ){ EventParameters event_param; if(weight_index < 0) event_param = event.central(); else if ( static_cast<size_t>(weight_index) < event.variations().size()) event_param = event.variations(weight_index); else throw std::invalid_argument{ "HepMC3Interface tried to access a weight outside of the variation range." }; const double wt = event_param.weight; tot_weight_ += wt; tot_weight2_ += wt * wt; ++event_count_; // central always on first assert(out_ev.weights().size() == event.variations().size()+1); out_ev.weights()[0] = wt; // out_ev can be setup with a different central scale -> save xs manually out_ev.set_cross_section(xs_); assert(out_ev.cross_section() && out_ev.cross_section() == xs_); // overwrites all previously set xs ... xs_->set_cross_section(tot_weight_,sqrt(tot_weight2_)); out_ev.set_event_number(event_count_); /// @TODO add number of attempted events xs_->set_accepted_events(event_count_); /// @TODO add alphaQCD (need function) and alphaQED /// @TODO output pdf (currently not avaiable from event alone) } HepMC3::GenEvent HepMC3Interface::operator()(Event const & event, ssize_t const weight_index ){ HepMC3::GenEvent out_ev(init_event(event)); set_central(out_ev, event, weight_index); return out_ev; } } #else // no HepMC3 => empty class namespace HepMC3 { class GenEvent {}; class GenCrossSection {}; class GenRunInfo {}; } namespace HEJ{ HepMC3Interface::HepMC3Interface(LHEF::HEPRUP const &){ throw std::invalid_argument( "Failed to create HepMC3Interface: " "HEJ 2 was built without HepMC3 support" ); } HepMC3::GenEvent HepMC3Interface::operator()(Event const &, ssize_t) {return HepMC3::GenEvent();} HepMC3::GenEvent HepMC3Interface::init_event(Event const &) const {return HepMC3::GenEvent();} void HepMC3Interface::set_central(HepMC3::GenEvent &, Event const &, ssize_t){} } #endif namespace HEJ{ HepMC3Interface::~HepMC3Interface() = default; } diff --git a/src/HepMC3Writer.cc b/src/HepMC3Writer.cc index 6657d87..b8ae99a 100644 --- a/src/HepMC3Writer.cc +++ b/src/HepMC3Writer.cc @@ -1,94 +1,94 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC3Writer.hh" #include <cassert> #include "LHEF/LHEF.h" #include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HepMC3 #include "HepMC3/WriterAscii.h" #include <utility> #include "HEJ/Event.hh" #include "HEJ/HepMC3Interface.hh" namespace HEJ{ struct HepMC3Writer::HepMC3WriterImpl{ HepMC3Interface HepMC3_; HepMC3WriterImpl & operator=(HepMC3WriterImpl const & other) = delete; HepMC3WriterImpl(HepMC3WriterImpl const & other) = delete; HepMC3WriterImpl & operator=(HepMC3WriterImpl && other) = delete; HepMC3WriterImpl(HepMC3WriterImpl && other) = delete; std::unique_ptr<HepMC3::WriterAscii> writer_; std::string const file_; HepMC3WriterImpl( std::string const & file, LHEF::HEPRUP && heprup ): HepMC3_{std::move(heprup)}, file_{file} {} void init_writer(){ writer_ = std::make_unique<HepMC3::WriterAscii>(file_, HepMC3_.run_info); } ~HepMC3WriterImpl(){ if(!writer_) // make sure that we always write something init_writer(); writer_->close(); } void write(Event const & ev){ auto out_ev = HepMC3_(ev); //! weight names are only available after first event if(!writer_) init_writer(); writer_->write_event(out_ev); } }; HepMC3Writer::HepMC3Writer(std::string const & file, LHEF::HEPRUP heprup): impl_{ std::make_unique<HepMC3WriterImpl>(file, std::move(heprup)) } {} void HepMC3Writer::write(Event const & ev){ impl_->write(ev); } } // namespace HEJ #else // no HepMC3 namespace HEJ{ class HepMC3Writer::HepMC3WriterImpl{}; HepMC3Writer::HepMC3Writer(std::string const &, LHEF::HEPRUP){ throw std::invalid_argument( "Failed to create HepMC3 writer: " "HEJ 2 was built without HepMC3 support" ); } void HepMC3Writer::write(Event const &){ assert(false); } } #endif namespace HEJ{ HepMC3Writer::~HepMC3Writer() = default; } diff --git a/src/Hjets.cc b/src/Hjets.cc index 568eeae..82ff293 100644 --- a/src/Hjets.cc +++ b/src/Hjets.cc @@ -1,1087 +1,1087 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/jets.hh" #include "HEJ/Hjets.hh" #include <assert.h> #include <limits> #include "HEJ/Constants.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_QCDLOOP #include "qcdloop/qcdloop.h" #endif const COM looprwfactor = (COM(0.,1.)*M_PI*M_PI)/pow((2.*M_PI),4); constexpr double infinity = std::numeric_limits<double>::infinity(); namespace { // Loop integrals #ifdef HEJ_BUILD_WITH_QCDLOOP COM B0DD(HLV q, double mq) { static std::vector<std::complex<double>> result(3); static auto ql_B0 = [](){ ql::Bubble<std::complex<double>,double,double> ql_B0; ql_B0.setCacheSize(100); return ql_B0; }(); static std::vector<double> masses(2); static std::vector<double> momenta(1); for(auto & m: masses) m = mq*mq; momenta.front() = q.m2(); ql_B0.integral(result, 1, masses, momenta); return result[0]; } COM C0DD(HLV q1, HLV q2, double mq) { static std::vector<std::complex<double>> result(3); static auto ql_C0 = [](){ ql::Triangle<std::complex<double>,double,double> ql_C0; ql_C0.setCacheSize(100); return ql_C0; }(); static std::vector<double> masses(3); static std::vector<double> momenta(3); for(auto & m: masses) m = mq*mq; momenta[0] = q1.m2(); momenta[1] = q2.m2(); momenta[2] = (q1+q2).m2(); ql_C0.integral(result, 1, masses, momenta); return result[0]; } COM D0DD(HLV q1,HLV q2, HLV q3, double mq) { static std::vector<std::complex<double>> result(3); static auto ql_D0 = [](){ ql::Box<std::complex<double>,double,double> ql_D0; ql_D0.setCacheSize(100); return ql_D0; }(); static std::vector<double> masses(4); static std::vector<double> momenta(6); for(auto & m: masses) m = mq*mq; momenta[0] = q1.m2(); momenta[1] = q2.m2(); momenta[2] = q3.m2(); momenta[3] = (q1+q2+q3).m2(); momenta[4] = (q1+q2).m2(); momenta[5] = (q2+q3).m2(); ql_D0.integral(result, 1, masses, momenta); return result[0]; } COM A1(HLV q1, HLV q2, double mt) // As given in Eq. (B.2) of VDD { double q12,q22,Q2; HLV Q; double Delta3,mt2; COM ans(COM(0.,0.)); q12=q1.m2(); q22=q2.m2(); Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD Q2=Q.m2(); Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2; assert(mt > 0.); mt2=mt*mt; ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 4.*mt2/Delta3*(Q2-q12-q22) -1.-4.*q12*q22/Delta3-12.*q12*q22*Q2/Delta3/Delta3*(q12+q22-Q2) ) - looprwfactor*COM(0,-1)*( B0DD(q2,mt)-B0DD(Q,mt) ) * ( 2.*q22/Delta3+12.*q12*q22/Delta3/Delta3*(q22-q12+Q2) ) - looprwfactor*COM(0,-1)*( B0DD(q1,mt)-B0DD(Q,mt) ) * ( 2.*q12/Delta3+12.*q12*q22/Delta3/Delta3*(q12-q22+Q2) ) - 2./Delta3/16/M_PI/M_PI*(q12+q22-Q2); return ans; } COM A2(HLV q1, HLV q2, double mt) // As given in Eq. (B.2) of VDD, but with high energy limit // of invariants taken. { double q12,q22,Q2; HLV Q; double Delta3,mt2; COM ans(COM(0.,0.)); assert(mt > 0.); mt2=mt*mt; q12=q1.m2(); q22=q2.m2(); Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD Q2=Q.m2(); Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2; ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 2.*mt2+1./2.*(q12+q22-Q2) +2.*q12*q22*Q2/Delta3 ) +looprwfactor*COM(0,-1)*(B0DD(q2,mt)-B0DD(Q,mt)) *q22*(q22-q12-Q2)/Delta3 +looprwfactor*COM(0,-1)*(B0DD(q1,mt)-B0DD(Q,mt)) *q12*(q12-q22-Q2)/Delta3+1./16/M_PI/M_PI; return ans; } #else // no QCDloop COM A1(HLV, HLV, double) { throw std::logic_error{"A1 called without QCDloop support"}; } COM A2(HLV, HLV, double) { throw std::logic_error{"A2 called without QCDloop support"}; } #endif void to_current(const HLV & q, current & ret){ ret[0]=q.e(); ret[1]=q.x(); ret[2]=q.y(); ret[3]=q.z(); } /** * @brief Higgs vertex contracted with current @param C1 and @param C2 */ COM cHdot(const current & C1, const current & C2, const current & q1, const current & q2, double mt, bool incBot, double mb, double vev) { if (mt == infinity) { return (cdot(C1,C2)*cdot(q1,q2)-cdot(C1,q2)*cdot(C2,q1))/(3*M_PI*vev); } else { HLV vq1,vq2; vq1.set(q1[1].real(),q1[2].real(),q1[3].real(),q1[0].real()); vq2.set(q2[1].real(),q2[2].real(),q2[3].real(),q2[0].real()); // first minus sign obtained because of q1-difference to VDD // Factor is because 4 mt^2 g^2/vev A1 -> 16 pi mt^2/vev alphas, if(!(incBot)) return 16.*M_PI*mt*mt/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt) -cdot(C1,C2)*A2(-vq1,vq2,mt)); else return 16.*M_PI*mt*mt/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt) -cdot(C1,C2)*A2(-vq1,vq2,mt)) + 16.*M_PI*mb*mb/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mb) -cdot(C1,C2)*A2(-vq1,vq2,mb)); } } //@{ /** * @brief Higgs+Jets FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. (W emission) * @param p1in Incoming Particle 1. (W emission) * @param p2out Outgoing Particle 2 (Quark, unordered emission this side.) * @param p2in Incoming Particle 2 (Quark, unordered emission this side.) * @param q1 t-channel momenta into higgs vertex * @param q2 t-channel momenta out of higgs vertex * @param mt top mass (inf or value) * @param incBot Bool, to include bottom mass (true) or not (false)? * @param mb bottom mass (value) * @param pg Unordered Gluon momenta * * Calculates j^\mu H j_\mu. FKL with higgs vertex somewhere in the FKL chain. * Handles all possible incoming states. */ double j_h_j(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & q1, HLV const & q2, double mt, bool incBot, double mb, double vev ){ current j1p,j1m,j2p,j2m, q1v, q2v; // Note need to flip helicities in anti-quark case. joi(p1out, false, p1in, false, j1p); joi(p1out, true, p1in, true, j1m); joi(p2out, false, p2in, false, j2p); joi(p2out, true, p2in, true, j2m); to_current(q1, q1v); to_current(q2, q2v); COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb, vev); COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb, vev); COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb, vev); COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb, vev); // average over helicities const double sst=(abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm))/4.; return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2()); } } // namespace anonymous double ME_H_qQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev); } double ME_H_qQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev); } double ME_H_qbarQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev); } double ME_H_qbarQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev); } double ME_H_qg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev) * K_g(p2out,p2in)/HEJ::C_A; } double ME_H_qbarg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev) * K_g(p2out,p2in)/HEJ::C_A; } double ME_H_gg(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV q1, HLV q2, double mt, bool incBot, double mb, double vev){ return j_h_j(p1out, p1in, p2out, p2in, q1, q2, mt, incBot, mb, vev) * K_g(p2out,p2in)/HEJ::C_A * K_g(p1out,p1in)/HEJ::C_A; } //@} namespace { //@{ /// @brief Higgs vertex contracted with one current CCurrent jH(HLV const & pout, bool helout, HLV const & pin, bool helin, HLV const & q1, HLV const & q2, double mt, bool incBot, double mb, double vev) { CCurrent j2 = joi(pout,helout,pin,helin); CCurrent jq2(q2.e(),q2.px(),q2.py(),q2.pz()); if(mt == infinity) return ((q1.dot(q2))*j2 - j2.dot(q1)*jq2)/(3*M_PI*vev); else { if(incBot) return (-16.*M_PI*mb*mb/vev*j2.dot(q1)*jq2*A1(-q1,q2,mb) -16.*M_PI*mb*mb/vev*j2*A2(-q1,q2,mb)) + (-16.*M_PI*mt*mt/vev*j2.dot(q1)*jq2*A1(-q1,q2,mt) -16.*M_PI*mt*mt/vev*j2*A2(-q1,q2,mt)); else return (-16.*M_PI*mt*mt/vev*j2.dot(q1)*jq2*A1(-q1,q2,mt) -16.*M_PI*mt*mt/vev*j2*A2(-q1,q2,mt)); } } //@} //@{ /** * @brief Higgs+Jets Unordered Contributions, function to handle all incoming types. * @param pg Unordered Gluon momenta * @param p1out Outgoing Particle 1. (W emission) * @param p1in Incoming Particle 1. (W emission) * @param p2out Outgoing Particle 2 (Quark, unordered emission this side.) * @param p2in Incoming Particle 2 (Quark, unordered emission this side.) * @param q1 t-channel momenta into higgs vertex * @param q2 t-channel momenta out of higgs vertex * @param mt top mass (inf or value) * @param incBot Bool, to include bottom mass (true) or not (false)? * @param mb bottom mass (value) * * Calculates j_{uno}^\mu H j_\mu. Unordered with higgs vertex * somewhere in the FKL chain. Handles all possible incoming states. */ double juno_h_j(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool incBot, double mb, double vev ){ // This construction is taking rapidity order: pg > p1out >> p2out HLV q1=p1in-p1out; // Top End HLV q2=-(p2in-p2out); // Bottom End HLV qg=p1in-p1out-pg; // Extra bit post-gluon // Note <p1|eps|pa> current split into two by gauge choice. // See James C's Thesis (p72). <p1|eps|pa> -> <p1|pg><pg|pa> CCurrent mj1p=joi(p1out,false, p1in,false); CCurrent mj1m=joi(p1out, true, p1in, true); CCurrent jgap=joi(pg, false, p1in,false); CCurrent jgam=joi(pg, true, p1in, true); // Note for function joo(): <p1+|pg+> = <pg-|p1->. CCurrent j2gp=joo(p1out, false, pg, false); CCurrent j2gm=joo(p1out, true, pg, true); CCurrent mjH2p=jH(p2out, true,p2in, true,qH1,qH2,mt,incBot,mb, vev); CCurrent mjH2m=jH(p2out,false,p2in,false,qH1,qH2,mt,incBot,mb, vev); // Dot products of these which occur again and again COM MHmp=mj1m.dot(mjH2p); COM MHmm=mj1m.dot(mjH2m); COM MHpp=mj1p.dot(mjH2p); COM MHpm=mj1p.dot(mjH2m); CCurrent p2o(p2out), p2i(p2in), p1o(p1out), p1i(p1in), qsum(q1+qg); CCurrent Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m + 2.*mj1m.dot(pg)*mjH2m + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHmm/2.) )/q1.m2(); CCurrent Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m + 2.*mj1m.dot(pg)*mjH2p + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHmp/2.) )/q1.m2(); CCurrent Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p + 2.*mj1p.dot(pg)*mjH2m + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHpm/2.) )/q1.m2(); CCurrent Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p + 2.*mj1p.dot(pg)*mjH2p + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHpp/2.) )/q1.m2(); CCurrent U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2(); CCurrent U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2(); CCurrent U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2(); CCurrent U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2(); CCurrent U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2(); CCurrent U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2(); CCurrent U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2(); CCurrent U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2(); constexpr double cf=HEJ::C_F; double amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm); double amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp); double apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm); double app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp); double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2()); // Now add the t-channels for the Higgs ampsq/=qH1.m2()*qg.m2(); ampsq/=16.; // Factor of (Cf/Ca) for each quark to match ME_H_qQ. ampsq*=HEJ::C_F*HEJ::C_F/HEJ::C_A/HEJ::C_A; return ampsq; } } // namespace anonymous double ME_H_unob_qQ(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev); } double ME_H_unob_qbarQ(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev); } double ME_H_unob_qQbar(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev); } double ME_H_unob_qbarQbar(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev); } double ME_H_unob_gQ(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev) *K_g(p2out,p2in)/HEJ::C_F; } double ME_H_unob_gQbar(HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV qH1, HLV qH2, double mt, bool incBot, double mb, double vev){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, vev) *K_g(p2out,p2in)/HEJ::C_F; } //@} // Begin finite mass stuff #ifdef HEJ_BUILD_WITH_QCDLOOP namespace { // All the stuff needed for the box functions in qg->qgH now... COM E1(HLV k1, HLV k2, HLV kh, double mq){ HLV q2=-(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*(1 - 8.*mq*mq/s12 + S2/(2.*s12) + S2*(s12 - 8.*mq*mq)*(s34 + S1)/(2.*s12*Delta) + 2.*(s34 + S1)*(s34 + S1)/Delta + S2*pow((s34 + S1),3)/Delta/Delta) - ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) + 2.*(s34 + S1)/Delta + S2*pow((s34 + S1),2)/Delta/Delta) + (C0DD(k1, q2, mq) - C0DD(k1 + k2, q2, mq))*(1. - 4.*mq*mq/s12) - C0DD(k1 + k2, q2, mq)*2.*s34/ S1 - (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*s34*(s34 + S1)/(S1*Delta) + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(2.*s34*(s34 + S1)*(S1 - S2)/(Delta*Sigma) + 2.*s34*(s34 + S1)/(S1*Delta)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 + S1)*(2.*s12*s34 - S2*(S1 + S2))/(Delta*Sigma)); } COM F1(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor*(-S2*D0DD(k1, k2, q2, mq)*(0.5 - (s12 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) - s12*pow((s34 + S2),3)/Delta/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) + S2*pow((s34 + S2),2)/Delta/Delta) - (C0DD(k1 + k2, q2, mq) - C0DD(k1, k2 + q2, mq))*(1. - 4.*mq*mq/s12) - C0DD(k1, k2 + q2, mq) + (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*pow((s34 + S2),2)/((s12 + S1)*Delta) - (B0DD( q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*2.*s34*(s34 + S2)*(S2 - S1)/(Delta*Sigma) + (B0DD( k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 + S2)*(2.*s12*s34 - S2*(S1 + S2))/(Delta*Sigma)); } COM G1(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; return looprwfactor*(S2*D0DD(k1, q2, k2, mq)*(Delta/s12/s12 - 4.*mq*mq/s12) - S2*((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./ s12/s12 - (s12 - 4.*mq*mq)/(2.*s12*Delta)) - S2*((s12 + S2)*C0DD(k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./ s12/s12 + (s12 - 4.*mq*mq)/(2.*s12*Delta)) - C0DD(k1, q2, mq) - (C0DD(k1, k2 + q2, mq) - C0DD(k1, q2, mq))*4.*mq*mq/ s12 + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2./ s12 + (B0DD(k1 + q2, mq) - B0DD(q2, mq))*2.*s34/(s12*S1) + (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*(s34 + S2)/(s12*(s12 + S1))); } COM E4(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor* (-s12*D0DD(k2, k1, q2, mq)*(0.5 - (S1 - 8.*mq*mq)*(s34 + S1)/(2.*Delta) - s12*pow((s34 + S1),3)/Delta/Delta) + ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) + s12*pow((s34 + S1),2)/Delta/Delta) - C0DD(k1 + k2, q2, mq) + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(2.*s34/Delta + 2.*s12*(s34 + S1)/((s12 + S2)*Delta)) - (B0DD( q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*((2.*s34*(2.*s12*s34 - S2*(S1 + S2) + s12*(S1 - S2)))/(Delta*Sigma)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq)) *((2.*s12*(2.*s12*s34 - S1*(S1 + S2) + s34*(S2 - S1)))/(Delta*Sigma))); } COM F4(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor* (-s12*D0DD(k1, k2, q2, mq)*(0.5 + (S1 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) + s12*pow((s34 + S2),3)/Delta/Delta) - ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) + s12*pow((s34 + S2),2)/Delta/Delta) - C0DD(k1 + k2, q2, mq) - (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*(s34 + S2)/Delta + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*2.*s34*(2.*s12*s34 - S1*(S1 + S2) + s12*(S2 - S1))/(Delta*Sigma) - (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq)) *(2.*s12*(2.*s12*s34 - S2*(S1 + S2) + s34*(S1 - S2))/(Delta*Sigma))); } COM G4(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; return looprwfactor* (-D0DD(k1, q2, k2, mq)*(Delta/s12 + (s12 + S1)/2. - 4.*mq*mq) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./ s12 - (S1 - 4.*mq*mq)/(2.*Delta)) + ((s12 + S2)*C0DD( k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./ s12 + (S1 - 4.*mq*mq)/(2.*Delta)) + (B0DD( k1 + k2 + q2, mq) - B0DD(k1 + q2, mq))*2./(s12 + S2)); } COM E10(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*((s34 + S1)/Delta + 12.*mq*mq*S1*(s34 + S1)/Delta/Delta - 4.*s12*S1*pow((s34 + S1),3)/Delta/Delta/Delta) - ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*(1./Delta + 4.*mq*mq*S1/Delta/Delta - 4.*s12*S1*pow((s34 + S1),2)/Delta/Delta/Delta) + C0DD(k1 + k2, q2, mq)*(4.*s12*s34*(S1 - S2)/(Delta*Sigma) - 4.*(s12 - 2.*mq*mq)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)) + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(4.*(s34 + S1)/((s12 + S2)*Delta) + 8.*S1*(s34 + S1)/Delta/Delta) + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(12.*s34*(2.*s12 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) - 4.*s34*(4.*s12 + 3.*S1 + S2)/(Delta*Sigma) + 8.*s12*s34*(s34*(s12 + S2) - S1*(s34 + S1))/(Delta*Delta*Sigma)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(12.*s12*(2.*s34 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) + 8.*s12*S1*(s34*(s12 + S2) - S1*(s34 + S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)); } COM F10(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, Sigma, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; Sigma = 4.*s12*s34 - pow(S1+S2,2); return looprwfactor* (s12*D0DD(k1, k2, q2, mq)*((s34 + S2)/Delta - 4.*mq*mq/Delta + 12.*mq*mq*s34*(s12 + S1)/Delta/Delta - 4.*s12*pow((s34 + S2),2)/Delta/Delta - 4.*s12*S1*pow((s34 + S2),3)/Delta/Delta/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*(1./Delta + 4.*mq*mq*S1/Delta/Delta - 4.*s12*(s34 + S2)/Delta/Delta - 4.*s12*S1*pow((s34 + S2),2)/Delta/Delta/Delta) - C0DD(k1 + k2, q2, mq)*(4.*s12*s34/(S2*Delta) + 4.*s12*s34*(S2 - S1)/(Delta*Sigma) + 4.*(s12 - 2.*mq*mq)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)) - (B0DD( k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(4.*s34/(S2*Delta) + 8.*s34*(s12 + S1)/Delta/Delta) - (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(-12*s34*(2*s12 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) - 4.*s12*s34*s34/(S2*Delta*Delta) + 4.*s34*S1/(Delta*Sigma) - 4.*s34*(s12*s34*(2.*s12 + S2) - S1*S1*(2.*s12 + S1))/(Delta*Delta*Sigma)) - (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(-12.*s12*(2.*s34 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) + 8.*s12*(2.*s34 + S1)/(Delta*Sigma) - 8.*s12*s34*(2.*s12*s34 - S1*(S1 + S2) + s12*(S2 - S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)); } COM G10(HLV k1, HLV k2, HLV kh, double mq){ HLV q2 = -(k1+k2+kh); double Delta, S1, S2, s12, s34; S1 = 2.*k1.dot(q2); S2 = 2.*k2.dot(q2); s12 = 2.*k1.dot(k2); s34 = q2.m2(); Delta = s12*s34 - S1*S2; return looprwfactor* (-D0DD(k1, q2, k2, mq)*(1. + 4.*S1*mq*mq/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./Delta + 4.*S1*mq*mq/Delta/Delta) - ((s12 + S2)*C0DD(k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./Delta + 4.*S1*mq*mq/Delta/Delta) + (B0DD(k1 + k2 + q2, mq) - B0DD(k1 + q2, mq))*4.*(s34 + S1)/(Delta*(s12 + S2)) + (B0DD(q2, mq) - B0DD(k2 + q2, mq))*4.*s34/(Delta*S2)); } COM H1(HLV k1, HLV k2, HLV kh, double mq){ return E1(k1,k2,kh,mq)+F1(k1,k2,kh,mq)+G1(k1,k2,kh,mq); } COM H4(HLV k1, HLV k2, HLV kh, double mq){ return E4(k1,k2,kh,mq)+F4(k1,k2,kh,mq)+G4(k1,k2,kh,mq); } COM H10(HLV k1, HLV k2, HLV kh, double mq){ return E10(k1,k2,kh,mq)+F10(k1,k2,kh,mq)+G10(k1,k2,kh,mq); } COM H2(HLV k1, HLV k2, HLV kh, double mq){ return -1.*H1(k2,k1,kh,mq); } COM H5(HLV k1, HLV k2, HLV kh, double mq){ return -1.*H4(k2,k1,kh,mq); } COM H12(HLV k1, HLV k2, HLV kh, double mq){ return -1.*H10(k2,k1,kh,mq); } // FL and FT functions COM FL(HLV q1, HLV q2, double mq){ HLV Q = q1 + q2; double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2); return -1./(2.*detQ2)*((2.- 3.*q1.m2()*q2.dot(Q)/detQ2)*(B0DD(q1, mq) - B0DD(Q, mq)) + (2. - 3.*q2.m2()*q1.dot(Q)/detQ2)*(B0DD(q2, mq) - B0DD(Q, mq)) - (4.*mq*mq + q1.m2() + q2.m2() + Q.m2() - 3.*q1.m2()*q2.m2()*Q.m2()/detQ2)*C0DD( q1, q2, mq) - 2.); } COM FT(HLV q1, HLV q2, double mq){ HLV Q = q1 + q2; double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2); return -1./(2.*detQ2)*(Q.m2()*(B0DD(q1, mq) + B0DD(q2, mq) - 2.*B0DD(Q, mq) - 2.*q1.dot(q2)*C0DD(q1, q2, mq)) + (q1.m2() - q2.m2()) *(B0DD(q1, mq) - B0DD(q2, mq))) - q1.dot(q2)*FL(q1, q2, mq); } HLV ParityFlip(HLV p){ HLV flippedVector; flippedVector.setE(p.e()); flippedVector.setX(-p.x()); flippedVector.setY(-p.y()); flippedVector.setZ(-p.z()); return flippedVector; } /// @brief HC amp for qg->qgH with finite top (i.e. j^{++}_H) void g_gH_HC(HLV pa, HLV p1, HLV pH, double mq, double vev, current &retAns) { current cura1,pacur,p1cur,pHcur,conjeps1,conjepsH1,epsa,epsHa,epsHapart1, epsHapart2,conjepsH1part1,conjepsH1part2; COM ang1a,sqa1; const double F = 4.*mq*mq/vev; // Easier to have the whole thing as current object so I can use cdot functionality. // Means I need to write pa,p1 as current objects to_current(pa, pacur); to_current(p1,p1cur); to_current(pH,pHcur); bool gluonforward = true; if(pa.z() < 0) gluonforward = false; //HEJ gauge jio(pa,false,p1,false,cura1); if(gluonforward){ // sqrt(2pa_-/p1_-)*p1_perp/abs(p1_perp) ang1a = sqrt(pa.plus()*p1.minus())*(p1.x()+COM(0.,1.)*p1.y())/p1.perp(); // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp) sqa1 = sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp(); } else { ang1a = sqrt(pa.minus()*p1.plus()); sqa1 = sqrt(pa.minus()*p1.plus()); } const double prop = (pa-p1-pH).m2(); cmult(-1./sqrt(2)/ang1a,cura1,conjeps1); cmult(1./sqrt(2)/sqa1,cura1,epsa); const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2(); const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2(); const COM h4 = H4(p1,-pa,pH,mq); const COM h5 = H5(p1,-pa,pH,mq); const COM h10 = H10(p1,-pa,pH,mq); const COM h12 = H12(p1,-pa,pH,mq); cmult(Fta*pa.dot(pH), epsa, epsHapart1); cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2); cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1); cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2); cadd(epsHapart1, epsHapart2, epsHa); cadd(conjepsH1part1, conjepsH1part2, conjepsH1); const COM aH1 = cdot(pHcur, cura1); current T1,T2,T3,T4,T5,T6,T7,T8,T9,T10; if(gluonforward){ cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1); cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/ang1a, epsHa, T2); } else{ cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus()) *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/sqa1, conjepsH1, T1); cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus()) *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/ang1a, epsHa, T2); } cmult(sqrt(2.)/ang1a*aH1, epsHa, T3); cmult(sqrt(2.)/sqa1*aH1, conjepsH1, T4); cmult(-sqrt(2.)*Fta*pa.dot(p1)*aH1/sqa1, conjeps1, T5); cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/ang1a, epsa, T6); cmult(-aH1/sqrt(2.)/sqa1*h4*8.*COM(0.,1.)*M_PI*M_PI, conjeps1, T7); cmult(aH1/sqrt(2.)/ang1a*h5*8.*COM(0.,1.)*M_PI*M_PI, epsa, T8); cmult(aH1*aH1/2./ang1a/sqa1*h10*8.*COM(0.,1.)*M_PI*M_PI, pacur, T9); cmult(-aH1*aH1/2./ang1a/sqa1*h12*8.*COM(0.,1.)*M_PI*M_PI, p1cur, T10); current ans; for(int i=0;i<4;++i) { ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5[i]+T6[i]+T7[i]+T8[i]+T9[i]+T10[i]; } retAns[0] = F/prop*ans[0]; retAns[1] = F/prop*ans[1]; retAns[2] = F/prop*ans[2]; retAns[3] = F/prop*ans[3]; } /// @brief HNC amp for qg->qgH with finite top (i.e. j^{+-}_H) void g_gH_HNC(HLV pa, HLV p1, HLV pH, double mq, double vev, current &retAns) { const double F = 4.*mq*mq/vev; COM ang1a,sqa1; current conjepsH1,epsHa,p1cur,pacur,pHcur,conjeps1,epsa,paplusp1cur, p1minuspacur,cur1a,cura1,epsHapart1,epsHapart2,conjepsH1part1, conjepsH1part2; // Find here if pa, meaning the gluon, is forward or backward bool gluonforward = true; if(pa.z() < 0) gluonforward = false; jio(pa,true,p1,true,cura1); joi(p1,true,pa,true,cur1a); to_current(pa,pacur); to_current(p1,p1cur); to_current(pH,pHcur); to_current(pa+p1,paplusp1cur); to_current(p1-pa,p1minuspacur); const COM aH1 = cdot(pHcur,cura1); const COM oneHa = std::conj(aH1); // = cdot(pHcur,cur1a) if(gluonforward){ // sqrt(2pa_-/p1_-)*p1_perp/abs(p1_perp) ang1a = sqrt(pa.plus()*p1.minus())*(p1.x()+COM(0.,1.)*p1.y())/p1.perp(); // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp) sqa1 = sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp(); } else { ang1a = sqrt(pa.minus()*p1.plus()); sqa1 = sqrt(pa.minus()*p1.plus()); } const double prop = (pa-p1-pH).m2(); cmult(1./sqrt(2)/sqa1, cur1a, epsa); cmult(-1./sqrt(2)/sqa1, cura1, conjeps1); const COM phase = cdot(conjeps1, epsa); const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2(); const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2(); const COM Falpha = FT(p1-pa,pa-p1-pH,mq); const COM Fbeta = FL(p1-pa,pa-p1-pH,mq); const COM h1 = H1(p1,-pa, pH, mq); const COM h2 = H2(p1,-pa, pH, mq); const COM h4 = H4(p1,-pa, pH, mq); const COM h5 = H5(p1,-pa, pH, mq); const COM h10 = H10(p1,-pa, pH, mq); const COM h12 = H12(p1,-pa, pH, mq); cmult(Fta*pa.dot(pH), epsa, epsHapart1); cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2); cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1); cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2); cadd(epsHapart1, epsHapart2, epsHa); cadd(conjepsH1part1, conjepsH1part2, conjepsH1); current T1,T2,T3,T4,T5a,T5b,T6,T7,T8a,T8b,T9,T10,T11a, T11b,T12a,T12b,T13; if(gluonforward){ cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1); cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/sqa1, epsHa, T2); } else{ cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus())*((p1.x()-COM(0.,1.)*p1.y())/p1.perp()) *prop/sqa1, conjepsH1, T1); cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus())*((p1.x()+COM(0.,1.)*p1.y())/p1.perp()) *prop/sqa1, epsHa, T2); } const COM boxdiagFact = 8.*COM(0.,1.)*M_PI*M_PI; cmult(aH1*sqrt(2.)/sqa1, epsHa, T3); cmult(oneHa*sqrt(2.)/sqa1, conjepsH1, T4); cmult(-2.*phase*Fta*pa.dot(pH), p1cur, T5a); cmult(2.*phase*Ft1*p1.dot(pH), pacur, T5b); cmult(-sqrt(2.)*Fta*p1.dot(pa)*oneHa/sqa1, conjeps1, T6); cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/sqa1, epsa, T7); cmult(-boxdiagFact*phase*h2, pacur, T8a); cmult(boxdiagFact*phase*h1, p1cur, T8b); cmult(boxdiagFact*aH1/sqrt(2.)/sqa1*h5, epsa, T9); cmult(-boxdiagFact*oneHa/sqrt(2.)/sqa1*h4, conjeps1, T10); cmult(boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h10, pacur, T11a); cmult(-boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h12, p1cur, T11b); cmult(-phase/(pa-p1).m2()*Falpha*(p1-pa).dot(pa-p1-pH), paplusp1cur, T12a); cmult(phase/(pa-p1).m2()*Falpha*(pa+p1).dot(pa-p1-pH), p1minuspacur, T12b); cmult(-phase*Fbeta*(pa-p1-pH).m2(), paplusp1cur, T13); current ans; for(int i=0;i<4;++i) { ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5a[i]+T5b[i]+T6[i]+T7[i]+T8a[i]+T8b[i] +T9[i]+T10[i]+T11a[i]+T11b[i]+T12a[i]+T12b[i]+T13[i]; } retAns[0] = F/prop*ans[0]; retAns[1] = F/prop*ans[1]; retAns[2] = F/prop*ans[2]; retAns[3] = F/prop*ans[3]; } } // namespace anonymous // JDC - new amplitude with Higgs emitted close to gluon with full mt effects. // Keep usual HEJ-style function call double ME_Houtside_gq(HLV p1out, HLV p1in, HLV p2out, HLV p2in, HLV pH, double mq, bool includeBottom, double mq2, double vev ){ current cur2bplus,cur2bminus, cur2bplusFlip, cur2bminusFlip; current retAns,retAnsb; joi(p2out,true,p2in,true,cur2bplus); joi(p2out,false,p2in,false,cur2bminus); joi(ParityFlip(p2out),true,ParityFlip(p2in),true,cur2bplusFlip); joi(ParityFlip(p2out),false,ParityFlip(p2in),false,cur2bminusFlip); COM app1,app2,apm1,apm2; COM app3, app4, apm3, apm4; if(!includeBottom) { g_gH_HC(p1in,p1out,pH,mq,vev,retAns); app1=cdot(retAns,cur2bplus); app2=cdot(retAns,cur2bminus); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); app3=cdot(retAns,cur2bplusFlip); app4=cdot(retAns,cur2bminusFlip); // And non-conserving bits g_gH_HNC(p1in,p1out,pH,mq,vev,retAns); apm1=cdot(retAns,cur2bplus); apm2=cdot(retAns,cur2bminus); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); apm3=cdot(retAns,cur2bplusFlip); apm4=cdot(retAns,cur2bminusFlip); } else { g_gH_HC(p1in,p1out,pH,mq,vev,retAns); g_gH_HC(p1in,p1out,pH,mq2,vev,retAnsb); app1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus); app2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,vev,retAnsb); app3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip); app4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip); // And non-conserving bits g_gH_HNC(p1in,p1out,pH,mq,vev,retAns); g_gH_HNC(p1in,p1out,pH,mq2,vev,retAnsb); apm1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus); apm2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,vev,retAnsb); apm3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip); apm4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip); } return abs2(app1) + abs2(app2) + abs2(app3) + abs2(app4) + abs2(apm1) + abs2(apm2) + abs2(apm3) + abs2(apm4); } #endif // HEJ_BUILD_WITH_QCDLOOP double C2gHgm(HLV p2, HLV p1, HLV pH, double vev) { const double A=1./(3.*M_PI*vev); // Implements Eq. (4.22) in hep-ph/0301013 with modifications to incoming plus momenta double s12,p1p,p2p; COM p1perp,p3perp,phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 p1p=p1.plus(); p2p=p2.plus(); } else { // opposite case p1p=p1.minus(); p2p=p2.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=COM(0,1)*A/(2.*s12)*(p2p/p1p*conj(p1perp)*p3perp +p1p/p2p*p1perp*conj(p3perp)); temp=temp*conj(temp); return temp.real(); } double C2gHgp(HLV p2, HLV p1, HLV pH, double vev) { const double A=1./(3.*M_PI*vev); // Implements Eq. (4.23) in hep-ph/0301013 double s12,php,p1p,phm; COM p1perp,p3perp,phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 php=pH.plus(); phm=pH.minus(); p1p=p1.plus(); } else { // opposite case php=pH.minus(); phm=pH.plus(); p1p=p1.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=-COM(0,1)*A/(2.*s12)*( conj(p1perp*p3perp)*pow(php/p1p,2)/(1.+php/p1p) +s12*(pow(conj(phperp),2)/(pow(abs(phperp),2)+p1p*phm) -pow(conj(p3perp)+(1.+php/p1p)*conj(p1perp),2) /((1.+php/p1p)*(pH.m2()+2.*p1.dot(pH)))) ); temp=temp*conj(temp); return temp.real(); } double C2qHqm(HLV p2, HLV p1, HLV pH, double vev) { const double A=1./(3.*M_PI*vev); // Implements Eq. (4.21) in hep-ph/0301013 double s12,p2p,p1p; COM p1perp,p3perp,phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 p2p=p2.plus(); p1p=p1.plus(); } else { // opposite case p2p=p2.minus(); p1p=p1.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=A/(2.*s12)*( sqrt(p2p/p1p)*p3perp*conj(p1perp) +sqrt(p1p/p2p)*p1perp*conj(p3perp) ); temp=temp*conj(temp); return temp.real(); } diff --git a/src/JetSplitter.cc b/src/JetSplitter.cc index 96a9162..59138fb 100644 --- a/src/JetSplitter.cc +++ b/src/JetSplitter.cc @@ -1,188 +1,188 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/JetSplitter.hh" #include <array> #include <assert.h> #include <numeric> #include <utility> #include "fastjet/ClusterSequence.hh" #include "fastjet/PseudoJet.hh" #include "HEJ/Constants.hh" #include "HEJ/exceptions.hh" #include "HEJ/RNG.hh" namespace HEJ { namespace{ constexpr double ccut=HEJ::CMINPT; // min parton pt template<class Iterator> bool same_pt_and_rapidity( Iterator begin, Iterator end, fastjet::PseudoJet const & jet ){ constexpr double ep = 1e-2; const fastjet::PseudoJet reconstructed_jet = std::accumulate( begin, end, fastjet::PseudoJet{} ); return (std::abs(reconstructed_jet.pt() - jet.pt()) < ep) && (std::abs(reconstructed_jet.rapidity() - jet.rapidity()) < ep) ; } bool all_in_one_jet( std::vector<fastjet::PseudoJet> const & partons, fastjet::JetDefinition jet_def, double min_jet_pt ){ fastjet::ClusterSequence ev(partons, jet_def); const std::vector<fastjet::PseudoJet> testjet = ev.inclusive_jets(min_jet_pt); return testjet.size() == 1u && testjet[0].constituents().size() == partons.size(); } } JetSplitter::JetSplitter( fastjet::JetDefinition jet_def, double min_pt ): min_jet_pt_{min_pt}, jet_def_{std::move(jet_def)} {} using SplitResult = JetSplitter::SplitResult; SplitResult JetSplitter::split( fastjet::PseudoJet const & j2split, int ncons, RNG & ran ) const{ if(ncons <= 0) { throw std::invalid_argument{ "number of requested jet constituents less than 1" }; } double swt = 1.; std::vector<fastjet::PseudoJet> jcons; if(ncons == 1){ jcons.emplace_back(j2split); jcons.back().set_user_index(0); return {jcons, swt}; } if(ncons == 2){ return Split2(j2split, ran); } const double R_max = R_factor*jet_def_.R(); assert(R_max < M_PI); double pt_remaining = j2split.pt(); const double phi_jet = j2split.phi(); const double y_jet = j2split.rapidity(); for(int i = 0; i < ncons - 1; ++i){ /** * Generate rapidity and azimuthal angle with a distance * R = sqrt(delta_y^2 + delta_phi^2) < R_max * from the jet centre */ const double R = R_max*ran.flat(); const double theta = 2*M_PI*ran.flat(); const double delta_phi = R*cos(theta); const double delta_y = R*sin(theta); /** * Generate pt such that the total contribution of all partons * along the jet pt axis does not exceed the jet pt */ const double pt_max = pt_remaining/cos(delta_phi); assert(pt_max > 0); if(pt_max < ccut) return {}; // no pt remaining for this parton const double pt = (pt_max - ccut)*ran.flat() + ccut; pt_remaining -= pt*cos(delta_phi); jcons.emplace_back( pt*cos(phi_jet + delta_phi), pt*sin(phi_jet + delta_phi), pt*sinh(y_jet + delta_y), pt*cosh(y_jet + delta_y) ); jcons.back().set_user_index(i); swt *= 2*M_PI*R*R_max*pt*(pt_max - ccut); } const fastjet::PseudoJet p_total = std::accumulate( jcons.begin(), jcons.end(), fastjet::PseudoJet{} ); // Calculate the pt of the last parton const double last_px = j2split.px() - p_total.px(); const double last_py = j2split.py() - p_total.py(); const double last_pt = sqrt(last_px*last_px + last_py*last_py); if(last_pt < ccut) return {}; // Calculate the rapidity of the last parton using the requirement that the // new jet must have the same rapidity as the LO jet. const double exp_2y_jet = (j2split.e() + j2split.pz())/(j2split.e() - j2split.pz()); const double bb = (p_total.e()+p_total.pz()) - exp_2y_jet*(p_total.e()-p_total.pz()); const double lasty = log((-bb+sqrt(bb*bb+4.*exp_2y_jet*last_pt*last_pt))/(2.*last_pt)); jcons.emplace_back( last_px, last_py, last_pt*sinh(lasty), last_pt*cosh(lasty) ); jcons.back().set_user_index(ncons-1); assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split)); // Test that the last parton is not too far away from the jet centre. if (jcons.back().delta_R(j2split) > R_max) return {}; if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {}; return {jcons, swt}; } double JetSplitter::sample_distance_2p(double & wt, RNG & ran) const{ static constexpr double x_small = 0.1; static constexpr double p_small = 0.4; const double pR = ran.flat(); if(pR < p_small){ wt *= x_small/p_small; return x_small/p_small*pR; } wt *= (1-x_small)/(1-p_small); return (1-x_small)/(1-p_small)*(pR-p_small) + x_small; } SplitResult JetSplitter::Split2( fastjet::PseudoJet const & j2split, RNG & ran ) const{ static constexpr size_t ncons = 2; std::vector<fastjet::PseudoJet> jcons(ncons); std::array<double, ncons> R, phi, y, pt; double wt = 1; const double theta = 2*M_PI*ran.flat(); // angle in y-phi plane // empiric observation: we are always within the jet radius R[0] = sample_distance_2p(wt, ran)*jet_def_.R(); R[1] = -sample_distance_2p(wt, ran)*jet_def_.R(); for(size_t i = 0; i <= 1; ++i){ phi[i] = j2split.phi() + R[i]*cos(theta); y[i] = j2split.rapidity() + R[i]*sin(theta); } for(size_t i = 0; i <= 1; ++i){ pt[i] = (j2split.py() - tan(phi[1-i])*j2split.px())/ (sin(phi[i]) - tan(phi[1-i])*cos(phi[i])); if(pt[i] < ccut) return {}; jcons[i].reset_PtYPhiM(pt[i], y[i], phi[i]); jcons[i].set_user_index(i); } assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split)); if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {}; wt *= 2*M_PI*pt[0]*R[0]*jet_def_.R()*jet_def_.R(); // from transformation of delta(R[1] - ...) to delta(pt[0] - ...) const double dphi0 = phi[0] - j2split.phi(); const double ptJ = j2split.pt(); const double jacobian = cos(theta)*pt[1]*pt[1]/(ptJ*sin(dphi0)); return {jcons, jacobian*wt}; } } diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc index e96ee0e..51ea535 100644 --- a/src/LesHouchesWriter.cc +++ b/src/LesHouchesWriter.cc @@ -1,127 +1,127 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <cassert> #include <utility> #include <vector> #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/LesHouchesWriter.hh" #include "HEJ/utility.hh" namespace HEJ{ namespace{ template<class T, class... Args> std::unique_ptr<T> make_unique(Args&&... a){ return std::unique_ptr<T>{new T{std::forward<Args>(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<LHEF::Writer>(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<double>(max_number_types, 0.); writer_->heprup.XERRUP = std::vector<double>(max_number_types, 0.); writer_->heprup.XMAXUP = std::vector<double>(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("<event", 0) == 0); #endif } void LesHouchesWriter::rewrite_init(){ assert(writer_ && out_.is_open()); // replace placeholder entries const auto pos = out_.tellp(); out_.seekp(0); write_init(); assert_next_event_intact(out_); out_.seekp(pos); } LesHouchesWriter::~LesHouchesWriter(){ assert(writer_ && out_.is_open()); for(auto & xs_err: heprup().XERRUP) { xs_err = sqrt(xs_err); } rewrite_init(); } } diff --git a/src/MatrixElement.cc b/src/MatrixElement.cc index c04e705..93111e2 100644 --- a/src/MatrixElement.cc +++ b/src/MatrixElement.cc @@ -1,1712 +1,1712 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/MatrixElement.hh" #include <algorithm> #include <assert.h> #include <limits> #include <math.h> #include <stddef.h> #include <unordered_map> #include <utility> #include "CLHEP/Vector/LorentzVector.h" #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" #include "HEJ/Hjets.hh" #include "HEJ/jets.hh" #include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/utility.hh" #include "HEJ/Wjets.hh" namespace HEJ{ double MatrixElement::omega0( double alpha_s, double mur, fastjet::PseudoJet const & q_j ) const { const double lambda = param_.regulator_lambda; const double result = - alpha_s*N_C/M_PI*log(q_j.perp2()/(lambda*lambda)); if(! param_.log_correction) return result; return ( 1. + alpha_s/(4.*M_PI)*beta0*log(mur*mur/(q_j.perp()*lambda)) )*result; } Weights MatrixElement::operator()(Event const & event) const { return tree(event)*virtual_corrections(event); } Weights MatrixElement::tree(Event const & event) const { return tree_param(event)*tree_kin(event); } Weights MatrixElement::tree_param(Event const & event) const { if(! is_resummable(event.type())) { return Weights{0., std::vector<double>(event.variations().size(), 0.)}; } Weights result; // only compute once for each renormalisation scale std::unordered_map<double, double> known; result.central = tree_param(event, event.central().mur); known.emplace(event.central().mur, result.central); for(auto const & var: event.variations()) { const auto ME_it = known.find(var.mur); if(ME_it == end(known)) { const double wt = tree_param(event, var.mur); result.variations.emplace_back(wt); known.emplace(var.mur, wt); } else { result.variations.emplace_back(ME_it->second); } } return result; } Weights MatrixElement::virtual_corrections(Event const & event) const { if(! is_resummable(event.type())) { return Weights{0., std::vector<double>(event.variations().size(), 0.)}; } Weights result; // only compute once for each renormalisation scale std::unordered_map<double, double> known; result.central = virtual_corrections(event, event.central().mur); known.emplace(event.central().mur, result.central); for(auto const & var: event.variations()) { const auto ME_it = known.find(var.mur); if(ME_it == end(known)) { const double wt = virtual_corrections(event, var.mur); result.variations.emplace_back(wt); known.emplace(var.mur, wt); } else { result.variations.emplace_back(ME_it->second); } } return result; } double MatrixElement::virtual_corrections_W( Event const & event, const double mur, Particle const & WBoson ) const{ auto const & in = event.incoming(); const auto partons = filter_partons(event.outgoing()); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; double const norm = (in.front().p + in.back().p).E(); #endif assert(std::is_sorted(partons.begin(), partons.end(), rapidity_less{})); assert(partons.size() >= 2); assert(pa.pz() < pb.pz()); fastjet::PseudoJet q = pa - partons[0].p; size_t first_idx = 0; size_t last_idx = partons.size() - 1; #ifndef NDEBUG bool wc = true; #endif bool wqq = false; // With extremal qqx or unordered gluon outside the extremal // partons then it is not part of the FKL ladder and does not // contribute to the virtual corrections. W emitted from the // most backward leg must be taken into account in t-channel if (event.type() == event_type::unob) { q -= partons[1].p; ++first_idx; if (in[0].type != partons[1].type ){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } else if (event.type() == event_type::qqxexb) { q -= partons[1].p; ++first_idx; if (abs(partons[0].type) != abs(partons[1].type)){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } else { if(event.type() == event_type::unof || event.type() == event_type::qqxexf){ --last_idx; } if (in[0].type != partons[0].type ){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } size_t first_idx_qqx = last_idx; size_t last_idx_qqx = last_idx; //if qqxMid event, virtual correction do not occur between //qqx pair. if(event.type() == event_type::qqxmid){ const auto backquark = std::find_if( begin(partons) + 1, end(partons) - 1 , [](Particle const & s){ return (s.type != pid::gluon); } ); if(backquark == end(partons) || (backquark+1)->type==pid::gluon) return 0; if(abs(backquark->type) != abs((backquark+1)->type)) { wqq=true; #ifndef NDEBUG wc=false; #endif } last_idx = std::distance(begin(partons), backquark); first_idx_qqx = last_idx+1; } double exponent = 0; const double alpha_s = alpha_s_(mur); for(size_t j = first_idx; j < last_idx; ++j){ exponent += omega0(alpha_s, mur, q)*( partons[j+1].rapidity() - partons[j].rapidity() ); q -=partons[j+1].p; } // End Loop one if (last_idx != first_idx_qqx) q -= partons[last_idx+1].p; if (wqq) q -= WBoson.p; for(size_t j = first_idx_qqx; j < last_idx_qqx; ++j){ exponent += omega0(alpha_s, mur, q)*( partons[j+1].rapidity() - partons[j].rapidity() ); q -= partons[j+1].p; } #ifndef NDEBUG if (wc) q -= WBoson.p; assert( nearby(q, -1*pb, norm) || is_AWZH_boson(partons.back().type) || event.type() == event_type::unof || event.type() == event_type::qqxexf ); #endif return exp(exponent); } double MatrixElement::virtual_corrections( Event const & event, const double mur ) const{ auto const & in = event.incoming(); auto const & out = event.outgoing(); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; double const norm = (in.front().p + in.back().p).E(); #endif const auto AWZH_boson = std::find_if( begin(out), end(out), [](Particle const & p){ return is_AWZH_boson(p); } ); if(AWZH_boson != end(out) && abs(AWZH_boson->type) == pid::Wp){ return virtual_corrections_W(event, mur, *AWZH_boson); } assert(std::is_sorted(out.begin(), out.end(), rapidity_less{})); assert(out.size() >= 2); assert(pa.pz() < pb.pz()); fastjet::PseudoJet q = pa - out[0].p; size_t first_idx = 0; size_t last_idx = out.size() - 1; // if there is a Higgs boson, extremal qqx or unordered gluon // outside the extremal partons then it is not part of the FKL // ladder and does not contribute to the virtual corrections if((out.front().type == pid::Higgs) || event.type() == event_type::unob || event.type() == event_type::qqxexb){ q -= out[1].p; ++first_idx; } if((out.back().type == pid::Higgs) || event.type() == event_type::unof || event.type() == event_type::qqxexf){ --last_idx; } size_t first_idx_qqx = last_idx; size_t last_idx_qqx = last_idx; //if qqxMid event, virtual correction do not occur between //qqx pair. if(event.type() == event_type::qqxmid){ const auto backquark = std::find_if( begin(out) + 1, end(out) - 1 , [](Particle const & s){ return (s.type != pid::gluon && is_parton(s.type)); } ); if(backquark == end(out) || (backquark+1)->type==pid::gluon) return 0; last_idx = std::distance(begin(out), backquark); first_idx_qqx = last_idx+1; } double exponent = 0; const double alpha_s = alpha_s_(mur); for(size_t j = first_idx; j < last_idx; ++j){ exponent += omega0(alpha_s, mur, q)*( out[j+1].rapidity() - out[j].rapidity() ); q -= out[j+1].p; } if (last_idx != first_idx_qqx) q -= out[last_idx+1].p; for(size_t j = first_idx_qqx; j < last_idx_qqx; ++j){ exponent += omega0(alpha_s, mur, q)*( out[j+1].rapidity() - out[j].rapidity() ); q -= out[j+1].p; } assert( nearby(q, -1*pb, norm) || out.back().type == pid::Higgs || event.type() == event_type::unof || event.type() == event_type::qqxexf ); return exp(exponent); } namespace { //! Lipatov vertex for partons emitted into extremal jets double C2Lipatov( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2 ){ const CLHEP::HepLorentzVector temptrans=-(qav+qbv); const CLHEP::HepLorentzVector p5=qav-qbv; const CLHEP::HepLorentzVector CL=temptrans + p1*(qav.m2()/p5.dot(p1) + 2.*p5.dot(p2)/p1.dot(p2)) - p2*(qbv.m2()/p5.dot(p2) + 2.*p5.dot(p1)/p1.dot(p2)); return -CL.dot(CL); } //! Lipatov vertex with soft subtraction for partons emitted into extremal jets double C2Lipatovots( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls=(C2Lipatov(qav, qbv, p1, p2)/(qav.m2()*qbv.m2())); const double kperp=(qav-qbv).perp(); if (kperp>lambda) return Cls; return Cls-4./(kperp*kperp); } //! Lipatov vertex double C2Lipatov( // B CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & pim, CLHEP::HepLorentzVector const & pip, CLHEP::HepLorentzVector const & pom, CLHEP::HepLorentzVector const & pop ){ const CLHEP::HepLorentzVector temptrans=-(qav+qbv); const CLHEP::HepLorentzVector p5=qav-qbv; const CLHEP::HepLorentzVector CL=temptrans + qav.m2()*(1./p5.dot(pip)*pip + 1./p5.dot(pop)*pop)/2. - qbv.m2()*(1./p5.dot(pim)*pim + 1./p5.dot(pom)*pom)/2. + ( pip*(p5.dot(pim)/pip.dot(pim) + p5.dot(pom)/pip.dot(pom)) + pop*(p5.dot(pim)/pop.dot(pim) + p5.dot(pom)/pop.dot(pom)) - pim*(p5.dot(pip)/pip.dot(pim) + p5.dot(pop)/pop.dot(pim)) - pom*(p5.dot(pip)/pip.dot(pom) + p5.dot(pop)/pop.dot(pom)) )/2.; return -CL.dot(CL); } //! Lipatov vertex with soft subtraction double C2Lipatovots( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls=(C2Lipatov(qav, qbv, pa, pb, p1, p2)/(qav.m2()*qbv.m2())); const double kperp=(qav-qbv).perp(); if (kperp>lambda) return Cls; return Cls-4./(kperp*kperp); } /** Matrix element squared for tree-level current-current scattering * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pg Unordered gluon momentum * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @returns ME Squared for Tree-Level Current-Current Scattering * * @note The unof contribution can be calculated by reversing the argument ordering. */ double ME_uno_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa ){ assert(aptype!=pid::gluon); // aptype cannot be gluon if (bptype==pid::gluon) { if (is_quark(aptype)) return ME_unob_qg(pg,p1,pa,pn,pb); else return ME_unob_qbarg(pg,p1,pa,pn,pb); } else if (is_antiquark(bptype)) { if (is_quark(aptype)) return ME_unob_qQbar(pg,p1,pa,pn,pb); else return ME_unob_qbarQbar(pg,p1,pa,pn,pb); } else { //bptype == quark if (is_quark(aptype)) return ME_unob_qQ(pg,p1,pa,pn,pb); else return ME_unob_qbarQ(pg,p1,pa,pn,pb); } throw std::logic_error("unreachable"); } /** Matrix element squared for tree-level current-current scattering * @param bptype Particle b PDG ID * @param pgin Incoming gluon momentum * @param pq Quark from splitting Momentum * @param pqbar Anti-quark from splitting Momentum * @param pn Particle n Momentum * @param pb Particle b Momentum * @param swap_q_qx Boolean. Ordering of qqbar pair. False: pqbar extremal. * @returns ME Squared for Tree-Level Current-Current Scattering * * @note The qqxf contribution can be calculated by reversing the argument ordering. */ double ME_qqx_current( ParticleID bptype, CLHEP::HepLorentzVector const & pgin, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, bool const swap_q_qx ){ if (bptype==pid::gluon) { if (swap_q_qx) // pq extremal return ME_Exqqx_qqbarg(pgin,pq,pqbar,pn,pb); else // pqbar extremal return ME_Exqqx_qbarqg(pgin,pq,pqbar,pn,pb); } else { // b leg quark line if (swap_q_qx) //extremal pq return ME_Exqqx_qqbarQ(pgin,pq,pqbar,pn,pb); else return ME_Exqqx_qbarqQ(pgin,pq,pqbar,pn,pb); } throw std::logic_error("unreachable"); } /* \brief Matrix element squared for central qqx tree-level current-current * scattering * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param nabove Number of gluons emitted before central qqxpair * @param nbelow Number of gluons emitted after central qqxpair * @param pa Initial state a Momentum * @param pb Initial state b Momentum * @param pq Final state qbar Momentum * @param pqbar Final state q Momentum * @param partons Vector of all outgoing partons * @returns ME Squared for qqxmid Tree-Level Current-Current Scattering */ double ME_qqxmid_current( ParticleID aptype, ParticleID bptype, int nabove, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, std::vector<HLV> const & partons){ // CAM factors for the qqx amps, and qqbar ordering (default, pq backwards) const bool swap_q_qx=pqbar.rapidity() < pq.rapidity(); double wt=1.; if (aptype==pid::gluon) wt*=K_g(partons.front(),pa)/HEJ::C_F; if (bptype==pid::gluon) wt*=K_g(partons.back(),pb)/HEJ::C_F; return wt*ME_Cenqqx_qq(pa, pb, partons,is_antiquark(bptype),is_antiquark(aptype), swap_q_qx, nabove); } /** Matrix element squared for tree-level current-current scattering * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @returns ME Squared for Tree-Level Current-Current Scattering */ double ME_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa ){ if (aptype==pid::gluon && bptype==pid::gluon) { return ME_gg(pn,pb,p1,pa); } else if (aptype==pid::gluon && bptype!=pid::gluon) { if (is_quark(bptype)) return ME_qg(pn,pb,p1,pa); else return ME_qbarg(pn,pb,p1,pa); } else if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_qg(p1,pa,pn,pb); else return ME_qbarg(p1,pa,pn,pb); } else { // they are both quark if (is_quark(bptype)) { if (is_quark(aptype)) return ME_qQ(pn,pb,p1,pa); else return ME_qQbar(pn,pb,p1,pa); } else { if (is_quark(aptype)) return ME_qQbar(p1,pa,pn,pb); else return ME_qbarQbar(pn,pb,p1,pa); } } throw std::logic_error("unreachable"); } /** Matrix element squared for tree-level current-current scattering With W+Jets * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for Tree-Level Current-Current Scattering */ double ME_W_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wc, ParticleProperties const & Wprop ){ // We know it cannot be gg incoming. assert(!(aptype==pid::gluon && bptype==pid::gluon)); if (aptype==pid::gluon && bptype!=pid::gluon) { if (is_quark(bptype)) return ME_W_qg(pn,plbar,pl,pb,p1,pa,Wprop); else return ME_W_qbarg(pn,plbar,pl,pb,p1,pa,Wprop); } else if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_W_qg(p1,plbar,pl,pa,pn,pb,Wprop); else return ME_W_qbarg(p1,plbar,pl,pa,pn,pb,Wprop); } else { // they are both quark if (wc==true){ // emission off b, (first argument pbout) if (is_quark(bptype)) { if (is_quark(aptype)) return ME_W_qQ(pn,plbar,pl,pb,p1,pa,Wprop); else return ME_W_qQbar(pn,plbar,pl,pb,p1,pa,Wprop); } else { if (is_quark(aptype)) return ME_W_qbarQ(pn,plbar,pl,pb,p1,pa,Wprop); else return ME_W_qbarQbar(pn,plbar,pl,pb,p1,pa,Wprop); } } else{ // emission off a, (first argument paout) if (is_quark(aptype)) { if (is_quark(bptype)) return ME_W_qQ(p1,plbar,pl,pa,pn,pb,Wprop); else return ME_W_qQbar(p1,plbar,pl,pa,pn,pb,Wprop); } else { // a is anti-quark if (is_quark(bptype)) return ME_W_qbarQ(p1,plbar,pl,pa,pn,pb,Wprop); else return ME_W_qbarQbar(p1,plbar,pl,pa,pn,pb,Wprop); } } } throw std::logic_error("unreachable"); } /** Matrix element squared for backwards uno tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param pg Unordered gluon momentum * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for unob Tree-Level Current-Current Scattering * * @note The unof contribution can be calculated by reversing the argument ordering. */ double ME_W_uno_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wc, ParticleProperties const & Wprop ){ // we know they are not both gluons assert(bptype != pid::gluon || aptype != pid::gluon); if (bptype == pid::gluon && aptype != pid::gluon) { // b gluon => W emission off a if (is_quark(aptype)) return ME_Wuno_qg(p1,pa,pn,pb,pg,plbar,pl,Wprop); else return ME_Wuno_qbarg(p1,pa,pn,pb,pg,plbar,pl,Wprop); } else { // they are both quark if (wc) {// emission off b, i.e. b is first current if (is_quark(bptype)){ if (is_quark(aptype)) return ME_W_unob_qQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); else return ME_W_unob_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } else{ if (is_quark(aptype)) return ME_W_unob_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); else return ME_W_unob_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } } else {// wc == false, emission off a, i.e. a is first current if (is_quark(aptype)) { if (is_quark(bptype)) //qq return ME_Wuno_qQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); else //qqbar return ME_Wuno_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } else { // a is anti-quark if (is_quark(bptype)) //qbarq return ME_Wuno_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); else //qbarqbar return ME_Wuno_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } } } throw std::logic_error("unreachable"); } /** \brief Matrix element squared for backward qqx tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pa Initial state a Momentum * @param pb Initial state b Momentum * @param pq Final state q Momentum * @param pqbar Final state qbar Momentum * @param pn Final state n Momentum * @param plbar Final state anti-lepton momentum * @param pl Final state lepton momentum * @param swap_q_qx Boolean. Ordering of qqbar pair. False: pqbar extremal. * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for qqxb Tree-Level Current-Current Scattering * * @note calculate forwards qqx contribution by reversing argument ordering. */ double ME_W_qqx_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const swap_q_qx, bool const wc, ParticleProperties const & Wprop ){ // CAM factors for the qqx amps, and qqbar ordering (default, qbar extremal) const double CFbackward = K_g( (swap_q_qx)?pq:pqbar ,pa)/HEJ::C_F; // With qqbar we could have 2 incoming gluons and W Emission if (aptype==pid::gluon && bptype==pid::gluon) { //a gluon, b gluon gg->qqbarWg // This will be a wqqx emission as there is no other possible W Emission // Site. if (swap_q_qx) return ME_WExqqx_qqbarg(pa, pqbar, plbar, pl, pq, pn, pb, Wprop)*CFbackward; else return ME_WExqqx_qbarqg(pa, pq, plbar, pl, pqbar, pn, pb, Wprop)*CFbackward; } else { assert(aptype==pid::gluon && bptype!=pid::gluon ); //a gluon => W emission off b leg or qqx if (!wc){ // W Emitted from backwards qqx if (swap_q_qx) return ME_WExqqx_qqbarQ(pa, pqbar, plbar, pl, pq, pn, pb, Wprop)*CFbackward; else return ME_WExqqx_qbarqQ(pa, pq, plbar, pl, pqbar, pn, pb, Wprop)*CFbackward; } else { // W Must be emitted from forwards leg. if (swap_q_qx) return ME_W_Exqqx_QQq(pb, pa, pn, pqbar, pq, plbar, pl, is_antiquark(bptype), Wprop)*CFbackward; else return ME_W_Exqqx_QQq(pb, pa, pn, pq, pqbar, plbar, pl, is_antiquark(bptype), Wprop)*CFbackward; } } throw std::logic_error("unreachable"); } /* \brief Matrix element squared for central qqx tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param nabove Number of gluons emitted before central qqxpair * @param nbelow Number of gluons emitted after central qqxpair * @param pa Initial state a Momentum * @param pb Initial state b Momentum\ * @param pq Final state qbar Momentum * @param pqbar Final state q Momentum * @param partons Vector of all outgoing partons * @param plbar Final state anti-lepton momentum * @param pl Final state lepton momentum * @param wqq Boolean. True siginfies W boson is emitted from Central qqx * @param wc Boolean. wc=true signifies w boson emitted from leg b; if wqq=false. * @returns ME Squared for qqxmid Tree-Level Current-Current Scattering */ double ME_W_qqxmid_current( ParticleID aptype, ParticleID bptype, int nabove, int nbelow, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, std::vector<HLV> const & partons, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wqq, bool const wc, ParticleProperties const & Wprop ){ // CAM factors for the qqx amps, and qqbar ordering (default, pq backwards) const bool swap_q_qx=pqbar.rapidity() < pq.rapidity(); double wt=1.; if (aptype==pid::gluon) wt*=K_g(partons.front(),pa)/HEJ::C_F; if (bptype==pid::gluon) wt*=K_g(partons.back(),pb)/HEJ::C_F; if(wqq) return wt*ME_WCenqqx_qq(pa, pb, pl, plbar, partons,(is_antiquark(bptype)),(is_antiquark(aptype)), swap_q_qx, nabove, Wprop); return wt*ME_W_Cenqqx_qq(pa, pb, pl, plbar, partons, (is_antiquark(bptype)), (is_antiquark(aptype)), swap_q_qx, nabove, nbelow, wc, Wprop); } /** \brief Matrix element squared for tree-level current-current scattering with Higgs * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param qH t-channel momentum before Higgs * @param qHp1 t-channel momentum after Higgs * @returns ME Squared for Tree-Level Current-Current Scattering with Higgs */ double ME_Higgs_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & qH, // t-channel momentum before Higgs CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs double mt, bool include_bottom, double mb, double vev ){ if (aptype==pid::gluon && bptype==pid::gluon) // gg initial state return ME_H_gg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev); else if (aptype==pid::gluon&&bptype!=pid::gluon) { if (is_quark(bptype)) return ME_H_qg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4./9.; else return ME_H_qbarg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4./9.; } else if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_H_qg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev)*4./9.; else return ME_H_qbarg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev)*4./9.; } else { // they are both quark if (is_quark(bptype)) { if (is_quark(aptype)) return ME_H_qQ(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); else return ME_H_qQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); } else { if (is_quark(aptype)) return ME_H_qQbar(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev)*4.*4./(9.*9.); else return ME_H_qbarQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); } } throw std::logic_error("unreachable"); } /** \brief Current matrix element squared with Higgs and unordered backward emission * @param aptype Particle A PDG ID * @param bptype Particle B PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param pg Unordered back Particle Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param qH t-channel momentum before Higgs * @param qHp1 t-channel momentum after Higgs * @returns ME Squared with Higgs and unordered backward emission * * @note This function assumes unordered gluon backwards from pa-p1 current. * For unof, reverse call order */ double ME_Higgs_current_uno( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & qH, // t-channel momentum before Higgs CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs double mt, bool include_bottom, double mb, double vev ){ if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_H_unob_gQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); else return ME_H_unob_gQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } else { // they are both quark if (is_quark(aptype)) { if (is_quark(bptype)) return ME_H_unob_qQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); else return ME_H_unob_qbarQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } else { if (is_quark(bptype)) return ME_H_unob_qQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); else return ME_H_unob_qbarQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } } throw std::logic_error("unreachable"); } CLHEP::HepLorentzVector to_HepLorentzVector(HEJ::Particle const & particle){ return {particle.p.px(), particle.p.py(), particle.p.pz(), particle.p.E()}; } void validate(HEJ::MatrixElementConfig const & config) { #ifndef HEJ_BUILD_WITH_QCDLOOP if(!config.Higgs_coupling.use_impact_factors) { throw std::invalid_argument{ "Invalid Higgs coupling settings.\n" "HEJ without QCDloop support can only use impact factors.\n" "Set use_impact_factors to true or recompile HEJ.\n" }; } #endif if(config.Higgs_coupling.use_impact_factors && config.Higgs_coupling.mt != std::numeric_limits<double>::infinity()) { throw std::invalid_argument{ "Conflicting settings: " "impact factors may only be used in the infinite top mass limit" }; } } } // namespace anonymous MatrixElement::MatrixElement( std::function<double (double)> alpha_s, MatrixElementConfig conf ): alpha_s_{std::move(alpha_s)}, param_{std::move(conf)} { validate(param_); } double MatrixElement::tree_kin( Event const & ev ) const { if(! is_resummable(ev.type())) return 0.; auto AWZH_boson = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](Particle const & p){return is_AWZH_boson(p);} ); if(AWZH_boson == end(ev.outgoing())) return tree_kin_jets(ev); switch(AWZH_boson->type){ case pid::Higgs: return tree_kin_Higgs(ev); case pid::Wp: case pid::Wm: return tree_kin_W(ev); // TODO case pid::photon: case pid::Z: default: throw not_implemented("Emission of boson of unsupported type"); } } namespace{ constexpr int extremal_jet_idx = 1; constexpr int no_extremal_jet_idx = 0; bool treat_as_extremal(Particle const & parton){ return parton.p.user_index() == extremal_jet_idx; } template<class InputIterator> double FKL_ladder_weight( InputIterator begin_gluon, InputIterator end_gluon, CLHEP::HepLorentzVector const & q0, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pn, double lambda ){ double wt = 1; auto qi = q0; for(auto gluon_it = begin_gluon; gluon_it != end_gluon; ++gluon_it){ assert(gluon_it->type == pid::gluon); const auto g = to_HepLorentzVector(*gluon_it); const auto qip1 = qi - g; if(treat_as_extremal(*gluon_it)){ wt *= C2Lipatovots(qip1, qi, pa, pb, lambda)*C_A; } else{ wt *= C2Lipatovots(qip1, qi, pa, pb, p1, pn, lambda)*C_A; } qi = qip1; } return wt; } } // namespace anonymous std::vector<Particle> MatrixElement::tag_extremal_jet_partons( Event const & ev ) const{ auto out_partons = filter_partons(ev.outgoing()); if(out_partons.size() == ev.jets().size()){ // no additional emissions in extremal jets, don't need to tag anything for(auto & parton: out_partons){ parton.p.set_user_index(no_extremal_jet_idx); } return out_partons; } const auto & jets = ev.jets(); assert(jets.size() >= 2); auto most_backward = begin(jets); auto most_forward = end(jets) - 1; // skip jets caused by unordered emission or qqx if(ev.type() == event_type::unob || ev.type() == event_type::qqxexb){ assert(jets.size() >= 3); ++most_backward; } else if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){ assert(jets.size() >= 3); --most_forward; } const auto extremal_jet_indices = ev.particle_jet_indices( {*most_backward, *most_forward} ); assert(extremal_jet_indices.size() == out_partons.size()); for(size_t i = 0; i < out_partons.size(); ++i){ assert(HEJ::is_parton(out_partons[i])); const int idx = (extremal_jet_indices[i]>=0)? extremal_jet_idx: no_extremal_jet_idx; out_partons[i].p.set_user_index(idx); } return out_partons; } namespace { double tree_kin_jets_qqxmid( ParticleID aptype, ParticleID bptype, HLV pa, HLV pb, std::vector<Particle> const & partons, double lambda ){ HLV pq,pqbar; const auto backmidquark = std::find_if( begin(partons)+1, end(partons)-1, [](Particle const & s){ return s.type != pid::gluon; } ); assert(backmidquark!=end(partons)-1); if (is_quark(backmidquark->type)){ pq = to_HepLorentzVector(*backmidquark); pqbar = to_HepLorentzVector(*(backmidquark+1)); } else { pqbar = to_HepLorentzVector(*backmidquark); pq = to_HepLorentzVector(*(backmidquark+1)); } auto p1 = to_HepLorentzVector(partons[0]); auto pn = to_HepLorentzVector(partons[partons.size() - 1]); auto q0 = pa - p1; // t-channel momentum after qqx auto qqxt = q0; const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder_1 = (backmidquark); const auto begin_ladder_2 = (backmidquark+2); const auto end_ladder = cend(partons) - 1; for(auto parton_it = begin_ladder; parton_it < begin_ladder_2; ++parton_it){ qqxt -= to_HepLorentzVector(*parton_it); } const int nabove = std::distance(begin_ladder, backmidquark); std::vector<HLV> partonsHLV; partonsHLV.reserve(partons.size()); for (size_t i = 0; i != partons.size(); ++i) { partonsHLV.push_back(to_HepLorentzVector(partons[i])); } const double current_factor = ME_qqxmid_current( aptype, bptype, nabove, pa, pb, pq, pqbar, partonsHLV ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder_1, q0, pa, pb, p1, pn, lambda )*FKL_ladder_weight( begin_ladder_2, end_ladder, qqxt, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template<class InIter, class partIter> double tree_kin_jets_qqx(InIter BeginIn, InIter EndIn, partIter BeginPart, partIter EndPart, double lambda){ const bool swap_q_qx = is_quark(*BeginPart); const auto pgin = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); const auto pq = to_HepLorentzVector(*(BeginPart+(swap_q_qx?0:1))); const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_q_qx?1:0))); const auto p1 = to_HepLorentzVector(*(BeginPart)); const auto pn = to_HepLorentzVector(*(EndPart-1)); assert((BeginIn)->type==pid::gluon); // Incoming a must be gluon. const double current_factor = ME_qqx_current( (EndIn-1)->type, pgin, pq, pqbar, pn, pb, swap_q_qx )/(4.*(N_C*N_C - 1.)); const double ladder_factor = FKL_ladder_weight( (BeginPart+2), (EndPart-1), pgin-pq-pqbar, pgin, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template<class InIter, class partIter> double tree_kin_jets_uno(InIter BeginIn, InIter EndIn, partIter BeginPart, partIter EndPart, double lambda){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); const double current_factor = ME_uno_current( (BeginIn)->type, (EndIn-1)->type, pg, pn, pb, p1, pa )/(4.*(N_C*N_C - 1.)); const double ladder_factor = FKL_ladder_weight( (BeginPart+2), (EndPart-1), pa-p1-pg, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } } double MatrixElement::tree_kin_jets(Event const & ev) const { auto const & incoming = ev.incoming(); const auto partons = tag_extremal_jet_partons(ev); if (ev.type()==HEJ::event_type::FKL){ const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); return ME_current( incoming[0].type, incoming[1].type, pn, pb, p1, pa )/(4.*(N_C*N_C - 1.))*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, pa - p1, pa, pb, p1, pn, param_.regulator_lambda ); } else if (ev.type()==HEJ::event_type::unordered_backward){ return tree_kin_jets_uno(incoming.begin(), incoming.end(), partons.begin(), partons.end(), param_.regulator_lambda); } else if (ev.type()==HEJ::event_type::unordered_forward){ return tree_kin_jets_uno(incoming.rbegin(), incoming.rend(), partons.rbegin(), partons.rend(), param_.regulator_lambda); } else if (ev.type()==HEJ::event_type::extremal_qqxb){ return tree_kin_jets_qqx(incoming.begin(), incoming.end(), partons.begin(), partons.end(), param_.regulator_lambda); } else if (ev.type()==HEJ::event_type::extremal_qqxf){ return tree_kin_jets_qqx(incoming.rbegin(), incoming.rend(), partons.rbegin(), partons.rend(), param_.regulator_lambda); } else if (ev.type()==HEJ::event_type::central_qqx){ return tree_kin_jets_qqxmid(incoming[0].type, incoming[1].type, to_HepLorentzVector(incoming[0]), to_HepLorentzVector(incoming[1]), partons, param_.regulator_lambda); } else { throw std::logic_error("Cannot reweight non-resummable processes in Pure Jets"); } } namespace{ double tree_kin_W_FKL( ParticleID aptype, ParticleID bptype, HLV pa, HLV pb, std::vector<Particle> const & partons, HLV plbar, HLV pl, double lambda, ParticleProperties const & Wprop ){ auto p1 = to_HepLorentzVector(partons[0]); auto pn = to_HepLorentzVector(partons[partons.size() - 1]); const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder = cend(partons) - 1; bool wc = aptype==partons[0].type; //leg b emits w auto q0 = pa - p1; if(!wc) q0 -= pl + plbar; const double current_factor = ME_W_current( aptype, bptype, pn, pb, p1, pa, plbar, pl, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder, q0, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template<class InIter, class partIter> double tree_kin_W_uno(InIter BeginIn, partIter BeginPart, partIter EndPart, const HLV & plbar, const HLV & pl, double lambda, ParticleProperties const & Wprop){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(BeginIn+1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); bool wc = (BeginIn)->type==(BeginPart+1)->type; //leg b emits w auto q0 = pa - p1 - pg; if(!wc) q0 -= pl + plbar; const double current_factor = ME_W_uno_current( (BeginIn)->type, (BeginIn+1)->type, pn, pb, p1, pa, pg, plbar, pl, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } template<class InIter, class partIter> double tree_kin_W_qqx(InIter BeginIn, partIter BeginPart, partIter EndPart, const HLV & plbar, const HLV & pl, double lambda, ParticleProperties const & Wprop){ const bool swap_q_qx=is_quark(*BeginPart); const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(BeginIn+1)); const auto pq = to_HepLorentzVector(*(BeginPart+(swap_q_qx?0:1))); const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_q_qx?1:0))); const auto p1 = to_HepLorentzVector(*(BeginPart)); const auto pn = to_HepLorentzVector(*(EndPart-1)); const bool wc = (BeginIn+1)->type!=(EndPart-1)->type; //leg b emits w auto q0 = pa - pq - pqbar; if(!wc) q0 -= pl + plbar; const double current_factor = ME_W_qqx_current( (BeginIn)->type, (BeginIn+1)->type, pa, pb, pq, pqbar, pn, plbar, pl, swap_q_qx, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } double tree_kin_W_qqxmid( ParticleID aptype, ParticleID bptype, HLV pa, HLV pb, std::vector<Particle> const & partons, HLV plbar, HLV pl, double lambda, ParticleProperties const & Wprop ){ HLV pq,pqbar; const auto backmidquark = std::find_if( begin(partons)+1, end(partons)-1, [](Particle const & s){ return s.type != pid::gluon; } ); assert(backmidquark!=end(partons)-1); if (is_quark(backmidquark->type)){ pq = to_HepLorentzVector(*backmidquark); pqbar = to_HepLorentzVector(*(backmidquark+1)); } else { pqbar = to_HepLorentzVector(*backmidquark); pq = to_HepLorentzVector(*(backmidquark+1)); } auto p1 = to_HepLorentzVector(partons.front()); auto pn = to_HepLorentzVector(partons.back()); auto q0 = pa - p1; // t-channel momentum after qqx auto qqxt = q0; bool wqq = backmidquark->type != -(backmidquark+1)->type; // qqx emit W bool wc = !wqq & (aptype==partons.front().type); //leg b emits w assert(!wqq || (wqq && !wc)); if(wqq){ // emission from qqx qqxt -= pl + plbar; } else if(!wc) { // emission from leg a q0 -= pl + plbar; qqxt -= pl + plbar; } const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder_1 = (backmidquark); const auto begin_ladder_2 = (backmidquark+2); const auto end_ladder = cend(partons) - 1; for(auto parton_it = begin_ladder; parton_it < begin_ladder_2; ++parton_it){ qqxt -= to_HepLorentzVector(*parton_it); } const int nabove = std::distance(begin_ladder, backmidquark); const int nbelow = std::distance(begin_ladder_2, end_ladder); std::vector<HLV> partonsHLV; partonsHLV.reserve(partons.size()); for (size_t i = 0; i != partons.size(); ++i) { partonsHLV.push_back(to_HepLorentzVector(partons[i])); } const double current_factor = ME_W_qqxmid_current( aptype, bptype, nabove, nbelow, pa, pb, pq, pqbar, partonsHLV, plbar, pl, wqq, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder_1, q0, pa, pb, p1, pn, lambda )*FKL_ladder_weight( begin_ladder_2, end_ladder, qqxt, pa, pb, p1, pn, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } } // namespace anonymous double MatrixElement::tree_kin_W(Event const & ev) const { using namespace event_type; auto const & incoming(ev.incoming()); #ifndef NDEBUG // assert that there is exactly one decay corresponding to the W assert(ev.decays().size() == 1); auto const & w_boson{ std::find_if(ev.outgoing().cbegin(), ev.outgoing().cend(), [] (Particle const & p) -> bool { return std::abs(p.type) == ParticleID::Wp; }) }; assert(w_boson != ev.outgoing().cend()); assert( static_cast<long int>(ev.decays().cbegin()->first) == std::distance(ev.outgoing().cbegin(), w_boson) ); #endif // find decay products of W auto const & decay{ ev.decays().cbegin()->second }; assert(decay.size() == 2); assert( ( is_anylepton(decay.at(0)) && is_anyneutrino(decay.at(1)) ) || ( is_anylepton(decay.at(1)) && is_anyneutrino(decay.at(0)) ) ); // get lepton & neutrino HLV plbar, pl; if (decay.at(0).type < 0){ plbar = to_HepLorentzVector(decay.at(0)); pl = to_HepLorentzVector(decay.at(1)); } else{ pl = to_HepLorentzVector(decay.at(0)); plbar = to_HepLorentzVector(decay.at(1)); } const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto partons = tag_extremal_jet_partons(ev); if(ev.type() == FKL){ return tree_kin_W_FKL(incoming[0].type, incoming[1].type, pa, pb, partons, plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == unordered_backward){ return tree_kin_W_uno(cbegin(incoming), cbegin(partons), cend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == unordered_forward){ return tree_kin_W_uno(crbegin(incoming), crbegin(partons), crend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == extremal_qqxb){ return tree_kin_W_qqx(cbegin(incoming), cbegin(partons), cend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == extremal_qqxf){ return tree_kin_W_qqx(crbegin(incoming), crbegin(partons), crend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } assert(ev.type() == central_qqx); return tree_kin_W_qqxmid(incoming[0].type, incoming[1].type, pa, pb, partons, plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } double MatrixElement::tree_kin_Higgs(Event const & ev) const { if(is_uno(ev.type())){ return tree_kin_Higgs_between(ev); } if(ev.outgoing().front().type == pid::Higgs){ return tree_kin_Higgs_first(ev); } if(ev.outgoing().back().type == pid::Higgs){ return tree_kin_Higgs_last(ev); } return tree_kin_Higgs_between(ev); } namespace { // Colour acceleration multipliers, for gluons see eq. (7) in arXiv:0910.5113 #ifdef HEJ_BUILD_WITH_QCDLOOP // TODO: code duplication with jets.cc double K_g(double p1minus, double paminus) { return 1./2.*(p1minus/paminus + paminus/p1minus)*(C_A - 1./C_A) + 1./C_A; } double K_g( CLHEP::HepLorentzVector const & pout, CLHEP::HepLorentzVector const & pin ) { if(pin.z() > 0) return K_g(pout.plus(), pin.plus()); return K_g(pout.minus(), pin.minus()); } double K( ParticleID type, CLHEP::HepLorentzVector const & pout, CLHEP::HepLorentzVector const & pin ) { if(type == pid::gluon) return K_g(pout, pin); return C_F; } #endif // Colour factor in strict MRK limit double K_MRK(ParticleID type) { return (type == pid::gluon)?C_A:C_F; } } double MatrixElement::MH2_forwardH( CLHEP::HepLorentzVector const & p1out, CLHEP::HepLorentzVector const & p1in, ParticleID type2, CLHEP::HepLorentzVector const & p2out, CLHEP::HepLorentzVector const & p2in, CLHEP::HepLorentzVector const & pH, double t1, double t2 ) const{ ignore(p2out, p2in); const double shat = p1in.invariantMass2(p2in); const double vev = param_.ew_parameters.vev(); // gluon case #ifdef HEJ_BUILD_WITH_QCDLOOP if(!param_.Higgs_coupling.use_impact_factors){ return K(type2, p2out, p2in)*C_A*1./(16*M_PI*M_PI)*t1/t2*ME_Houtside_gq( p1out, p1in, p2out, p2in, pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, vev )/(4*(N_C*N_C - 1)); } #endif return K_MRK(type2)/C_A*9./2.*shat*shat*( C2gHgp(p1in,p1out,pH,vev) + C2gHgm(p1in,p1out,pH,vev) )/(t1*t2); } double MatrixElement::tree_kin_Higgs_first(Event const & ev) const { auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); assert(outgoing.front().type == pid::Higgs); if(outgoing[1].type != pid::gluon) { assert(incoming.front().type == outgoing[1].type); return tree_kin_Higgs_between(ev); } const auto pH = to_HepLorentzVector(outgoing.front()); const auto partons = tag_extremal_jet_partons( ev ); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); const auto q0 = pa - p1 - pH; const double t1 = q0.m2(); const double t2 = (pn - pb).m2(); return MH2_forwardH( p1, pa, incoming[1].type, pn, pb, pH, t1, t2 )*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, q0, pa, pb, p1, pn, param_.regulator_lambda ); } double MatrixElement::tree_kin_Higgs_last(Event const & ev) const { auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); assert(outgoing.back().type == pid::Higgs); if(outgoing[outgoing.size()-2].type != pid::gluon) { assert(incoming.back().type == outgoing[outgoing.size()-2].type); return tree_kin_Higgs_between(ev); } const auto pH = to_HepLorentzVector(outgoing.back()); const auto partons = tag_extremal_jet_partons( ev ); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); auto q0 = pa - p1; const double t1 = q0.m2(); const double t2 = (pn + pH - pb).m2(); return MH2_forwardH( pn, pb, incoming[0].type, p1, pa, pH, t2, t1 )*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, q0, pa, pb, p1, pn, param_.regulator_lambda ); } namespace { template<class InIter, class partIter> double tree_kin_Higgs_uno(InIter BeginIn, InIter EndIn, partIter BeginPart, partIter EndPart, const HLV & qH, const HLV & qHp1, double mt, bool inc_bot, double mb, double vev){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); return ME_Higgs_current_uno( (BeginIn)->type, (EndIn-1)->type, pg, pn, pb, p1, pa, qH, qHp1, mt, inc_bot, mb, vev ); } } double MatrixElement::tree_kin_Higgs_between(Event const & ev) const { using namespace event_type; auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); const auto the_Higgs = std::find_if( begin(outgoing), end(outgoing), [](Particle const & s){ return s.type == pid::Higgs; } ); assert(the_Higgs != end(outgoing)); const auto pH = to_HepLorentzVector(*the_Higgs); const auto partons = tag_extremal_jet_partons(ev); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); auto p1 = to_HepLorentzVector( partons[(ev.type() == unob)?1:0] ); auto pn = to_HepLorentzVector( partons[partons.size() - ((ev.type() == unof)?2:1)] ); auto first_after_Higgs = begin(partons) + (the_Higgs-begin(outgoing)); assert( (first_after_Higgs == end(partons) && ( (ev.type() == unob) || partons.back().type != pid::gluon )) || first_after_Higgs->rapidity() >= the_Higgs->rapidity() ); assert( (first_after_Higgs == begin(partons) && ( (ev.type() == unof) || partons.front().type != pid::gluon )) || (first_after_Higgs-1)->rapidity() <= the_Higgs->rapidity() ); // always treat the Higgs as if it were in between the extremal FKL partons if(first_after_Higgs == begin(partons)) ++first_after_Higgs; else if(first_after_Higgs == end(partons)) --first_after_Higgs; // t-channel momentum before Higgs auto qH = pa; for(auto parton_it = begin(partons); parton_it != first_after_Higgs; ++parton_it){ qH -= to_HepLorentzVector(*parton_it); } auto q0 = pa - p1; auto begin_ladder = begin(partons) + 1; auto end_ladder = end(partons) - 1; double current_factor; if(ev.type() == FKL){ current_factor = ME_Higgs_current( incoming[0].type, incoming[1].type, pn, pb, p1, pa, qH, qH - pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); } else if(ev.type() == unob){ current_factor = HEJ::C_A*HEJ::C_A/2*tree_kin_Higgs_uno( begin(incoming), end(incoming), begin(partons), end(partons), qH, qH-pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); const auto p_unob = to_HepLorentzVector(partons.front()); q0 -= p_unob; p1 += p_unob; ++begin_ladder; } else if(ev.type() == unof){ current_factor = HEJ::C_A*HEJ::C_A/2*tree_kin_Higgs_uno( rbegin(incoming), rend(incoming), rbegin(partons), rend(partons), qH-pH, qH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); pn += to_HepLorentzVector(partons.back()); --end_ladder; } else{ throw std::logic_error("Can only reweight FKL or uno processes in H+Jets"); } const double ladder_factor = FKL_ladder_weight( begin_ladder, first_after_Higgs, q0, pa, pb, p1, pn, param_.regulator_lambda )*FKL_ladder_weight( first_after_Higgs, end_ladder, qH - pH, pa, pb, p1, pn, param_.regulator_lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } namespace { double get_AWZH_coupling(Event const & ev, double alpha_s, double alpha_w) { const auto AWZH_boson = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](auto const & p){return is_AWZH_boson(p);} ); if(AWZH_boson == end(ev.outgoing())) return 1.; switch(AWZH_boson->type){ case pid::Higgs: return alpha_s*alpha_s; case pid::Wp: case pid::Wm: return alpha_w*alpha_w; // TODO case pid::photon: case pid::Z: default: throw not_implemented("Emission of boson of unsupported type"); } } } double MatrixElement::tree_param(Event const & ev, double mur) const { assert(is_resummable(ev.type())); const auto begin_partons = ev.begin_partons(); const auto end_partons = ev.end_partons(); const auto num_partons = std::distance(begin_partons, end_partons); const double alpha_s = alpha_s_(mur); const double gs2 = 4.*M_PI*alpha_s; double res = std::pow(gs2, num_partons); if(param_.log_correction){ // use alpha_s(q_perp), evolved to mur assert(num_partons >= 2); const auto first_emission = std::next(begin_partons); const auto last_emission = std::prev(end_partons); for(auto parton = first_emission; parton != last_emission; ++parton){ res *= 1. + alpha_s/(2.*M_PI)*beta0*log(mur/parton->perp()); } } return get_AWZH_coupling(ev, alpha_s, param_.ew_parameters.alpha_w())*res; } } // namespace HEJ diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc index d3b8dbc..b1c29c1 100644 --- a/src/PhaseSpacePoint.cc +++ b/src/PhaseSpacePoint.cc @@ -1,840 +1,840 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/PhaseSpacePoint.hh" #include <algorithm> #include <assert.h> #include <numeric> #include <random> #include "fastjet/ClusterSequence.hh" #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/JetSplitter.hh" #include "HEJ/kinematics.hh" #include "HEJ/resummation_jet.hh" #include "HEJ/utility.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/event_types.hh" namespace HEJ{ namespace { constexpr int max_jet_user_idx = PhaseSpacePoint::ng_max; bool is_nonjet_parton(fastjet::PseudoJet const & parton){ assert(parton.user_index() != -1); return parton.user_index() > max_jet_user_idx; } bool is_jet_parton(fastjet::PseudoJet const & parton){ assert(parton.user_index() != -1); return parton.user_index() <= max_jet_user_idx; } // user indices for partons with extremal rapidity constexpr int qqxmid1_uid = -9; constexpr int qqxmid2_uid = -8; constexpr int qqxb_uid = -7; constexpr int qqxf_uid = -6; constexpr int unob_uid = -5; constexpr int unof_uid = -4; constexpr int backward_FKL_uid = -3; constexpr int forward_FKL_uid = -2; double estimate_ng_mean(std::vector<fastjet::PseudoJet> const & Born_jets){ const double delta_y = Born_jets.back().rapidity() - Born_jets.front().rapidity(); assert(delta_y > 0); // Formula derived from fit in arXiv:1805.04446 (see Fig. 2) return 0.975052*delta_y; } static_assert( std::numeric_limits<double>::has_quiet_NaN, "no quiet NaN for double" ); constexpr double NaN = std::numeric_limits<double>::quiet_NaN(); } // namespace anonymous Event::EventData to_EventData(PhaseSpacePoint psp){ Event::EventData result; result.incoming = std::move(psp).incoming_; assert(result.incoming.size() == 2); result.outgoing = std::move(psp).outgoing_; // technically Event::EventData doesn't have to be sorted, // but PhaseSpacePoint should be anyway assert( std::is_sorted( begin(result.outgoing), end(result.outgoing), HEJ::rapidity_less{} ) ); assert(result.outgoing.size() >= 2); result.decays = std::move(psp).decays_; result.parameters.central = {NaN, NaN, psp.weight()}; return result; } std::vector<fastjet::PseudoJet> PhaseSpacePoint::cluster_jets( std::vector<fastjet::PseudoJet> const & partons ) const{ fastjet::ClusterSequence cs(partons, param_.jet_param.def); return sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt)); } bool PhaseSpacePoint::pass_resummation_cuts( std::vector<fastjet::PseudoJet> const & jets ) const{ return cluster_jets(jets).size() == jets.size(); } int PhaseSpacePoint::sample_ng( std::vector<fastjet::PseudoJet> const & Born_jets, RNG & ran ){ const double ng_mean = estimate_ng_mean(Born_jets); std::poisson_distribution<int> dist(ng_mean); const int ng = dist(ran); assert(ng >= 0); assert(ng < ng_max); weight_ *= std::tgamma(ng + 1)*std::exp(ng_mean)*std::pow(ng_mean, -ng); return ng; } void PhaseSpacePoint::copy_AWZH_boson_from(Event const & event){ auto const & from = event.outgoing(); const auto AWZH_boson = std::find_if( begin(from), end(from), [](Particle const & p){ return is_AWZH_boson(p); } ); if(AWZH_boson == end(from)) return; auto insertion_point = std::lower_bound( begin(outgoing_), end(outgoing_), *AWZH_boson, rapidity_less{} ); outgoing_.insert(insertion_point, *AWZH_boson); // copy decay products const int idx = std::distance(begin(from), AWZH_boson); assert(idx >= 0); const auto decay_it = event.decays().find(idx); if(decay_it != end(event.decays())){ const int new_idx = std::distance(begin(outgoing_), insertion_point); assert(new_idx >= 0); assert(outgoing_[new_idx].type == AWZH_boson->type); decays_.emplace(new_idx, decay_it->second); } assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); } namespace { auto get_first_anyquark_emission(Event const & ev) { // find born quarks (ignore extremal partons) auto const firstquark = std::find_if( std::next(ev.begin_partons()), std::prev(ev.end_partons(), 2), [](Particle const & s){ return (is_anyquark(s)); } ); // assert that it is a q-q_bar pair. assert(std::distance(firstquark, ev.end_partons()) != 2); assert( ( is_quark(*firstquark) && is_antiquark(*std::next(firstquark)) ) || ( is_antiquark(*firstquark) && is_quark(*std::next(firstquark)) ) ); return firstquark; } //! returns index of most backward q-qbar jet template<class Iterator> int get_back_quark_jet(Event const & ev, Iterator firstquark){ // find jets at FO corresponding to the quarks // technically this isn't necessary for LO std::vector<int> const born_indices{ ev.particle_jet_indices() }; const auto firstquark_idx = std::distance(ev.begin_partons(), firstquark); int const firstjet_idx = born_indices[firstquark_idx]; assert(firstjet_idx>0); assert( born_indices[firstquark_idx+1] == firstjet_idx+1 ); return firstjet_idx; } //! returns index of most backward q-qbar jet int getBackQuarkJet(Event const & ev){ const auto firstquark = get_first_anyquark_emission(ev); return get_back_quark_jet(ev, firstquark); } template<class ConstIterator, class Iterator> void label_extremal_qqx( ConstIterator born_begin, ConstIterator born_end, Iterator first_out ){ // find born quarks const auto firstquark = std::find_if( born_begin, born_end-1, [](Particle const & s){ return (is_anyquark(s)); } ); assert(firstquark != born_end-1); const auto secondquark = std::find_if( firstquark+1, born_end, [](Particle const & s){ return (is_anyquark(s)); } ); assert(secondquark != born_end); assert( ( is_quark(*firstquark) && is_antiquark(*secondquark) ) || ( is_antiquark(*firstquark) && is_quark(*secondquark) )); assert(first_out->type == ParticleID::gluon); assert((first_out+1)->type == ParticleID::gluon); // copy type from born first_out->type = firstquark->type; (first_out+1)->type = secondquark->type; } } void PhaseSpacePoint::label_qqx(Event const & event){ assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); assert(filter_partons(outgoing_).size() == outgoing_.size()); if(qqxb_){ label_extremal_qqx( event.outgoing().cbegin(), event.outgoing().cend(), outgoing_.begin() ); return; } if(qqxf_){ // same as qqxb with reversed order label_extremal_qqx( event.outgoing().crbegin(), event.outgoing().crend(), outgoing_.rbegin() ); return; } // central qqx const auto firstquark = get_first_anyquark_emission(event); // find jets at FO corresponding to the quarks // technically this isn't necessary for LO const auto firstjet_idx = get_back_quark_jet(event, firstquark); // find corresponding jets after resummation fastjet::ClusterSequence cs{to_PseudoJet(outgoing_), param_.jet_param.def}; auto const jets = fastjet::sorted_by_rapidity( cs.inclusive_jets( param_.jet_param.min_pt )); std::vector<int> const resum_indices{ cs.particle_jet_indices({jets}) }; // assert that jets didn't move assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx )->rapidity(), jets[ firstjet_idx ].rapidity(), 1e-2) ); assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx+1 )->rapidity(), jets[ firstjet_idx+1 ].rapidity(), 1e-2) ); // find last partons in first (central) jet size_t idx_out = 0; for(size_t i=resum_indices.size()-2; i>0; --i) if(resum_indices[i] == firstjet_idx){ idx_out = i; break; } assert(idx_out != 0); // check that there is sufficient pt in jets from the quarks const double minpartonjetpt = 1. - param_.max_ext_soft_pt_fraction; if (outgoing_[idx_out].p.pt()<minpartonjetpt*( event.jets().cbegin()+firstjet_idx )->pt()){ weight_=0.; status_ = StatusCode::wrong_jets; return; } if (outgoing_[idx_out+1].p.pt()<minpartonjetpt*( event.jets().cbegin()+firstjet_idx+1 )->pt()){ weight_=0.; status_ = StatusCode::wrong_jets; return; } // check that no additional emission between jets // such configurations are possible if we have an gluon gets generated // inside the rapidities of the qqx chain, but clusted to a // differnet/outside jet. Changing this is non trivial if(resum_indices[idx_out+1] != resum_indices[idx_out]+1){ weight_=0.; status_ = StatusCode::gluon_in_qqx; return; } outgoing_[idx_out].type = firstquark->type; outgoing_[idx_out+1].type = std::next(firstquark)->type; } void PhaseSpacePoint::label_quarks(Event const & ev){ const auto WEmit = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](Particle const & s){ return abs(s.type) == pid::Wp; } ); if (WEmit != end(ev.outgoing())){ if(!qqxb_) { const size_t backward_FKL_idx = unob_?1:0; const auto backward_FKL = std::next(ev.begin_partons(), backward_FKL_idx); outgoing_[backward_FKL_idx].type = backward_FKL->type; } if(!qqxf_) { const size_t forward_FKL_idx = unof_?1:0; const auto forward_FKL = std::prev(ev.end_partons(), 1+forward_FKL_idx); outgoing_.rbegin()[unof_].type = forward_FKL->type; } } else { most_backward_FKL(outgoing_).type = ev.incoming().front().type; most_forward_FKL(outgoing_).type = ev.incoming().back().type; } if(qqxmid_||qqxb_||qqxf_){ label_qqx(ev); } } PhaseSpacePoint::PhaseSpacePoint( Event const & ev, PhaseSpacePointConfig conf, RNG & ran ): unob_{ev.type() == event_type::unob}, unof_{ev.type() == event_type::unof}, qqxb_{ev.type() == event_type::qqxexb}, qqxf_{ev.type() == event_type::qqxexf}, qqxmid_{ev.type() == event_type::qqxmid}, param_{std::move(conf)}, status_{unspecified} { weight_ = 1; const auto & Born_jets = ev.jets(); const int ng = sample_ng(Born_jets, ran); weight_ /= std::tgamma(ng + 1); const int ng_jets = sample_ng_jets(ng, Born_jets, ran); std::vector<fastjet::PseudoJet> out_partons = gen_non_jet( ng - ng_jets, CMINPT, param_.jet_param.min_pt, ran ); int qqxbackjet(-1); if(qqxmid_){ qqxbackjet = getBackQuarkJet(ev); } const auto qperp = std::accumulate( begin(out_partons), end(out_partons), fastjet::PseudoJet{} ); const auto jets = reshuffle(Born_jets, qperp); if(weight_ == 0.) { status_ = failed_reshuffle; return; } if(! pass_resummation_cuts(jets)){ status_ = failed_resummation_cuts; weight_ = 0.; return; } std::vector<fastjet::PseudoJet> jet_partons = split( jets, ng_jets, qqxbackjet, ran ); if(weight_ == 0.) { status_ = StatusCode::failed_split; return; } if(qqxmid_){ rescale_qqx_rapidities( out_partons, jets, most_backward_FKL(jet_partons).rapidity(), most_forward_FKL(jet_partons).rapidity(), qqxbackjet ); } else{ rescale_rapidities( out_partons, most_backward_FKL(jet_partons).rapidity(), most_forward_FKL(jet_partons).rapidity() ); } if(! cluster_jets(out_partons).empty()){ weight_ = 0.; status_ = StatusCode::empty_jets; return; } std::sort(begin(out_partons), end(out_partons), rapidity_less{}); assert( std::is_sorted(begin(jet_partons), end(jet_partons), rapidity_less{}) ); const auto first_jet_parton = out_partons.insert( end(out_partons), begin(jet_partons), end(jet_partons) ); std::inplace_merge( begin(out_partons), first_jet_parton, end(out_partons), rapidity_less{} ); if(! jets_ok(Born_jets, out_partons)){ weight_ = 0.; status_ = StatusCode::wrong_jets; return; } weight_ *= phase_space_normalisation(Born_jets.size(), out_partons.size()); outgoing_.reserve(out_partons.size() + 1); // one slot for possible A, W, Z, H for(auto & p: out_partons){ outgoing_.emplace_back(Particle{pid::gluon, std::move(p), {}}); } assert(!outgoing_.empty()); label_quarks(ev); if(weight_ == 0.) { //! @TODO optimise s.t. this is not possible // status is handled internally return; } copy_AWZH_boson_from(ev); reconstruct_incoming(ev.incoming()); status_ = StatusCode::good; } std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_non_jet( int count, double ptmin, double ptmax, RNG & ran ){ // heuristic parameters for pt sampling const double ptpar = 1.3 + count/5.; const double temp1 = atan((ptmax - ptmin)/ptpar); std::vector<fastjet::PseudoJet> partons(count); for(size_t i = 0; i < static_cast<size_t>(count); ++i){ const double r1 = ran.flat(); const double pt = ptmin + ptpar*tan(r1*temp1); const double temp2 = cos(r1*temp1); const double phi = 2*M_PI*ran.flat(); weight_ *= 2.0*M_PI*pt*ptpar*temp1/(temp2*temp2); // we don't know the allowed rapidity span yet, // set a random value to be rescaled later on const double y = ran.flat(); partons[i].reset_PtYPhiM(pt, y, phi); // Set user index higher than any jet-parton index // in order to assert that these are not inside jets partons[i].set_user_index(i + 1 + ng_max); assert(ptmin-1e-5 <= partons[i].pt() && partons[i].pt() <= ptmax+1e-5); } assert(std::all_of(partons.cbegin(), partons.cend(), is_nonjet_parton)); return sorted_by_rapidity(partons); } void PhaseSpacePoint::rescale_qqx_rapidities( std::vector<fastjet::PseudoJet> & out_partons, std::vector<fastjet::PseudoJet> const & jets, const double ymin1, const double ymax2, const int qqxbackjet ){ const double ymax1 = jets[qqxbackjet].rapidity(); const double ymin2 = jets[qqxbackjet+1].rapidity(); constexpr double ep = 1e-7; const double tot_y = ymax1 - ymin1 + ymax2 - ymin2; std::vector<std::reference_wrapper<fastjet::PseudoJet>> refpart( out_partons.begin(), out_partons.end()); double ratio = (ymax1 - ymin1)/tot_y; const auto gap{ std::find_if(refpart.begin(), refpart.end(), [ratio](fastjet::PseudoJet p){ return (p.rapidity()>=ratio);} ) }; double ymin = ymin1; double ymax = ymax1; double dy = ymax - ymin - 2*ep; double offset = 0.; for(auto it_part=refpart.begin(); it_part<refpart.end(); ++it_part){ if(it_part == gap){ ymin = ymin2; ymax = ymax2; dy = ymax - ymin - 2*ep; offset = ratio; ratio = 1-ratio; } fastjet::PseudoJet & part = *it_part; assert(offset <= part.rapidity() && part.rapidity() < ratio+offset); const double y = ymin + ep + dy*((part.rapidity()-offset)/ratio); part.reset_momentum_PtYPhiM(part.pt(), y, part.phi()); weight_ *= tot_y-4.*ep; assert(ymin <= part.rapidity() && part.rapidity() <= ymax); } assert(is_sorted(begin(out_partons), end(out_partons), rapidity_less{})); } void PhaseSpacePoint::rescale_rapidities( std::vector<fastjet::PseudoJet> & partons, double ymin, double ymax ){ constexpr double ep = 1e-7; for(auto & parton: partons){ assert(0 <= parton.rapidity() && parton.rapidity() <= 1); const double dy = ymax - ymin - 2*ep; const double y = ymin + ep + dy*parton.rapidity(); parton.reset_momentum_PtYPhiM(parton.pt(), y, parton.phi()); weight_ *= dy; assert(ymin <= parton.rapidity() && parton.rapidity() <= ymax); } } namespace { template<typename T, typename... Rest> auto min(T const & a, T const & b, Rest&&... r) { using std::min; return min(a, min(b, std::forward<Rest>(r)...)); } } double PhaseSpacePoint::probability_in_jet( std::vector<fastjet::PseudoJet> const & Born_jets ) const{ assert(std::is_sorted(begin(Born_jets), end(Born_jets), rapidity_less{})); assert(Born_jets.size() >= 2); const double dy = Born_jets.back().rapidity() - Born_jets.front().rapidity(); const double R = param_.jet_param.def.R(); const int njets = Born_jets.size(); const double p_J_y_large = (njets-1)*R*R/(2.*dy); const double p_J_y0 = njets*R/M_PI; return min(p_J_y_large, p_J_y0, 1.); } int PhaseSpacePoint::sample_ng_jets( int ng, std::vector<fastjet::PseudoJet> const & Born_jets, RNG & ran ){ const double p_J = probability_in_jet(Born_jets); std::binomial_distribution<> bin_dist(ng, p_J); const int ng_J = bin_dist(ran); weight_ *= std::pow(p_J, -ng_J)*std::pow(1 - p_J, ng_J - ng); return ng_J; } std::vector<fastjet::PseudoJet> PhaseSpacePoint::reshuffle( std::vector<fastjet::PseudoJet> const & Born_jets, fastjet::PseudoJet const & q ){ if(q == fastjet::PseudoJet{0, 0, 0, 0}) return Born_jets; const auto jets = resummation_jet_momenta(Born_jets, q); if(jets.empty()){ weight_ = 0; return {}; } // additional Jacobian to ensure Born integration over delta gives 1 weight_ *= resummation_jet_weight(Born_jets, q); return jets; } std::vector<int> PhaseSpacePoint::distribute_jet_partons( int ng_jets, std::vector<fastjet::PseudoJet> const & jets, RNG & ran ){ size_t first_valid_jet = 0; size_t num_valid_jets = jets.size(); const double R_eff = 5./3.*param_.jet_param.def.R(); // if there is an unordered jet too far away from the FKL jets // then extra gluon constituents of the unordered jet would // violate the FKL rapidity ordering if((unob_||qqxb_) && jets[0].delta_R(jets[1]) > R_eff){ ++first_valid_jet; --num_valid_jets; } else if((unof_||qqxf_) && jets[jets.size()-1].delta_R(jets[jets.size()-2]) > R_eff){ --num_valid_jets; } std::vector<int> np(jets.size(), 1); for(int i = 0; i < ng_jets; ++i){ ++np[first_valid_jet + ran.flat() * num_valid_jets]; } weight_ *= std::pow(num_valid_jets, ng_jets); return np; } #ifndef NDEBUG namespace{ bool tagged_FKL_backward( std::vector<fastjet::PseudoJet> const & jet_partons ){ return std::find_if( begin(jet_partons), end(jet_partons), [](fastjet::PseudoJet const & p){ return p.user_index() == backward_FKL_uid; } ) != end(jet_partons); } bool tagged_FKL_forward( std::vector<fastjet::PseudoJet> const & jet_partons ){ // the most forward FKL parton is most likely near the end of jet_partons; // start search from there return std::find_if( jet_partons.rbegin(), jet_partons.rend(), [](fastjet::PseudoJet const & p){ return p.user_index() == forward_FKL_uid; } ) != jet_partons.rend(); } bool tagged_FKL_extremal( std::vector<fastjet::PseudoJet> const & jet_partons ){ return tagged_FKL_backward(jet_partons) && tagged_FKL_forward(jet_partons); } } // namespace anonymous #endif std::vector<fastjet::PseudoJet> PhaseSpacePoint::split( std::vector<fastjet::PseudoJet> const & jets, int ng_jets, size_t qqxbackjet, RNG & ran ){ return split( jets, distribute_jet_partons(ng_jets, jets, ran), qqxbackjet, ran); } bool PhaseSpacePoint::pass_extremal_cuts( fastjet::PseudoJet const & ext_parton, fastjet::PseudoJet const & jet ) const{ if(ext_parton.pt() < param_.min_extparton_pt) return false; return (ext_parton - jet).pt()/jet.pt() < param_.max_ext_soft_pt_fraction; } std::vector<fastjet::PseudoJet> PhaseSpacePoint::split( std::vector<fastjet::PseudoJet> const & jets, std::vector<int> const & np, size_t qqxbackjet, RNG & ran ){ assert(! jets.empty()); assert(jets.size() == np.size()); assert(pass_resummation_cuts(jets)); const size_t most_backward_FKL_idx = 0 + unob_ + qqxb_; const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqxf_; const auto & jet = param_.jet_param; const JetSplitter jet_splitter{jet.def, jet.min_pt}; std::vector<fastjet::PseudoJet> jet_partons; // randomly distribute jet gluons among jets for(size_t i = 0; i < jets.size(); ++i){ auto split_res = jet_splitter.split(jets[i], np[i], ran); weight_ *= split_res.weight; if(weight_ == 0) return {}; assert( std::all_of( begin(split_res.constituents), end(split_res.constituents), is_jet_parton ) ); const auto first_new_parton = jet_partons.insert( end(jet_partons), begin(split_res.constituents), end(split_res.constituents) ); // mark uno and extremal FKL emissions here so we can check // their position once all emissions are generated // also mark qqxmid partons, and apply appropriate pt cut. auto extremal = end(jet_partons); if (i == most_backward_FKL_idx){ //FKL backward emission extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(backward_FKL_uid); } else if(((unob_ || qqxb_) && i == 0)){ // unordered/qqxb extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index((unob_)?unob_uid:qqxb_uid); } else if (i == most_forward_FKL_idx){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(forward_FKL_uid); } else if(((unof_ || qqxf_) && i == jets.size() - 1)){ // unordered/qqxf extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index((unof_)?unof_uid:qqxf_uid); } else if((qqxmid_ && i == qqxbackjet)){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(qqxmid1_uid); } else if((qqxmid_ && i == qqxbackjet+1)){ extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(qqxmid2_uid); } if( extremal != end(jet_partons) && !pass_extremal_cuts(*extremal, jets[i]) ){ weight_ = 0; return {}; } } assert(tagged_FKL_extremal(jet_partons)); std::sort(begin(jet_partons), end(jet_partons), rapidity_less{}); if( !extremal_ok(jet_partons) || !split_preserved_jets(jets, jet_partons) ){ weight_ = 0.; return {}; } return jet_partons; } bool PhaseSpacePoint::extremal_ok( std::vector<fastjet::PseudoJet> const & partons ) const{ assert(std::is_sorted(begin(partons), end(partons), rapidity_less{})); if(unob_ && partons.front().user_index() != unob_uid) return false; if(unof_ && partons.back().user_index() != unof_uid) return false; if(qqxb_ && partons.front().user_index() != qqxb_uid) return false; if(qqxf_ && partons.back().user_index() != qqxf_uid) return false; return most_backward_FKL(partons).user_index() == backward_FKL_uid && most_forward_FKL(partons).user_index() == forward_FKL_uid; } bool PhaseSpacePoint::split_preserved_jets( std::vector<fastjet::PseudoJet> const & jets, std::vector<fastjet::PseudoJet> const & jet_partons ) const{ assert(std::is_sorted(begin(jets), end(jets), rapidity_less{})); const auto split_jets = cluster_jets(jet_partons); // this can happen if two overlapping jets // are both split into more than one parton if(split_jets.size() != jets.size()) return false; for(size_t i = 0; i < split_jets.size(); ++i){ // this can happen if there are two overlapping jets // and a parton is assigned to the "wrong" jet if(!nearby_ep(jets[i].rapidity(), split_jets[i].rapidity(), 1e-2)){ return false; } } return true; } template<class Particle> Particle const & PhaseSpacePoint::most_backward_FKL( std::vector<Particle> const & partons ) const{ return partons[0 + unob_ + qqxb_]; } template<class Particle> Particle const & PhaseSpacePoint::most_forward_FKL( std::vector<Particle> const & partons ) const{ const size_t idx = partons.size() - 1 - unof_ - qqxf_; assert(idx < partons.size()); return partons[idx]; } template<class Particle> Particle & PhaseSpacePoint::most_backward_FKL( std::vector<Particle> & partons ) const{ return partons[0 + unob_ + qqxb_]; } template<class Particle> Particle & PhaseSpacePoint::most_forward_FKL( std::vector<Particle> & partons ) const{ const size_t idx = partons.size() - 1 - unof_ - qqxf_; assert(idx < partons.size()); return partons[idx]; } bool PhaseSpacePoint::contains_idx( fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton ) const { auto const & constituents = jet.constituents(); const int idx = parton.user_index(); const bool injet = std::find_if( begin(constituents), end(constituents), [idx](fastjet::PseudoJet const & con){return con.user_index() == idx;} ) != end(constituents); const double minpartonjetpt = 1. - param_.max_ext_soft_pt_fraction; return ((parton.pt()>minpartonjetpt*jet.pt())&&injet); } bool PhaseSpacePoint::jets_ok( std::vector<fastjet::PseudoJet> const & Born_jets, std::vector<fastjet::PseudoJet> const & partons ) const{ fastjet::ClusterSequence cs(partons, param_.jet_param.def); const auto jets = sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt)); if(jets.size() != Born_jets.size()) return false; int in_jet = 0; for(size_t i = 0; i < jets.size(); ++i){ assert(jets[i].has_constituents()); for(auto && parton: jets[i].constituents()){ if(is_nonjet_parton(parton)) return false; } in_jet += jets[i].constituents().size(); } const int expect_in_jet = std::count_if( partons.cbegin(), partons.cend(), is_jet_parton ); if(in_jet != expect_in_jet) return false; // note that PseudoJet::contains does not work here if(! ( contains_idx(most_backward_FKL(jets), most_backward_FKL(partons)) && contains_idx(most_forward_FKL(jets), most_forward_FKL(partons)) )) return false; if(unob_ && !contains_idx(jets.front(), partons.front())) return false; if(qqxb_ && !contains_idx(jets.front(), partons.front())) return false; if(unof_ && !contains_idx(jets.back(), partons.back())) return false; if(qqxf_ && !contains_idx(jets.back(), partons.back())) return false; #ifndef NDEBUG for(size_t i = 0; i < jets.size(); ++i){ assert(nearby_ep(jets[i].rapidity(), Born_jets[i].rapidity(), 1e-2)); } #endif return true; } void PhaseSpacePoint::reconstruct_incoming( std::array<Particle, 2> const & Born_incoming ){ std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_); for(size_t i = 0; i < incoming_.size(); ++i){ incoming_[i].type = Born_incoming[i].type; } assert(momentum_conserved()); } double PhaseSpacePoint::phase_space_normalisation( int num_Born_jets, int num_out_partons ) const{ return pow(16*pow(M_PI,3), num_Born_jets - num_out_partons); } bool PhaseSpacePoint::momentum_conserved() const{ fastjet::PseudoJet diff; for(auto const & in: incoming()) diff += in.p; const double norm = diff.E(); for(auto const & out: outgoing()) diff -= out.p; return nearby(diff, fastjet::PseudoJet{}, norm); } } //namespace HEJ diff --git a/src/RivetAnalysis.cc b/src/RivetAnalysis.cc index ccf3943..975a564 100644 --- a/src/RivetAnalysis.cc +++ b/src/RivetAnalysis.cc @@ -1,186 +1,186 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/RivetAnalysis.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_RIVET #include <ostream> #include <stddef.h> #include "yaml-cpp/yaml.h" #include "Rivet/AnalysisHandler.hh" #include "Rivet/Config/RivetConfig.hh" #ifdef RIVET_ENABLE_HEPMC_3 #include "HepMC3/GenEvent.h" #include "HEJ/HepMC3Interface.hh" #else // rivet with HepMC 2 #include "HepMC/GenEvent.h" #include "HEJ/HepMC2Interface.hh" #endif #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #endif namespace HEJ{ std::unique_ptr<Analysis> RivetAnalysis::create( YAML::Node const & config, LHEF::HEPRUP const & heprup ){ return std::make_unique<RivetAnalysis>(config, heprup); } } #ifdef HEJ_BUILD_WITH_RIVET namespace HEJ { #ifdef RIVET_ENABLE_HEPMC_3 using HepMCInterface=HepMC3Interface; #else using HepMCInterface=HepMC2Interface; #endif struct RivetAnalysis::rivet_info { rivet_info(std::unique_ptr<Rivet::AnalysisHandler> && h, std::string && s, HEJ::HepMCInterface && m): handler{std::move(h)}, name{std::move(s)}, hepmc{std::move(m)} {} // AnalysisHandler doesn't allow a copy constructor -> use ptr std::unique_ptr<Rivet::AnalysisHandler> handler; std::string name; HEJ::HepMCInterface hepmc; }; RivetAnalysis::RivetAnalysis(YAML::Node const & config, LHEF::HEPRUP const & heprup ): heprup_{heprup}, first_event_(true) { // assert that only supported options are provided if(!config.IsMap()) throw invalid_type{"Rivet config has to be a map!"}; for(auto const & entry: config){ const auto name = entry.first.as<std::string>(); if(name != "output" && name != "rivet") throw unknown_option{"Unknown option \'"+name+"\' provided to rivet."}; } // get output name if(!config["output"]) throw std::invalid_argument{ "No output was provided to rivet. " "Either give an output or deactivate rivet." }; if(!config["output"].IsScalar()) throw invalid_type{ "Output for rivet must be a scalar." }; output_name_ = config["output"].as<std::string>(); // read in analyses const auto & name_node = config["rivet"]; switch(name_node.Type()){ case YAML::NodeType::Scalar: analyses_names_.push_back(name_node.as<std::string>()); break; case YAML::NodeType::Sequence: for(YAML::const_iterator it = name_node.begin(); it != name_node.end(); ++it){ analyses_names_.push_back(it->as<std::string>()); } break; default: throw std::invalid_argument{ "No analysis was provided to rivet. " "Either give an analysis or deactivate rivet." }; } } // it is a bit ugly that we can't do this directly in `initialise` void RivetAnalysis::init(Event const & event){ #ifdef HEJ_USE_RIVET2 rivet_runs_.reserve(event.variations().size()+1); #else (void) event; // shut up compiler #endif rivet_runs_.emplace_back( std::make_unique<Rivet::AnalysisHandler>(), std::string(""), HepMCInterface(heprup_) ); rivet_runs_.back().handler->addAnalyses(analyses_names_); #ifdef HEJ_USE_RIVET2 //! scale variation in rivet 2 through multiple analyses if( !event.variations().empty() ){ for(auto const & vari : event.variations()){ rivet_runs_.emplace_back( std::make_unique<Rivet::AnalysisHandler>(), "."+to_simple_string(*vari.description), HepMCInterface(heprup_) ); rivet_runs_.back().handler->addAnalyses(analyses_names_); } } #endif } void RivetAnalysis::fill(Event const & event, Event const &){ if(first_event_){ first_event_=false; init(event); } auto hepmc_event(rivet_runs_.front().hepmc(event)); rivet_runs_.front().handler->analyze(hepmc_event); #ifdef HEJ_USE_RIVET2 for(size_t i = 1; i < rivet_runs_.size(); ++i){ auto & run = rivet_runs_[i]; run.hepmc.set_central(hepmc_event, event, i-1); run.handler->analyze(hepmc_event); } #endif } void RivetAnalysis::finalise(){ for(auto & run: rivet_runs_){ run.handler->finalize(); run.handler->writeData(output_name_+run.name+std::string(".yoda")); } } } // namespace HEJ #else // no rivet => create empty analysis namespace Rivet { class AnalysisHandler {}; } namespace HEJ { struct RivetAnalysis::rivet_info{}; RivetAnalysis::RivetAnalysis(YAML::Node const &, LHEF::HEPRUP const &){ throw std::invalid_argument( "Failed to create RivetAnalysis: " "HEJ 2 was built without rivet support" ); } void RivetAnalysis::init(Event const &){} void RivetAnalysis::fill(Event const &, Event const &){} void RivetAnalysis::finalise(){} } // namespace HEJ #endif namespace HEJ { RivetAnalysis::~RivetAnalysis() = default; } diff --git a/src/YAMLreader.cc b/src/YAMLreader.cc index 2a7cd19..f01914a 100644 --- a/src/YAMLreader.cc +++ b/src/YAMLreader.cc @@ -1,565 +1,565 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/YAMLreader.hh" #include <algorithm> #include <iostream> #include <limits> #include <map> #include <string> #include <unordered_map> #include <vector> #include <dlfcn.h> #include "HEJ/Constants.hh" #include "HEJ/event_types.hh" #include "HEJ/ConfigFlags.hh" #include "HEJ/output_formats.hh" #include "HEJ/ScaleFunction.hh" namespace HEJ{ class Event; namespace{ //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "trials", "min extparton pt", "max ext soft pt fraction", "scales", "scale factors", "max scale ratio", "import scales", "log correction", "event output", "analysis", "analyses", "vev", "regulator parameter", "max events" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "algorithm", "R"}){ supported["resummation jets"][jet_opt] = ""; supported["fixed order jets"][jet_opt] = ""; } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } for(auto && opt: {"name", "seed"}){ supported["random generator"][opt] = ""; } for(auto && opt: {"FKL", "unordered", "extremal qqx", "central qqx", "non-resummable"}){ supported["event treatment"][opt] = ""; } for(auto && particle_type: {"Higgs", "W", "Z"}){ for(auto && particle_opt: {"mass", "width"}){ supported["particle properties"][particle_type][particle_opt] = ""; } } for(auto && opt: {"type", "trials", "max deviation"}){ supported["unweight"][opt] = ""; } return supported; }(); return supported; } fastjet::JetAlgorithm to_JetAlgorithm(std::string const & algo){ using namespace fastjet; static const std::map<std::string, fastjet::JetAlgorithm> known = { {"kt", kt_algorithm}, {"cambridge", cambridge_algorithm}, {"antikt", antikt_algorithm}, {"cambridge for passive", cambridge_for_passive_algorithm}, {"plugin", plugin_algorithm} }; const auto res = known.find(algo); if(res == known.end()){ throw std::invalid_argument("Unknown jet algorithm \"" + algo + "\""); } return res->second; } EventTreatment to_EventTreatment(std::string const & name){ static const std::map<std::string, EventTreatment> known = { {"reweight", EventTreatment::reweight}, {"keep", EventTreatment::keep}, {"discard", EventTreatment::discard} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown event treatment \"" + name + "\""); } return res->second; } WeightType to_weight_type(std::string const & setting){ if(setting == "weighted") return WeightType::weighted; if(setting =="resummation") return WeightType::unweighted_resum; if(setting =="partial") return WeightType::partially_unweighted; throw std::invalid_argument{"Unknown weight type \"" + setting + "\""}; } } // namespace anonymous namespace detail{ void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml){ setting = to_JetAlgorithm(yaml.as<std::string>()); } void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml){ setting = to_EventTreatment(yaml.as<std::string>()); } void set_from_yaml(ParticleID & setting, YAML::Node const & yaml){ setting = to_ParticleID(yaml.as<std::string>()); } void set_from_yaml(WeightType & setting, YAML::Node const & yaml){ setting = to_weight_type(yaml.as<std::string>()); } } // namespace detail JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ assert(node); JetParameters result; fastjet::JetAlgorithm jet_algo = fastjet::antikt_algorithm; double R; set_from_yaml_if_defined(jet_algo, node, entry, "algorithm"); set_from_yaml(R, node, entry, "R"); result.def = fastjet::JetDefinition{jet_algo, R}; set_from_yaml(result.min_pt, node, entry, "min pt"); return result; } RNGConfig to_RNGConfig( YAML::Node const & node, std::string const & entry ){ assert(node); RNGConfig result; set_from_yaml(result.name, node, entry, "name"); set_from_yaml_if_defined(result.seed, node, entry, "seed"); return result; } ParticleProperties get_particle_properties( YAML::Node const & node, std::string const & entry, std::string const & boson ){ ParticleProperties result; set_from_yaml(result.mass, node, entry, boson, "mass"); set_from_yaml(result.width, node, entry, boson, "width"); return result; } EWConstants get_ew_parameters(YAML::Node const & node){ EWConstants result; double vev; set_from_yaml(vev, node, "vev"); result.set_vevWZH(vev, get_particle_properties(node, "particle properties", "W"), get_particle_properties(node, "particle properties", "Z"), get_particle_properties(node, "particle properties", "Higgs") ); return result; } HiggsCouplingSettings get_Higgs_coupling( YAML::Node const & node, std::string const & entry ){ assert(node); static constexpr double mt_max = 2e4; #ifndef HEJ_BUILD_WITH_QCDLOOP if(node[entry]){ throw std::invalid_argument{ "Higgs coupling settings require building HEJ 2 " "with QCDloop support" }; } #endif HiggsCouplingSettings settings; set_from_yaml_if_defined(settings.mt, node, entry, "mt"); set_from_yaml_if_defined(settings.mb, node, entry, "mb"); set_from_yaml_if_defined(settings.include_bottom, node, entry, "include bottom"); set_from_yaml_if_defined(settings.use_impact_factors, node, entry, "use impact factors"); if(settings.use_impact_factors){ if(settings.mt != std::numeric_limits<double>::infinity()){ throw std::invalid_argument{ "Conflicting settings: " "impact factors may only be used in the infinite top mass limit" }; } } else{ // huge values of the top mass are numerically unstable settings.mt = std::min(settings.mt, mt_max); } return settings; } FileFormat to_FileFormat(std::string const & name){ static const std::map<std::string, FileFormat> known = { {"Les Houches", FileFormat::Les_Houches}, {"HepMC", FileFormat::HepMC}, {"HepMC2", FileFormat::HepMC2}, {"HepMC3", FileFormat::HepMC3}, {"HDF5", FileFormat::HDF5} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown file format \"" + name + "\""); } return res->second; } std::string extract_suffix(std::string const & filename){ size_t separator = filename.rfind('.'); if(separator == filename.npos) return {}; return filename.substr(separator + 1); } FileFormat format_from_suffix(std::string const & filename){ const std::string suffix = extract_suffix(filename); if(suffix == "lhe") return FileFormat::Les_Houches; if(suffix == "hepmc") return FileFormat::HepMC; if(suffix == "hepmc3") return FileFormat::HepMC3; if(suffix == "hepmc2") return FileFormat::HepMC2; if(suffix == "hdf5") return FileFormat::HDF5; throw std::invalid_argument{ "Can't determine format for output file \"" + filename + "\"" }; } void assert_all_options_known( YAML::Node const & conf, YAML::Node const & supported ){ if(!conf.IsMap()) return; if(!supported.IsMap()) throw invalid_type{"must not have sub-entries"}; for(auto const & entry: conf){ const auto name = entry.first.as<std::string>(); if(! supported[name]) throw unknown_option{name}; /* check sub-options, e.g. 'resummation jets: min pt' * we don't check analyses sub-options * those depend on the analysis being used and should be checked there * similar for "import scales" */ if(name != "analyses" && name != "analysis" && name != "import scales"){ try{ assert_all_options_known(conf[name], supported[name]); } catch(unknown_option const & ex){ throw unknown_option{name + ": " + ex.what()}; } catch(invalid_type const & ex){ throw invalid_type{name + ": " + ex.what()}; } } } } } // namespace HEJ namespace YAML { Node convert<HEJ::OutputFile>::encode(HEJ::OutputFile const & outfile) { Node node; node[to_string(outfile.format)] = outfile.name; return node; } bool convert<HEJ::OutputFile>::decode(Node const & node, HEJ::OutputFile & out) { switch(node.Type()){ case NodeType::Map: { YAML::const_iterator it = node.begin(); out.format = HEJ::to_FileFormat(it->first.as<std::string>()); out.name = it->second.as<std::string>(); return true; } case NodeType::Scalar: out.name = node.as<std::string>(); out.format = HEJ::format_from_suffix(out.name); return true; default: return false; } } } // namespace YAML namespace HEJ{ namespace detail{ void set_from_yaml(OutputFile & setting, YAML::Node const & yaml){ setting = yaml.as<OutputFile>(); } } namespace{ void update_fixed_order_jet_parameters( JetParameters & fixed_order_jets, YAML::Node const & yaml ){ if(!yaml["fixed order jets"]) return; set_from_yaml_if_defined( fixed_order_jets.min_pt, yaml, "fixed order jets", "min pt" ); fastjet::JetAlgorithm algo = fixed_order_jets.def.jet_algorithm(); set_from_yaml_if_defined(algo, yaml, "fixed order jets", "algorithm"); double R = fixed_order_jets.def.R(); set_from_yaml_if_defined(R, yaml, "fixed order jets", "R"); fixed_order_jets.def = fastjet::JetDefinition{algo, R}; } // like std::stod, but throw if not the whole string can be converted double to_double(std::string const & str){ std::size_t pos; const double result = std::stod(str, &pos); if(pos < str.size()){ throw std::invalid_argument(str + " is not a valid double value"); } return result; } using EventScale = double (*)(Event const &); void import_scale_functions( std::string const & file, std::vector<std::string> const & scale_names, std::unordered_map<std::string, EventScale> & known ) { auto handle = dlopen(file.c_str(), RTLD_NOW); char * error = dlerror(); if(error != nullptr) throw std::runtime_error{error}; for(auto const & scale: scale_names) { void * sym = dlsym(handle, scale.c_str()); error = dlerror(); if(error != nullptr) throw std::runtime_error{error}; known.emplace(scale, reinterpret_cast<EventScale>(sym)); } } auto get_scale_map( YAML::Node const & yaml ) { std::unordered_map<std::string, EventScale> scale_map; scale_map.emplace("H_T", H_T); scale_map.emplace("max jet pperp", max_jet_pt); scale_map.emplace("jet invariant mass", jet_invariant_mass); scale_map.emplace("m_j1j2", m_j1j2); if(yaml["import scales"]) { if(! yaml["import scales"].IsMap()) { throw invalid_type{"Entry 'import scales' is not a map"}; } for(auto const & import: yaml["import scales"]) { const auto file = import.first.as<std::string>(); const auto scale_names = import.second.IsSequence() ?import.second.as<std::vector<std::string>>() :std::vector<std::string>{import.second.as<std::string>()}; import_scale_functions(file, scale_names, scale_map); } } return scale_map; } // simple (as in non-composite) scale functions /** * An example for a simple scale function would be H_T, * H_T/2 is then composite (take H_T and then divide by 2) */ ScaleFunction parse_simple_ScaleFunction( std::string const & scale_fun, std::unordered_map<std::string, EventScale> const & known ) { assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); const auto it = known.find(scale_fun); if(it != end(known)) return {it->first, it->second}; try{ const double scale = to_double(scale_fun); return {scale_fun, FixedScale{scale}}; } catch(std::invalid_argument const &){} throw std::invalid_argument{"Unknown scale choice: \"" + scale_fun + "\""}; } std::string trim_front(std::string const & str){ const auto new_begin = std::find_if( begin(str), end(str), [](char c){ return ! std::isspace(c); } ); return std::string(new_begin, end(str)); } std::string trim_back(std::string str){ size_t pos = str.size() - 1; // use guaranteed wrap-around behaviour to check whether we have // traversed the whole string for(; pos < str.size() && std::isspace(str[pos]); --pos) {} str.resize(pos + 1); // note that pos + 1 can be 0 return str; } ScaleFunction parse_ScaleFunction( std::string const & scale_fun, std::unordered_map<std::string, EventScale> const & known ){ assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); // parse from right to left => a/b/c gives (a/b)/c const size_t delim = scale_fun.find_last_of("*/"); if(delim == scale_fun.npos){ return parse_simple_ScaleFunction(scale_fun, known); } const std::string first = trim_back(std::string{scale_fun, 0, delim}); const std::string second = trim_front(std::string{scale_fun, delim+1}); if(scale_fun[delim] == '/'){ return parse_ScaleFunction(first, known) / parse_ScaleFunction(second, known); } else{ assert(scale_fun[delim] == '*'); return parse_ScaleFunction(first, known) * parse_ScaleFunction(second, known); } } EventTreatMap get_event_treatment( YAML::Node const & node, std::string const & entry ){ using namespace event_type; EventTreatMap treat { {no_2_jets, EventTreatment::discard}, {bad_final_state, EventTreatment::discard}, {FKL, EventTreatment::discard}, {unob, EventTreatment::discard}, {unof, EventTreatment::discard}, {qqxexb, EventTreatment::discard}, {qqxexf, EventTreatment::discard}, {qqxmid, EventTreatment::discard}, {non_resummable, EventTreatment::discard} }; set_from_yaml(treat.at(FKL), node, entry, "FKL"); set_from_yaml(treat.at(unob), node, entry, "unordered"); treat.at(unof) = treat.at(unob); set_from_yaml(treat.at(qqxexb), node, entry, "extremal qqx"); treat.at(qqxexf) = treat.at(qqxexb); set_from_yaml(treat.at(qqxmid), node, entry, "central qqx"); set_from_yaml(treat.at(non_resummable), node, entry, "non-resummable"); if(treat[non_resummable] == EventTreatment::reweight){ throw std::invalid_argument{"Cannot reweight non-resummable events"}; } return treat; } Config to_Config(YAML::Node const & yaml){ try{ assert_all_options_known(yaml, get_supported_options()); } catch(unknown_option const & ex){ throw unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.resummation_jets = get_jet_parameters(yaml, "resummation jets"); config.fixed_order_jets = config.resummation_jets; update_fixed_order_jet_parameters(config.fixed_order_jets, yaml); set_from_yaml_if_defined(config.min_extparton_pt, yaml, "min extparton pt"); if(config.min_extparton_pt!=0) std::cerr << "WARNING: \"min extparton pt\" is deprecated." << " Please use \"max ext soft pt fraction\" instead.\n"; set_from_yaml( config.max_ext_soft_pt_fraction, yaml, "max ext soft pt fraction" ); // Sets the standard value, then changes this if defined config.regulator_lambda=CLAMBDA; set_from_yaml_if_defined(config.regulator_lambda, yaml, "regulator parameter"); set_from_yaml_if_defined(config.max_events, yaml, "max events"); set_from_yaml(config.trials, yaml, "trials"); config.weight_type = WeightType::weighted; set_from_yaml_if_defined(config.weight_type, yaml, "unweight", "type"); if(config.weight_type == WeightType::partially_unweighted) { config.unweight_config = PartialUnweightConfig{}; set_from_yaml( config.unweight_config->trials, yaml, "unweight", "trials" ); set_from_yaml( config.unweight_config->max_dev, yaml, "unweight", "max deviation" ); } else if(yaml["unweight"]) { for(auto && opt: {"trials", "max deviation"}) { if(yaml["unweight"][opt]) { throw std::invalid_argument{ "'unweight: " + std::string{opt} + "' " "is only supported if 'unweight: type' is set to 'partial'" }; } } } set_from_yaml(config.log_correction, yaml, "log correction"); config.treat = get_event_treatment(yaml, "event treatment"); set_from_yaml_if_defined(config.output, yaml, "event output"); config.rng = to_RNGConfig(yaml, "random generator"); set_from_yaml_if_defined(config.analyses_parameters, yaml, "analyses"); if(yaml["analysis"]){ std::cerr << "WARNING: Configuration entry 'analysis' is deprecated. " " Use 'analyses' instead.\n"; set_from_yaml(config.analysis_parameters, yaml, "analysis"); if(!config.analysis_parameters.IsNull()){ config.analyses_parameters.push_back(config.analysis_parameters); } } config.scales = to_ScaleConfig(yaml); config.ew_parameters = get_ew_parameters(yaml); config.Higgs_coupling = get_Higgs_coupling(yaml, "Higgs coupling"); return config; } } // namespace anonymous ScaleConfig to_ScaleConfig(YAML::Node const & yaml){ ScaleConfig config; auto scale_funs = get_scale_map(yaml); std::vector<std::string> scales; set_from_yaml(scales, yaml, "scales"); config.base.reserve(scales.size()); std::transform( begin(scales), end(scales), std::back_inserter(config.base), [scale_funs](auto const & entry){ return parse_ScaleFunction(entry, scale_funs); } ); set_from_yaml_if_defined(config.factors, yaml, "scale factors"); config.max_ratio = std::numeric_limits<double>::infinity(); set_from_yaml_if_defined(config.max_ratio, yaml, "max scale ratio"); return config; } Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } } // namespace HEJ diff --git a/src/bin/HEJ.cc b/src/bin/HEJ.cc index 165c5eb..1ff402b 100644 --- a/src/bin/HEJ.cc +++ b/src/bin/HEJ.cc @@ -1,382 +1,382 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <array> #include <chrono> #include <iostream> #include <limits> #include <memory> #include <numeric> #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/optional.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<std::unique_ptr<HEJ::Analysis>> get_analyses( std::vector<YAML::Node> 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: // HEJ::optional is a better fit, but gives spurious errors with g++ 7.3.0 std::unique_ptr<HEJ::ProgressBar<double>> make_progress_bar( std::vector<double> const & xs ) { if(xs.empty()) return {}; const double Born_xs = std::accumulate(begin(xs), end(xs), 0.); return std::make_unique<HEJ::ProgressBar<double>>(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; } HEJ::Event to_event( LHEF::HEPEUP const & hepeup, HEJ::JetParameters const & fixed_order_jets ) { HEJ::Event::EventData event_data{hepeup}; event_data.reconstruct_intermediate(); return HEJ::Event{ std::move(event_data).cluster( fixed_order_jets.def, fixed_order_jets.min_pt ) }; } void unweight( HEJ::Unweighter & unweighter, HEJ::WeightType weight_type, std::vector<HEJ::Event> & 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<LHEF::HEPEUP> peek_events( HEJ::BufferedEventReader & reader, const int nevents ) { std::vector<LHEF::HEPEUP> events; while( static_cast<int>(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<HEJ::Event> & resummation_events, HEJ::EventReweighter & reweighter, LHEF::HEPEUP const & hepeup, const size_t trials, HEJ::JetParameters const & fixed_order_jets ) { const HEJ::Event FO_event = to_event(hepeup, fixed_order_jets); 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 ) { 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<int>{ std::cout, static_cast<int>(FO_events.size()) }; std::vector<HEJ::Event> resummation_events; for(auto const & hepeup: FO_events) { append_resummed_events( resummation_events, reweighter, hepeup, trials, fixed_order_jets ); ++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); 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.; const auto & 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<double>(*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<HEJ::RNG> 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 }; HEJ::optional<HEJ::Unweighter> 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 ); reader = std::make_unique<HEJ::BufferedEventReader>( std::move(buffered_reader) ); } // status infos & eye candy size_t nevent = 0; std::array<int, HEJ::event_type::last_type + 1> nevent_type{0}, nfailed_type{0}; auto progress = make_progress_bar(reader->heprup().XSECUP); HEJ::CrossSectionAccumulator xs; std::map<HEJ::StatusCode, int> 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; // 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); 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'; for(auto const & analysis: analyses){ analysis->finalise(); } 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<EventType>(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<double>(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 << "] " <<std::setw(2)<<std::right<< percent << "%\n"; } std::chrono::duration<double> 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; } diff --git a/src/get_analysis.cc b/src/get_analysis.cc index 56f56c5..1262a28 100644 --- a/src/get_analysis.cc +++ b/src/get_analysis.cc @@ -1,51 +1,51 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/get_analysis.hh" #include <dlfcn.h> #include <string> #include "yaml-cpp/yaml.h" #include "HEJ/EmptyAnalysis.hh" #include "HEJ/RivetAnalysis.hh" namespace HEJ{ std::unique_ptr<Analysis> get_analysis( YAML::Node const & parameters, LHEF::HEPRUP const & heprup ){ if(!parameters["plugin"]){ if(parameters["rivet"]) return RivetAnalysis::create(parameters, heprup); return EmptyAnalysis::create(parameters, heprup); } using AnalysisMaker = std::unique_ptr<Analysis> (*)( YAML::Node const &, LHEF::HEPRUP const &); const auto plugin_name = parameters["plugin"].as<std::string>(); auto handle = dlopen(plugin_name.c_str(), RTLD_NOW); char * error = dlerror(); if(error != nullptr) throw std::runtime_error(error); void * sym = dlsym(handle, "make_analysis"); error = dlerror(); if(error != nullptr) throw std::runtime_error(error); auto make_analysis = reinterpret_cast<AnalysisMaker>(sym); return make_analysis(parameters, heprup); } std::vector<std::unique_ptr<Analysis>> get_analyses( std::vector<YAML::Node> const & parameters, LHEF::HEPRUP const & heprup ){ std::vector<std::unique_ptr<Analysis>> anas; for(auto const & param: parameters){ anas.emplace_back(get_analysis(param, heprup)); } return anas; } } diff --git a/src/jets.cc b/src/jets.cc index 297e67f..fd71516 100644 --- a/src/jets.cc +++ b/src/jets.cc @@ -1,792 +1,792 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/jets.hh" #include <algorithm> #include "HEJ/Constants.hh" namespace { double metric(const size_t mu, const size_t nu) { if(mu != nu) return 0.; return (mu == 0)?1.:-1.; } } // Colour acceleration multiplier for gluons see eq. (7) in arXiv:0910.5113 // @TODO: this is not a current and should be moved somewhere else double K_g(double p1minus, double paminus) { return 1./2.*(p1minus/paminus + paminus/p1minus)*(HEJ::C_A - 1./HEJ::C_A) + 1./HEJ::C_A; } double K_g( HLV const & pout, HLV const & pin ) { if(pin.z() > 0) return K_g(pout.plus(), pin.plus()); return K_g(pout.minus(), pin.minus()); } CCurrent CCurrent::operator+(const CCurrent& other) { COM result_c0=c0 + other.c0; COM result_c1=c1 + other.c1; COM result_c2=c2 + other.c2; COM result_c3=c3 + other.c3; return CCurrent(result_c0,result_c1,result_c2,result_c3); } CCurrent CCurrent::operator-(const CCurrent& other) { COM result_c0=c0 - other.c0; COM result_c1=c1 - other.c1; COM result_c2=c2 - other.c2; COM result_c3=c3 - other.c3; return CCurrent(result_c0,result_c1,result_c2,result_c3); } CCurrent CCurrent::operator*(const double x) { COM result_c0=x*CCurrent::c0; COM result_c1=x*CCurrent::c1; COM result_c2=x*CCurrent::c2; COM result_c3=x*CCurrent::c3; return CCurrent(result_c0,result_c1,result_c2,result_c3); } CCurrent CCurrent::operator/(const double x) { COM result_c0=CCurrent::c0/x; COM result_c1=CCurrent::c1/x; COM result_c2=CCurrent::c2/x; COM result_c3=CCurrent::c3/x; return CCurrent(result_c0,result_c1,result_c2,result_c3); } CCurrent CCurrent::operator*(const COM x) { COM result_c0=x*CCurrent::c0; COM result_c1=x*CCurrent::c1; COM result_c2=x*CCurrent::c2; COM result_c3=x*CCurrent::c3; return CCurrent(result_c0,result_c1,result_c2,result_c3); } CCurrent CCurrent::operator/(const COM x) { COM result_c0=(CCurrent::c0)/x; COM result_c1=(CCurrent::c1)/x; COM result_c2=(CCurrent::c2)/x; COM result_c3=(CCurrent::c3)/x; return CCurrent(result_c0,result_c1,result_c2,result_c3); } std::ostream& operator <<(std::ostream& os, const CCurrent& cur) { os << "("<<cur.c0<< " ; "<<cur.c1<<" , "<<cur.c2<<" , "<<cur.c3<<")"; return os; } CCurrent operator * ( double x, CCurrent& m) { return m*x; } CCurrent operator * ( COM x, CCurrent& m) { return m*x; } CCurrent operator / ( double x, CCurrent& m) { return m/x; } CCurrent operator / ( COM x, CCurrent& m) { return m/x; } COM CCurrent::dot(HLV p1) { // Current goes (E,px,py,pz) // Vector goes (px,py,pz,E) return p1[3]*c0-p1[0]*c1-p1[1]*c2-p1[2]*c3; } COM CCurrent::dot(CCurrent p1) { return p1.c0*c0-p1.c1*c1-p1.c2*c2-p1.c3*c3; } //Current Functions void joi(HLV pout, bool helout, HLV pin, bool helin, current &cur) { cur[0]=0.; cur[1]=0.; cur[2]=0.; cur[3]=0.; const double sqpop = sqrt(std::abs(pout.plus())); const double sqpom = sqrt(std::abs(pout.minus())); // Allow for "jii" format const COM poperp = (pout.x()==0 && pout.y() ==0) ? -1 : pout.x()+COM(0,1)*pout.y(); if (helout != helin) { throw std::invalid_argument{"Non-matching helicities"}; } else if (helout == false) { // negative helicity if (pin.plus() > pin.minus()) { // if forward const double sqpip = sqrt(std::abs(pin.plus())); cur[0] = sqpop * sqpip; cur[1] = sqpom * sqpip * poperp / std::abs(poperp); cur[2] = -COM(0,1) * cur[1]; cur[3] = cur[0]; } else { // if backward const double sqpim = sqrt(std::abs(pin.minus())); cur[0] = -sqpom * sqpim * poperp / std::abs(poperp); cur[1] = -sqpim * sqpop; cur[2] = COM(0,1) * cur[1]; cur[3] = -cur[0]; } } else { // positive helicity if (pin.plus() > pin.minus()) { // if forward const double sqpip = sqrt(std::abs(pin.plus())); cur[0] = sqpop * sqpip; cur[1] = sqpom * sqpip * conj(poperp) / std::abs(poperp); cur[2] = COM(0,1) * cur[1]; cur[3] = cur[0]; } else { // if backward double sqpim = sqrt(std::abs(pin.minus())); cur[0] = -sqpom * sqpim * conj(poperp) / std::abs(poperp); cur[1] = -sqpim * sqpop; cur[2] = -COM(0,1) * cur[1]; cur[3] = -cur[0]; } } } CCurrent joi (HLV pout, bool helout, HLV pin, bool helin) { current cur; joi(pout, helout, pin, helin, cur); return CCurrent(cur[0],cur[1],cur[2],cur[3]); } void jio(HLV pin, bool helin, HLV pout, bool helout, current &cur) { joi(pout, !helout, pin, !helin, cur); } CCurrent jio (HLV pin, bool helin, HLV pout, bool helout) { current cur; jio(pin, helin, pout, helout, cur); return CCurrent(cur[0],cur[1],cur[2],cur[3]); } void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur) { // Zero our current cur[0] = 0.0; cur[1] = 0.0; cur[2] = 0.0; cur[3] = 0.0; if (heli!=helj) { throw std::invalid_argument{"Non-matching helicities"}; } else if ( heli == true ) { // If positive helicity swap momenta std::swap(pi,pj); } const double sqpjp = sqrt(std::abs(pj.plus() )); const double sqpjm = sqrt(std::abs(pj.minus())); const double sqpip = sqrt(std::abs(pi.plus() )); const double sqpim = sqrt(std::abs(pi.minus())); // Allow for "jii" format const COM piperp = (pi.x()==0 && pi.y() ==0) ? -1 : pi.x()+COM(0,1)*pi.y(); const COM pjperp = (pj.x()==0 && pj.y() ==0) ? -1 : pj.x()+COM(0,1)*pj.y(); const COM phasei = piperp / std::abs(piperp); const COM phasej = pjperp / std::abs(pjperp); cur[0] = sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp; cur[1] = sqpim * sqpjp * phasei + sqpip * sqpjm * conj(phasej); cur[2] = -COM(0, 1) * (sqpim * sqpjp * phasei - sqpip * sqpjm * conj(phasej)); cur[3] = -sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp; } CCurrent joo (HLV pi, bool heli, HLV pj, bool helj) { current cur; joo(pi, heli, pj, helj, cur); return CCurrent(cur[0],cur[1],cur[2],cur[3]); } namespace{ //@{ /** * @brief Pure Jet FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. * @param p1in Incoming Particle 1. * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * * Calculates j_\mu j^\mu. * Handles all possible incoming states. Helicity doesn't matter since we sum * over all of them. */ double j_j(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ HLV const q1=p1in-p1out; HLV const q2=-(p2in-p2out); current mj1m,mj1p,mj2m,mj2p; // Note need to flip helicities in anti-quark case. joi(p1out, false, p1in, false, mj1p); joi(p1out, true, p1in, true, mj1m); joi(p2out, false, p2in, false, mj2p); joi(p2out, true, p2in, true, mj2m); COM const Mmp=cdot(mj1m,mj2p); COM const Mmm=cdot(mj1m,mj2m); COM const Mpp=cdot(mj1p,mj2p); COM const Mpm=cdot(mj1p,mj2m); double const sst=abs2(Mmm)+abs2(Mmp)+abs2(Mpp)+abs2(Mpm); // Multiply by Cf^2 return HEJ::C_F*HEJ::C_F*(sst)/(q1.m2()*q2.m2()); } } //anonymous namespace double ME_qQ(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in); } double ME_qQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in); } double ME_qbarQbar(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in); } double ME_qg(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in)*K_g(p2out, p2in)/HEJ::C_F; } double ME_qbarg(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in)*K_g(p2out, p2in)/(HEJ::C_F); } double ME_gg(HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return j_j(p1out, p1in, p2out, p2in)*K_g(p1out, p1in)*K_g(p2out, p2in)/(HEJ::C_F*HEJ::C_F); } //@} namespace{ double juno_j(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ // This construction is taking rapidity order: pg > p1out >> p2out HLV q1=p1in-p1out; // Top End HLV q2=-(p2in-p2out); // Bottom End HLV qg=p1in-p1out-pg; // Extra bit post-gluon // Note <p1|eps|pa> current split into two by gauge choice. // See James C's Thesis (p72). <p1|eps|pa> -> <p1|pg><pg|pa> CCurrent mj1p=joi(p1out, false, p1in, false); CCurrent mj1m=joi(p1out, true, p1in, true); CCurrent jgap=joi(pg, false, p1in, false); CCurrent jgam=joi(pg, true, p1in, true); // Note for function joo(): <p1+|pg+> = <pg-|p1->. CCurrent j2gp=joo(p1out, false, pg, false); CCurrent j2gm=joo(p1out, true, pg, true); CCurrent mj2p=joi(p2out, false, p2in, false); CCurrent mj2m=joi(p2out, true, p2in, true); // Dot products of these which occur again and again COM Mmp=mj1m.dot(mj2p); COM Mmm=mj1m.dot(mj2m); COM Mpp=mj1p.dot(mj2p); COM Mpm=mj1p.dot(mj2m); CCurrent p1o(p1out),p2o(p2out),p2i(p2in),qsum(q1+qg),p1i(p1in); CCurrent Lmm=(qsum*(Mmm)+(-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mmm/2.))/q1.m2(); CCurrent Lmp=(qsum*(Mmp) + (-2.*mj2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2p +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mmp/2.))/q1.m2(); CCurrent Lpm=(qsum*(Mpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mpm/2.))/q1.m2(); CCurrent Lpp=(qsum*(Mpp) + (-2.*mj2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2p +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mpp/2.))/q1.m2(); CCurrent U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*Mmm)/(p1out+pg).m2(); CCurrent U1mp=(jgam.dot(mj2p)*j2gm+2.*p1o*Mmp)/(p1out+pg).m2(); CCurrent U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*Mpm)/(p1out+pg).m2(); CCurrent U1pp=(jgap.dot(mj2p)*j2gp+2.*p1o*Mpp)/(p1out+pg).m2(); CCurrent U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*Mmm)/(p1in-pg).m2(); CCurrent U2mp=((-1.)*j2gm.dot(mj2p)*jgam+2.*p1i*Mmp)/(p1in-pg).m2(); CCurrent U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*Mpm)/(p1in-pg).m2(); CCurrent U2pp=((-1.)*j2gp.dot(mj2p)*jgap+2.*p1i*Mpp)/(p1in-pg).m2(); constexpr double cf=HEJ::C_F; double amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm); double amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp); double apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm); double app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp); double ampsq=-(amm+amp+apm+app); //Divide by t-channels ampsq/=q2.m2()*qg.m2(); ampsq/=16.; // Factor of (Cf/Ca) for each quark to match j_j. ampsq*=(HEJ::C_F*HEJ::C_F)/(HEJ::C_A*HEJ::C_A); return ampsq; } } //Unordered bits for pure jet double ME_unob_qQ (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qbarQ (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qQbar (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qbarQbar (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qg (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in)*K_g(p2out,p2in)/HEJ::C_F; } double ME_unob_qbarg (HLV pg, HLV p1out, HLV p1in, HLV p2out, HLV p2in){ return juno_j(pg, p1out, p1in, p2out, p2in)*K_g(p2out,p2in)/HEJ::C_F; } namespace { void eps(HLV refmom, HLV kb, bool hel, current &ep){ //Positive helicity eps has negative helicity choices for spinors and vice versa joi(refmom,hel,kb,hel,ep); double norm; if(kb.z()<0.) norm = sqrt(2.*refmom.plus()*kb.minus()); else norm = sqrt(2.*refmom.minus()*kb.plus()); // Normalise std::for_each(begin(ep), end(ep), [&,norm](auto & val){val/=norm;}); } COM qggm1(HLV pa, HLV pb, HLV p1, HLV p2, HLV p3, bool helchain, bool heltop, bool helb,HLV refmom){ // Since everything is defined with currents, need to use compeleness relation // to expand p slash. i.e. pslash = |p><p|. Only one helicity 'survives' as // defined by the helicities of the spinors at the end of the chain. current cur33, cur23, curb3, cur2b, cur1a, ep; joo(p3, helchain, p3, helchain,cur33); joo(p2,helchain,p3,helchain,cur23); jio(pb,helchain,p3,helchain,curb3); joi(p2,helchain,pb,helchain,cur2b); joi(p1, heltop, pa, heltop,cur1a); const double t2 = (p3-pb)*(p3-pb); //Calculate Term 1 in Equation 3.23 in James Cockburn's Thesis. COM v1[4][4]; for(int u=0; u<4;++u){ for(int v=0; v<4;++v){ v1[u][v]=(cur23[u]*cur33[v]-cur2b[u]*curb3[v])/t2*(-1.); } } //Dot in current and eps //eps eps(refmom,pb,helb, ep); COM M1=0.; // Perform Contraction: g^{ik} j_{1a}_k * v1_i^j eps^l g_lj for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M1+= metric(i,i) *cur1a[i]*(v1[i][j])*ep[j]*metric(j,j); } } return M1; } COM qggm2(HLV pa, HLV pb, HLV p1, HLV p2, HLV p3, bool helchain, bool heltop, bool helb,HLV refmom){ // Since everything is defined with currents, need to use completeness relation // to expand p slash. i.e. pslash = |p><p|. Only one helicity 'survives' as // defined by the helicities of the spinors at the end of the chain. current cur22, cur23, curb3, cur2b, cur1a, ep; joo(p2, helchain, p2, helchain, cur22); joo(p2, helchain, p3, helchain, cur23); jio(pb, helchain, p3, helchain, curb3); joi(p2, helchain, pb, helchain, cur2b); joi(p1, heltop, pa, heltop, cur1a); const double t2t = (p2-pb)*(p2-pb); //Calculate Term 2 in Equation 3.23 in James Cockburn's Thesis. COM v2[4][4]={}; for(int u=0; u<4;++u){ for(int v=0; v<4; ++v){ v2[u][v]=(cur22[v]*cur23[u]-cur2b[v]*curb3[u])/t2t; } } //Dot in current and eps //eps eps(refmom,pb,helb, ep); COM M2=0.; // Perform Contraction: g^{ik} j_{1a}_k * v2_i^j eps^l g_lj for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M2+= metric(i,i)*cur1a[i]*(v2[i][j])*ep[j]*metric(j,j); } } return M2; } COM qggm3(HLV pa, HLV pb, HLV p1, HLV p2, HLV p3, bool helchain, bool heltop, bool helb,HLV refmom){ current qqcur,ep,cur1a; const double s23 = (p2+p3)*(p2+p3); joo(p2,helchain,p3,helchain,qqcur); joi(p1, heltop, pa, heltop,cur1a); //Redefine relevant momenta as currents - for ease of calling correct part of vector const current kb{pb.e(), pb.x(), pb.y(), pb.z()}; const current k2{p2.e(), p2.x(), p2.y(), p2.z()}; const current k3{p3.e(), p3.x(), p3.y(), p3.z()}; //Calculate Term 3 in Equation 3.23 in James Cockburn's Thesis. COM V3g[4][4]={}; const COM kbqq=kb[0]*qqcur[0] -kb[1]*qqcur[1] -kb[2]*qqcur[2] -kb[3]*qqcur[3]; for(int u=0;u<4;++u){ for(int v=0;v<4;++v){ V3g[u][v] += 2.*COM(0.,1.)*(((k2[v]+k3[v])*qqcur[u] - (kb[u])*qqcur[v])+ kbqq*metric(u,v))/s23; } } eps(refmom,pb,helb, ep); COM M3=0.; // Perform Contraction: g^{ik} j_{1a}_k * (v2_i^j) eps^l g_lj for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M3+= metric(i,i)*cur1a[i]*(V3g[i][j])*ep[j]*metric(j,j); } } return M3; } //Now the function to give helicity/colour sum/average double MqgtqQQ(HLV pa, HLV pb, HLV p1, HLV p2, HLV p3){ // 4 indepedent helicity choices (complex conjugation related). //Need to evalute each independent hel configuration and store that result somewhere const COM Mmmm1 = qggm1(pa,pb,p1,p2,p3,false,false,false, pa); const COM Mmmm2 = qggm2(pa,pb,p1,p2,p3,false,false,false, pa); const COM Mmmm3 = qggm3(pa,pb,p1,p2,p3,false,false,false, pa); const COM Mmmp1 = qggm1(pa,pb,p1,p2,p3,false,true, false, pa); const COM Mmmp2 = qggm2(pa,pb,p1,p2,p3,false,true, false, pa); const COM Mmmp3 = qggm3(pa,pb,p1,p2,p3,false,true, false, pa); const COM Mpmm1 = qggm1(pa,pb,p1,p2,p3,false,false,true, pa); const COM Mpmm2 = qggm2(pa,pb,p1,p2,p3,false,false,true, pa); const COM Mpmm3 = qggm3(pa,pb,p1,p2,p3,false,false,true, pa); const COM Mpmp1 = qggm1(pa,pb,p1,p2,p3,false,true, true, pa); const COM Mpmp2 = qggm2(pa,pb,p1,p2,p3,false,true, true, pa); const COM Mpmp3 = qggm3(pa,pb,p1,p2,p3,false,true, true, pa); //Colour factors: const COM cm1m1 = 8./3.; const COM cm2m2 = 8./3.; const COM cm3m3 = 6.; const COM cm1m2 = -1./3.; const COM cm1m3 = -3.*COM(0.,1.); const COM cm2m3 = 3.*COM(0.,1.); //Sqaure and sum for each helicity config: const double Mmmm = real(cm1m1*pow(abs(Mmmm1),2)+cm2m2*pow(abs(Mmmm2),2)+ cm3m3*pow(abs(Mmmm3),2)+2.*real(cm1m2*Mmmm1*conj(Mmmm2))+ 2.*real(cm1m3*Mmmm1*conj(Mmmm3))+2.*real(cm2m3*Mmmm2*conj(Mmmm3))); const double Mmmp = real(cm1m1*pow(abs(Mmmp1),2)+cm2m2*pow(abs(Mmmp2),2)+ cm3m3*pow(abs(Mmmp3),2)+2.*real(cm1m2*Mmmp1*conj(Mmmp2))+ 2.*real(cm1m3*Mmmp1*conj(Mmmp3))+2.*real(cm2m3*Mmmp2*conj(Mmmp3))); const double Mpmm = real(cm1m1*pow(abs(Mpmm1),2)+cm2m2*pow(abs(Mpmm2),2)+ cm3m3*pow(abs(Mpmm3),2)+2.*real(cm1m2*Mpmm1*conj(Mpmm2))+ 2.*real(cm1m3*Mpmm1*conj(Mpmm3))+2.*real(cm2m3*Mpmm2*conj(Mpmm3))); const double Mpmp = real(cm1m1*pow(abs(Mpmp1),2)+cm2m2*pow(abs(Mpmp2),2)+ cm3m3*pow(abs(Mpmp3),2)+2.*real(cm1m2*Mpmp1*conj(Mpmp2))+ 2.*real(cm1m3*Mpmp1*conj(Mpmp3))+2.*real(cm2m3*Mpmp2*conj(Mpmp3))); // Factor of 2 for the helicity for conjugate configurations return (2.*(Mmmm+Mmmp+Mpmm+Mpmp)/3.)/(pa-p1).m2()/(p2+p3-pb).m2(); } } // Extremal qqx double ME_Exqqx_qbarqQ(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in){ return MqgtqQQ(p2in, pgin, p2out, pqout, pqbarout); } double ME_Exqqx_qqbarQ(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in){ return MqgtqQQ(p2in, pgin, p2out, pqbarout, pqout); } double ME_Exqqx_qbarqg(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in){ return MqgtqQQ(p2in, pgin, p2out, pqout, pqbarout)*K_g(p2out,p2in)/HEJ::C_F; } double ME_Exqqx_qqbarg(HLV pgin, HLV pqout, HLV pqbarout, HLV p2out, HLV p2in){ return MqgtqQQ(p2in, pgin, p2out, pqbarout, pqout)*K_g(p2out,p2in)/HEJ::C_F; } namespace { void CurrentMatrix(current j1, current j2, COM array[4][4]){ for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ array[i][j]=j1[i]*j2[j]; } } } //qqbar produced in the middle COM m1(current jtop, current jbot, bool hel2, HLV ka, HLV kb, HLV k2, HLV k3, std::vector<HLV> partons, unsigned int nabove){ const double s23 = 2.*(k2*k3); const double sa2 = 2.*(ka*k2); const double sa3 = 2.*(ka*k3); const double s12 = 2.*(partons.front()*k2); const double s13 = 2.*(partons.front()*k3); const double sb2 = 2.*(kb*k2); const double sb3 = 2.*(kb*k3); const double s42 = 2.*(partons.back()*k2); const double s43 = 2.*(partons.back()*k3); HLV q1=ka-partons.front(); for(unsigned int i=1;i<nabove+1;++i) q1-=partons.at(i); const HLV q2=q1-partons.at(nabove+2)-partons.at(nabove+1); const double t1 = q1.m2(); const double t3 = q2.m2(); current cur23; joo(k2,hel2,k3,hel2,cur23); const current curka{ka.e(),ka.px(),ka.py(),ka.pz()}; const current curkb{kb.e(),kb.px(),kb.py(),kb.pz()}; const current curk1{partons.front().e(),partons.front().px(), partons.front().py(),partons.front().pz()}; const current curk2{k2.e(),k2.px(),k2.py(),k2.pz()}; const current curk3{k3.e(),k3.px(),k3.py(),k3.pz()}; const current curk4{partons.back().e(),partons.back().px(), partons.back().py(),partons.back().pz()}; const current qc1{q1.e(),q1.px(),q1.py(),q1.pz()}; const current qc2{q2.e(),q2.px(),q2.py(),q2.pz()}; //Create the two bits of this vertex COM veik[4][4],v3g[4][4]; for(int i=0;i<4;++i) { for(int j=0;j<4;++j){ veik[i][j] = (cdot(cur23,curka)*(t1/(sa2+sa3))+cdot(cur23,curk1)* (t1/(s12+s13))-cdot(cur23,curkb)*(t3/(sb2+sb3))- cdot(cur23,curk4)*(t3/(s42+s43)))*metric(i,j); } } for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ v3g[i][j] = qc1[j]*cur23[i]+curk2[j]*cur23[i]+curk3[j]*cur23[i]+ qc2[i]*cur23[j]-curk2[i]*cur23[j]-curk3[i]*cur23[j]- (cdot(qc1,cur23)+cdot(qc2,cur23))*metric(i,j); } } //Now dot in the currents - potential problem here with Lorentz //indicies, so check this COM M1=0; for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M1+= metric(i,i)*jtop[i]*(veik[i][j]+v3g[i][j])*jbot[j]*metric(j,j); } } M1/=s23; return M1; } COM m2 (current jtop, current jbot, bool hel2, HLV ka, HLV k2, HLV k3, std::vector<HLV> partons, unsigned int nabove){ //In order to get correct momentum dependence in the vertex, forst //have to work with CCurrent objects and then convert to 'current' current cur22,cur23,cur2q,curq3; COM qarray[4][4]={}; COM temp[4][4]={}; joo(k2,hel2,k2,hel2,cur22); joo(k2,hel2,k3,hel2,cur23); joi(k2,hel2,ka,hel2,cur2q); jio(ka,hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, qarray); for(unsigned int i =0; i<nabove+1; ++i){ joo(k2,hel2,partons.at(i),hel2,cur2q); joo(partons.at(i),hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, temp); for(int ii=0;ii<4;++ii){ for(int jj=0;jj<4;++jj){ qarray[ii][jj]=qarray[ii][jj]-temp[ii][jj]; } } } HLV qt=ka-k2; for(unsigned int i=0; i<nabove+1;++i){ qt-=partons.at(i); } const double t2=qt*qt; COM tempv[4][4]; for(int i=0; i<4;++i){ for(int j=0;j<4;++j){ tempv[i][j] = COM(0.,1.)*(qarray[i][j]-cur22[i]*cur23[j]); } } COM M2=0.; for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M2+= metric(i,i)*jtop[i]*(tempv[i][j])*jbot[j]*metric(j,j); } } M2/=t2; return M2; } COM m3 (current jtop, current jbot, bool hel2, HLV ka, HLV k2, HLV k3, std::vector<HLV> partons, unsigned int nabove){ COM M3=0.; current cur23,cur33,cur2q,curq3; COM qarray[4][4]={}; COM temp[4][4]={}; joo(k3,hel2,k3,hel2,cur33); joo(k2,hel2,k3,hel2,cur23); joi(k2,hel2,ka,hel2,cur2q); jio(ka,hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, qarray); for(unsigned int i =0; i<nabove+1; ++i){ joo(k2,hel2,partons.at(i),hel2,cur2q); joo(partons.at(i),hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, temp); for(int ii=0;ii<4;++ii){ for(int jj=0;jj<4;++jj){ qarray[ii][jj]=qarray[ii][jj]-temp[ii][jj]; } } } HLV qt=ka-k3; for(unsigned int i=0; i<nabove+1;++i){ qt-=partons.at(i); } const double t2t=qt*qt; COM tempv[4][4]; for(int i=0; i<4;++i){ for(int j=0;j<4;++j){ tempv[i][j] = COM(0.,-1.)*(qarray[j][i]-cur23[j]*cur33[i]); } } for(int i=0;i<4;++i){ for(int j=0;j<4;++j){ M3+= metric(i,i)*jtop[i]*(tempv[i][j])*jbot[j]*metric(j,j); } } M3/= t2t; return M3; } } double ME_Cenqqx_qq(HLV ka, HLV kb, std::vector<HLV> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove){ //Get all the possible outer currents current j1p,j1m,j4p,j4m; if(!(aqlinepa)){ joi(partons.front(),true,ka,true,j1p); joi(partons.front(),false,ka,false,j1m); } if(aqlinepa){ jio(ka,true,partons.front(),true,j1p); jio(ka,false,partons.front(),false,j1m); } if(!(aqlinepb)){ joi(partons.back(),true,kb,true,j4p); joi(partons.back(),false,kb,false,j4m); } if(aqlinepb){ jio(kb,true,partons.back(),true,j4p); jio(kb,false,partons.back(),false,j4m); } HLV k2,k3; if(!(qqxmarker)){ k2=partons.at(nabove+1); k3=partons.at(nabove+2); } else{ k2=partons.at(nabove+2); k3=partons.at(nabove+1); } //8 helicity choices we can make, but only 4 indepedent ones //(complex conjugation related). const COM Mmmm1 = m1(j1m,j4m,false,ka,kb,k2,k3,partons,nabove); const COM Mmmm2 = m2(j1m,j4m,false,ka, k2,k3,partons,nabove); const COM Mmmm3 = m3(j1m,j4m,false,ka, k2,k3,partons,nabove); const COM Mmmp1 = m1(j1m,j4m,true, ka,kb,k2,k3,partons,nabove); const COM Mmmp2 = m2(j1m,j4m,true, ka, k2,k3,partons,nabove); const COM Mmmp3 = m3(j1m,j4m,true, ka, k2,k3,partons,nabove); const COM Mpmm1 = m1(j1p,j4m,false,ka,kb,k2,k3,partons,nabove); const COM Mpmm2 = m2(j1p,j4m,false,ka, k2,k3,partons,nabove); const COM Mpmm3 = m3(j1p,j4m,false,ka, k2,k3,partons,nabove); const COM Mpmp1 = m1(j1p,j4m,true, ka,kb,k2,k3,partons,nabove); const COM Mpmp2 = m2(j1p,j4m,true, ka, k2,k3,partons,nabove); const COM Mpmp3 = m3(j1p,j4m,true, ka, k2,k3,partons,nabove); //Colour factors: const COM cm1m1=3.; const COM cm2m2=4./3.; const COM cm3m3=4./3.; const COM cm1m2 =3./2.*COM(0.,1.); const COM cm1m3 = -3./2.*COM(0.,1.); const COM cm2m3 = -1./6.; //Square and sum for each helicity config: const double Mmmm = real(cm1m1*pow(abs(Mmmm1),2)+cm2m2*pow(abs(Mmmm2),2)+ cm3m3*pow(abs(Mmmm3),2)+2.*real(cm1m2*Mmmm1*conj(Mmmm2))+ 2.*real(cm1m3*Mmmm1*conj(Mmmm3))+2.*real(cm2m3*Mmmm2*conj(Mmmm3))); const double Mmmp = real(cm1m1*pow(abs(Mmmp1),2)+cm2m2*pow(abs(Mmmp2),2)+ cm3m3*pow(abs(Mmmp3),2)+2.*real(cm1m2*Mmmp1*conj(Mmmp2))+ 2.*real(cm1m3*Mmmp1*conj(Mmmp3))+2.*real(cm2m3*Mmmp2*conj(Mmmp3))); const double Mpmm = real(cm1m1*pow(abs(Mpmm1),2)+cm2m2*pow(abs(Mpmm2),2)+ cm3m3*pow(abs(Mpmm3),2)+2.*real(cm1m2*Mpmm1*conj(Mpmm2))+ 2.*real(cm1m3*Mpmm1*conj(Mpmm3))+2.*real(cm2m3*Mpmm2*conj(Mpmm3))); const double Mpmp = real(cm1m1*pow(abs(Mpmp1),2)+cm2m2*pow(abs(Mpmp2),2)+ cm3m3*pow(abs(Mpmp3),2)+2.*real(cm1m2*Mpmp1*conj(Mpmp2))+ 2.*real(cm1m3*Mpmp1*conj(Mpmp3))+2.*real(cm2m3*Mpmp2*conj(Mpmp3))); //Result (averaged, without coupling or t-channel props). Factor of //2 for the 4 helicity configurations I didn't work out explicitly HLV prop1 = ka; for(int i=0; i<=nabove; ++i){ prop1 -= partons[i]; } const HLV prop2 = prop1 - k2 - k3; return (2.*(Mmmm+Mmmp+Mpmm+Mpmp)/9./4.) / ((ka-partons.front()).m2()*(kb-partons.back()).m2()*prop1.m2()*prop2.m2()); } diff --git a/src/resummation_jet.cc b/src/resummation_jet.cc index 9f94c1d..af6f53a 100644 --- a/src/resummation_jet.cc +++ b/src/resummation_jet.cc @@ -1,113 +1,113 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/resummation_jet.hh" #include <assert.h> #include <math.h> #include <stdio.h> #include <boost/numeric/ublas/lu.hpp> #include <boost/numeric/ublas/matrix.hpp> #include "fastjet/PseudoJet.hh" #include "HEJ/utility.hh" namespace HEJ{ std::vector<fastjet::PseudoJet> resummation_jet_momenta( std::vector<fastjet::PseudoJet> const & p_born, fastjet::PseudoJet const & qperp ) { // for "new" reshuffling p^B = p + qperp*|p^B|/P^B double Pperp_born = 0.; for(auto const & p: p_born) Pperp_born += p.perp(); std::vector<fastjet::PseudoJet> p_res; p_res.reserve(p_born.size()); for(auto & pB: p_born) { const double px = pB.px() - qperp.px()*pB.perp()/Pperp_born; const double py = pB.py() - qperp.py()*pB.perp()/Pperp_born; const double pperp = sqrt(px*px + py*py); // keep the rapidities fixed const double pz = pperp*sinh(pB.rapidity()); const double E = pperp*cosh(pB.rapidity()); p_res.emplace_back(px, py, pz, E); assert( HEJ::nearby_ep( p_res.back().rapidity(), pB.rapidity(), 1e-5 ) ); } return p_res; } namespace{ enum coordinates : size_t { x1, x2 }; namespace ublas = boost::numeric::ublas; template<class Matrix> double det(ublas::matrix_expression<Matrix> const& m) { ublas::permutation_matrix<size_t> pivots{m().size1()}; Matrix mLu{m()}; const auto is_singular = lu_factorize(mLu, pivots); if(is_singular) return 0.; double det = 1.0; for (std::size_t i = 0; i < pivots.size(); ++i){ if (pivots(i) != i) det = -det; det *= mLu(i,i); } return det; } using ublas::matrix; } double resummation_jet_weight( std::vector<fastjet::PseudoJet> const & p_born, fastjet::PseudoJet const & qperp ) { static constexpr int num_coordinates = 2; auto Jacobian = matrix<double>{ num_coordinates*p_born.size(), num_coordinates*p_born.size() }; double P_perp = 0.; for(auto const & J: p_born) P_perp += J.perp(); for(size_t l = 0; l < p_born.size(); ++l){ const double Jl_perp = p_born[l].perp(); for(size_t lp = 0; lp < p_born.size(); ++lp){ const int delta_l = (l == lp); const auto & Jlp = p_born[lp]; const double Jlp_perp = Jlp.perp(); for(size_t x = x1; x <= x2; ++x){ const double qxy = (x==x1)?qperp.px():qperp.py(); for(size_t xp = x1; xp <= x2; ++xp){ const double Jxy = (xp==x1)?Jlp.px():Jlp.py(); const int delta_x = x == xp; Jacobian(2*l + x, 2*lp + xp) = + delta_l*delta_x - qxy*Jxy/(P_perp*Jlp_perp) * (delta_l - Jl_perp/P_perp); } } } } return det(Jacobian); } } diff --git a/t/check_lhe.cc b/t/check_lhe.cc index ad9714d..4f5ab0c 100644 --- a/t/check_lhe.cc +++ b/t/check_lhe.cc @@ -1,68 +1,68 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <iostream> #include <unordered_map> #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<int, double> 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<HEJ::event_type::EventType>(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 7f82d07..751ddff 100644 --- a/t/check_lhe_sherpa.cc +++ b/t/check_lhe_sherpa.cc @@ -1,52 +1,52 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <iostream> #include <string> #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; } diff --git a/t/check_res.cc b/t/check_res.cc index d4a7306..81b2c8e 100644 --- a/t/check_res.cc +++ b/t/check_res.cc @@ -1,150 +1,150 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <cmath> #include <iostream> #include "LHEF/LHEF.h" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/EventReweighter.hh" #include "HEJ/Mixmax.hh" #include "HEJ/stream.hh" #include "hej_test.hh" namespace{ const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4}; const fastjet::JetDefinition Born_jet_def{jet_def}; constexpr double Born_jetptmin = 30; constexpr double max_ext_soft_pt_fraction = 0.1; constexpr double jetptmin = 35; constexpr bool log_corr = false; const HEJ::ParticleProperties Wprop{80.385, 2.085}; const HEJ::ParticleProperties Zprop{91.187, 2.495}; const HEJ::ParticleProperties Hprop{125, 0.004165}; constexpr double vev = 246.2196508; using EventTreatment = HEJ::EventTreatment; using namespace HEJ::event_type; HEJ::EventTreatMap treat{ {no_2_jets, EventTreatment::discard}, {bad_final_state, EventTreatment::discard}, {non_resummable, EventTreatment::discard}, {unof, EventTreatment::discard}, {unob, EventTreatment::discard}, {qqxexb, EventTreatment::discard}, {qqxexf, EventTreatment::discard}, {qqxmid, EventTreatment::discard}, {FKL, EventTreatment::reweight} }; bool correct_colour(HEJ::Event const & ev){ if(!HEJ::event_type::is_resummable(ev.type())) return true; if(!ev.is_leading_colour()) return false; return true; } } int main(int argn, char** argv) { if(argn == 5 && std::string(argv[4]) == "unof"){ --argn; treat[unof] = EventTreatment::reweight; treat[unob] = EventTreatment::discard; treat[FKL] = EventTreatment::discard; } if(argn == 5 && std::string(argv[4]) == "unob"){ --argn; treat[unof] = EventTreatment::discard; treat[unob] = EventTreatment::reweight; treat[FKL] = EventTreatment::discard; } else if(argn == 5 && std::string(argv[4]) == "splitf"){ --argn; treat[qqxexb] = EventTreatment::discard; treat[qqxexf] = EventTreatment::reweight; treat[FKL] = EventTreatment::discard; } else if(argn == 5 && std::string(argv[4]) == "splitb"){ --argn; treat[qqxexb] = EventTreatment::reweight; treat[qqxexf] = EventTreatment::discard; treat[FKL] = EventTreatment::discard; } else if(argn == 5 && std::string(argv[4]) == "qqxmid"){ --argn; treat[qqxmid] = EventTreatment::reweight; treat[FKL] = EventTreatment::discard; } if(argn != 4){ std::cerr << "Usage: check_res eventfile xsection tolerance [uno]"; return EXIT_FAILURE; } const double xsec_ref = std::stod(argv[2]); const double tolerance = std::stod(argv[3]); HEJ::istream in{argv[1]}; LHEF::Reader reader{in}; HEJ::PhaseSpacePointConfig psp_conf; psp_conf.jet_param = HEJ::JetParameters{jet_def, jetptmin}; psp_conf.max_ext_soft_pt_fraction = max_ext_soft_pt_fraction; HEJ::MatrixElementConfig ME_conf; ME_conf.log_correction = log_corr; ME_conf.Higgs_coupling = HEJ::HiggsCouplingSettings{}; ME_conf.ew_parameters.set_vevWZH(vev, Wprop, Zprop, Hprop); HEJ::EventReweighterConfig conf; conf.psp_config = std::move(psp_conf); conf.ME_config = std::move(ME_conf); conf.treat = treat; reader.readEvent(); const bool has_Higgs = std::find( begin(reader.hepeup.IDUP), end(reader.hepeup.IDUP), 25 ) != end(reader.hepeup.IDUP); const double mu = has_Higgs?125.:91.188; HEJ::ScaleGenerator scale_gen{ {{std::to_string(mu), HEJ::FixedScale{mu}}}, {}, 1. }; std::shared_ptr<HEJ::RNG> ran{std::make_shared<HEJ::Mixmax>()}; HEJ::EventReweighter hej{reader.heprup, std::move(scale_gen), conf, ran}; HEJ::CrossSectionAccumulator xs; do{ auto ev_data = HEJ::Event::EventData{reader.hepeup}; shuffle_particles(ev_data); ev_data.reconstruct_intermediate(); HEJ::Event ev{ ev_data.cluster( Born_jet_def, Born_jetptmin ) }; auto resummed_events = hej.reweight(ev, 100); for(auto const & res_ev: resummed_events) { ASSERT(correct_colour(res_ev)); ASSERT(std::isfinite(res_ev.central().weight)); // we fill the xs uncorrelated since we only want to test the uncertainty // of the resummation xs.fill(res_ev); } } while(reader.readEvent()); const double xsec = xs.total().value; const double xsec_err = std::sqrt(xs.total().error); const double significance = std::abs(xsec - xsec_ref) / std::sqrt( xsec_err*xsec_err + tolerance*tolerance ); std::cout << xsec_ref << " +/- " << tolerance << " ~ " << xsec << " +- " << xsec_err << " => " << significance << " sigma\n"; if(significance > 3.){ std::cerr << "Cross section is off by over 3 sigma!\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/t/hej_test.hh b/t/hej_test.hh index ecce1da..db07910 100644 --- a/t/hej_test.hh +++ b/t/hej_test.hh @@ -1,42 +1,42 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" //! throw error if condition not fulfilled #define ASSERT(x) if(!(x)) { \ throw std::logic_error("Assertion '" #x "' failed."); \ } //! throw error if prop is different between ev1 and ev2 #define ASSERT_PROPERTY(ev1,ev2,prop) ASSERT(ev1.prop == ev2.prop) /** @brief get specific Phase Space Points for njets with boson at pos_boson * * if pos_boson = -1 (or not implemented) -> no boson * * njet==7 is special: has less jets, i.e. multiple parton in one jet, * all partons are massive (4 GeV) -> can be boson/decay * pos_boson < 0 to select process (see list for details) */ HEJ::Event::EventData get_process(int njet, int pos_boson); //! select process from string input (see also get_process) //! //! overwrite_boson to force a specific boson position, indepentent from input //! (useful for njet == 7) HEJ::Event::EventData parse_configuration( std::array<std::string,2> const & in, std::vector<std::string> const & out, int overwrite_boson = 0 ); //! shuffle particles around void shuffle_particles(HEJ::Event::EventData & ev); //! Decay W boson to lepton & neutrino std::vector<HEJ::Particle> decay_W( HEJ::Particle const & parent ); diff --git a/t/test_ME_generic.cc b/t/test_ME_generic.cc index 59db263..3c5b245 100644 --- a/t/test_ME_generic.cc +++ b/t/test_ME_generic.cc @@ -1,180 +1,180 @@ /** * \brief Generic tester for the ME for a given set of PSP * * \note reference weights and PSP (as LHE file) have to be given as * _individual_ files * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <algorithm> #include <cmath> #include <fstream> #include "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/stream.hh" #include "HEJ/YAMLreader.hh" #include "hej_test.hh" constexpr double alpha_s = 0.118; constexpr double ep = 1e-9; constexpr double ep_mirror = 1e-3; enum MEComponent {tree, virt}; MEComponent guess_component(std::string const & data_file) { if(data_file.find("virt") != data_file.npos) return MEComponent::virt; return MEComponent::tree; } HEJ::Event::EventData mirror_event(HEJ::Event::EventData ev){ for(auto & part: ev.incoming){ auto & p{ part.p }; p.reset(p.px(),p.py(),-p.pz(),p.E()); } for(auto & part: ev.outgoing){ auto & p{ part.p }; p.reset(p.px(),p.py(),-p.pz(),p.E()); } for(auto & decay: ev.decays){ for(auto & part: decay.second){ auto & p{ part.p }; p.reset(p.px(),p.py(),-p.pz(),p.E()); } } return ev; } int main(int argn, char** argv){ if(argn != 4 && argn != 5){ std::cerr << "\n# Usage:\n."<< argv[0] <<" config.yml ME_weights input_file.lhe\n\n"; return EXIT_FAILURE; } bool OUTPUT_MODE = false; if(argn == 5 && std::string("OUTPUT")==std::string(argv[4])) OUTPUT_MODE = true; const HEJ::Config config = HEJ::load_config(argv[1]); std::fstream wgt_file; if ( OUTPUT_MODE ) { std::cout << "_______________________USING OUTPUT MODE!_______________________" << std::endl; wgt_file.open(argv[2], std::fstream::out); wgt_file.precision(9); wgt_file << std::scientific; } else { wgt_file.open(argv[2], std::fstream::in); } auto reader{ HEJ::make_reader(argv[3])}; const auto component = guess_component(argv[2]); HEJ::MatrixElement ME{ [](double){ return alpha_s; }, HEJ::to_MatrixElementConfig(config) }; double max_ratio = 0.; size_t idx_max_ratio = 0; HEJ::Event ev_max_ratio(HEJ::Event::EventData{}.cluster( config.resummation_jets.def,0 ) ); double av_ratio = 0; size_t i = 0; while(reader->read_event()){ ++i; HEJ::Event::EventData data{reader->hepeup()}; shuffle_particles(data); HEJ::Event::EventData data_mirror{mirror_event(data)}; shuffle_particles(data_mirror); HEJ::Event event{ data.cluster( config.resummation_jets.def, config.resummation_jets.min_pt ) }; HEJ::Event event_mirror{ data_mirror.cluster( config.resummation_jets.def, config.resummation_jets.min_pt ) }; const double our_ME = (component == MEComponent::tree)? ME.tree(event).central: ME.virtual_corrections(event).central ; if(!std::isfinite(our_ME)){ std::cerr << "Found non-finite ME ("<< our_ME <<")\n" << event << std::endl; return EXIT_FAILURE; } const double ME_mirror = (component == MEComponent::tree)? ME.tree(event_mirror).central: ME.virtual_corrections(event_mirror).central ; if(!std::isfinite(ME_mirror)){ std::cerr << "Found non-finite ME ("<< ME_mirror <<")\n" << event_mirror << std::endl; return EXIT_FAILURE; } if(std::abs(our_ME/ME_mirror-1.)>ep_mirror){ size_t precision(std::cout.precision()); std::cerr.precision(16); std::cerr<< "z-Mirrored ME gives different result " << i << "\n" <<our_ME << " vs " << ME_mirror << " => difference: " << std::abs(our_ME/ME_mirror-1.) << "\n" << event << "\nmirrored:\n" << event_mirror << std::endl; std::cerr.precision(precision); return EXIT_FAILURE; } if ( OUTPUT_MODE ) { wgt_file << our_ME << std::endl; } else { std::string line; if(!std::getline(wgt_file,line)) break; const double ref_ME = std::stod(line); double diff; if(ref_ME == 0.){ diff = (our_ME != 0.); } else { diff = std::abs(our_ME/ref_ME-1.); } av_ratio+=diff; if( diff > max_ratio ) { max_ratio = diff; idx_max_ratio = i; ev_max_ratio = event; } if( diff > ep ){ size_t precision(std::cout.precision()); std::cerr.precision(16); std::cerr<< "Large difference in PSP " << i << "\nis: "<<our_ME << " should: " << ref_ME << " => difference: " << diff << "\n" << event << std::endl; std::cerr.precision(precision); return EXIT_FAILURE; } } } wgt_file.close(); if ( i<100 ) throw std::invalid_argument{"Not enough PSP tested"}; if ( !OUTPUT_MODE ) { size_t precision(std::cout.precision()); std::cout.precision(16); std::cout << "Avg ratio after " << i << " PSP: " << av_ratio/i << std::endl; std::cout << "maximal ratio at " << idx_max_ratio << ": " << max_ratio << std::endl; std::cout.precision(precision); } return EXIT_SUCCESS; } diff --git a/t/test_classify.cc b/t/test_classify.cc index 3d342d5..80c4a24 100644 --- a/t/test_classify.cc +++ b/t/test_classify.cc @@ -1,491 +1,491 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <iostream> #include <random> #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #include "hej_test.hh" namespace { const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double min_jet_pt{30.}; const std::vector<std::string> all_quarks{"-4","-1","1","2","3","4"}; const std::vector<std::string> all_partons{"g","-2","-1","1","2","3","4"}; const std::vector<std::string> all_bosons{"h", "Wp", "Wm"}; const std::vector<std::string> all_gZ{"photon", "Z"}; const std::vector<std::string> all_w{"W+", "W-"}; static std::mt19937_64 ran{0}; bool couple_quark(std::string const & boson, std::string & quark){ if(abs(HEJ::to_ParticleID(boson)) == HEJ::ParticleID::Wp){ auto qflav{ HEJ::to_ParticleID(quark) }; if(!HEJ::is_anyquark(qflav)) return false; const int W_charge = HEJ::to_ParticleID(boson)>0?1:-1; if(W_charge*qflav < 0 && !(abs(qflav)%2)) return false; // not anti-down if(W_charge*qflav > 0 && (abs(qflav)%2)) return false; // not up quark=std::to_string(qflav-W_charge); } return true; } bool match_expectation( HEJ::event_type::EventType expected, std::array<std::string,2> const & in, std::vector<std::string> const & out, int const overwrite_boson = 0 ){ HEJ::Event ev{ parse_configuration( in,out,overwrite_boson ).cluster(jet_def, min_jet_pt)}; if(ev.type() != expected){ std::cerr << "Expected type " << HEJ::event_type::name(expected) << " but found " << HEJ::event_type::name(ev.type()) << "\n" << ev; auto jet_idx{ ev.particle_jet_indices() }; std::cout << "Particle Jet indices: "; for(int const i: jet_idx) std::cout << i << " "; std::cout << std::endl; return false; } return true; } //! test FKL configurations //! if implemented==false : check processes that are not in HEJ yet bool check_fkl( bool const implemented=true ){ using namespace HEJ; auto const type{ implemented?event_type::FKL:event_type::non_resummable }; std::vector<std::string> bosons; if(implemented) bosons = all_bosons; else { bosons = all_gZ; } for(std::string const & first: all_partons) // all quark flavours for(std::string const & last: all_partons){ for(int njet=2; njet<=6; ++njet){ // all multiplicities if(njet==5) continue; std::array<std::string,2> base_in{first,last}; std::vector<std::string> base_out(njet, "g"); base_out.front() = first; base_out.back() = last; if(implemented && !match_expectation(type, base_in, base_out)) return false; for(auto const & boson: bosons) // any boson for(int pos=0; pos<=njet; ++pos){ // at any position auto in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution<int>{0,1}(ran); if(!couple_quark(boson, couple_idx?out.back():out.front())) continue; out.insert(out.begin()+pos, boson); if(!match_expectation(type, in, out)) return false; } } } return true; } //! test unordered configurations //! if implemented==false : check processes that are not in HEJ yet bool check_uno( bool const implemented=true ){ using namespace HEJ; auto const b{ implemented?event_type::unob:event_type::non_resummable }; auto const f{ implemented?event_type::unof:event_type::non_resummable }; std::vector<std::string> bosons; if(implemented) bosons = all_bosons; else { bosons = all_gZ; } for(std::string const & uno: all_quarks) // all quark flavours for(std::string const & fkl: all_partons){ for(int njet=3; njet<=6; ++njet){ // all multiplicities >2 if(njet==5) continue; for(int i=0; i<2; ++i){ // forward & backwards std::array<std::string,2> base_in; std::vector<std::string> base_out(njet, "g"); const int uno_pos = i?1:(njet-2); const int fkl_pos = i?(njet-1):0; base_in[i?0:1] = uno; base_in[i?1:0] = fkl; base_out[uno_pos] = uno; base_out[fkl_pos] = fkl; auto expectation{ i?b:f }; if( implemented && !match_expectation(expectation, base_in, base_out) ) return false; for(auto const & boson: bosons){ // any boson // at any position (higgs only inside FKL chain) int start = 0; int end = njet; if(to_ParticleID(boson) == pid::higgs){ start = i?(uno_pos+1):fkl_pos; end = i?(fkl_pos+1):uno_pos; } for(int pos=start; pos<=end; ++pos){ auto in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution<int>{0,1}(ran); if(!couple_quark(boson, couple_idx?out[fkl_pos]:out[uno_pos])) continue; out.insert(out.begin()+pos, boson); if(!match_expectation(expectation, in, out)) return false; } } } } } return true; } //! test extremal qqx configurations //! if implemented==false : check processes that are not in HEJ yet bool check_extremal_qqx( bool const implemented=true ){ using namespace HEJ; auto const b{ implemented?event_type::qqxexb:event_type::non_resummable }; auto const f{ implemented?event_type::qqxexf:event_type::non_resummable }; std::vector<std::string> bosons; if(implemented) bosons = all_w; else { bosons = all_gZ; bosons.push_back("h"); } for(std::string const & qqx: all_quarks) // all quark flavours for(std::string const & fkl: all_partons){ std::string const qqx2{ std::to_string(HEJ::to_ParticleID(qqx)*-1) }; for(int njet=3; njet<=6; ++njet){ // all multiplicities >2 if(njet==5) continue; for(int i=0; i<2; ++i){ // forward & backwards std::array<std::string,2> base_in; std::vector<std::string> base_out(njet, "g"); const int qqx_pos = i?0:(njet-2); const int fkl_pos = i?(njet-1):0; base_in[i?0:1] = "g"; base_in[i?1:0] = fkl; base_out[fkl_pos] = fkl; base_out[qqx_pos] = qqx; base_out[qqx_pos+1] = qqx2; auto expectation{ i?b:f }; if( implemented && !match_expectation(expectation, base_in, base_out) ) return false; for(auto const & boson: bosons){ // all bosons // at any position (higgs only inside FKL chain) int start = 0; int end = njet; if(to_ParticleID(boson) == pid::higgs){ start = i?(qqx_pos+2):fkl_pos; end = i?(fkl_pos+1):qqx_pos; } for(int pos=start; pos<=end; ++pos){ auto in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution<int>{0,1}(ran); if(couple_idx || !couple_quark(boson, out[fkl_pos]) ){ // (randomly) try couple to FKL, else fall-back to qqx if(!couple_quark(boson, out[qqx_pos])) couple_quark(boson, out[qqx_pos+1]); } out.insert(out.begin()+pos, boson); if(!match_expectation(expectation, in, out)) return false; } } } } // test allowed jet configurations if( implemented){ if( !( match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -3) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -4) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -5) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -5) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -6) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -7) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -7) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -8) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -8) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -9) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -10) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -11) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -11) && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -12) && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -12) )) return false; if (fkl == "2") { if( !( match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -3) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -4) && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -5) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -5) && match_expectation(f,{"2","g"},{"1","g","Wp","g","g",qqx,qqx2}, -6) && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -7) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","g","g","Wp","1"}, -7) && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -8) && match_expectation(b,{"g","2"},{qqx,qqx2,"Wp","g","g","g","1"}, -8) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -9) && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -10) && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -11) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","g","g","Wp","1"}, -11) && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -12) && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -12) )) return false; } } } return true; } //! test central qqx configurations //! if implemented==false : check processes that are not in HEJ yet bool check_central_qqx(bool const implemented=true){ using namespace HEJ; auto const t{ implemented?event_type::qqxmid:event_type::non_resummable }; std::vector<std::string> bosons; if(implemented) bosons = all_w; else { bosons = all_gZ; bosons.push_back("h"); } for(std::string const & qqx: all_quarks) // all quark flavours for(std::string const & fkl1: all_partons) for(std::string const & fkl2: all_partons){ std::string const qqx2{ std::to_string(HEJ::to_ParticleID(qqx)*-1) }; for(int njet=4; njet<=6; ++njet){ // all multiplicities >3 if(njet==5) continue; for(int qqx_pos=1; qqx_pos<njet-2; ++qqx_pos){ // any qqx position std::array<std::string,2> base_in; std::vector<std::string> base_out(njet, "g"); base_in[0] = fkl1; base_in[1] = fkl2; base_out.front() = fkl1; base_out.back() = fkl2; base_out[qqx_pos] = qqx; base_out[qqx_pos+1] = qqx2; if( implemented && !match_expectation(t, base_in, base_out) ) return false; for(auto const & boson: bosons) // any boson for(int pos=0; pos<=njet; ++pos){ // at any position if( to_ParticleID(boson) == pid::higgs && (pos==qqx_pos || pos==qqx_pos+1) ) continue; auto in{base_in}; auto out{base_out}; // change quark flavours for W const int couple_idx{ std::uniform_int_distribution<int>{0,2}(ran) }; // (randomly) try couple to FKL, else fall-back to qqx if( couple_idx == 0 && couple_quark(boson, out.front()) ){} else if( couple_idx == 1 && couple_quark(boson, out.back()) ){} else { if(!couple_quark(boson, out[qqx_pos])) couple_quark(boson, out[qqx_pos+1]); } out.insert(out.begin()+pos, boson); if(!match_expectation(t, in, out)) return false; } } } } return true; } // this checks a (non excessive) list of non-resummable states bool check_non_resummable(){ auto type{ HEJ::event_type::non_resummable}; return // 2j - crossing lines match_expectation(type, {"g","2"}, {"2","g"}) && match_expectation(type, {"-1","g"}, {"g","-1"}) && match_expectation(type, {"1","-1"}, {"-1","1"}) && match_expectation(type, {"g","2"}, {"2","g","h"}) && match_expectation(type, {"1","2"}, {"2","h","1"}) && match_expectation(type, {"1","-1"}, {"h","-1","1"}) && match_expectation(type, {"g","2"}, {"Wp","1","g"}) && match_expectation(type, {"1","-1"}, {"-2","Wp","1"}) && match_expectation(type, {"4","g"}, {"g","3","Wp"}) && match_expectation(type, {"1","-2"}, {"-1","Wm","1"}) && match_expectation(type, {"g","3"}, {"4","g","Wm"}) && match_expectation(type, {"1","3"}, {"Wm","4","1"}) // 2j - qqx && match_expectation(type, {"g","g"}, {"1","-1"}) && match_expectation(type, {"g","g"}, {"-2","2","h"}) && match_expectation(type, {"g","g"}, {"-4","Wp","3"}) && match_expectation(type, {"g","g"}, {"Wm","-1","2"}) // 3j - crossing lines && match_expectation(type, {"g","4"}, {"4","g","g"}) && match_expectation(type, {"-1","g"}, {"g","g","-1"}) && match_expectation(type, {"1","3"}, {"3","g","1"}) && match_expectation(type, {"-2","2"}, {"2","g","-2","h"}) && match_expectation(type, {"-3","g"}, {"g","g","Wp","-4"}) && match_expectation(type, {"1","-2"}, {"Wm","-1","g","1"}) && match_expectation(type, {"-1","g"}, {"1","-1","-1"}) // higgs inside uno && match_expectation(type, {"-1","g"}, {"g","h","-1","g"}) && match_expectation(type, {"-1","1"}, {"g","h","-1","1"}) && match_expectation(type, {"g","2"}, {"g","2","h","g"}) && match_expectation(type, {"-1","1"}, {"-1","1","h","g"}) // higgs outside uno && match_expectation(type, {"-1","g"}, {"h","g","-1","g"}) && match_expectation(type, {"-1","1"}, {"-1","1","g","h"}) // higgs inside qqx && match_expectation(type, {"g","g"}, {"-1","h","1","g","g"}) && match_expectation(type, {"g","g"}, {"g","-1","h","1","g"}) && match_expectation(type, {"g","g"}, {"g","g","2","h","-2"}) // higgs outside qqx && match_expectation(type, {"g","g"}, {"h","-1","1","g","g"}) && match_expectation(type, {"g","g"}, {"g","g","2","-2","h"}) // 4j - two uno && match_expectation(type, {"-2","2"}, {"g","-2","2","g"}) && match_expectation(type, {"1","3"}, {"g","1","h","3","g"}) && match_expectation(type, {"1","2"}, {"g","1","3","Wp","g"}) && match_expectation(type, {"1","-2"}, {"g","Wm","1","-1","g"}) // 4j - two gluon outside && match_expectation(type, {"g","4"}, {"g","4","g","g"}) && match_expectation(type, {"1","3"}, {"1","3","h","g","g"}) && match_expectation(type, {"1","2"}, {"1","3","g","Wp","g"}) && match_expectation(type, {"1","-2"}, {"1","Wm","-1","g","g"}) && match_expectation(type, {"-1","g"}, {"g","g","-1","g"}) && match_expectation(type, {"1","3"}, {"g","g","1","3","h"}) && match_expectation(type, {"1","2"}, {"g","g","1","Wp","3"}) && match_expectation(type, {"1","-2"}, {"Wm","g","g","1","-1"}) // 4j - ggx+uno && match_expectation(type, {"g","4"}, {"1","-1","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","-3","3"}) && match_expectation(type, {"g","4"}, {"1","-1","h","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","-3","3","h"}) && match_expectation(type, {"g","4"}, {"Wp","1","-1","3","g"}) && match_expectation(type, {"2","g"}, {"g","2","-4","Wp","3"}) && match_expectation(type, {"g","4"}, {"2","Wm","-1","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","Wp","-3","4"}) // 3j - crossing+uno && match_expectation(type, {"1","4"}, {"g","4","1"}) && match_expectation(type, {"1","4"}, {"4","1","g"}) && match_expectation(type, {"1","4"}, {"g","h","4","1"}) && match_expectation(type, {"-1","-3"},{"Wm","g","-4","-1"}) && match_expectation(type, {"1","4"}, {"3","1","Wp","g"}) && match_expectation(type, {"1","4"}, {"3","1","g","h"}) // 3j - crossing+qqx && match_expectation(type, {"1","g"}, {"-1","1","g","1"}) && match_expectation(type, {"1","g"}, {"-1","1","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","1","-1"}) && match_expectation(type, {"g","1"}, {"g","1","1","-1"}) && match_expectation(type, {"1","g"}, {"2","-2","g","1"}) && match_expectation(type, {"1","g"}, {"2","-2","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","-2","2"}) && match_expectation(type, {"g","1"}, {"g","1","-2","2"}) && match_expectation(type, {"1","g"}, {"-1","1","h","g","1"}) && match_expectation(type, {"1","g"}, {"-1","h","1","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","1","h","-1"}) && match_expectation(type, {"g","1"}, {"h","g","1","1","-1"}) && match_expectation(type, {"1","g"}, {"2","-2","1","g","h"}) && match_expectation(type, {"g","1"}, {"g","h","1","-2","2"}) && match_expectation(type, {"1","g"}, {"Wp","3","-4","g","1"}) && match_expectation(type, {"3","g"}, {"-2","Wm","1","3","g"}) && match_expectation(type, {"g","1"}, {"1","g","Wm","-3","4"}) && match_expectation(type, {"g","-3"}, {"g","-3","-1","Wp","2"}) // 4j- gluon in qqx && match_expectation(type, {"g","1"}, {"1","g","-1","1"}) && match_expectation(type, {"1","g"}, {"1","-1","g","1"}) && match_expectation(type, {"g","1"}, {"1","g","Wm","-2","1"}) && match_expectation(type, {"2","g"}, {"2","-2","g","Wp","1"}) && match_expectation(type, {"g","g"}, {"Wp","3","g","-4","g"}) && match_expectation(type, {"1","g"}, {"1","h","-1","g","1"}) // 6j - two qqx && match_expectation(type, {"g","g"}, {"1","-1","g","g","1","-1"}) && match_expectation(type, {"g","g"}, {"1","-1","g","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","-1","g","1","-1"}) && match_expectation(type, {"g","g"}, {"g","1","-1","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","1","-1","-1","g"}) && match_expectation(type, {"g","g"}, {"h","1","-1","g","g","1","-1"}) && match_expectation(type, {"g","g"}, {"1","Wp","-2","g","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","Wp","-1","g","1","-2"}) && match_expectation(type, {"g","g"}, {"g","1","-1","Wm","2","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","2","-1","Wm","-1","g"}) // random stuff (can be non-physical) && match_expectation(type, {"g","g"}, {"1","-2","2","-1"}) // != 2 qqx && match_expectation(type, {"g","g"}, {"1","-2","2","g"}) // could be qqx && match_expectation(type, {"e+","e-"},{"1","-1"}) // bad initial state && match_expectation(type, {"1","e-"}, {"g","1","Wm"}) // bad initial state && match_expectation(type, {"h","g"}, {"g","g"}) // bad initial state && match_expectation(type, {"-1","g"}, {"-1","1","1"}) // bad qqx && match_expectation(type, {"-1","g"}, {"1","1","-1"}) // crossing in bad qqx && match_expectation(type, {"-1","g"}, {"-2","1","1","Wp"}) // bad qqx && match_expectation(type, {"1","2"}, {"1","-1","g","g","g","2"}) // bad qqx && match_expectation(type, {"1","2"}, {"1","-1","-2","g","g","2"}) // gluon in bad qqx && match_expectation(type, {"g","g"}, {"-1","2","g","g"}) // wrong back qqx && match_expectation(type, {"g","g"}, {"g","g","2","1"}) // wrong forward qqx && match_expectation(type, {"g","g"}, {"g","-2","1","g"}) // wrong central qqx && match_expectation(type, {"1","g"}, {"1","-2","g","g","Wp"}) // extra quark && match_expectation(type, {"g","1"}, {"g","g","-2","1","Wp"}) // extra quark && match_expectation(type, {"g","1"}, {"g","g","Wp","-2","1"}) // extra quark && match_expectation(type, {"g","1"}, {"g","-2","1","g","Wp"}) // extra quark && match_expectation(type, {"g","g"}, {"g","g","g","-2","1","-1","Wp"}) // extra quark && match_expectation(type, {"1","g"}, {"g","Wp","1","-2","g"}) // extra quark && match_expectation(type, {"g","g"}, {"1","-1","-2","g","g","g","Wp"}) // extra quark ; } // Two boson states, that are currently not implemented bool check_bad_FS(){ auto type{ HEJ::event_type::bad_final_state}; return match_expectation(type, {"g","g"}, {"g","h","h","g"}) && match_expectation(type, {"g","g"}, {"h","g","h","g"}) && match_expectation(type, {"g","-1"}, {"g","h","Wp","-2"}) && match_expectation(type, {"-3","-1"},{"-4","g","Wp","Wp","-2"}) && match_expectation(type, {"-4","-1"},{"-3","Wp","g","Wm","-2"}) && match_expectation(type, {"-4","-1"},{"g","-3","Wp","Wm","-2"}) && match_expectation(type, {"-4","-1"},{"-4","g","11","-11","-2"}) && match_expectation(type, {"-4","-1"},{"-4","g","-13","g","-2"}) && match_expectation(type, {"3","-2"}, {"Wp","3","Wm","g","g","g","-2"}, -13) ; } // not 2 jets bool check_not_2_jets(){ auto type{ HEJ::event_type::no_2_jets}; return match_expectation(type, {"g","g"}, {}) && match_expectation(type, {"1","-1"}, {}) && match_expectation(type, {"g","-1"}, {"-1"}) && match_expectation(type, {"g","g"}, {"g"}) ; } // not implemented processes bool check_not_implemented(){ return check_fkl(false) && check_uno(false) && check_extremal_qqx(false) && check_central_qqx(false); } } int main() { // tests for "no false negatives" // i.e. all HEJ-configurations get classified correctly if(!check_fkl()) return EXIT_FAILURE; if(!check_uno()) return EXIT_FAILURE; if(!check_extremal_qqx()) return EXIT_FAILURE; if(!check_central_qqx()) return EXIT_FAILURE; // test for "no false positive" // i.e. non-resummable gives non-resummable if(!check_non_resummable()) return EXIT_FAILURE; if(!check_bad_FS()) return EXIT_FAILURE; if(!check_not_2_jets()) return EXIT_FAILURE; if(!check_not_implemented()) return EXIT_FAILURE; return EXIT_SUCCESS; } diff --git a/t/test_colours.cc b/t/test_colours.cc index 5435f44..28b9546 100644 --- a/t/test_colours.cc +++ b/t/test_colours.cc @@ -1,352 +1,352 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <stdexcept> #include <utility> #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/RNG.hh" #include "hej_test.hh" /// biased RNG to connect always to colour class dum_rnd: public HEJ::DefaultRNG { public: dum_rnd() = default; double flat() override { return 0.; } }; HEJ::Event::EventData decay_boson( HEJ::Event::EventData ev ){ for( size_t i=0; i<ev.outgoing.size(); ++i ){ if( std::abs(ev.outgoing[i].type) == HEJ::ParticleID::Wp){ ev.decays[i] = decay_W(ev.outgoing[i]); } } return ev; } void dump_event(HEJ::Event const & ev){ for(auto const & in: ev.incoming()){ std::cerr << "in type=" << in.type << ", colour={" << (*in.colour).first << ", " << (*in.colour).second << "}\n"; } for(auto const & out: ev.outgoing()){ std::cerr << "out type=" << out.type << ", colour={"; if(out.colour) std::cerr << (*out.colour).first << ", " << (*out.colour).second; else std::cerr << "non, non"; std::cerr << "}\n"; } } /// true if colour is allowed for particle bool correct_colour(HEJ::Particle const & part){ if(!HEJ::is_parton(part) && !part.colour) return true; if(!part.colour) return false; int const colour = part.colour->first; int const anti_colour = part.colour->second; if(part.type == HEJ::ParticleID::gluon) return colour != anti_colour && colour >= HEJ::COLOUR_OFFSET && anti_colour >= HEJ::COLOUR_OFFSET; if(HEJ::is_quark(part)) return anti_colour == 0 && colour >= HEJ::COLOUR_OFFSET; return colour == 0 && anti_colour >= HEJ::COLOUR_OFFSET; } bool correct_colour(HEJ::Event const & ev){ if(!ev.is_leading_colour()) return false; // some of these additional checks are also in ev.is_leading_colour() for(auto const & part: ev.incoming()){ if(!correct_colour(part)) return false; } for(auto const & part: ev.outgoing()){ if(!correct_colour(part)) return false; } return true; } bool match_expected( HEJ::Event const & ev, std::vector<HEJ::Colour> const & expected ){ ASSERT(ev.outgoing().size()+2==expected.size()); for(size_t i=0; i<ev.incoming().size(); ++i){ ASSERT(ev.incoming()[i].colour); if( *ev.incoming()[i].colour != expected[i]) return false; } for(size_t i=2; i<ev.outgoing().size()+2; ++i){ if( ev.outgoing()[i-2].colour ){ if( *ev.outgoing()[i-2].colour != expected[i] ) return false; } else if( expected[i].first != 0 || expected[i].second != 0) return false; } return true; } void check_event( HEJ::Event::EventData unc_ev, std::vector<HEJ::Colour> const & expected_colours ){ unc_ev = decay_boson(std::move(unc_ev)); shuffle_particles(unc_ev); // make sure incoming order doesn't matter HEJ::Event ev{unc_ev.cluster( fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30.) }; ASSERT(HEJ::event_type::is_resummable(ev.type())); dum_rnd rng; ASSERT(!ev.is_leading_colour()); ASSERT(ev.generate_colours(rng)); if(!correct_colour(ev)){ std::cerr << "Found illegal colours for event\n"; dump_event(ev); throw std::invalid_argument("Illegal colour set"); } if(!match_expected(ev, expected_colours)){ std::cerr << "Colours didn't match expectation. Found\n"; dump_event(ev); std::cerr << "but expected\n"; for(auto const & col: expected_colours){ std::cerr << "colour={" << col.first << ", " << col.second << "}\n"; } throw std::logic_error("Colours did not match expectation"); } } HEJ::Event::EventData reset_colour( HEJ::Event::EventData ev, std::vector<HEJ::Colour> const & goal ){ for(size_t i=0; i<2; ++i){ ev.incoming[i].colour = goal[i]; } for(size_t i=0; i<ev.outgoing.size(); ++i){ auto const & col_goal{ goal[i+2] }; if(col_goal.first == 0 && col_goal.second == 0) ev.outgoing[i].colour = HEJ::optional<HEJ::Colour>{}; else ev.outgoing[i].colour = col_goal; } return ev; } int main() { HEJ::Event::EventData ev; std::vector<HEJ::Colour> expected_colours(7); /// pure gluon (they all have a mass of 4 GeV to allow decays) ev.incoming[0] = { HEJ::ParticleID::gluon, { 0, 0, -205, 205}, {}}; ev.incoming[1] = { HEJ::ParticleID::gluon, { 0, 0, 279, 279}, {}}; ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-15, -82, -82, 117}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, { 68, 93, 20, 117}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::higgs, {-30, -65, 22, 75}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-12, 92, 76, 120}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-11, -38, 38, 55}, {}}); expected_colours[0] = {502, 501}; expected_colours[1] = {509, 502}; expected_colours[2] = {503, 501}; expected_colours[3] = {505, 503}; expected_colours[4] = { 0, 0}; expected_colours[5] = {507, 505}; expected_colours[6] = {509, 507}; // default colours is always forbidden! // default: swap last two (anti-)colour -> crossing ev=reset_colour(ev, expected_colours); std::swap(ev.outgoing[4].colour, ev.outgoing[3].colour); check_event(ev, expected_colours); /// last g to Qx (=> gQx -> g ... Qx) ev.incoming[1].type = HEJ::ParticleID::d_bar; ev.outgoing[4].type = HEJ::ParticleID::d_bar; // => only end changes expected_colours[1].first = 0; expected_colours[6].first = 0; // default: swap last two anti-colours -> last gluon colour singlet ev=reset_colour(ev, expected_colours); std::swap(ev.outgoing[4].colour->second, ev.outgoing[3].colour->second); check_event(ev, expected_colours); { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; /// uno forward (=> gQx -> g ... Qx g) std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type); // => uno quarks eats colour and gluon connects to anti-colour new_expected[5] = {0, expected_colours[3].first}; new_expected[6] = {expected_colours[0].first, expected_colours[0].first+2}; new_expected[1].second += 2; // one more anti-colour in line // default: swap last two anti-colours -> crossing new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[4].colour->second, new_ev.outgoing[3].colour->second); check_event(new_ev, new_expected); } /// swap Qx <-> Q (=> gQ -> g ... Q) ev.incoming[1].type = HEJ::ParticleID::d; ev.outgoing[4].type = HEJ::ParticleID::d; // => swap: colour<->anti && initial<->final std::swap(expected_colours[1], expected_colours[6]); std::swap(expected_colours[1].first, expected_colours[1].second); std::swap(expected_colours[6].first, expected_colours[6].second); // default: swap incoming <-> outgoing ev=reset_colour(ev, expected_colours); std::swap(ev.incoming[0].colour, ev.outgoing[0].colour); check_event(ev, expected_colours); /// first g to qx (=> qxQ -> qx ... Q) ev.incoming[0].type = HEJ::ParticleID::u_bar; ev.outgoing[0].type = HEJ::ParticleID::u_bar; expected_colours[0] = { 0, 501}; // => shift anti-colour index one up expected_colours[1].first -= 2; expected_colours[5] = expected_colours[3]; expected_colours[3] = expected_colours[2]; expected_colours[2] = { 0, 502}; // default: closed qx->qx g ev=reset_colour(ev, expected_colours); ev.outgoing[1].colour->first = ev.outgoing[0].colour->second; ev.outgoing[1].colour->second = ev.incoming[0].colour->second; ev.outgoing[4].colour->first = ev.outgoing[3].colour->second; check_event(ev, expected_colours); { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; /// uno backward (=> qxQ -> g qx ... Q) std::swap(new_ev.outgoing[0].type, new_ev.outgoing[1].type); // => uno gluon connects to quark colour new_expected[3] = expected_colours[2]; new_expected[2] = {expected_colours[0].second+2, expected_colours[0].second}; // default: Colourful Higgs new_ev=reset_colour(new_ev, new_expected); new_ev.outgoing[2].colour = std::make_pair(1,1); check_event(new_ev, new_expected); /// swap qx <-> q (=> qQ -> g q ... Q) new_ev.incoming[0].type = HEJ::ParticleID::u; new_ev.outgoing[1].type = HEJ::ParticleID::u; // => swap: colour<->anti && inital<->final std::swap(new_expected[0], new_expected[3]); std::swap(new_expected[0].first, new_expected[0].second); std::swap(new_expected[3].first, new_expected[3].second); // => & connect first gluon with remaining anti-colour new_expected[2] = {new_expected[0].first, new_expected[0].first+2}; // shift colour line one down new_expected[1].first-=2; new_expected[5].first-=2; new_expected[5].second-=2; // shift anti-colour line one up new_expected[6].first+=2; // default: swap 2 quarks -> disconnected new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[1].colour, new_ev.outgoing[4].colour); check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; /// uno forward (=> qxQ -> qx ... Q g) std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type); // => uno gluon connects to remaining colour new_expected[5] = expected_colours[6]; new_expected[6] = {expected_colours[3].first+2, expected_colours[3].first}; // default: no colour on last gluon new_ev=reset_colour(new_ev, new_expected); new_ev.incoming[1].colour->first = new_ev.outgoing[4].colour->second; new_ev.outgoing[4].colour = {}; check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; /// qqx backward (=> gQ -> qx q ... Q) with Wp // => swap: incoming q <-> outgoing gluon std::swap(new_ev.incoming[0].type, new_ev.outgoing[1].type); new_ev.outgoing[1].type=static_cast<HEJ::ParticleID>( -(new_ev.outgoing[1].type+1) ); new_ev.outgoing[2].type = HEJ::ParticleID::Wp; // incoming q -> outgoing q (colour<->anti) std::swap(new_expected[0], new_expected[3]); std::swap(new_expected[3].first, new_expected[3].second); new_expected[3].first+=2; new_expected[0].first-=1; // skip one index // couple first in to first out new_expected[2].second=new_expected[0].second; // default: swap qqx <-> first g new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[0].colour->second, new_ev.outgoing[3].colour->second); std::swap(new_ev.outgoing[1].colour->first, new_ev.outgoing[3].colour->first); check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; /// qqx forward (=> qx g -> qx ... Qx Q) with Wp // => swap: incoming Q <-> outgoing gluon std::swap(new_ev.incoming[1].type, new_ev.outgoing[3].type); new_ev.outgoing[3].type=static_cast<HEJ::ParticleID>( -(new_ev.outgoing[3].type+1)); new_ev.outgoing[2].type = HEJ::ParticleID::Wp; // incoming q -> outgoing q (colour<->anti) std::swap(new_expected[1], new_expected[5]); std::swap(new_expected[5].first, new_expected[5].second); new_expected[5].second-=2; new_expected[1].second-=1; // skip one index // couple last in to last out new_expected[6].first=new_expected[1].first; // default: uncoloured quark new_ev=reset_colour(new_ev, new_expected); new_ev.outgoing[0].colour = {}; check_event(new_ev, new_expected); // move Higgs to position 1 (=> qx g -> qx h g Qx Q) std::swap(new_ev.outgoing[1].type, new_ev.outgoing[2].type); std::swap(new_expected[3], new_expected[4]); // trivial // default: incoming qx wrong colour new_ev=reset_colour(new_ev, new_expected); new_ev.incoming[0].colour->first = 1; check_event(new_ev, new_expected); // central qqx (=> qx g -> qx h Q Qx g) // => swap: Q <-> g std::swap(new_ev.outgoing[2].type, new_ev.outgoing[4].type); std::swap(new_expected[4], new_expected[6]); // gluon was connected on left side, i.e. doesn't matter for QQx // => couple Q to out qx new_expected[4].first = new_expected[2].second; // Qx next in line new_expected[5].second = new_expected[4].first+2; // incoming g shifted by one position in line new_expected[1].first-=2; new_expected[1].second+=2; // default: wrong colour in last incoming new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.incoming[1].colour->first, new_ev.incoming[1].colour->second); check_event(new_ev, new_expected); } return EXIT_SUCCESS; } diff --git a/t/test_hdf5_write.cc b/t/test_hdf5_write.cc index 1fe7ed5..1f03a36 100644 --- a/t/test_hdf5_write.cc +++ b/t/test_hdf5_write.cc @@ -1,77 +1,77 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <iostream> #include <unistd.h> #include "HEJ/Event.hh" #include "HEJ/HDF5Reader.hh" #include "HEJ/make_writer.hh" #include "HEJ/utility.hh" #include "hej_test.hh" namespace { const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double min_jet_pt{20.}; } int main(int argc, char** argv) { if(argc != 3) { std::cerr << "Usage: " << argv[0] << " in_file.hdf5 out_file.hdf5\n"; return EXIT_FAILURE; } std::vector<HEJ::Event> events; size_t max_nevent = 15299; // this scope is needed to trigger the HEJ::HDF5Writer destructor // don't remove it { auto reader = HEJ::make_reader(argv[1]); auto writer = HEJ::make_format_writer( HEJ::FileFormat::HDF5, argv[2], reader->heprup() ); size_t nevent = 0; while(reader->read_event()) { ++nevent; const auto event = HEJ::Event::EventData{reader->hepeup()}.cluster( jet_def, min_jet_pt ); events.emplace_back(event); writer->write(event); if(nevent>=max_nevent) break; } max_nevent = std::min(nevent,max_nevent); } HEJ::HDF5Reader reader{argv[2]}; size_t nevent = 0; for(auto const & event: events) { ++nevent; reader.read_event(); const auto ev = HEJ::Event::EventData{reader.hepeup()}.cluster( jet_def, min_jet_pt ); ASSERT_PROPERTY(ev, event, incoming().size()); for(size_t i = 0; i < ev.incoming().size(); ++i) { ASSERT(HEJ::nearby(ev.incoming()[i].p, event.incoming()[i].p)); } ASSERT_PROPERTY(ev, event, outgoing().size()); for(size_t i = 0; i < ev.outgoing().size(); ++i) { ASSERT(HEJ::nearby(ev.outgoing()[i].p, event.outgoing()[i].p)); } ASSERT_PROPERTY(ev, event, decays().size()); ASSERT_PROPERTY(ev, event, type()); ASSERT_PROPERTY(ev, event, central().weight); } ASSERT(nevent == max_nevent); return EXIT_SUCCESS; } diff --git a/t/test_jet_cuts.cc b/t/test_jet_cuts.cc index 7abc0eb..7208eec 100644 --- a/t/test_jet_cuts.cc +++ b/t/test_jet_cuts.cc @@ -1,302 +1,302 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <iostream> #include "hej_test.hh" namespace { const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double min_jet_pt{30.}; const double max_ext_soft_pt_fraction{0.4}; bool correct( bool const expected, std::array<std::string,2> const & in, std::vector<std::string> const & out, int const overwrite_boson ){ HEJ::Event ev{ parse_configuration( in,out,overwrite_boson ).cluster(jet_def, min_jet_pt)}; ASSERT(is_resummable(ev.type())); // only test jet configurations if(ev.valid_hej_state(max_ext_soft_pt_fraction) != expected){ std::cerr << "Expected " << (expected?"valid":"invalid") << " but found " << (ev.valid_hej_state(max_ext_soft_pt_fraction)?"valid":"invalid") << " jets (" << overwrite_boson << ")\n" << ev; auto jet_idx{ ev.particle_jet_indices() }; std::cout << "Particle Jet indices: "; for(int const i: jet_idx) std::cout << i << " "; std::cout << std::endl; return false; } return true; } // valid jet configurations bool valid_jets(){ // FKL // extremal parton inside jet for(int i=-3; i>-13;--i){ std::array<std::string,2> base_in{"g","g"}; std::vector<std::string> base_out(7, "g"); if(!correct(true, base_in, base_out, i)) return false; } std::cout << __LINE__ <<std::endl; // replace one of the extremal with a boson if( !( correct(true,{"g","d"},{"g","g","g","g","g","d","h"}, -13) && correct(true,{"d","d"},{"d","g","g","g","g","d","h"}, -13) && correct(true,{"2","2"},{"1","g","g","g","g","2","Wp"},-14) )) return false; std::cout << __LINE__ <<std::endl; // uno if( !( correct(true,{"g","3"},{"h","g","g","g","g","3","g"}, -1) && correct(true,{"3","2"},{"Wp","g","3","g","g","g","1"}, -1) && correct(true,{"1","2"},{"Wm","g","2","g","g","g","2"}, -1) && correct(true,{"1","3"},{"1","g","g","g","g","3","g"}, -3) && correct(true,{"-1","3"},{"-1","g","g","g","g","3","g"},-5) && correct(true,{"3","-2"},{"g","3","g","g","g","g","-2"},-5) && correct(true,{"-3","3"},{"g","-3","g","g","g","g","3"},-6) && correct(true,{"-4","3"},{"-4","g","g","g","g","3","g"},-7) && correct(true,{"3","-5"},{"g","3","g","g","g","g","-5"},-7) && correct(true,{"g","3"},{"g","g","g","g","g","3","g"}, -8) && correct(true,{"3","g"},{"g","3","g","g","g","g","g"}, -8) && correct(true,{"2","3"},{"g","1","g","Wp","g","g","3"}, -9) && correct(true,{"2","3"},{"1","g","g","g","3","Wp","g"}, -9) && correct(true,{"2","3"},{"1","g","Wp","g","g","3","g"}, -10) && correct(true,{"3","g"},{"g","3","g","g","g","g","g"}, -11) && correct(true,{"1","3"},{"1","g","g","g","g","3","g"}, -12) && correct(true,{"3","2"},{"g","3","g","g","g","g","2"}, -12) && correct(true,{"3","g"},{"g","3","g","g","g","g","h"}, -13) && correct(true,{"2","3"},{"1","g","g","g","3","g","Wp"}, -13) && correct(true,{"2","3"},{"g","1","g","g","g","3","Wp"}, -14) )) return false; std::cout << __LINE__ <<std::endl; // extremal qqx if( !( correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -3) && correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -5) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -5) && correct(true,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -7) && correct(true,{"g","2"},{"1","-1","g","g","g","Wp","1"}, -7) && correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -8) && correct(true,{"g","2"},{"1","-1","Wp","g","g","g","1"}, -8) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -9) && correct(true,{"g","3"},{"-2","1","g","Wp","g","g","3"}, -9) && correct(true,{"2","g"},{"1","g","g","g","3","Wp","-3"}, -9) && correct(true,{"2","g"},{"1","g","Wp","g","g","-3","3"}, -10) && correct(true,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -12) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -12) && correct(true,{"2","g"},{"1","g","g","g","-3","3","Wp"}, -13) && correct(true,{"g","g"},{"-2","1","g","g","g","g","Wp"}, -14) )) return false; std::cout << __LINE__ <<std::endl; // central qqx if( !( correct(true,{"1","g"},{"2","g","-2","Wm","2","g","g"}, -3) && correct(true,{"1","g"},{"2","g","g","-2","2","Wm","g"}, -4) && correct(true,{"1","g"},{"2","g","g","Wm","-2","2","g"}, -5) && correct(true,{"1","g"},{"2","g","g","Wm","-2","2","g"}, -8) && correct(true,{"1","g"},{"2","Wm","g","-2","2","g","g"}, -9) && correct(true,{"1","g"},{"2","Wm","-2","2","g","g","g"}, -9) && correct(true,{"1","g"},{"2","g","g","-2","2","Wm","g"}, -10) && correct(true,{"1","g"},{"2","g","Wm","2","-2","g","g"}, -11) && correct(true,{"1","g"},{"2","g","g","-2","2","g","Wm"}, -12) && correct(true,{"1","g"},{"2","2","-2","g","g","g","Wm"}, -13) && correct(true,{"1","g"},{"2","2","-2","g","g","g","Wm"}, -14) )) return false; std::cout << __LINE__ <<std::endl; return true; } // invalid jet configurations bool invalid_jets(){ std::cout << __LINE__ <<std::endl; // not implemented extremal qqx for pure jets // if( !( true // && correct(false,{"2","g"}, {"2","g","g","g","g","2","-2"}, -1) // && correct(false,{"g","-2"},{"4","-4","g","g","g","g","-2"}, -2) // // qqx backward not in jet // && correct(false,{"g","g"}, {"g","g","g","g","g","-2","2"}, -2) // // qqx backward in same jet // && correct(false,{"g","2"}, {"-4","4","g","g","g","g","2"}, -3) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -3) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -4) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -5) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -5) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -6) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -7) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -7) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -8) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -8) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -9) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -10) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -11) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -11) // && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -12) // && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -12) // )) // return false; std::cout << __LINE__ <<std::endl; // not implemented central qqx for pure jets // if( !( true // && correct(false,{"-1","g"},{"-1","1","-1","g","g","g","g"}, -1) // && correct(false,{"4","-3"},{"4","g","g","1","-1","g","-3"}, -2) // && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -3) // && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -3) // && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -4) // && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -4) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -5) // && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -6) // && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -7) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -7) // && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -8) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -8) // && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -9) // && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -9) // && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -10) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -10) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -11) // && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -12) // && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -12) // )) // return false; std::cout << __LINE__ <<std::endl; // implemented processes failing jet cuts // FKL if( !( true && correct(false,{"1","g"}, {"g","1","g","g","g","g","g"}, -1) // FKL not in own jet && correct(false,{"1","1"}, {"1","g","g","g","g","g","1"}, -1) && correct(false,{"g","g"}, {"g","g","g","g","g","g","g"}, -2) && correct(false,{"1","-3"},{"1","g","g","g","g","-3","g"}, -1) && correct(false,{"2","g"}, {"g","2","g","g","g","g","g"}, -2) // FKL below pt && correct(false,{"1","1"}, {"1","g","g","g","g","g","1"}, -13) && correct(false,{"g","3"},{"h","g","g","g","g","3","g"}, -13) && correct(false,{"g","1"},{"Wm","g","g","g","g","2","g"}, -13) && correct(false,{"3","1"},{"Wm","g","3","g","g","g","2"}, -13) // uno in same jet as FKL && correct(false,{"-1","1"},{"-1","g","g","g","g","1","g"}, -9) && correct(false,{"3","3"}, {"g","3","g","g","g","g","3"}, -9) && correct(false,{"-1","1"},{"g","-1","g","g","g","g","1"}, -10) && correct(false,{"-1","1"},{"-1","g","g","g","g","1","g"}, -13) && correct(false,{"-1","1"},{"g","-1","g","g","g","g","1"}, -13) // FKL not in jet & uno other side && correct(false,{"2","3"},{"Wp","1","g","g","g","3","g"}, -15) )) return false; std::cout << __LINE__ <<std::endl; // uno if( !( true // uno backward not in jet && correct(false,{"1","2"}, {"g","1","g","g","g","g","2"}, -1) // uno forward not in jet && correct(false,{"3","3"}, {"3","g","g","g","g","3","g"}, -2) // uno backward in same jet && correct(false,{"1","g"}, {"g","1","g","g","g","g","g"}, -3) // uno forward in same jet && correct(false,{"3","2"}, {"3","g","g","g","g","2","g"}, -4) // uno backward below pt && correct(false,{"3","g"}, {"g","3","g","g","g","g","g"}, -4) // uno forward below pt && correct(false,{"4","3"}, {"4","g","g","g","g","3","g"}, -10) && correct(false,{"2","3"}, {"1","g","g","g","3","g","Wp"}, -14) // uno forward not in jet && correct(false,{"5","3"}, {"5","g","g","g","g","3","g"}, -11) )) return false; std::cout << __LINE__ <<std::endl; // extremal qqx if( !( true // qqxf in same jet && correct(false,{"g","g"}, {"Wm","g","g","g","g","-1","2"}, -4) // qqxb below pt && correct(false,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -4) // central qqx not in jet && correct(false,{"1","2"}, {"1","g","-2","2","Wp","g","1"}, -5) // central qqx in same jet && correct(false,{"1","2"}, {"1","-2","2","g","Wp","g","1"}, -6) // central qqx in same jet && correct(false,{"1","2"}, {"1","Wm","g","g","2","-1","2"}, -6) // qqxf below pt && correct(false,{"2","g"},{"1","g","Wp","g","g","1","-1"}, -6) // central qqx in same jet && correct(false,{"1","2"}, {"1","-1","1","g","g","Wp","1"}, -7) // central qqx in same jet && correct(false,{"1","2"}, {"1","Wp","g","3","-3","g","1"}, -7) // central qqx in same jet && correct(false,{"g","3"}, {"g","Wp","-2","1","g","g","3"}, -8) // central qqx in same jet && correct(false,{"g","-2"},{"Wm","g","g","2","-1","g","-2"}, -8) // qqxf below pt && correct(false,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -10) // qqxf outside jet && correct(false,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -11) // extremal qqx in same jet as FKL && correct(false,{"-1","g"},{"-1","g","Wm","g","g","2","-1"},-9) && correct(false,{"g","1"}, {"2","-1","g","g","g","Wm","1"}, -10) // extraml qqx below pt && correct(false,{"-1","g"},{"-1","Wm","g","g","g","-1","2"},-13) && correct(false,{"g","g"}, {"g","g","g","g","-1","Wm","2"}, -14) && correct(false,{"g","g"}, {"-1","2","g","g","g","g","Wm"}, -15) )) return false; std::cout << __LINE__ <<std::endl; // central qqx if( !( true && correct(false,{"1","g"}, {"2","Wm","-2","2","g","g","g"}, -3) && correct(false,{"1","g"}, {"2","g","Wm","-2","2","g","g"}, -3) && correct(false,{"1","g"}, {"2","-2","2","g","Wm","g","g"}, -4) && correct(false,{"-1","g"},{"-1","g","g","Wp","1","-2","g"}, -4) && correct(false,{"-1","g"},{"-1","1","-2","g","g","Wp","g"}, -5) && correct(false,{"-1","g"},{"-1","g","1","-2","g","Wp","g"}, -5) && correct(false,{"-1","g"},{"-1","g","g","1","-2","Wp","g"}, -5) && correct(false,{"1","g"}, {"2","g","g","Wm","2","-2","g"}, -6) && correct(false,{"1","g"}, {"2","g","Wm","-2","2","g","g"}, -6) && correct(false,{"g","4"}, {"g","g","-4","4","g","Wp","3"}, -6) && correct(false,{"1","g"}, {"2","g","Wm","g","-2","2","g"}, -7) && correct(false,{"g","4"}, {"g","g","-4","4","g","Wp","3"}, -7) && correct(false,{"g","4"}, {"g","Wp","g","-4","4","g","3"}, -7) && correct(false,{"1","g"}, {"2","-2","2","Wm","g","g","g"}, -8) && correct(false,{"g","4"}, {"g","-4","4","g","Wp","g","3"}, -8) && correct(false,{"g","g"}, {"g","g","g","g","-2","1","Wp"}, -9) && correct(false,{"1","g"}, {"2","-2","2","g","Wm","g","g"}, -9) && correct(false,{"1","g"}, {"2","g","-2","2","g","Wm","g"}, -9) && correct(false,{"1","g"}, {"2","g","g","Wm","-2","2","g"}, -10) && correct(false,{"g","g"}, {"g","1","-2","Wp","g","g","g"}, -10) && correct(false,{"g","1"}, {"g","g","Wp","g","-2","1","1"}, -11) && correct(false,{"1","g"}, {"2","g","Wm","g","-2","2","g"}, -11) && correct(false,{"1","g"}, {"2","Wm","g","-2","2","g","g"}, -12) && correct(false,{"3","2"}, {"3","g","-1","2","Wm","g","2"}, -12) && correct(false,{"3","-2"},{"3","-1","2","g","g","Wm","-2"}, -13) && correct(false,{"3","-2"},{"3","-1","2","g","g","Wm","-2"}, -14) && correct(false,{"3","-2"},{"3","g","g","-1","2","Wm","-2"}, -14) && correct(false,{"1","g"}, {"Wm","2","2","-2","g","g","g"}, -15) && correct(false,{"3","-2"},{"3","-1","2","g","g","-2","Wm"}, -15) )) return false; std::cout << __LINE__ <<std::endl; return true; } } int main() { if(!valid_jets()) return EXIT_FAILURE; if(!invalid_jets()) return EXIT_FAILURE; return EXIT_SUCCESS; } diff --git a/t/test_scale_arithmetics.cc b/t/test_scale_arithmetics.cc index 268fc9e..19bb514 100644 --- a/t/test_scale_arithmetics.cc +++ b/t/test_scale_arithmetics.cc @@ -1,95 +1,95 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include <fstream> #include <algorithm> #include "LHEF/LHEF.h" #include "HEJ/EventReweighter.hh" #include "HEJ/make_RNG.hh" #include "HEJ/Event.hh" #include "HEJ/YAMLreader.hh" #include "HEJ/stream.hh" #include "hej_test.hh" constexpr double ep = 1e-13; void dump(HEJ::Event const & ev){ { LHEF::Writer writer{std::cout}; std::cout << std::setprecision(6); writer.hepeup = to_HEPEUP(std::move(ev), nullptr); writer.writeEvent(); } std::cout << "Rapidity ordering:\n"; for(const auto & part: ev.outgoing()){ std::cout << std::setw(2) << part.type << ": "<< std::setw(7) << part.rapidity() << std::endl; } } int main(int argn, char** argv){ if(argn != 3){ std::cerr << "\n# Usage:\n."<< argv[0] <<" config.yml input_file.lhe\n\n"; return EXIT_FAILURE; } HEJ::Config config = HEJ::load_config(argv[1]); config.scales = HEJ::to_ScaleConfig( YAML::Load("scales: [H_T, 1 * H_T, 2/2 * H_T, 2*H_T/2, H_T/2*2, H_T/2/2*4, H_T*H_T/H_T]") ); HEJ::istream in{argv[2]}; LHEF::Reader reader{in}; std::shared_ptr<HEJ::RNG> ran{ HEJ::make_RNG(config.rng.name, config.rng.seed)}; HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; HEJ::EventReweighter resum{ reader.heprup, std::move(scale_gen), to_EventReweighterConfig(config), ran }; size_t i = 0; while(reader.readEvent()){ ++i; HEJ::Event::EventData data{reader.hepeup}; shuffle_particles(data); HEJ::Event event{ data.cluster( config.resummation_jets.def, config.resummation_jets.min_pt ) }; auto resummed = resum.reweight(event, config.trials); for(auto && ev: resummed) { for(auto &&var: ev.variations()) { if(std::abs(var.muf - ev.central().muf) > ep) { std::cerr << std::setprecision(15) << "unequal scales: " << var.muf << " != " << ev.central().muf << '\n' << "in resummed event:\n"; dump(ev); std::cerr << "\noriginal event:\n"; dump(event); return EXIT_FAILURE; } } } } return EXIT_SUCCESS; } diff --git a/t/test_unweighter.cc b/t/test_unweighter.cc index 6c880e2..9f39a55 100644 --- a/t/test_unweighter.cc +++ b/t/test_unweighter.cc @@ -1,152 +1,152 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Unweighter.hh" #include "hej_test.hh" namespace{ const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4}; const double min_pt{30.}; const double sample_ratio{10.}; const double max_dev{2.}; bool compare_xs( HEJ::XSWithError<double> const & xs1, HEJ::XSWithError<double> const & xs2 ){ std::cout << xs1.value << "+/-" << xs1.error << " vs. " << xs2.value << "+/-" << xs2.error << std::endl; return std::abs(xs1.value/xs2.value-1.)<xs1.error; } } int main(int argc, char** argv) { if(argc != 2) { std::cerr << "Usage: " << argv[0] << " event_file\n"; return EXIT_FAILURE; } std::string file{argv[1]}; auto reader = HEJ::make_reader(file); // number of events auto nevents{reader->number_events()}; if(!nevents) { auto t_reader = HEJ::make_reader(file); nevents = 0; while(t_reader->read_event()) ++(*nevents); } ASSERT(*nevents>sample_ratio); const size_t size_sample = floor(*nevents/sample_ratio); HEJ::Mixmax ran{}; // no unweighting HEJ::CrossSectionAccumulator xs_base; std::vector<HEJ::Event> all_evts; // full unweighting HEJ::CrossSectionAccumulator xs_max; HEJ::Unweighter unw_max; size_t n_max{0}; // midpoint on full sample HEJ::CrossSectionAccumulator xs_mid; HEJ::Unweighter unw_mid; size_t n_mid{0}; // calc max from partial sample HEJ::CrossSectionAccumulator xs_pmax; HEJ::Unweighter unw_pmax; size_t n_pmax{0}; // midpoint on partial sample HEJ::CrossSectionAccumulator xs_pmid; HEJ::Unweighter unw_pmid; size_t n_pmid{0}; // initialise sample for(size_t n = 0; n < size_sample; ++n){ if(!reader->read_event()){ std::cerr << "Sample size bigger than event sample\n"; return EXIT_FAILURE; } const HEJ::Event event{ HEJ::Event::EventData{reader->hepeup()}.cluster(jet_def, min_pt) }; xs_base.fill(event); all_evts.push_back(event); } // calculate partial settings unw_pmax.set_cut_to_maxwt(all_evts); unw_pmid.set_cut_to_peakwt(all_evts, max_dev); for(auto const & ev: unw_pmax.unweight(all_evts, ran)){ xs_pmax.fill(ev); ++n_pmax; } for(auto const & ev: unw_pmid.unweight(all_evts, ran)){ xs_pmid.fill(ev); ++n_pmid; } while(reader->read_event()){ const HEJ::Event event{ HEJ::Event::EventData{reader->hepeup()}.cluster(jet_def, min_pt) }; xs_base.fill(event); auto ev{ unw_pmid.unweight(event, ran) }; if(ev){ xs_pmid.fill(*ev); ++n_pmid; } ev = unw_pmax.unweight(event, ran); if(ev){ xs_pmax.fill(*ev); ++n_pmax; } all_evts.push_back(event); } unw_max.set_cut_to_maxwt(all_evts); unw_mid.set_cut_to_peakwt(all_evts, max_dev); for(auto const & ev: unw_max.unweight(all_evts, ran)){ // make sure all the events have the same weight ASSERT( std::abs( std::abs(unw_max.get_cut()/ev.central().weight)-1. ) < 10e-16); xs_max.fill(ev); ++n_max; } for(auto const & ev: unw_mid.unweight(all_evts, ran)){ xs_mid.fill(ev); ++n_mid; } // sanity check number of events ASSERT( all_evts.size() > 0); ASSERT( n_pmax > 0); ASSERT( n_max > 0); ASSERT( n_pmid > 0); ASSERT( n_mid > 0); ASSERT( n_pmax < all_evts.size() ); ASSERT( n_max < n_pmax ); ASSERT( n_pmid < all_evts.size() ); ASSERT( n_mid < all_evts.size() ); ASSERT( n_max < n_mid ); std::cout << "all_evts.size() " << all_evts.size() << " n_pmax " << n_pmax << " n_max " << n_max << " n_pmid " << n_pmid << " n_mid " << n_mid << std::endl; // control xs (in circle) ASSERT(compare_xs( xs_base.total(), xs_pmax.total() )); ASSERT(compare_xs( xs_pmax.total(), xs_max.total() )); ASSERT(compare_xs( xs_max.total() , xs_pmid.total() )); ASSERT(compare_xs( xs_pmid.total(), xs_mid.total() )); ASSERT(compare_xs( xs_mid.total() , xs_base.total() )); return EXIT_SUCCESS; }