Page MenuHomeHEPForge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/FixedOrderGen/include/EventGenerator.hh b/FixedOrderGen/include/EventGenerator.hh
index 899a72c..4ac94b5 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-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <memory>
+#include "HEJ/EWConstants.hh"
#include "HEJ/MatrixElement.hh"
#include "HEJ/optional.hh"
#include "HEJ/PDF.hh"
-#include "HEJ/RNG.hh"
+#include "HEJ/ScaleFunction.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;
+ class RNG;
}
//! 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 a6eef97..3eb0a29 100644
--- a/FixedOrderGen/include/PhaseSpacePoint.hh
+++ b/FixedOrderGen/include/PhaseSpacePoint.hh
@@ -1,233 +1,238 @@
/** \file PhaseSpacePoint.hh
* \brief Contains the PhaseSpacePoint Class
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
+#include <array>
#include <bitset>
+#include <stddef.h>
+#include <unordered_map>
#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 "fastjet/PseudoJet.hh"
+
#include "Decay.hh"
#include "Status.hh"
namespace HEJ{
class EWConstants;
+ class PDF;
+ class RNG;
}
namespace HEJFOG{
+ class JetParameters;
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{
+ std::array<HEJ::Particle, 2> const & incoming() const{
return incoming_;
}
//! Get Outgoing Function
/**
* @returns Outgoing Particles
*/
- std::vector<Particle> const & outgoing() const{
+ std::vector<HEJ::Particle> const & outgoing() const{
return outgoing_;
}
- std::unordered_map<size_t, std::vector<Particle>> const & decays() const{
+ std::unordered_map<size_t, std::vector<HEJ::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::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_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(
+ std::vector<HEJ::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(
+ std::vector<HEJ::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_;
+ std::array<HEJ::Particle, 2> incoming_;
+ std::vector<HEJ::Particle> outgoing_;
//! Particle decays in the format {outgoing index, decay products}
- std::unordered_map<size_t, std::vector<Particle>> decays_;
+ std::unordered_map<size_t, std::vector<HEJ::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 df5b36b..bb1d1ec 100644
--- a/FixedOrderGen/include/config.hh
+++ b/FixedOrderGen/include/config.hh
@@ -1,46 +1,49 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
-#include "yaml-cpp/yaml.h"
+#include <stddef.h>
+#include <string>
+#include <vector>
-#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 "HEJ/HiggsCouplingSettings.hh"
+#include "HEJ/optional.hh"
+#include "HEJ/output_formats.hh"
+
+#include "yaml-cpp/yaml.h"
#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 852d462..44f2572 100644
--- a/FixedOrderGen/src/EventGenerator.cc
+++ b/FixedOrderGen/src/EventGenerator.cc
@@ -1,95 +1,99 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "EventGenerator.hh"
#include <utility>
+#include <stddef.h>
+
+#include "HEJ/Config.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+#include "HEJ/EWConstants.hh"
+#include "HEJ/exceptions.hh"
+#include "HEJ/HiggsCouplingSettings.hh"
#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 bd85e39..e925902 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,695 +1,710 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "PhaseSpacePoint.hh"
#include <algorithm>
-
-#include "CLHEP/Vector/LorentzVector.h"
+#include <assert.h>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "fastjet/ClusterSequence.hh"
#include "HEJ/Constants.hh"
#include "HEJ/EWConstants.hh"
#include "HEJ/exceptions.hh"
#include "HEJ/kinematics.hh"
#include "HEJ/Particle.hh"
+#include "HEJ/PDF.hh"
+#include "HEJ/RNG.hh"
#include "HEJ/utility.hh"
+#include "JetParameters.hh"
#include "Process.hh"
#include "Subleading.hh"
-using namespace HEJ;
+namespace {
+ using namespace HEJ;
+ 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
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){
+ Event::EventData to_EventData(PhaseSpacePoint psp){
//! @TODO Same function already in HEJ
- HEJ::Event::EventData result;
+ 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{}
+ rapidity_less{}
)
);
assert(result.outgoing.size() >= 2);
result.decays = std::move(psp).decays_;
result.parameters.central = {NaN, NaN, psp.weight()};
return result;
}
- namespace{
+ namespace {
bool can_swap_to_uno(
- HEJ::Particle const & p1, HEJ::Particle const & p2
+ Particle const & p1, 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){
+ 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){
+ 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);
+ return is_anyquark(part) && !(std::abs(part.type)%2);
}
bool is_down_type(Particle const & part){
- return HEJ::is_anyquark(part) && abs(part.type)%2;
+ return is_anyquark(part) && std::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
+ return std::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
+ 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);})) {
+ [&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(std::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
+ 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){
+ void PhaseSpacePoint::turn_to_qqx(const bool allow_strange, 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());
+ const size_t idx = std::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);
+ const double mperp = std::sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square);
+ const double pz=mperp*std::sinh(y);
+ const double E=mperp*std::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
+ auto insert_particle(std::vector<Particle> & target,
+ 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,
+ 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
+ EWConstants const & ew_parameters,
+ 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);
+ weight_ *= std::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
+ RNG & ran
) {
// heuristic parameters for pt sampling
const double ptpar = ptmin + np/5.;
- const double arg_small_y = atan((ptmax - ptmin)/ptpar);
+ const double arg_small_y = std::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);
+ const double temp = std::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) {
+ double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, 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
+ 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
+ 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::pow(16.*std::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
+ ParticleID bosonid, double mass, double width,
+ RNG & ran
){
// Usual phase space measure
- weight_ /= 16.*pow(M_PI, 3);
+ weight_ /= 16.*std::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))
+ mass + width*std::tan(M_PI/2.*r1 + (r1-1.)*std::atan(mass/width))
);
// off-shell s_boson sampling, compensates for Breit-Wigner
/// @TODO use a flag instead
- if(abs(bosonid) == pid::Wp){
+ if(std::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) ) );
+ weight_*= mass*width*( M_PI+2.*std::atan(mass/width) )
+ / ( 1. + std::cos( M_PI*r1 + 2.*(r1-1.)*std::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];
+ if(!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];
+ if(!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];
+ if(!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];
+ if(!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;
+ return id>0 ? id*2-1 : std::abs(id)*2;
}
std::bitset<11> init_allowed(ParticleID const id){
- if(abs(id) == pid::proton)
+ if(std::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){
+ if(std::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
+ Process const & proc, unsigned int const subl_channels, 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,
+ PDF & pdf, double E_beam,
double uf,
- HEJ::RNG & ran
+ 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(
+ 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
+ PDF & pdf, std::bitset<11> allowed_partons, RNG & ran
){
std::array<double,11> pdf_wt;
- pdf_wt[0] = allowed_partons[0]?fabs(pdf.pdfpt(beam_idx,x,uf,pid::gluon)):0.;
+ pdf_wt[0] = allowed_partons[0]?
+ std::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;
+ pdf_wt[i] = allowed_partons[i]?
+ 4./9.*std::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
+ ParticleID const boson, RNG & ran
){
- if(abs(boson) != pid::Wp) return; // only matters for W
+ if(std::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());
+ idx = std::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
+ 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;
+ const double result = stddev*std::sqrt(2.*lninvr1)*std::cos(2.*M_PI*r2);
+ weight_ *= exp(result*result/(2*stddev*stddev))*std::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
+ 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,
+ Particle const & parent,
std::vector<Decay> const & decays,
- HEJ::RNG & ran
+ 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
+ Particle const & parent,
+ std::vector<ParticleID> const & decays,
+ RNG & ran
){
if(decays.size() != 2){
- throw HEJ::not_implemented{
+ throw 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 sin_phi = std::sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi
+ const double px = E*std::cos(theta)*sin_phi;
+ const double py = E*std::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 0f51c49..6557fe5 100644
--- a/FixedOrderGen/src/config.cc
+++ b/FixedOrderGen/src/config.cc
@@ -1,432 +1,439 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "config.hh"
+#include <algorithm>
+#include <assert.h>
#include <cctype>
+#include <cstdlib>
+#include <iostream>
+#include <iterator>
+#include <stdexcept>
-#include "Subleading.hh"
-
-#include "HEJ/Config.hh"
+#include "HEJ/exceptions.hh"
+#include "HEJ/PDG_codes.hh"
#include "HEJ/YAMLreader.hh"
+#include "Subleading.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 4595f0c..0bc1df1 100644
--- a/FixedOrderGen/src/main.cc
+++ b/FixedOrderGen/src/main.cc
@@ -1,277 +1,292 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include <algorithm>
+#include <assert.h>
#include <chrono>
-#include <fstream>
+#include <cmath>
+#include <cstdint>
+#include <ctime>
+#include <iomanip>
#include <iostream>
+#include <iterator>
#include <map>
#include <memory>
-#include <cstdint>
-
-#include "LHEF/LHEF.h"
-
-#include "yaml-cpp/yaml.h"
+#include <stdlib.h>
+#include <string>
+#include <utility>
+#include <vector>
#include <boost/iterator/filter_iterator.hpp>
+#include "fastjet/ClusterSequence.hh"
+#include "fastjet/PseudoJet.hh"
+
+#include "HEJ/Analysis.hh"
#include "HEJ/CombinedEventWriter.hh"
#include "HEJ/CrossSectionAccumulator.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/exceptions.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/ScaleFunction.hh"
#include "HEJ/Unweighter.hh"
+#include "LHEF/LHEF.h"
+
#include "config.hh"
#include "EventGenerator.hh"
-#include "PhaseSpacePoint.hh"
+#include "Status.hh"
#include "Version.hh"
-namespace{
+namespace YAML {
+ class Node;
+}
+
+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 a95c0f6..adedb07 100644
--- a/FixedOrderGen/t/2j.cc
+++ b/FixedOrderGen/t/2j.cc
@@ -1,67 +1,69 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
-#include <cmath>
#include <cassert>
+#include <cmath>
#include <iostream>
-
-#include "config.hh"
-#include "EventGenerator.hh"
-#include "HEJ/Mixmax.hh"
+#include <memory>
+#include <stdlib.h>
#include "HEJ/Event.hh"
-#include "HEJ/PDF.hh"
-#include "HEJ/MatrixElement.hh"
+#include "HEJ/Mixmax.hh"
+#include "HEJ/ScaleFunction.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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 6329d71..3944f0f 100644
--- a/FixedOrderGen/t/4j.cc
+++ b/FixedOrderGen/t/4j.cc
@@ -1,70 +1,72 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
-#include <cmath>
#include <cassert>
+#include <cmath>
#include <iostream>
-
-#include "config.hh"
-#include "EventGenerator.hh"
-#include "HEJ/Mixmax.hh"
+#include <memory>
+#include <stdlib.h>
#include "HEJ/Event.hh"
-#include "HEJ/PDF.hh"
-#include "HEJ/MatrixElement.hh"
+#include "HEJ/Mixmax.hh"
+#include "HEJ/ScaleFunction.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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_2j_classify.cc b/FixedOrderGen/t/W_2j_classify.cc
index 62e8779..77bfbc2 100644
--- a/FixedOrderGen/t/W_2j_classify.cc
+++ b/FixedOrderGen/t/W_2j_classify.cc
@@ -1,158 +1,168 @@
/**
* \brief check that the PSP generates only "valid" W + 2 jets events
*
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
-#include "Decay.hh"
-#include "JetParameters.hh"
-#include "PhaseSpacePoint.hh"
-#include "Process.hh"
-#include "Subleading.hh"
+#include <iostream>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include "fastjet/JetDefinition.hh"
#include "HEJ/EWConstants.hh"
+#include "HEJ/exceptions.hh"
#include "HEJ/Mixmax.hh"
+#include "HEJ/Particle.hh"
#include "HEJ/PDF.hh"
-#include "HEJ/utility.hh"
+#include "HEJ/PDG_codes.hh"
-using namespace HEJFOG;
-using namespace HEJ;
+#include "Decay.hh"
+#include "JetParameters.hh"
+#include "PhaseSpacePoint.hh"
+#include "Process.hh"
+#include "Status.hh"
+#include "Subleading.hh"
namespace {
+ using namespace HEJFOG;
+ using namespace HEJ;
+
void print_psp(PhaseSpacePoint const & psp){
std::cerr << "Process:\n"
<< psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> ";
for(auto const & out: psp.outgoing()){
std::cerr << out.type << " ";
}
std::cerr << "\n";
}
void bail_out(PhaseSpacePoint const & psp, std::string msg){
print_psp(psp);
throw std::logic_error{msg};
}
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 check_W2j(PhaseSpacePoint const & psp, ParticleID const W_type){
bool found_quark = false;
bool found_anti = false;
std::vector<Particle> out_partons;
std::vector<Particle> Wp;
for(auto const & p: psp.outgoing()){
if(p.type == W_type) Wp.push_back(p);
else if(is_parton(p)) out_partons.push_back(p);
else bail_out(psp, "Found particle with is not "
+std::to_string(int(W_type))+" or parton");
}
if(Wp.size() != 1 || out_partons.size() != 2){
bail_out(psp, "Found wrong number of outgoing partons");
}
for(size_t j=0; j<2; ++j){
auto const & in = psp.incoming()[j];
auto const & out = out_partons[j];
if(is_quark(in) || is_antiquark(in)) {
found_quark = true;
if(in.type != out.type) { // switch in quark type -> Wp couples to it
if(found_anti){ // already found qq for coupling to W
bail_out(psp, "Found second up/down pair");
} else if(abs(in.type)>4 || abs(out.type)>4){
bail_out(psp, "Found bottom/top pair");
}
found_anti = true;
if( is_up_type(in)) { // "up" in
if(W_type > 0){
// -> only allowed u -> Wp + d
if(in.type < 0 || is_up_type(out) || out.type < 0)
bail_out(psp, "u -/> Wp + d");
} else {
// -> only allowed ux -> Wm + dx
if(in.type > 0 || is_up_type(out) || out.type > 0)
bail_out(psp, "ux -/> Wm + dx");
}
} else { // "down" in
if(W_type > 0){
// -> only allowed dx -> Wp + ux
if(in.type > 0 || is_down_type(out) || out.type > 0)
bail_out(psp, "dx -/> Wp + ux");
} else {
// -> only allowed d -> Wm + u
if(in.type < 0 || is_down_type(out) || out.type < 0)
bail_out(psp, "d -/> Wm + u");
}
}
}
}
}
if(!found_quark) {
bail_out(psp, "Found no initial quarks");
} else if(!found_anti){
bail_out(psp, "Found no up/down pair");
}
return true;
}
}
int main(){
constexpr size_t n_psp_base = 1337;
const JetParameters jet_para{
fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30};
PDF pdf(11000, pid::proton, pid::proton);
constexpr double E_cms = 13000.;
constexpr double subl_change = 0.5;
constexpr auto subl_channels = Subleading::all;
const ParticlesDecayMap boson_decays{
{pid::Wp, {Decay{ {pid::e_bar, pid::nu_e}, 1.} }},
{pid::Wm, {Decay{ {pid::e, pid::nu_e_bar}, 1.} }}
};
const EWConstants ew_constants{246.2196508,
ParticleProperties{80.385, 2.085},
ParticleProperties{91.187, 2.495},
ParticleProperties{125, 0.004165}
};
HEJ::Mixmax ran{};
// Wp2j
Process proc {{pid::proton,pid::proton}, 2, pid::Wp, {}};
size_t n_psp = n_psp_base;
for( size_t i = 0; i<n_psp; ++i){
const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
boson_decays, ew_constants, ran};
if(psp.status()==good){
check_W2j(psp, *proc.boson);
} else { // bad process -> try again
++n_psp;
}
}
std::cout << "Wp+2j: Took " << n_psp << " to generate "
<< n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
// Wm2j
proc = Process{{pid::proton,pid::proton}, 2, pid::Wm, {}};
n_psp = n_psp_base;
for( size_t i = 0; i<n_psp; ++i){
const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
boson_decays, ew_constants, ran};
if(psp.status()==good){
check_W2j(psp, *proc.boson);
} else { // bad process -> try again
++n_psp;
}
}
std::cout << "Wm+2j: Took " << n_psp << " to generate "
<< n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
std::cout << "All processes passed." << std::endl;
return EXIT_SUCCESS;
}
diff --git a/FixedOrderGen/t/W_nj_classify.cc b/FixedOrderGen/t/W_nj_classify.cc
index 47b8bcd..ed2a92b 100644
--- a/FixedOrderGen/t/W_nj_classify.cc
+++ b/FixedOrderGen/t/W_nj_classify.cc
@@ -1,198 +1,210 @@
/**
* \brief check that the PSP generates the all W+jet subleading processes
*
* \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 "JetParameters.hh"
-#include "Decay.hh"
-#include "PhaseSpacePoint.hh"
-#include "Process.hh"
-#include "Subleading.hh"
+#include <iomanip>
+#include <iostream>
+#include <math.h>
+#include <stdlib.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
#include "HEJ/EWConstants.hh"
+#include "HEJ/exceptions.hh"
#include "HEJ/Mixmax.hh"
#include "HEJ/PDF.hh"
-#include "HEJ/utility.hh"
+#include "HEJ/PDG_codes.hh"
-using namespace HEJFOG;
-using namespace HEJ;
+#include "fastjet/JetDefinition.hh"
+
+#include "Decay.hh"
+#include "JetParameters.hh"
+#include "PhaseSpacePoint.hh"
+#include "Process.hh"
+#include "Status.hh"
+#include "Subleading.hh"
namespace {
+ using namespace HEJFOG;
+ using namespace HEJ;
+
void print_psp(PhaseSpacePoint const & psp){
std::cerr << "Process:\n"
<< psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> ";
for(auto const & out: psp.outgoing()){
std::cerr << out.type << " ";
}
std::cerr << "\n";
}
void bail_out(PhaseSpacePoint const & psp, std::string msg){
print_psp(psp);
throw std::logic_error{msg};
}
}
int main(){
constexpr size_t n_psp_base = 10375;
const JetParameters jet_para{
fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30};
PDF pdf(11000, pid::proton, pid::proton);
constexpr double E_cms = 13000.;
constexpr double subl_change = 0.8;
const ParticlesDecayMap boson_decays{
{pid::Wp, {Decay{ {pid::e_bar, pid::nu_e}, 1.} }},
{pid::Wm, {Decay{ {pid::e, pid::nu_e_bar}, 1.} }}
};
const EWConstants ew_constants{246.2196508,
ParticleProperties{80.385, 2.085},
ParticleProperties{91.187, 2.495},
ParticleProperties{125, 0.004165}
};
HEJ::Mixmax ran{};
auto subl_channels = Subleading::all;
std::vector<event_type::EventType> allowed_types{event_type::FKL,
event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf};
std::cout << "Wp3j" << std::endl;
// Wp3j
Process proc {{pid::proton,pid::proton}, 3, pid::Wp, {}};
size_t n_psp = n_psp_base;
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 6)
// gcc version < 6 explicitly needs hash function for enum
// see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
std::unordered_map<event_type::EventType, size_t, std::hash<size_t>> type_counter;
#else
std::unordered_map<event_type::EventType, size_t> type_counter;
#endif
for( size_t i = 0; i<n_psp; ++i){
const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
boson_decays, ew_constants, ran};
if(psp.status()==good){
const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt) };
++type_counter[ev.type()];
if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
== allowed_types.cend()) {
bail_out(psp, "Found not allowed event of type "
+std::string(event_type::name(ev.type())));
}
} else { // bad process -> try again
++n_psp;
}
}
std::cout << "Wp+3j: Took " << n_psp << " to generate "
<< n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
std::cout << "States by classification:\n";
for(auto const & entry: type_counter){
const double fraction = static_cast<double>(entry.second)/n_psp_base;
const int percent = std::round(100*fraction);
std::cout << std::left << std::setw(25)
<< (event_type::name(entry.first) + std::string(":"))
<< entry.second << " (" << percent << "%)\n";
}
for(auto const & t: allowed_types){
if(type_counter[t] < 0.05 * n_psp_base){
std::cerr << "Less than 5% of the events are of type " << event_type::name(t) << std::endl;
return EXIT_FAILURE;
}
}
// Wm3j - only uno
proc = Process{{pid::proton,pid::proton}, 3, pid::Wm, {}};
n_psp = n_psp_base;
subl_channels = Subleading::uno;
allowed_types = {event_type::FKL, event_type::unob, event_type::unof};
type_counter.clear();
for( size_t i = 0; i<n_psp; ++i){
const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
boson_decays, ew_constants, ran};
if(psp.status()==good){
const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt) };
++type_counter[ev.type()];
if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
== allowed_types.cend()) {
bail_out(psp, "Found not allowed event of type "
+std::string(event_type::name(ev.type())));
}
} else { // bad process -> try again
++n_psp;
}
}
std::cout << "Wm+3j (only uno): Took " << n_psp << " to generate "
<< n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
std::cout << "States by classification:\n";
for(auto const & entry: type_counter){
const double fraction = static_cast<double>(entry.second)/n_psp_base;
const int percent = std::round(100*fraction);
std::cout << std::left << std::setw(25)
<< (event_type::name(entry.first) + std::string(":"))
<< entry.second << " (" << percent << "%)\n";
}
for(auto const & t: allowed_types){
if(type_counter[t] < 0.05 * n_psp_base){
std::cerr << "Less than 5% of the events are of type " << event_type::name(t) << std::endl;
return EXIT_FAILURE;
}
}
// Wm4j
proc = Process{{pid::proton,pid::proton}, 4, pid::Wm, {}};
n_psp = n_psp_base;
subl_channels = Subleading::all;
allowed_types = {event_type::FKL,
event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf,
event_type::qqxmid};
type_counter.clear();
for( size_t i = 0; i<n_psp; ++i){
const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
boson_decays, ew_constants, ran};
if(psp.status()==good){
const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt)};
++type_counter[ev.type()];
if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
== allowed_types.cend()) {
bail_out(psp, "Found not allowed event of type "
+std::string(event_type::name(ev.type())));
}
} else { // bad process -> try again
++n_psp;
}
}
std::cout << "Wm+4j: Took " << n_psp << " to generate "
<< n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
std::cout << "States by classification:\n";
for(auto const & entry: type_counter){
const double fraction = static_cast<double>(entry.second)/n_psp_base;
const int percent = std::round(100*fraction);
std::cout << std::left << std::setw(25)
<< (event_type::name(entry.first) + std::string(":"))
<< entry.second << " (" << percent << "%)\n";
}
for(auto const & t: allowed_types){
if(type_counter[t] < 0.03 * n_psp_base){
std::cerr << "Less than 3% of the events are of type " << event_type::name(t) << std::endl;
return EXIT_FAILURE;
}
}
std::cout << "All processes passed." << std::endl;
return EXIT_SUCCESS;
}
diff --git a/FixedOrderGen/t/W_reconstruct_enu.cc b/FixedOrderGen/t/W_reconstruct_enu.cc
index 7b655ec..b20abaa 100644
--- a/FixedOrderGen/t/W_reconstruct_enu.cc
+++ b/FixedOrderGen/t/W_reconstruct_enu.cc
@@ -1,72 +1,79 @@
/**
* \brief that the reconstruction of the W works
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
-#include <algorithm>
-
-#include "config.hh"
-#include "EventGenerator.hh"
+#include <assert.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <stdlib.h>
#include "HEJ/Event.hh"
#include "HEJ/Mixmax.hh"
+#include "HEJ/ScaleFunction.hh"
+
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
-using namespace HEJFOG;
-using namespace HEJ;
namespace {
+ using namespace HEJFOG;
+ using namespace HEJ;
+
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 1a33407..edc7900 100644
--- a/FixedOrderGen/t/h_2j.cc
+++ b/FixedOrderGen/t/h_2j.cc
@@ -1,75 +1,78 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
-#include <cmath>
#include <cassert>
+#include <cmath>
#include <iostream>
-
-#include "config.hh"
-#include "EventGenerator.hh"
-#include "HEJ/Mixmax.hh"
+#include <memory>
+#include <stdlib.h>
#include "HEJ/Event.hh"
-#include "HEJ/PDF.hh"
-#include "HEJ/MatrixElement.hh"
+#include "HEJ/Mixmax.hh"
+#include "HEJ/PDG_codes.hh"
+#include "HEJ/ScaleFunction.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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 72be5e4..1604e8c 100644
--- a/FixedOrderGen/t/h_2j_decay.cc
+++ b/FixedOrderGen/t/h_2j_decay.cc
@@ -1,94 +1,101 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
-#include <cmath>
#include <cassert>
+#include <cmath>
#include <iostream>
+#include <stdlib.h>
+#include <vector>
-#include "config.hh"
-#include "EventGenerator.hh"
+#include "fastjet/PseudoJet.hh"
#include "HEJ/Event.hh"
-#include "HEJ/MatrixElement.hh"
#include "HEJ/Particle.hh"
-#include "HEJ/PDF.hh"
+#include "HEJ/PDG_codes.hh"
#include "HEJ/Ranlux64.hh"
+#include "HEJ/ScaleFunction.hh"
#include "HEJ/utility.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
+
+namespace {
+ 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;
+ 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;
}
- 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());
+ const auto decay = ev->decays().begin();
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 7d32f28..bca6eed 100644
--- a/FixedOrderGen/t/h_3j.cc
+++ b/FixedOrderGen/t/h_3j.cc
@@ -1,76 +1,79 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
-#include <cmath>
#include <cassert>
+#include <cmath>
#include <iostream>
-
-#include "config.hh"
-#include "EventGenerator.hh"
-#include "HEJ/Ranlux64.hh"
+#include <memory>
+#include <stdlib.h>
#include "HEJ/Event.hh"
-#include "HEJ/MatrixElement.hh"
-#include "HEJ/PDF.hh"
+#include "HEJ/PDG_codes.hh"
+#include "HEJ/Ranlux64.hh"
+#include "HEJ/ScaleFunction.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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 df5aae2..3bbcfa0 100644
--- a/FixedOrderGen/t/h_3j_uno1.cc
+++ b/FixedOrderGen/t/h_3j_uno1.cc
@@ -1,81 +1,85 @@
/**
* check that adding uno emissions doesn't change the FKL cross section
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
+#include <memory>
+#include <stdlib.h>
+
+#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+#include "HEJ/PDG_codes.hh"
+#include "HEJ/Ranlux64.hh"
+#include "HEJ/ScaleFunction.hh"
#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;
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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 13e1ae9..eb60ba8 100644
--- a/FixedOrderGen/t/h_3j_uno2.cc
+++ b/FixedOrderGen/t/h_3j_uno2.cc
@@ -1,75 +1,79 @@
/**
* check uno cross section
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
+#include <memory>
+#include <stdlib.h>
+
+#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+#include "HEJ/PDG_codes.hh"
+#include "HEJ/Ranlux64.hh"
+#include "HEJ/ScaleFunction.hh"
#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;
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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 e852111..b7632b9 100644
--- a/FixedOrderGen/t/h_5j.cc
+++ b/FixedOrderGen/t/h_5j.cc
@@ -1,72 +1,74 @@
/**
* 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-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 <memory>
+#include <stdlib.h>
#include "HEJ/Event.hh"
-#include "HEJ/MatrixElement.hh"
-#include "HEJ/PDF.hh"
+#include "HEJ/Ranlux64.hh"
+#include "HEJ/ScaleFunction.hh"
-using namespace HEJFOG;
+#include "config.hh"
+#include "EventGenerator.hh"
+#include "Status.hh"
int main(){
+ using namespace HEJFOG;
+
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/cmake/Templates/HEJ-config.cc.in b/cmake/Templates/HEJ-config.cc.in
index e756541..d4ad38d 100644
--- a/cmake/Templates/HEJ-config.cc.in
+++ b/cmake/Templates/HEJ-config.cc.in
@@ -1,59 +1,66 @@
+/**
+ * \authors The HEJ collaboration (see AUTHORS for details)
+ * \date 2019-2020
+ * \copyright GPLv2 or later
+ */
#include <iostream>
+#include <stdlib.h>
+#include <string>
#include "cxxopts.hpp"
int main(int argc, char** argv) {
cxxopts::Options options{
argv[0],
"Configuration of the @CMAKE_PROJECT_NAME@ installation"
};
options.add_options()
("h,help", "show this help message and exit")
("prefix", "show @CMAKE_PROJECT_NAME@ installation prefix")
("includedir", "show the path with the installed @CMAKE_PROJECT_NAME@ header files")
("bindir", "show the path with the installed @CMAKE_PROJECT_NAME@ executable")
("libdir", "show the path with the installed @CMAKE_PROJECT_NAME@ library file")
("libs", "show flags for linking the @CMAKE_PROJECT_NAME@ library")
("cxxflags", "show the compiler flags for the @CMAKE_PROJECT_NAME@ headers")
("cxx", "show the compiler used to build @CMAKE_PROJECT_NAME@")
("version", "show the installed @CMAKE_PROJECT_NAME@ version")
("revision", "show the hash of the installed @CMAKE_PROJECT_NAME@ git revision")
;
if(argc == 1) {
std::cout << options.help();
return EXIT_SUCCESS;
}
try {
const auto opts = options.parse(argc, argv);
if(opts.count("help")) {
std::cout << options.help();
return EXIT_SUCCESS;
}
for(auto const & op: opts.arguments()){
if(op.key()=="prefix")
std::cout << "@CMAKE_INSTALL_PREFIX@";
else if(op.key()=="includedir")
std::cout << "@CMAKE_INSTALL_PREFIX@/@INSTALL_INCLUDE_DIR_BASE@";
else if(op.key()=="bindir")
std::cout << "@CMAKE_INSTALL_PREFIX@/@INSTALL_BIN_DIR@";
else if(op.key()=="libdir")
std::cout << "@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB_DIR@";
else if(op.key()=="libs")
std::cout << "-L@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB_DIR@ -lHEJ";
else if(op.key()=="cxxflags")
std::cout << "-I@CMAKE_INSTALL_PREFIX@/@INSTALL_INCLUDE_DIR_BASE@";
else if(op.key()=="cxx")
std::cout << "@CMAKE_CXX_COMPILER@";
else if(op.key()=="version")
std::cout << "@PROJECT_VERSION@";
else if(op.key()=="revision")
std::cout << "@PROJECT_GIT_REVISION@";
std::cout << " ";
}
std::cout << std::endl;
}
catch(cxxopts::OptionException const &) {
std::cout << options.help();
}
}
diff --git a/include/HEJ/BufferedEventReader.hh b/include/HEJ/BufferedEventReader.hh
index 331fb48..b921e0b 100644
--- a/include/HEJ/BufferedEventReader.hh
+++ b/include/HEJ/BufferedEventReader.hh
@@ -1,66 +1,69 @@
/** \file
* \brief Event reader with internal buffer
*
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <memory>
#include <stack>
+#include <stddef.h>
#include <string>
+#include <utility>
#include <vector>
#include "LHEF/LHEF.h"
#include "HEJ/EventReader.hh"
+#include "HEJ/optional.hh"
namespace HEJ {
//! Event reader with internal buffer
/**
* Having a buffer makes it possible to put events back into the reader
* so that they are read again
*/
class BufferedEventReader: public EventReader {
public:
explicit BufferedEventReader(std::unique_ptr<EventReader> reader):
reader_{std::move(reader)}
{}
//! Read an event
bool read_event() override;
//! Access header text
std::string const & header() const override {
return reader_->header();
}
//! Access run information
LHEF::HEPRUP const & heprup() const override {
return reader_->heprup();
}
//! Access last read event
LHEF::HEPEUP const & hepeup() const override {
return cur_event_;
}
//! Guess number of events from header
HEJ::optional<size_t> number_events() const override {
return reader_->number_events();
}
//! Push event back into reader
template< class... T>
void emplace(T&&... args) {
buffer_.emplace(std::forward<T>(args)...);
}
private:
std::stack<LHEF::HEPEUP, std::vector<LHEF::HEPEUP>> buffer_;
std::unique_ptr<EventReader> reader_;
LHEF::HEPEUP cur_event_;
};
} // namespace HEJ
diff --git a/include/HEJ/CombinedEventWriter.hh b/include/HEJ/CombinedEventWriter.hh
index 02e5fef..411639d 100644
--- a/include/HEJ/CombinedEventWriter.hh
+++ b/include/HEJ/CombinedEventWriter.hh
@@ -1,44 +1,45 @@
/** \file
* \brief Declares the CombinedEventWriter 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 "HEJ/EventWriter.hh"
-#include "HEJ/output_formats.hh"
namespace LHEF {
struct HEPRUP;
}
namespace HEJ {
+ class Event;
+ struct OutputFile;
//! Write event output to zero or more output files.
class CombinedEventWriter: public EventWriter {
public:
//! Constructor
/**
* @param outfiles Specifies files output should be written to.
* Each entry in the vector contains a file name
* and output format.
* @param heprup General process information
*/
CombinedEventWriter(
std::vector<OutputFile> const & outfiles,
LHEF::HEPRUP const & heprup
);
//! Write one event to all output files
void write(Event const &) override;
private:
std::vector<std::unique_ptr<EventWriter>> writers_;
};
}
diff --git a/include/HEJ/EmptyAnalysis.hh b/include/HEJ/EmptyAnalysis.hh
index 7715a63..824c328 100644
--- a/include/HEJ/EmptyAnalysis.hh
+++ b/include/HEJ/EmptyAnalysis.hh
@@ -1,49 +1,53 @@
/** \file
* \brief Declaration of the trivial (empty) analysis
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <memory>
#include "HEJ/Analysis.hh"
-//! YAML Namespace
namespace YAML {
class Node;
}
+namespace LHEF {
+ class HEPRUP;
+}
namespace HEJ {
+ class Event;
+
/** 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 a15dbe4..2947a65 100644
--- a/include/HEJ/Event.hh
+++ b/include/HEJ/Event.hh
@@ -1,369 +1,371 @@
/** \file
* \brief Declares the Event class and helpers
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
-#include <memory>
-#include <string>
+#include <iosfwd>
+#include <iterator>
+#include <stddef.h>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "boost/iterator/filter_iterator.hpp"
#include "fastjet/ClusterSequence.hh"
+#include "fastjet/PseudoJet.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 RNG;
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 5a81752..9544b9a 100644
--- a/include/HEJ/EventReader.hh
+++ b/include/HEJ/EventReader.hh
@@ -1,57 +1,60 @@
/** \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-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <memory>
+#include <stddef.h>
#include <string>
-#include "LHEF/LHEF.h"
-
#include "HEJ/optional.hh"
+namespace LHEF {
+ class HEPEUP;
+ class HEPRUP;
+}
+
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 59a69a1..c355950 100644
--- a/include/HEJ/EventReweighter.hh
+++ b/include/HEJ/EventReweighter.hh
@@ -1,196 +1,198 @@
/** \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-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
#include <memory>
+#include <stddef.h>
+#include <utility>
#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 5e8a29b..25bcc3b 100644
--- a/include/HEJ/HDF5Reader.hh
+++ b/include/HEJ/HDF5Reader.hh
@@ -1,50 +1,51 @@
/** \file
* \brief Header file for reading events in the HDF5 event format.
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <string>
+#include <memory>
#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/HepMC2Interface.hh b/include/HEJ/HepMC2Interface.hh
index 6e7889a..ffc9fc2 100644
--- a/include/HEJ/HepMC2Interface.hh
+++ b/include/HEJ/HepMC2Interface.hh
@@ -1,79 +1,78 @@
/** \file
* \brief Header file for the HepMC2Interface
*
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
+#include <stddef.h>
#include <sys/types.h>
-#include <vector>
#include "HEJ/PDG_codes.hh"
namespace HepMC {
class GenCrossSection;
class GenEvent;
}
namespace LHEF {
class HEPRUP;
}
namespace HEJ {
class Event;
- class EventParameters;
//! This class converts the Events into HepMC::GenEvents
/**
* \details The output is depended on the HepMC version HEJ is compiled with,
* both HepMC 2 and HepMC 3 are supported. If HEJ 2 is compiled
* without HepMC calling this interface will throw an error.
*
* This interface will also keep track of the cross section of all the events that
* being fed into it.
*/
class HepMC2Interface{
public:
HepMC2Interface(LHEF::HEPRUP const &);
~HepMC2Interface();
/**
* \brief main function to convert an event into HepMC::GenEvent
*
* \param event Event to convert
* \param weight_index optional selection of specific weight
* (negative value gives central weight)
*/
HepMC::GenEvent operator()(Event const & event, ssize_t weight_index = -1);
/**
* \brief Sets the central value from \p event to \p out_ev
*
* \param out_ev HepMC::GenEvent to write to
* \param event Event to convert
* \param weight_index optional selection of specific weight
* (negative value gives "central")
*
* This overwrites the central value of \p out_ev.
*/
void set_central(HepMC::GenEvent & out_ev, Event const & event,
ssize_t weight_index = -1);
protected:
/**
* \brief initialise generic event infomrations (not central weights)
*
* \param event Event to convert
*/
HepMC::GenEvent init_event(Event const & event) const;
private:
std::array<ParticleID,2> beam_particle_;
std::array<double,2> beam_energy_;
size_t event_count_;
double tot_weight_;
double tot_weight2_;
HepMC::GenCrossSection cross_section() const;
};
} // namespace HEJ
diff --git a/include/HEJ/HepMC3Interface.hh b/include/HEJ/HepMC3Interface.hh
index 780c35c..21673d9 100644
--- a/include/HEJ/HepMC3Interface.hh
+++ b/include/HEJ/HepMC3Interface.hh
@@ -1,84 +1,83 @@
/** \file
* \brief Header file for the HepMC3Interface
*
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
#include <memory>
+#include <stddef.h>
#include <sys/types.h>
-#include <vector>
#include "HEJ/PDG_codes.hh"
namespace HepMC3 {
class GenCrossSection;
class GenEvent;
class GenRunInfo;
}
namespace LHEF {
class HEPRUP;
}
namespace HEJ {
class Event;
- class EventParameters;
//! This class converts the Events into HepMC3::GenEvents
/**
* \details The output is depended on the HepMC3 version HEJ is compiled with,
* both HepMC3 2 and HepMC3 3 are supported. If HEJ 2 is compiled
* without HepMC3 calling this interface will throw an error.
*
* This interface will also keep track of the cross section of all the events that
* being fed into it.
*/
class HepMC3Interface{
public:
HepMC3Interface(LHEF::HEPRUP const &);
~HepMC3Interface();
/**
* \brief main function to convert an event into HepMC3::GenEvent
*
* \param event Event to convert
* \param weight_index optional selection of specific weight
* (negative value gives central weight)
*/
HepMC3::GenEvent operator()(Event const & event, ssize_t weight_index = -1);
/**
* \brief Sets the central value from \p event to \p out_ev
*
* \param out_ev HepMC3::GenEvent to write to
* \param event Event to convert
* \param weight_index optional selection of specific weight
* (negative value gives "central")
*
* This overwrites the central value of \p out_ev.
*/
void set_central(HepMC3::GenEvent & out_ev, Event const & event,
ssize_t weight_index = -1);
//! Pointer to generic run informations
std::shared_ptr<HepMC3::GenRunInfo> run_info;
protected:
/**
* \brief initialise generic event infomrations (not central weights)
*
* \param event Event to convert
*/
HepMC3::GenEvent init_event(Event const & event) const;
private:
std::array<ParticleID,2> beam_particle_;
std::array<double,2> beam_energy_;
size_t event_count_;
double tot_weight_;
double tot_weight2_;
std::shared_ptr<HepMC3::GenCrossSection> xs_;
};
} // namespace HEJ
diff --git a/include/HEJ/JetSplitter.hh b/include/HEJ/JetSplitter.hh
index 69a2985..54deda8 100644
--- a/include/HEJ/JetSplitter.hh
+++ b/include/HEJ/JetSplitter.hh
@@ -1,71 +1,67 @@
/**
* \file
* \brief Declaration of the JetSplitter class
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
-#include <memory>
#include <vector>
#include "fastjet/JetDefinition.hh"
-
-namespace fastjet {
- class PseudoJet;
-}
+#include "fastjet/PseudoJet.hh"
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 83732d3..8f11749 100644
--- a/include/HEJ/LesHouchesReader.hh
+++ b/include/HEJ/LesHouchesReader.hh
@@ -1,83 +1,82 @@
/** \file
* \brief Header file for reading events in the Les Houches Event File format.
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 7f460f8..bae214d 100644
--- a/include/HEJ/LorentzVector.hh
+++ b/include/HEJ/LorentzVector.hh
@@ -1,55 +1,56 @@
/** \file
* \brief Auxiliary functions for Lorentz vectors
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <complex>
+#include <utility>
#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/PDF.hh b/include/HEJ/PDF.hh
index 5a81b6d..9b188cc 100644
--- a/include/HEJ/PDF.hh
+++ b/include/HEJ/PDF.hh
@@ -1,75 +1,76 @@
/** \file
*
* \brief Contains all the necessary classes and functions for interaction with PDFs.
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
#include <memory>
+#include <stddef.h>
#include "LHAPDF/LHAPDF.h"
#include "HEJ/PDG_codes.hh"
namespace HEJ{
//! Class for interaction with a PDF set
class PDF {
public:
/**
* \brief PDF Constructor
* @param id Particle ID according to PDG
* @param beam1 Particle ID of particle in beam 1
* @param beam2 Particle ID of particle in beam 2
*/
PDF(int id, ParticleID beam1, ParticleID beam2);
/**
* \brief Calculate the pdf value x*f(x, q)
* @param beam_idx Beam number (0 or 1)
* @param x Momentum fraction
* @param q Energy scale
* @param id PDG particle id
* @returns x*f(x, q)
*
* Returns 0 if x or q are outside the range covered by the PDF set
*/
double pdfpt(size_t beam_idx, double x, double q, ParticleID id) const;
/**
* \brief Value of the strong coupling \f$\alpha_s(q)\f$ at the given scale
* @param q Renormalisation scale
* @returns Value of the strong coupling constant
*/
double Halphas(double q) const;
//! Check if the energy scale is within the range covered by the PDF set
/**
* @param q Energy Scale
* @returns true if q is within the covered range, false otherwise
*/
bool inRangeQ(double q) const;
//! Check if the momentum fraction is within the range covered by the PDF set
/**
* @param x Momentum Fraction
* @returns true if x is within the covered range, false otherwise
*/
bool inRangeX(double x) const;
#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
//! PDF id of the current set
int id() const;
#endif
private:
#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
std::unique_ptr<LHAPDF::PDF> pdf;
#endif
std::array<int, 2> beamtype;
};
} // namespace HEJ
diff --git a/include/HEJ/PDG_codes.hh b/include/HEJ/PDG_codes.hh
index da923f7..8725a6a 100644
--- a/include/HEJ/PDG_codes.hh
+++ b/include/HEJ/PDG_codes.hh
@@ -1,216 +1,217 @@
/** \file PDG_codes.hh
* \brief Contains the Particle IDs of all relevant SM particles.
*
* Large enumeration included which has multiple entries for potential
* alternative names of different particles. There are also functions
* which can be used to determine if a particle is a parton or if
* it is a non-gluon boson.
*
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <string>
+#include <cstdlib>
namespace HEJ {
//! particle ids according to PDG
namespace pid {
//! The possible particle identities. We use PDG IDs as standard.
enum ParticleID: int{
d = 1, /*!< Down Quark */
down = d, /*!< Down Quark */
u = 2, /*!< Up Quark */
up = u, /*!< Up Quark */
s = 3, /*!< Strange Quark */
strange = s, /*!< Strange Quark */
c = 4, /*!< Charm Quark */
charm = c, /*!< Charm Quark */
b = 5, /*!< Bottom Quark */
bottom = b, /*!< Bottom Quark */
t = 6, /*!< Top Quark */
top = t, /*!< Top Quark */
e = 11, /*!< Electron */
electron = e, /*!< Electron */
nu_e = 12, /*!< Electron Neutrino */
electron_neutrino = nu_e, /*!< Electron neutrino */
mu = 13, /*!< Muon */
muon = mu, /*!< Muon */
nu_mu = 14, /*!< Muon Neutrino */
muon_neutrino = nu_mu, /*!< Muon Neutrino */
tau = 15, /*!< Tau */
nu_tau = 16, /*!< Tau Neutrino */
tau_neutrino = nu_tau, /*!< Tau Neutrino */
d_bar = -d, /*!< Anti-Down Quark */
antidown = d_bar, /*!< Anti-Down Quark */
u_bar = -u, /*!< Anti-Up quark */
antiup = -u, /*!< Anti-Up quark */
s_bar = -s, /*!< Anti-Strange Quark */
antistrange = -s, /*!< Anti-Strange Quark */
c_bar = -c, /*!< Anti-Charm Quark */
anticharm = -c, /*!< Anti-Charm Quark */
b_bar = -b, /*!< Anti-Bottom Quark */
antibottom = -b, /*!< Anti-Bottom Quark */
t_bar = -t, /*!< Anti-Top Quark */
antitop = -t, /*!< Anti-Top Quark */
e_bar = -e, /*!< Positron */
positron = e_bar, /*!< Positron */
antielectron = positron, /*!< Positron */
nu_e_bar = -nu_e, /*!< Electron Anti-Neutrino */
electron_antineutrino = nu_e_bar,/*!< Electron Anti-Neutrino */
mu_bar = -mu, /*!< Anti-Muon */
antimuon = -mu, /*!< Anti-Muon */
nu_mu_bar = -nu_mu, /*!< Muon Anti-Neutrino */
muon_antineutrino = nu_mu_bar, /*!< Muon Anti-Neutrino */
tau_bar = -tau, /*!< Anti-Tau */
antitau = tau_bar, /*!< Anti-Tau */
nu_tau_bar = -nu_tau, /*!< Tau Anti-Neutrino */
tau_antineutrino = nu_tau_bar, /*!< Tau Anti-Neutrino */
gluon = 21, /*!< Gluon */
g = gluon, /*!< Gluon */
photon = 22, /*!< Photon */
gamma = photon, /*!< Photon */
Z = 23, /*!< Z Boson */
Wp = 24, /*!< W- Boson */
Wm = -Wp, /*!< W+ Boson */
h = 25, /*!< Higgs Boson */
Higgs = h, /*!< Higgs Boson */
higgs = h, /*!< Higgs Boson */
p = 2212, /*!< Proton */
proton = p, /*!< Proton */
p_bar = -p, /*!< Anti-Proton */
antiproton = p_bar, /*!< Anti-Proton */
};
} // namespace pid
using ParticleID = pid::ParticleID;
//! Convert a particle name to the corresponding PDG particle ID
ParticleID to_ParticleID(std::string const & name);
//! Get the of the particle with the given PDG ID
std::string name(ParticleID id);
/**
* \brief Function to determine if particle is a parton
* @param id PDG ID of particle
* @returns true if the particle is a parton, false otherwise
*/
inline
constexpr bool is_parton(ParticleID id){
return id == pid::gluon || std::abs(id) <= pid::top;
}
/**
* \brief Function to determine if particle is a quark
* @param id PDG ID of particle
* @returns true if the particle is a quark, false otherwise
*/
inline
constexpr bool is_quark(ParticleID id){
return (id >= pid::down && id <= pid::top);
}
/**
* \brief Function to determine if particle is an antiquark
* @param id PDG ID of particle
* @returns true if the particle is an antiquark, false otherwise
*/
inline
constexpr bool is_antiquark(ParticleID id){
return (id <= pid::d_bar && id >= pid::t_bar);
}
/**
* \brief Function to determine if particle is an (anti-)quark
* @param id PDG ID of particle
* @returns true if the particle is a quark or antiquark, false otherwise
*/
inline
constexpr bool is_anyquark(ParticleID id){
return (id && id >= pid::t_bar && id <= pid::t);
}
/**
* \brief function to determine if the particle is a photon, W, Z, or Higgs boson
* @param id PDG ID of particle
* @returns true if the partice is an A,W,Z, or H, false otherwise
*/
inline
constexpr bool is_AWZH_boson(ParticleID id){
return id == pid::Wm || (id >= pid::photon && id <= pid::Higgs);
}
/**
* \brief function to determine if the particle is a photon, W or Z
* @param id PDG ID of particle
* @returns true if the partice is an A,W,Z, or H, false otherwise
*/
inline
constexpr bool is_AWZ_boson(ParticleID id){
return id == pid::Wm || (id >= pid::photon && id <= pid::Wp);
}
/**
* \brief Function to determine if particle is a lepton
* @param id PDG ID of particle
* @returns true if the particle is a lepton, false otherwise
*/
inline
constexpr bool is_lepton(ParticleID id){
return (id >= pid::electron && id <= pid::tau_neutrino);
}
/**
* \brief Function to determine if particle is an antilepton
* @param id PDG ID of particle
* @returns true if the particle is an antilepton, false otherwise
*/
inline
constexpr bool is_antilepton(ParticleID id){
return (id <= pid::positron && id >= pid::nu_tau_bar);
}
/**
* \brief Function to determine if particle is an (anti-)lepton
* @param id PDG ID of particle
* @returns true if the particle is a lepton or antilepton, false otherwise
*/
inline
constexpr bool is_anylepton(ParticleID id){
return ( is_lepton(id) || is_antilepton(id));
}
/**
* \brief Function to determine if particle is a neutrino
* @param id PDG ID of particle
* @returns true if the particle is a neutrino, false otherwise
*/
inline
constexpr bool is_neutrino(ParticleID id){
return (id == pid::nu_e || id == pid::tau_neutrino || id == pid::muon_neutrino);
}
/**
* \brief Function to determine if particle is an antineutrino
* @param id PDG ID of particle
* @returns true if the particle is an antineutrino, false otherwise
*/
inline
constexpr bool is_antineutrino(ParticleID id){
return (id == pid::nu_e_bar || id == pid::nu_tau_bar || id == pid::nu_mu_bar);
}
/**
* \brief Function to determine if particle is an (anti-)neutrino
* @param id PDG ID of particle
* @returns true if the particle is a neutrino or antineutrino, false otherwise
*/
inline
constexpr bool is_anyneutrino(ParticleID id){
return ( is_neutrino(id)||is_antineutrino(id));
}
} // namespace HEJ
diff --git a/include/HEJ/Parameters.hh b/include/HEJ/Parameters.hh
index d10bed3..ef497d8 100644
--- a/include/HEJ/Parameters.hh
+++ b/include/HEJ/Parameters.hh
@@ -1,164 +1,166 @@
/** \file
* \brief Containers for Parameter variations, e.g. different Weights
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <memory>
+#include <stddef.h>
#include <string>
+#include <utility>
#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) {
+ for(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) {
+ for(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 0359e2b..e79b950 100644
--- a/include/HEJ/PhaseSpacePoint.hh
+++ b/include/HEJ/PhaseSpacePoint.hh
@@ -1,202 +1,205 @@
/** \file
* \brief Contains the PhaseSpacePoint Class
*
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
#include <array>
+#include <stddef.h>
#include <unordered_map>
#include <vector>
+#include "fastjet/PseudoJet.hh"
+
#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/src/CombinedEventWriter.cc b/src/CombinedEventWriter.cc
index ff99b96..77c5432 100644
--- a/src/CombinedEventWriter.cc
+++ b/src/CombinedEventWriter.cc
@@ -1,28 +1,29 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/CombinedEventWriter.hh"
#include "HEJ/make_writer.hh"
+#include "HEJ/output_formats.hh"
namespace HEJ{
CombinedEventWriter::CombinedEventWriter(
std::vector<OutputFile> const & outfiles,
LHEF::HEPRUP const & heprup
){
writers_.reserve(outfiles.size());
for(OutputFile const & outfile: outfiles){
writers_.emplace_back(
make_format_writer(outfile.format, outfile.name, heprup)
);
}
}
void CombinedEventWriter::write(Event const & ev){
for(auto & writer: writers_) writer->write(ev);
}
}
diff --git a/src/CrossSectionAccumulator.cc b/src/CrossSectionAccumulator.cc
index 1cf82c9..b4e02cf 100644
--- a/src/CrossSectionAccumulator.cc
+++ b/src/CrossSectionAccumulator.cc
@@ -1,64 +1,67 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/CrossSectionAccumulator.hh"
#include <iomanip>
+#include <math.h>
#include <string>
+#include <utility>
#include "HEJ/Event.hh"
+#include "HEJ/Parameters.hh"
namespace HEJ {
void CrossSectionAccumulator::fill( double const wt, double const err,
event_type::EventType const type
){
total_.value += wt;
total_.error += err;
auto & entry = xs_[type];
entry.value += wt;
entry.error += err;
}
void CrossSectionAccumulator::fill(
double const wt, event_type::EventType const type
){
fill(wt, wt*wt, type);
}
void CrossSectionAccumulator::fill(Event const & ev){
fill(ev.central().weight, ev.type());
}
void CrossSectionAccumulator::fill_correlated(
double const sum, double const sum2, event_type::EventType const type
){
fill(sum, sum*sum+sum2, type);
}
void CrossSectionAccumulator::fill_correlated(std::vector<Event> const & evts){
if(evts.empty()) return;
fill_correlated(evts.cbegin(), evts.cend());
}
std::ostream& operator<<(std::ostream& os, const CrossSectionAccumulator& xs){
const std::streamsize orig_prec = os.precision();
os << std::scientific << std::setprecision(3)
<< " " << std::left << std::setw(25)
<< "Cross section: " << xs.total().value
<< " +- " << std::sqrt(xs.total().error) << " (pb)\n";
for(auto const & xs_type: xs) {
os << " " << std::left << std::scientific <<std::setw(25)
<< (event_type::name(xs_type.first) + std::string(": "));
os << xs_type.second.value << " +- "
<< std::sqrt(xs_type.second.error) << " (pb) "
<< std::fixed << std::setprecision(3)
<< "[" <<std::setw(6) <<std::right
<< ((xs_type.second.value)/xs.total().value)*100 << "%]"
<< std::endl;
}
os << std::defaultfloat;
os.precision(orig_prec);
return os;
}
}
diff --git a/src/Event.cc b/src/Event.cc
index b0a569d..6862b0f 100644
--- a/src/Event.cc
+++ b/src/Event.cc
@@ -1,1130 +1,1138 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/Event.hh"
#include <algorithm>
#include <assert.h>
+#include <iomanip>
#include <iterator>
+#include <memory>
#include <numeric>
-#include <unordered_set>
+#include <ostream>
+#include <stdlib.h>
+#include <string>
#include <utility>
-#include "LHEF/LHEF.h"
-
+#include "fastjet/ClusterSequence.hh"
#include "fastjet/JetDefinition.hh"
+#include "fastjet/PseudoJet.hh"
+
+#include "LHEF/LHEF.h"
#include "HEJ/Constants.hh"
#include "HEJ/exceptions.hh"
+#include "HEJ/optional.hh"
#include "HEJ/PDG_codes.hh"
+#include "HEJ/RNG.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 dccff81..d360ab4 100644
--- a/src/EventReader.cc
+++ b/src/EventReader.cc
@@ -1,73 +1,77 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/EventReader.hh"
-#include "HEJ/HDF5Reader.hh"
+#include <iostream>
+#include <stdexcept>
+
+#include "LHEF/LHEF.h"
+
#include "HEJ/ConfigFlags.hh"
+#include "HEJ/HDF5Reader.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 54b8491..d3fdb10 100644
--- a/src/EventReweighter.cc
+++ b/src/EventReweighter.cc
@@ -1,256 +1,257 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/EventReweighter.hh"
#include <algorithm>
#include <assert.h>
-#include <limits>
+#include <functional>
#include <math.h>
#include <stddef.h>
#include <string>
#include <unordered_map>
#include <utility>
#include "fastjet/ClusterSequence.hh"
+#include "fastjet/PseudoJet.hh"
#include "LHEF/LHEF.h"
+#include "HEJ/Fraction.hh"
#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 32c73ac..e86ebb7 100644
--- a/src/HDF5Reader.cc
+++ b/src/HDF5Reader.cc
@@ -1,306 +1,322 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/HDF5Reader.hh"
#include "HEJ/ConfigFlags.hh"
#ifdef HEJ_BUILD_WITH_HDF5
-#include <numeric>
+#include <algorithm>
+#include <assert.h>
+#include <cstddef>
+#include <functional>
#include <iterator>
+#include <numeric>
+#include <utility>
+#include <vector>
#include "highfive/H5File.hpp"
+#include "LHEF/LHEF.h"
+
+#else
+
+#include "HEJ/exceptions.hh"
+
+#endif
+
+#ifdef HEJ_BUILD_WITH_HDF5
+
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 59b93c3..59b084a 100644
--- a/src/HDF5Writer.cc
+++ b/src/HDF5Writer.cc
@@ -1,404 +1,416 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 <cmath>
#include <iterator>
+#include <stddef.h>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "highfive/H5File.hpp"
-#include "HEJ/event_types.hh"
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
-#include "highfive/H5File.hpp"
+#else
+
+#include "HEJ/exceptions.hh"
+
+#endif
+
+#ifdef HEJ_BUILD_WITH_HDF5
namespace HEJ{
using HighFive::File;
using HighFive::DataSpace;
namespace{
- constexpr std::size_t chunk_size = 1000;
+ constexpr 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;
+ return type==0?0:std::floor(std::log2(static_cast<size_t>(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_{std::make_unique<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 e63f1d1..5f5e602 100644
--- a/src/HepMC2Interface.cc
+++ b/src/HepMC2Interface.cc
@@ -1,156 +1,159 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 <assert.h>
#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"
+#include "HepMC/SimpleVector.h"
+#include "HepMC/Units.h"
+
+#include "HEJ/detail/HepMCInterface_common.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/Particle.hh"
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 4e21411..f83ff01 100644
--- a/src/HepMC2Writer.cc
+++ b/src/HepMC2Writer.cc
@@ -1,82 +1,85 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 <utility>
+
#include "HepMC/IO_GenEvent.h"
-#include <utility>
+#include "HEJ/HepMC2Interface.hh"
-#include "HepMC/GenParticle.h"
-#include "HepMC/GenVertex.h"
+#else
+
+#include <cassert>
-#include "HEJ/Event.hh"
#include "HEJ/exceptions.hh"
-#include "HEJ/HepMC2Interface.hh"
+
+#endif
+
+#ifdef HEJ_BUILD_WITH_HepMC2
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_{std::make_unique<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 84beaee..1bef781 100644
--- a/src/HepMC3Interface.cc
+++ b/src/HepMC3Interface.cc
@@ -1,203 +1,214 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 <assert.h>
+#include <cmath>
+#include <string>
#include <utility>
+#include <vector>
-#include "HEJ/detail/HepMCInterface_common.hh"
-#include "HEJ/Event.hh"
-#include "HEJ/Particle.hh"
-
+// include before HepMC3 to override include of "HepMC3/LHEF.h"
+// (theoretically both files should be the same)
#include "LHEF/LHEF.h"
+#include "HepMC3/Attribute.h"
+#include "HepMC3/FourVector.h"
#include "HepMC3/GenCrossSection.h"
+#include "HepMC3/GenCrossSection_fwd.h"
#include "HepMC3/GenEvent.h"
#include "HepMC3/GenParticle.h"
+#include "HepMC3/GenParticle_fwd.h"
#include "HepMC3/GenRunInfo.h"
#include "HepMC3/GenVertex.h"
#include "HepMC3/LHEFAttributes.h"
+#include "HepMC3/Units.h"
+
+#include "HEJ/detail/HepMCInterface_common.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/Parameters.hh"
+#include "HEJ/Particle.hh"
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_));
+ xs_->set_cross_section(tot_weight_,std::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 b8ae99a..f2d4025 100644
--- a/src/HepMC3Writer.cc
+++ b/src/HepMC3Writer.cc
@@ -1,94 +1,101 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \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 "HepMC3/Attribute.h"
+#include "HepMC3/WriterAscii.h"
+
#include "HEJ/HepMC3Interface.hh"
+#else
+
+#include <cassert>
+
+#include "HEJ/exceptions.hh"
+
+#endif
+
+#ifdef HEJ_BUILD_WITH_HepMC3
+
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 82ff293..a8bbcdf 100644
--- a/src/Hjets.cc
+++ b/src/Hjets.cc
@@ -1,1087 +1,1099 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/jets.hh"
#include "HEJ/Hjets.hh"
-#include <assert.h>
+#include <complex>
#include <limits>
+#include <math.h>
+
+#include "CLHEP/Vector/LorentzVector.h"
#include "HEJ/Constants.hh"
#include "HEJ/ConfigFlags.hh"
#ifdef HEJ_BUILD_WITH_QCDLOOP
+
+#include <assert.h>
+
#include "qcdloop/qcdloop.h"
+
+#else
+
+#include "HEJ/exceptions.hh"
+
#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 59138fb..f57e29a 100644
--- a/src/JetSplitter.cc
+++ b/src/JetSplitter.cc
@@ -1,188 +1,191 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/JetSplitter.hh"
#include <array>
#include <assert.h>
+#include <math.h>
#include <numeric>
+#include <stddef.h>
#include <utility>
#include "fastjet/ClusterSequence.hh"
+#include "fastjet/JetDefinition.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{}
+ jcons.cbegin(), jcons.cend(), 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));
+ assert(same_pt_and_rapidity(jcons.cbegin(), jcons.cend(), 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));
+ assert(same_pt_and_rapidity(jcons.cbegin(), jcons.cend(), 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/LesHouchesReader.cc b/src/LesHouchesReader.cc
index c00de59..d1e849c 100644
--- a/src/LesHouchesReader.cc
+++ b/src/LesHouchesReader.cc
@@ -1,31 +1,36 @@
+/**
+ * \authors The HEJ collaboration (see AUTHORS for details)
+ * \date 2020
+ * \copyright GPLv2 or later
+ */
#include "HEJ/LesHouchesReader.hh"
-#include <string>
+#include <vector>
namespace HEJ{
SherpaLHEReader::SherpaLHEReader(std::string const & filename):
LesHouchesReader{filename},
num_trials{0.}, num_events{0}
{
LesHouchesReader tmp_reader{filename};
reader_.heprup.XSECUP = std::vector<double>{0};
while(tmp_reader.read_event()){
++num_events;
num_trials += std::stod(tmp_reader.hepeup().attributes.at("trials"));
reader_.heprup.XSECUP.front() += tmp_reader.hepeup().XWGTUP;
}
reader_.heprup.XSECUP.front() /= num_trials;
// For IDWTUP == 1 or 4 we assume avg(weight)=xs
// With the modified weights we have in Sherpa sum(weight)=xs
// -> overwrite IDWTUP to "something neutral"
reader_.heprup.IDWTUP = reader_.heprup.IDWTUP>0?3:-3;
}
bool SherpaLHEReader::read_event() {
if(!LesHouchesReader::read_event()) return false;
reader_.hepeup.XWGTUP/=num_trials;
for(auto & wt: reader_.hepeup.weights)
wt.first/=num_trials;
return true;
}
}
diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc
index 46264c3..4574fcb 100644
--- a/src/LesHouchesWriter.cc
+++ b/src/LesHouchesWriter.cc
@@ -1,122 +1,125 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "HEJ/LesHouchesWriter.hh"
+
#include <cassert>
+#include <cmath>
+#include <stddef.h>
#include <utility>
#include <vector>
#include "HEJ/Event.hh"
#include "HEJ/event_types.hh"
-#include "HEJ/LesHouchesWriter.hh"
-#include "HEJ/utility.hh"
+#include "HEJ/Parameters.hh"
namespace HEJ{
namespace{
size_t to_index(event_type::EventType const type){
- return type==0?0:floor(log2(type))+1;
+ return type==0?0:std::floor(std::log2(static_cast<size_t>(type)))+1;
}
}
LesHouchesWriter::LesHouchesWriter(
std::string const & file, LHEF::HEPRUP heprup
):
out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc},
writer_{std::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);
+ xs_err = std::sqrt(xs_err);
}
rewrite_init();
}
}
diff --git a/src/MatrixElement.cc b/src/MatrixElement.cc
index 93111e2..4bb7271 100644
--- a/src/MatrixElement.cc
+++ b/src/MatrixElement.cc
@@ -1,1712 +1,1718 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/MatrixElement.hh"
#include <algorithm>
#include <assert.h>
+#include <cmath>
+#include <cstdlib>
+#include <iterator>
#include <limits>
-#include <math.h>
#include <stddef.h>
#include <unordered_map>
#include <utility>
#include "CLHEP/Vector/LorentzVector.h"
+#include "fastjet/PseudoJet.hh"
+
+#include "HEJ/ConfigFlags.hh"
#include "HEJ/Constants.hh"
#include "HEJ/Event.hh"
#include "HEJ/event_types.hh"
+#include "HEJ/EWConstants.hh"
#include "HEJ/exceptions.hh"
-#include "HEJ/ConfigFlags.hh"
+#include "HEJ/HiggsCouplingSettings.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));
+ const double result = - alpha_s*N_C/M_PI*std::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))
+ 1. + alpha_s/(4.*M_PI)*beta0*std::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)){
+ if (std::abs(partons[0].type) != std::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)) {
+ if(std::abs(backquark->type) != std::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);
+ return std::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){
+ if(AWZH_boson != end(out) && std::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);
+ return std::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());
+ res *= 1. + alpha_s/(2.*M_PI)*beta0*std::log(mur/parton->perp());
}
}
return get_AWZH_coupling(ev, alpha_s, param_.ew_parameters.alpha_w())*res;
}
} // namespace HEJ
diff --git a/src/PDF.cc b/src/PDF.cc
index c81a389..29942ec 100644
--- a/src/PDF.cc
+++ b/src/PDF.cc
@@ -1,112 +1,114 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/PDF.hh"
+#include <cmath>
+#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string>
namespace HEJ{
namespace{
int to_beam(ParticleID id){
if(std::abs(id) == pid::proton){
return (id > 0)?1:-1;
}
throw std::invalid_argument(
"unknown beam type: " + std::to_string(id)
);
}
}
#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
PDF::PDF(int id, ParticleID beam1, ParticleID beam2):
pdf{LHAPDF::mkPDF(id)},
beamtype{{to_beam(beam1), to_beam(beam2)}}
{}
double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{
if(!(inRangeQ(q) && inRangeX(x))) return 0.;
if(id == pid::gluon){
return pdf->xfxQ(21,x,q);
}
- else if(abs(id) < 7){
+ else if(std::abs(id) < 7){
return pdf->xfxQ(id*beamtype[beam_idx],x,q);
}
else {
std::cerr << "particle type unknown: "<< id << std::endl;
return 0.0;
}
}
double PDF::Halphas(double q) const{
double as = pdf->alphasQ(q);
if (std::isnan(as) || as > 0.5) {
as = 0.5;
}
return as;
}
int PDF::id() const{
return pdf->lhapdfID();
}
bool PDF::inRangeQ(double q) const{
return pdf->inRangeQ(q);
}
bool PDF::inRangeX(double x) const{
return pdf->inRangeX(x);
}
#else /* LHAPDF version unknown or older than 6 */
PDF::PDF(std::string LHAPDFName, int LHAPDFsubset, ParticleID beam1, ParticleID beam2):
beamtype{{to_beam(beam1_type), to_beam(beam2_type)}}
{
LHAPDF::initPDFSet(LHAPDFName, LHAPDF::LHGRID, LHAPDFsubset);
}
double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{
if(!(inRangeQ(q) && inRangeX(x))) return 0.;
if (id == pid::gluon){
return LHAPDF::xfx(x,q,0);
}
- else if (abs(id) < 7){
+ else if (std::abs(id) < 7){
return LHAPDF::xfx(x,q,id*beamtype[beam_idx]);
}
else {
std::cerr << "particle type unknown: "<< id <<std::endl;
return 0.0;
}
}
double PDF::Halphas(double q) const{
double as = LHAPDF::alphasPDF(q);
if (isnan(as) || as > 0.5) as = 0.5;
return as;
}
bool PDF::inRangeQ(double q) const{
// here we assume that all members actually have the same range!
static constexpr int member = 0;
return (LHAPDF::getQ2min(member) < q*q) && (q*q < LHAPDF::getQ2max(member));
}
bool PDF::inRangeX(double x) const{
// here we assume that all members actually have the same range!
static constexpr int member = 0;
return (LHAPDF::getXmin(member) < x) && (x < LHAPDF::getXmax(member));
}
#endif
}
diff --git a/src/Parameters.cc b/src/Parameters.cc
index 5e5155a..0239707 100644
--- a/src/Parameters.cc
+++ b/src/Parameters.cc
@@ -1,56 +1,56 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/Parameters.hh"
-#include <sstream>
#include <iostream>
+#include <sstream>
namespace HEJ {
namespace {
void replace(
std::string & str,
char to_replace, std::string const & replacement
) {
for( auto pos = str.find(to_replace); pos != str.npos;
pos = str.find(to_replace, pos)
){
str.replace(pos, 1, replacement);
pos += replacement.size();
}
}
// remove "special" characters from scale name
// so that we can more easily use it as part of a file name
std::string sanitise_scalename(std::string scalename) {
replace(scalename, '/', "_over_");
replace(scalename, '*', "_times_");
replace(scalename, ' ', "_");
return scalename;
}
}
std::string to_string(ParameterDescription const & p) {
// use ostringstream over std::to_string to remove trailing 0s
std::ostringstream stream;
stream << "\\mu_r = ";
if(p.mur_factor != 1.) stream << p.mur_factor << '*';
stream << p.scale_name << ", "
"\\mu_f = ";
if(p.muf_factor != 1.) stream << p.muf_factor << '*';
stream << p.scale_name;
return stream.str();
}
std::string to_simple_string(ParameterDescription const & p) {
// use ostringstream over std::to_string to remove trailing 0s
std::ostringstream stream;
stream << "Scale_" << sanitise_scalename(p.scale_name)
<< "_MuR" << p.mur_factor << "_MuF" << p.muf_factor;
return stream.str();
}
}
diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc
index b1c29c1..7b42d4e 100644
--- a/src/PhaseSpacePoint.cc
+++ b/src/PhaseSpacePoint.cc
@@ -1,840 +1,849 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/PhaseSpacePoint.hh"
#include <algorithm>
#include <assert.h>
+#include <cmath>
+#include <cstdlib>
+#include <functional>
+#include <iterator>
+#include <limits>
#include <numeric>
#include <random>
+#include <tuple>
+#include <utility>
#include "fastjet/ClusterSequence.hh"
+#include "fastjet/JetDefinition.hh"
#include "HEJ/Constants.hh"
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
#include "HEJ/JetSplitter.hh"
#include "HEJ/kinematics.hh"
+#include "HEJ/PDG_codes.hh"
#include "HEJ/resummation_jet.hh"
+#include "HEJ/RNG.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; }
+ [](Particle const & s){ return std::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);
+ const double temp1 = std::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 temp2 = std::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);
+ return std::pow(16*std::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/resummation_jet.cc b/src/resummation_jet.cc
index af6f53a..0f88fd2 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-2020
* \copyright GPLv2 or later
*/
#include "HEJ/resummation_jet.hh"
#include <assert.h>
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <stddef.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);
+ const double pperp = std::sqrt(px*px + py*py);
// keep the rapidities fixed
- const double pz = pperp*sinh(pB.rapidity());
- const double E = pperp*cosh(pB.rapidity());
+ const double pz = pperp*std::sinh(pB.rapidity());
+ const double E = pperp*std::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){
+ for (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_hepmc.cc b/t/check_hepmc.cc
index e26be89..315df5a 100644
--- a/t/check_hepmc.cc
+++ b/t/check_hepmc.cc
@@ -1,27 +1,28 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include <iostream>
-#include <stdexcept>
+#include <stdlib.h>
+#include "HepMC3/GenEvent.h"
#include "HepMC3/ReaderAscii.h"
-static constexpr double ep = 1e-3;
+#include "HEJ/exceptions.hh"
int main(int argn, char** argv) {
if(argn != 2){
std::cerr << "Usage: check_hepmc hepmc_file\n";
return EXIT_FAILURE;
}
HepMC3::ReaderAscii input{argv[1]};
if(input.failed()) throw std::runtime_error{"failed to open HepMC file"};
while(true){
HepMC3::GenEvent ev{};
if ( !input.read_event(ev) || ev.event_number() == 0 ) break;
if(input.failed()) throw std::runtime_error{"failed to read HepMC event"};
}
return EXIT_SUCCESS;
}
diff --git a/t/check_lhe.cc b/t/check_lhe.cc
index 4f5ab0c..4141e51 100644
--- a/t/check_lhe.cc
+++ b/t/check_lhe.cc
@@ -1,68 +1,74 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
#include <iostream>
+#include <stdlib.h>
#include <unordered_map>
+#include "LHEF/LHEF.h"
+
+#include "fastjet/JetDefinition.hh"
+
+#include "HEJ/Event.hh"
#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 751ddff..118b471 100644
--- a/t/check_lhe_sherpa.cc
+++ b/t/check_lhe_sherpa.cc
@@ -1,52 +1,56 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
#include <iostream>
+#include <memory>
+#include <stdlib.h>
#include <string>
-#include "HEJ/LesHouchesReader.hh"
+#include "HEJ/EventReader.hh"
-#include "hej_test.hh"
+#include "LHEF/LHEF.h"
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 81b2c8e..45f2ac7 100644
--- a/t/check_res.cc
+++ b/t/check_res.cc
@@ -1,150 +1,167 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+ #include "hej_test.hh"
+
+#include <algorithm>
#include <cmath>
#include <iostream>
+#include <iterator>
+#include <memory>
+#include <stdlib.h>
+#include <string>
+#include <utility>
-#include "LHEF/LHEF.h"
-
+#include "HEJ/Config.hh"
#include "HEJ/CrossSectionAccumulator.hh"
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
#include "HEJ/EventReweighter.hh"
+#include "HEJ/EWConstants.hh"
+#include "HEJ/Fraction.hh"
+#include "HEJ/HiggsCouplingSettings.hh"
#include "HEJ/Mixmax.hh"
+#include "HEJ/Parameters.hh"
+#include "HEJ/ScaleFunction.hh"
#include "HEJ/stream.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.hh"
+
+#include "LHEF/LHEF.h"
+
+namespace HEJ { struct RNG; }
-namespace{
+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/check_rivet.cc b/t/check_rivet.cc
index 3e294b3..9d0df16 100644
--- a/t/check_rivet.cc
+++ b/t/check_rivet.cc
@@ -1,76 +1,76 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
/**
* small test to see if we can rivet interface, and if rivet accepts our beams
*/
#include <array>
-#include <iostream>
+#include <stdlib.h>
#include <string>
#include <vector>
+#include "fastjet/JetDefinition.hh"
+
#include "HEJ/Event.hh"
#include "HEJ/exceptions.hh"
#include "HEJ/PDG_codes.hh"
#include "HEJ/RivetAnalysis.hh"
#include "LHEF/LHEF.h"
-#include "Rivet/AnalysisHandler.hh"
-
#include "yaml-cpp/yaml.h"
namespace {
const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4};
const double min_jet_pt{30.};
void test_analysis( std::vector<std::string> analysis,
std::array<double,2> energy, std::array<HEJ::ParticleID,2> beam
){
HEJ::Event::EventData ev;
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -5, -78, -54, 95}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { 23, -64, -44, 81}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { 68, 87, -24, 113}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -11, 92, -8, 93}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -12, 99, 44, 109}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -15, -52, 36, 65}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -48, -84, 97, 137}, {}});
ev.incoming[0] = {HEJ::ParticleID::gluon, { 0, 0, -323, 323}, {}};
ev.incoming[1] = {HEJ::ParticleID::gluon, { 0, 0, 370, 370}, {}};
auto const event{ ev.cluster(jet_def, min_jet_pt) };
std::string yaml_setup{ "{output: rivet_test, rivet: [" };
for(auto const & ana: analysis)
yaml_setup+=ana+",";
yaml_setup.pop_back();
yaml_setup+="]}";
auto ana_conf{ YAML::Load(yaml_setup) };
LHEF::HEPRUP heprup;
heprup.IDBMUP.first = beam[0];
heprup.IDBMUP.second = beam[1];
heprup.EBMUP.first = energy[0];
heprup.EBMUP.second = energy[1];
HEJ::RivetAnalysis rivet{ ana_conf, heprup };
if(!rivet.pass_cuts(event, event))
throw std::logic_error{"Rivet should not induce additional cuts!"};
rivet.fill(event, event);
// analyse twice since the first fill is special
if(!rivet.pass_cuts(event, event))
throw std::logic_error{"Rivet should not induce additional cuts!"};
rivet.fill(event, event);
rivet.finalise();
}
}
int main(){
using namespace HEJ;
test_analysis({"MC_XS"},{6500,6500},{ParticleID::e_bar,ParticleID::e}); // MC_XS accepts everything
test_analysis({"MC_XS","ATLAS_2016_I1419652"},{6500,6500},{ParticleID::p,ParticleID::p});
test_analysis({"ATLAS_2014_I1319490"},{3500,3500},{ParticleID::p,ParticleID::p});
return EXIT_SUCCESS;
}
diff --git a/t/hej_test.cc b/t/hej_test.cc
index 92444f0..099c4a2 100644
--- a/t/hej_test.cc
+++ b/t/hej_test.cc
@@ -1,523 +1,534 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "hej_test.hh"
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <iterator>
+#include <memory>
+#include <numeric>
#include <random>
+#include <stddef.h>
+#include <utility>
+
+#include "HEJ/Particle.hh"
+#include "HEJ/PDG_codes.hh"
HEJ::Event::EventData get_process(int const njet, int const pos_boson){
using namespace HEJ::pid;
HEJ::Event::EventData ev;
if(njet == 0){ // jet idx: -1 -1
ev.outgoing.push_back({gluon, { -24, 12, -57, 63}, {}});
ev.outgoing.push_back({gluon, { 24, -12, 41, 49}, {}});
ev.incoming[0] = {gluon, { 0, 0, -64, 64}, {}};
ev.incoming[1] = {gluon, { 0, 0, 48, 48}, {}};
return ev;
}
else if(njet == 1){ // jet idx: 0 -1 -1
ev.outgoing.push_back({gluon, { 23, 28, -44, 57}, {}});
ev.outgoing.push_back({gluon, { -11, -24, -12, 29}, {}});
ev.outgoing.push_back({gluon, { -12, -4, 39, 41}, {}});
ev.incoming[0] = {gluon, { 0, 0, -72, 72}, {}};
ev.incoming[1] = {gluon, { 0, 0, 55, 55}, {}};
return ev;
}
else if(njet == 2){
switch(pos_boson){
case 0:
ev.outgoing.push_back({higgs, { 198, 33, -170, 291}, {}});
ev.outgoing.push_back({gluon, {-154, 68, 44, 174}, {}});
ev.outgoing.push_back({gluon, { -44, -101, 88, 141}, {}});
ev.incoming[0] = {gluon, { 0, 0, -322, 322}, {}};
ev.incoming[1] = {gluon, { 0, 0, 284, 284}, {}};
return ev;
case 1:
ev.outgoing.push_back({gluon, { -6, 82, -159, 179}, {}});
ev.outgoing.push_back({higgs, { 195, -106, 74, 265}, {}});
ev.outgoing.push_back({gluon, {-189, 24, 108, 219}, {}});
ev.incoming[0] = {gluon, { 0, 0, -320, 320}, {}};
ev.incoming[1] = {gluon, { 0, 0, 343, 343}, {}};
return ev;
case 2:
ev.outgoing.push_back({gluon, { -80, -80, -140, 180}, {}});
ev.outgoing.push_back({gluon, { -60, -32, 0, 68}, {}});
ev.outgoing.push_back({higgs, { 140, 112, 177, 281}, {}});
ev.incoming[0] = {gluon, { 0, 0, -246, 246}, {}};
ev.incoming[1] = {gluon, { 0, 0, 283, 283}, {}};
return ev;
default:
ev.outgoing.push_back({gluon, { -72, 24, 18, 78}, {}});
ev.outgoing.push_back({gluon, { 72, -24, 74, 106}, {}});
ev.incoming[0] = {gluon, { 0, 0, -46, 46}, {}};
ev.incoming[1] = {gluon, { 0, 0, 138, 138}, {}};
return ev;
}
}
if(njet == 3){
switch(pos_boson){
case 0:
ev.outgoing.push_back({higgs, { 152, -117, -88, 245}, {}});
ev.outgoing.push_back({gluon, {-146, 62, -11, 159}, {}});
ev.outgoing.push_back({gluon, { 126, -72, 96, 174}, {}});
ev.outgoing.push_back({gluon, {-132, 127, 144, 233}, {}});
ev.incoming[0] = {gluon, { 0, 0, -335, 335}, {}};
ev.incoming[1] = {gluon, { 0, 0, 476, 476}, {}};
return ev;
case 1:
ev.outgoing.push_back({gluon, {-191, 188, -128, 297}, {}});
ev.outgoing.push_back({higgs, { 199, 72, -76, 257}, {}});
ev.outgoing.push_back({gluon, { 184, -172, -8, 252}, {}});
ev.outgoing.push_back({gluon, {-192, -88, 54, 218}, {}});
ev.incoming[0] = {gluon, { 0, 0, -591, 591}, {}};
ev.incoming[1] = {gluon, { 0, 0, 433, 433}, {}};
return ev;
case 2:
ev.outgoing.push_back({gluon, { -42, 18, -49, 67}, {}});
ev.outgoing.push_back({gluon, { -12, -54, -28, 62}, {}});
ev.outgoing.push_back({higgs, { 99, 32, -16, 163}, {}});
ev.outgoing.push_back({gluon, { -45, 4, 72, 85}, {}});
ev.incoming[0] = {gluon, { 0, 0, -199, 199}, {}};
ev.incoming[1] = {gluon, { 0, 0, 178, 178}, {}};
return ev;
case 3:
ev.outgoing.push_back({gluon, { -65, -32, -76, 105}, {}});
ev.outgoing.push_back({gluon, { -22, 31, -34, 51}, {}});
ev.outgoing.push_back({gluon, { -12, -67, -36, 77}, {}});
ev.outgoing.push_back({higgs, { 99, 68, -4, 173}, {}});
ev.incoming[0] = {gluon, { 0, 0, -278, 278}, {}};
ev.incoming[1] = {gluon, { 0, 0, 128, 128}, {}};
return ev;
default:
ev.outgoing.push_back({gluon, { -90, -135, 30, 165}, {}});
ev.outgoing.push_back({gluon, {-108, 198, 76, 238}, {}});
ev.outgoing.push_back({gluon, { 198, -63, 126, 243}, {}});
ev.incoming[0] = {gluon, { 0, 0, -207, 207}, {}};
ev.incoming[1] = {gluon, { 0, 0, 439, 439}, {}};
return ev;
}
}
if(njet == 4){
switch(pos_boson){
case 0:
ev.outgoing.push_back({higgs, { 199, 72, -76, 257}, {}});
ev.outgoing.push_back({gluon, {-200, -155, -64, 261}, {}});
ev.outgoing.push_back({gluon, { 198, 194, 57, 283}, {}});
ev.outgoing.push_back({gluon, { 1, 32, 8, 33}, {}});
ev.outgoing.push_back({gluon, {-198, -143, 186, 307}, {}});
ev.incoming[0] = {gluon, { 0, 0, -515, 515}, {}};
ev.incoming[1] = {gluon, { 0, 0, 626, 626}, {}};
return ev;
case 1:
ev.outgoing.push_back({gluon, { 198, 61, -162, 263}, {}});
ev.outgoing.push_back({higgs, { 199, 72, -76, 257}, {}});
ev.outgoing.push_back({gluon, {-200, 135, 144, 281}, {}});
ev.outgoing.push_back({gluon, {-198, -186, 171, 321}, {}});
ev.outgoing.push_back({gluon, { 1, -82, 122, 147}, {}});
ev.incoming[0] = {gluon, { 0, 0, -535, 535}, {}};
ev.incoming[1] = {gluon, { 0, 0, 734, 734}, {}};
return ev;
case 2:
ev.outgoing.push_back({gluon, {-180, -27, -164, 245}, {}});
ev.outgoing.push_back({gluon, {-108, 78, -36, 138}, {}});
ev.outgoing.push_back({higgs, { 196, -189, 68, 307}, {}});
ev.outgoing.push_back({gluon, {-107, 136, 76, 189}, {}});
ev.outgoing.push_back({gluon, { 199, 2, 178, 267}, {}});
ev.incoming[0] = {gluon, { 0, 0, -512, 512}, {}};
ev.incoming[1] = {gluon, { 0, 0, 634, 634}, {}};
return ev;
case 3:
ev.outgoing.push_back({gluon, { -12, -30, -84, 90}, {}});
ev.outgoing.push_back({gluon, { -72, 22, -96, 122}, {}});
ev.outgoing.push_back({gluon, { 68, 0, -51, 85}, {}});
ev.outgoing.push_back({higgs, { 64, 72, -81, 177}, {}});
ev.outgoing.push_back({gluon, { -48, -64, 84, 116}, {}});
ev.incoming[0] = {gluon, { 0, 0, -409, 409}, {}};
ev.incoming[1] = {gluon, { 0, 0, 181, 181}, {}};
return ev;
case 4:
ev.outgoing.push_back({gluon, { -72, -49, -72, 113}, {}});
ev.outgoing.push_back({gluon, { -48, 0, -36, 60}, {}});
ev.outgoing.push_back({gluon, { -12, 54, -36, 66}, {}});
ev.outgoing.push_back({gluon, { 68, -77, -56, 117}, {}});
ev.outgoing.push_back({higgs, { 64, 72, -81, 177}, {}});
ev.incoming[0] = {gluon, { 0, 0, -407, 407}, {}};
ev.incoming[1] = {gluon, { 0, 0, 126, 126}, {}};
return ev;
default:
ev.outgoing.push_back({gluon, { 248, -56, -122, 282}, {}});
ev.outgoing.push_back({gluon, { 249, 30, -10, 251}, {}});
ev.outgoing.push_back({gluon, {-249, -18, 26, 251}, {}});
ev.outgoing.push_back({gluon, {-248, 44, 199, 321}, {}});
ev.incoming[0] = {gluon, { 0, 0, -506, 506}, {}};
ev.incoming[1] = {gluon, { 0, 0, 599, 599}, {}};
return ev;
}
}
if(njet == 6){
switch(pos_boson){
case 0:
ev.outgoing.push_back({higgs, { 349, 330, -94, 505}, {}});
ev.outgoing.push_back({gluon, {-315, -300, 0, 435}, {}});
ev.outgoing.push_back({gluon, { 347, 306, 18, 463}, {}});
ev.outgoing.push_back({gluon, {-249, -342, 162, 453}, {}});
ev.outgoing.push_back({gluon, { 345, 312, 284, 545}, {}});
ev.outgoing.push_back({gluon, {-324, -126, 292, 454}, {}});
ev.outgoing.push_back({gluon, {-153, -180, 304, 385}, {}});
ev.incoming[0] = {gluon, { 0, 0, -1137, 1137}, {}};
ev.incoming[1] = {gluon, { 0, 0, 2103, 2103}, {}};
return ev;
case 1:
ev.outgoing.push_back({gluon, { 242, 241, -182, 387}, {}});
ev.outgoing.push_back({higgs, { 243, 238, -190, 409}, {}});
ev.outgoing.push_back({gluon, {-218, -215, -74, 315}, {}});
ev.outgoing.push_back({gluon, {-224, -224, 112, 336}, {}});
ev.outgoing.push_back({gluon, { 241, 182, 154, 339}, {}});
ev.outgoing.push_back({gluon, { -53, -234, 126, 271}, {}});
ev.outgoing.push_back({gluon, {-231, 12, 156, 279}, {}});
ev.incoming[0] = {gluon, { 0, 0, -1117, 1117}, {}};
ev.incoming[1] = {gluon, { 0, 0, 1219, 1219}, {}};
return ev;
case 2:
ev.outgoing.push_back({gluon, { 151, 102, -42, 187}, {}});
ev.outgoing.push_back({gluon, { -86, -46, -17, 99}, {}});
ev.outgoing.push_back({higgs, { 152, 153, 0, 249}, {}});
ev.outgoing.push_back({gluon, { -60, -135, 64, 161}, {}});
ev.outgoing.push_back({gluon, { 150, 123, 110, 223}, {}});
ev.outgoing.push_back({gluon, {-154, -49, 98, 189}, {}});
ev.outgoing.push_back({gluon, {-153, -148, 144, 257}, {}});
ev.incoming[0] = {gluon, { 0, 0, -504, 504}, {}};
ev.incoming[1] = {gluon, { 0, 0, 861, 861}, {}};
return ev;
case 3:
ev.outgoing.push_back({gluon, { 198, 197, -66, 287}, {}});
ev.outgoing.push_back({gluon, {-198, -189, -54, 279}, {}});
ev.outgoing.push_back({gluon, {-200, -64, 2, 210}, {}});
ev.outgoing.push_back({higgs, { 199, 158, 6, 283}, {}});
ev.outgoing.push_back({gluon, {-199, -184, 172, 321}, {}});
ev.outgoing.push_back({gluon, { 196, 168, 177, 313}, {}});
ev.outgoing.push_back({gluon, { 4, -86, 92, 126}, {}});
ev.incoming[0] = {gluon, { 0, 0, -745, 745}, {}};
ev.incoming[1] = {gluon, { 0, 0, 1074, 1074}, {}};
return ev;
case 4:
ev.outgoing.push_back({gluon, { 151, 102, -42, 187}, {}});
ev.outgoing.push_back({gluon, { -86, -133, -14, 159}, {}});
ev.outgoing.push_back({gluon, {-154, -104, -8, 186}, {}});
ev.outgoing.push_back({gluon, { -60, 11, 0, 61}, {}});
ev.outgoing.push_back({higgs, { 152, 153, 0, 249}, {}});
ev.outgoing.push_back({gluon, { 150, 125, 90, 215}, {}});
ev.outgoing.push_back({gluon, {-153, -154, 126, 251}, {}});
ev.incoming[0] = {gluon, { 0, 0, -578, 578}, {}};
ev.incoming[1] = {gluon, { 0, 0, 730, 730}, {}};
return ev;
case 5:
ev.outgoing.push_back({gluon, { -15, -90, -94, 131}, {}});
ev.outgoing.push_back({gluon, { -11, 82, -74, 111}, {}});
ev.outgoing.push_back({gluon, { 23, -80, -64, 105}, {}});
ev.outgoing.push_back({gluon, { -48, -25, -36, 65}, {}});
ev.outgoing.push_back({gluon, { -12, 99, -16, 101}, {}});
ev.outgoing.push_back({higgs, { 68, 92, -18, 170}, {}});
ev.outgoing.push_back({gluon, { -5, -78, 54, 95}, {}});
ev.incoming[0] = {gluon, { 0, 0, -513, 513}, {}};
ev.incoming[1] = {gluon, { 0, 0, 265, 265}, {}};
return ev;
case 6:
ev.outgoing.push_back({gluon, { 198, 197, -66, 287}, {}});
ev.outgoing.push_back({gluon, { 4, -84, -18, 86}, {}});
ev.outgoing.push_back({gluon, {-198, -60, -36, 210}, {}});
ev.outgoing.push_back({gluon, { 196, -78, -36, 214}, {}});
ev.outgoing.push_back({gluon, {-200, 45, 0, 205}, {}});
ev.outgoing.push_back({gluon, {-199, -178, 2, 267}, {}});
ev.outgoing.push_back({higgs, { 199, 158, 6, 283}, {}});
ev.incoming[0] = {gluon, { 0, 0, -850, 850}, {}};
ev.incoming[1] = {gluon, { 0, 0, 702, 702}, {}};
return ev;
default:
ev.outgoing.push_back({gluon, {-350, -112, -280, 462}, {}});
ev.outgoing.push_back({gluon, { 347, 266, -322, 543}, {}});
ev.outgoing.push_back({gluon, {-349, -314, -38, 471}, {}});
ev.outgoing.push_back({gluon, { 349, 348, 12, 493}, {}});
ev.outgoing.push_back({gluon, {-342, -54, 23, 347}, {}});
ev.outgoing.push_back({gluon, { 345, -134, 138, 395}, {}});
ev.incoming[0] = {gluon, { 0, 0, -1589, 1589}, {}};
ev.incoming[1] = {gluon, { 0, 0, 1122, 1122}, {}};
return ev;
}
}
if(njet == 7){
switch(pos_boson){
case -1: // jet idx: -1 0 1 2 3 4 5
ev.outgoing.push_back({gluon, { -15, -18, -54, 59}, {}});
ev.outgoing.push_back({gluon, { -11, 98, -70, 121}, {}});
ev.outgoing.push_back({gluon, { 23, -100, -64, 121}, {}});
ev.outgoing.push_back({gluon, { 68, 93, -20, 117}, {}});
ev.outgoing.push_back({gluon, { -5, -92, -12, 93}, {}});
ev.outgoing.push_back({gluon, { -48, -76, -2, 90}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.incoming[0] = {gluon, { 0, 0, -439, 439}, {}};
ev.incoming[1] = {gluon, { 0, 0, 273, 273}, {}};
return ev;
case -2: // jet idx: 0 1 2 3 4 -1 -1
ev.outgoing.push_back({gluon, { -5, -86, -82, 119}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -48, -14, 20, 54}, {}});
ev.outgoing.push_back({gluon, { 23, -50, 26, 61}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({gluon, { -15, -18, 54, 59}, {}});
ev.outgoing.push_back({gluon, { -11, -20, 88, 91}, {}});
ev.incoming[0] = {gluon, { 0, 0, -215, 215}, {}};
ev.incoming[1] = {gluon, { 0, 0, 397, 397}, {}};
return ev;
case -3: // jet idx: 0 0 1 2 2 3 4
// jet pt fraction: 0.6 0.38 1 0.49 0.51 1 1
ev.outgoing.push_back({gluon, { 23, -94, -62, 1.2e+02}, {}});
ev.outgoing.push_back({gluon, { -5, -62, -34, 71}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 1.2e+02}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 1.1e+02}, {}});
ev.outgoing.push_back({gluon, { -11, 98, 70, 1.2e+02}, {}});
ev.outgoing.push_back({gluon, { -48, -1e+02, 82, 1.4e+02}, {}});
ev.outgoing.push_back({gluon, { -15, -30, 78, 85}, {}});
ev.incoming[0] = {gluon, { 0, 0, -2.7e+02, 2.7e+02}, {}};
ev.incoming[1] = {gluon, { 0, 0, 4.8e+02, 4.8e+02}, {}};
return ev;
case -4: // jet idx: 0 1 1 2 3 4 4
// jet pt fraction: 1 0.51 0.49 1 1 0.25 0.75
ev.outgoing.push_back({gluon, { -5, -88, -64, 109}, {}});
ev.outgoing.push_back({gluon, { -11, 98, -70, 121}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { 23, -70, 22, 77}, {}});
ev.outgoing.push_back({gluon, { -15, -32, 16, 39}, {}});
ev.outgoing.push_back({gluon, { -48, -96, 75, 131}, {}});
ev.incoming[0] = {gluon, { 0, 0, -381, 381}, {}};
ev.incoming[1] = {gluon, { 0, 0, 324, 324}, {}};
return ev;
case -5: // jet idx: 0 1 -1 -1 2 3 4
ev.outgoing.push_back({gluon, { -15, -26, -62, 69}, {}});
ev.outgoing.push_back({gluon, { -48, -60, -54, 94}, {}});
ev.outgoing.push_back({gluon, { 23, 10, -14, 29}, {}});
ev.outgoing.push_back({gluon, { -5, -20, 0, 21}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -11, -92, 40, 101}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.incoming[0] = {gluon, { 0, 0, -278, 278}, {}};
ev.incoming[1] = {gluon, { 0, 0, 264, 264}, {}};
return ev;
case -6: // jet idx: 0 1 1 2 -1 2 3
// jet pt fraction: 1 0.63 0.36 0.49 1 0.51 1
ev.outgoing.push_back({gluon, { 68, 93, -20, 117}, {}});
ev.outgoing.push_back({gluon, { -48, -100, 26, 114}, {}});
ev.outgoing.push_back({gluon, { -15, -62, 26, 69}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({gluon, { -5, -28, 20, 35}, {}});
ev.outgoing.push_back({gluon, { -11, 98, 70, 121}, {}});
ev.outgoing.push_back({gluon, { 23, -96, 92, 135}, {}});
ev.incoming[0] = {gluon, { 0, 0, -216, 216}, {}};
ev.incoming[1] = {gluon, { 0, 0, 486, 486}, {}};
return ev;
case -7: // jet idx: 0 1 2 2 3 3 4
// jet pt fraction: 1 1 0.51 0.49 0.18 0.82 1
ev.outgoing.push_back({gluon, { -15, -80, -100, 129}, {}});
ev.outgoing.push_back({gluon, { 23, -96, -92, 135}, {}});
ev.outgoing.push_back({gluon, { -11, 98, -70, 121}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { -5, -22, -10, 25}, {}});
ev.outgoing.push_back({gluon, { -48, -88, -31, 105}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.incoming[0] = {gluon, { 0, 0, -541, 541}, {}};
ev.incoming[1] = {gluon, { 0, 0, 202, 202}, {}};
return ev;
case -8: // jet idx: 0 1 2 2 2 3 4
// jet pt fraction: 1 1 0.21 0.37 0.41 1 1
ev.outgoing.push_back({gluon, { -48, -44, -62, 90}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { -5, -50, -22, 55}, {}});
ev.outgoing.push_back({gluon, { 23, -90, -34, 99}, {}});
ev.outgoing.push_back({gluon, { -15, -100, -28, 105}, {}});
ev.outgoing.push_back({gluon, { 68, 93, -20, 117}, {}});
ev.outgoing.push_back({gluon, { -11, 96, 76, 123}, {}});
ev.incoming[0] = {gluon, { 0, 0, -423, 423}, {}};
ev.incoming[1] = {gluon, { 0, 0, 277, 277}, {}};
return ev;
case -9: // jet idx: 0 1 2 1 3 0 4
// jet pt fraction: 0.72 0.51 1 0.49 1 0.28 1
ev.outgoing.push_back({gluon, { -15, -98, -62, 117}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { 23, -76, -40, 89}, {}});
ev.outgoing.push_back({gluon, { -11, 92, -40, 101}, {}});
ev.outgoing.push_back({gluon, { -48, -68, -34, 90}, {}});
ev.outgoing.push_back({gluon, { -5, -38, -14, 41}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.incoming[0] = {gluon, { 0, 0, -446, 446}, {}};
ev.incoming[1] = {gluon, { 0, 0, 220, 220}, {}};
return ev;
case -10: // jet idx: 0 1 3 2 4 3 1
// jet pt fraction: 1 0.33 0.51 1 1 0.49 0.67
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -5, -48, 16, 51}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({gluon, { 23, -76, 52, 95}, {}});
ev.outgoing.push_back({gluon, { -48, -60, 54, 94}, {}});
ev.outgoing.push_back({gluon, { -11, 92, 68, 115}, {}});
ev.outgoing.push_back({gluon, { -15, -96, 72, 121}, {}});
ev.incoming[0] = {gluon, { 0, 0, -183, 183}, {}};
ev.incoming[1] = {gluon, { 0, 0, 521, 521}, {}};
return ev;
case -11: // jet idx: 0 1 2 3 4 -1 5
// jet pt fraction: 1 1 1 1 1 1 1
ev.outgoing.push_back({gluon, { -11, 98, -70, 121}, {}});
ev.outgoing.push_back({gluon, { -15, -98, -62, 117}, {}});
ev.outgoing.push_back({gluon, { 23, -90, -2, 93}, {}});
ev.outgoing.push_back({gluon, { -48, -76, 2, 90}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -5, -22, 10, 25}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.incoming[0] = {gluon, { 0, 0, -360, 360}, {}};
ev.incoming[1] = {gluon, { 0, 0, 314, 314}, {}};
return ev;
case -12: // jet idx: 0 1 -1 2 3 4 3
// jet pt fraction: 1 1 1 1 0.35 1 0.65
ev.outgoing.push_back({gluon, { 23, -94, -62, 115}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { -5, -28, 4, 29}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -15, -58, 34, 69}, {}});
ev.outgoing.push_back({gluon, { -11, 92, 68, 115}, {}});
ev.outgoing.push_back({gluon, { -48, -100, 82, 138}, {}});
ev.incoming[0] = {gluon, { 0, 0, -302, 302}, {}};
ev.incoming[1] = {gluon, { 0, 0, 392, 392}, {}};
return ev;
case -13: // jet idx: 0 1 2 3 3 4 2
// jet pt fraction: 1 1 0.5 0.35 0.65 1 0.5
ev.outgoing.push_back({gluon, { -15, -98, -62, 117}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({gluon, { -5, -28, 20, 35}, {}});
ev.outgoing.push_back({gluon, { -48, -96, 75, 131}, {}});
ev.outgoing.push_back({gluon, { 23, -62, 50, 83}, {}});
ev.outgoing.push_back({gluon, { -11, 96, 76, 123}, {}});
ev.incoming[0] = {gluon, { 0, 0, -241, 241}, {}};
ev.incoming[1] = {gluon, { 0, 0, 476, 476}, {}};
return ev;
case -14: // jet idx: 0 1 2 3 3 4 2
// jet pt fraction: 1 1 0.52 0.35 0.65 1 0.48
ev.outgoing.push_back({gluon, { 23, -94, -62, 115}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({gluon, { -15, -96, 72, 121}, {}});
ev.outgoing.push_back({gluon, { -5, -42, 38, 57}, {}});
ev.outgoing.push_back({gluon, { -48, -44, 62, 90}, {}});
ev.outgoing.push_back({gluon, { -11, 88, 88, 125}, {}});
ev.incoming[0] = {gluon, { 0, 0, -231, 231}, {}};
ev.incoming[1] = {gluon, { 0, 0, 505, 505}, {}};
return ev;
case -15: // jet idx: 0 -1 1 0 2 3 4
// jet pt fraction: 0.51 1 1 0.49 1 1 1
ev.outgoing.push_back({gluon, { -11, 98, -70, 121}, {}});
ev.outgoing.push_back({gluon, { -5, -16, -12, 21}, {}});
ev.outgoing.push_back({gluon, { 23, -94, -62, 115}, {}});
ev.outgoing.push_back({gluon, { -12, 95, -56, 111}, {}});
ev.outgoing.push_back({gluon, { 68, 93, 20, 117}, {}});
ev.outgoing.push_back({gluon, { -48, -76, 70, 114}, {}});
ev.outgoing.push_back({gluon, { -15, -100, 80, 129}, {}});
ev.incoming[0] = {gluon, { 0, 0, -379, 379}, {}};
ev.incoming[1] = {gluon, { 0, 0, 349, 349}, {}};
return ev;
}
}
throw HEJ::unknown_option{"unknown process"};
}
HEJ::Event::EventData parse_configuration(
std::array<std::string,2> const & in, std::vector<std::string> const & out,
int const overwrite_boson
){
auto boson = std::find_if(out.cbegin(), out.cend(),
[](std::string id){ return !HEJ::is_parton(HEJ::to_ParticleID(id)); });
int const pos_boson = (overwrite_boson!=0)?overwrite_boson:
((boson == out.cend())?-1:std::distance(out.cbegin(), boson));
size_t njets = out.size();
if( (overwrite_boson == 0) && boson != out.cend()) --njets;
HEJ::Event::EventData ev{get_process(njets, pos_boson)};
ASSERT((pos_boson<0) || (ev.outgoing[pos_boson].type == HEJ::ParticleID::higgs));
for(size_t i=0; i<out.size(); ++i){
ev.outgoing[i].type = HEJ::to_ParticleID(out[i]);
// decay W
if( std::abs(ev.outgoing[i].type) == HEJ::ParticleID::Wp )
ev.decays[i]=decay_W(ev.outgoing[i]);
}
for(size_t i=0; i<in.size(); ++i){
ev.incoming[i].type = HEJ::to_ParticleID(in[i]);
}
shuffle_particles(ev);
return ev;
}
namespace {
static std::mt19937_64 ran{0};
}
void shuffle_particles(HEJ::Event::EventData & ev) {
// incoming
- std::shuffle(begin(ev.incoming), end(ev.incoming), ran);
+ std::shuffle(ev.incoming.begin(), ev.incoming.end(), ran);
// outgoing (through index)
auto old_outgoing = std::move(ev).outgoing;
std::vector<size_t> idx(old_outgoing.size());
std::iota(idx.begin(), idx.end(), 0);
- std::shuffle(begin(idx), end(idx), ran);
+ std::shuffle(idx.begin(), idx.end(), ran);
ev.outgoing.clear();
ev.outgoing.reserve(old_outgoing.size());
for(size_t i: idx) {
ev.outgoing.emplace_back(std::move(old_outgoing[i]));
}
// find decays again
if(!ev.decays.empty()){
auto old_decays = std::move(ev).decays;
ev.decays.clear();
for(size_t i=0; i<idx.size(); ++i) {
auto decay = old_decays.find(idx[i]);
if(decay != old_decays.end())
ev.decays.emplace(i, std::move(decay->second));
}
for(auto & decay: ev.decays){
- std::shuffle(begin(decay.second), end(decay.second), ran);
+ std::shuffle(decay.second.begin(), decay.second.end(), ran);
}
}
}
std::vector<HEJ::Particle> decay_W( HEJ::Particle const & parent ){
if(parent.m() == 0.) // we can't decay massless partons
return {};
std::array<HEJ::ParticleID, 2> decays;
if(parent.type==HEJ::ParticleID::Wp){
// order matters: first particle, second anti
decays[0] = HEJ::ParticleID::nu_e;
decays[1] = HEJ::ParticleID::e_bar;
} else {
// this function is for testing: we don't check that parent==W boson
decays[0] = HEJ::ParticleID::e;
decays[1] = HEJ::ParticleID::nu_e_bar;
}
std::vector<HEJ::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()/(1.*ran.max());
- const double cos_phi = 2.*ran()/(1.*ran.max())-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 theta = 2.*M_PI*ran()/static_cast<double>(ran.max());
+ const double cos_phi = 2.*ran()/static_cast<double>(ran.max())-1.;
+ const double sin_phi = std::sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi
+ const double px = E*std::cos(theta)*sin_phi;
+ const double py = E*std::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/t/hej_test.hh b/t/hej_test.hh
index db07910..9a18f5c 100644
--- a/t/hej_test.hh
+++ b/t/hej_test.hh
@@ -1,42 +1,50 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#pragma once
+#include <array>
+#include <string>
+#include <vector>
+
#include "HEJ/Event.hh"
#include "HEJ/exceptions.hh"
+namespace HEJ {
+ struct Particle;
+}
+
//! 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 3c5b245..0343712 100644
--- a/t/test_ME_generic.cc
+++ b/t/test_ME_generic.cc
@@ -1,180 +1,184 @@
/**
* \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-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
-#include <algorithm>
#include <cmath>
#include <fstream>
+#include <iostream>
+#include <stdlib.h>
+#include <string>
+#include "HEJ/Config.hh"
#include "HEJ/Event.hh"
#include "HEJ/EventReader.hh"
+#include "HEJ/exceptions.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;
+namespace {
+ constexpr double alpha_s = 0.118;
+ constexpr double ep = 1e-9;
+ constexpr double ep_mirror = 1e-3;
-enum MEComponent {tree, virt};
+ 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());
+ MEComponent guess_component(std::string const & data_file) {
+ if(data_file.find("virt") != data_file.npos) return MEComponent::virt;
+ return MEComponent::tree;
}
- for(auto & decay: ev.decays){
- for(auto & part: decay.second){
+
+ 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;
}
- 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 80c4a24..e4cf547 100644
--- a/t/test_classify.cc
+++ b/t/test_classify.cc
@@ -1,491 +1,499 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
+#include <array>
#include <iostream>
#include <random>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include "fastjet/JetDefinition.hh"
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
#include "HEJ/exceptions.hh"
-
-#include "hej_test.hh"
+#include "HEJ/PDG_codes.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_classify_ref.cc b/t/test_classify_ref.cc
index 10cab20..8bd5f98 100644
--- a/t/test_classify_ref.cc
+++ b/t/test_classify_ref.cc
@@ -1,77 +1,78 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
#include <fstream>
-#include <algorithm>
+#include <iostream>
+#include <stdlib.h>
+#include <string>
-#include "LHEF/LHEF.h"
+#include <fastjet/JetDefinition.hh>
-#include "HEJ/YAMLreader.hh"
#include "HEJ/event_types.hh"
#include "HEJ/Event.hh"
#include "HEJ/EventReader.hh"
-#include "hej_test.hh"
-
-namespace{
+namespace {
// this is deliberately chosen bigger than in the generation,
// to cluster multiple partons in one jet
constexpr double min_jet_pt = 40.;
const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.6};
}
int main(int argn, char** argv) {
if(argn != 3 && argn != 4){
std::cerr << "Usage: " << argv[0]
<< " reference_classification input_file.lhe\n";
return EXIT_FAILURE;
}
bool OUTPUT_MODE = false;
if(argn == 4 && std::string("OUTPUT")==std::string(argv[3]))
OUTPUT_MODE = true;
std::fstream ref_file;
if ( OUTPUT_MODE ) {
std::cout << "_______________________USING OUTPUT MODE!_______________________" << std::endl;
ref_file.open(argv[1], std::fstream::out);
} else {
ref_file.open(argv[1], std::fstream::in);
}
auto reader{ HEJ::make_reader(argv[2]) };
size_t nevent{0};
while(reader->read_event()){
++nevent;
// We don't need to test forever, the first "few" are enough
if(nevent>4000) break;
HEJ::Event::EventData data{ reader->hepeup() };
shuffle_particles(data);
const HEJ::Event ev{
data.cluster(
jet_def, min_jet_pt
)
};
if ( OUTPUT_MODE ) {
ref_file << ev.type() << std::endl;
} else {
std::string line;
if(!std::getline(ref_file,line)) break;
const auto expected{static_cast<HEJ::event_type::EventType>(std::stoi(line))};
if(ev.type() != expected){
using HEJ::event_type::name;
std::cerr << "wrong classification of event " << nevent << ":\n" << ev
<< "classified as " << name(ev.type())
<< ", expected " << name(expected) << "\nJet indices: ";
for(auto const & idx: ev.particle_jet_indices())
std::cerr << idx << " ";
std::cerr << "\n";
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
diff --git a/t/test_colours.cc b/t/test_colours.cc
index 28b9546..b90107c 100644
--- a/t/test_colours.cc
+++ b/t/test_colours.cc
@@ -1,352 +1,360 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
+#include <iostream>
#include <stdexcept>
+#include <stdlib.h>
#include <utility>
+#include <vector>
#include "HEJ/Constants.hh"
#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+#include "HEJ/Particle.hh"
+#include "HEJ/PDG_codes.hh"
#include "HEJ/RNG.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.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_colours2.cc b/t/test_colours2.cc
index b7b4355..c413734 100644
--- a/t/test_colours2.cc
+++ b/t/test_colours2.cc
@@ -1,346 +1,352 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
#include <array>
#include <bitset>
+#include <iostream>
#include <stdlib.h>
#include <string>
+#include <utility>
#include <vector>
-#include "HEJ/Event.hh"
#include "HEJ/Constants.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/exceptions.hh"
#include "HEJ/Mixmax.hh"
+#include "HEJ/Particle.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.hh"
namespace {
const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4};
const double min_jet_pt{30.};
struct colour_flow {
std::array<std::string,2> incoming;
std::vector<std::string> outgoing;
std::string colour;
HEJ::Event to_event(){
using namespace HEJ;
auto event = parse_configuration(incoming, outgoing);
if(colour.size()==0) // no colour -> only parse
return event.cluster(jet_def,min_jet_pt);
ASSERT(colour.front()=='a' && colour.back()=='a'); // only closed loops
event.sort();
// fill colours with dummy value
for(auto & p: event.incoming)
p.colour = Colour{-1, -1};
for(auto & p: event.outgoing)
p.colour = Colour{-1, -1};
int current_colour = COLOUR_OFFSET;
int backup_colour = current_colour;
Particle* last_part = &event.incoming.front();
// loop connections
for(auto const & entry: colour){
if(entry == '_'){ // '_' -> skip connection
backup_colour = current_colour;
current_colour = 0;
continue;
}
auto & part = get_particle(event, entry);
part.colour->first = last_part->colour->second = current_colour;
current_colour = ++backup_colour;
last_part = &part;
}
for(auto & in: event.incoming)
std::swap(in.colour->first, in.colour->second);
// reset untouched colours
for(auto & out: event.outgoing)
if(out.colour->first == -1 && out.colour->second ==-1)
out.colour = {};
shuffle_particles(event);
return event.cluster(jet_def,min_jet_pt);
}
private:
HEJ::Particle & get_particle(
HEJ::Event::EventData & event, char const name
){
if(name == 'a')
return event.incoming[0];
if(name == 'b')
return event.incoming[1];
size_t idx = name-'0';
ASSERT(idx<event.outgoing.size());
return event.outgoing[idx];
}
};
const colour_flow FKL_ggg{{"g","g"},{"g","g","g"},{}};
const colour_flow FKL_qgq{{"1","2"},{"1","g","2"},{}};
const colour_flow FKL_qxgqx{{"-1","-2"},{"-1","g","-2"},{}};
const colour_flow FKL_qhgq{{"1","2"},{"1","h","g","2"},{}};
const colour_flow uno_gqgq{{"1","2"},{"g","1","g","2"},{}};
const colour_flow uno_qgqg{{"1","2"},{"1","g","2","g"},{}};
const colour_flow uno_Wgqq{{"2","2"},{"W+","g","1","2"},{}};
const colour_flow uno_gWqq{{"2","2"},{"g","W+","1","2"},{}};
const colour_flow uno_gqWq{{"2","2"},{"g","1","W+","2"},{}};
const colour_flow uno_qWqg{{"2","2"},{"1","W+","2","g"},{}};
const colour_flow uno_qqWg{{"2","2"},{"1","2","W+","g"},{}};
const colour_flow uno_qqgW{{"2","2"},{"1","2","g","W+"},{}};
const colour_flow qqx_qxqgq{{"g","2"},{"-1","1","g","2"},{}};
const colour_flow qqx_qgqqx{{"1","g"},{"1","g","2","-2"},{}};
const colour_flow qqx_qgqxq{{"1","g"},{"1","g","-2","2"},{}};
const colour_flow qqx_Wqxqq{{"g","2"},{"W+","-2","1","2"},{}};
const colour_flow qqx_qxWqq{{"g","2"},{"-2","W+","1","2"},{}};
const colour_flow qqx_qxqWq{{"g","2"},{"-2","1","W+","2"},{}};
const colour_flow qqx_qWqqx{{"2","g"},{"1","W+","2","-2"},{}};
const colour_flow qqx_qqWqx{{"2","g"},{"1","2","W+","-2"},{}};
const colour_flow qqx_qqqxW{{"2","g"},{"1","2","-2","W+"},{}};
const colour_flow qqx_gqqxq{{"g","2"},{"g","3","-3","2"},{}};
const colour_flow qqx_qqxqg{{"1","g"},{"1","-3","3","g"},{}};
const colour_flow qqx_qqxqqx{{"1","-1"},{"1","-1","1","-1"},{}};
const colour_flow qqx_qWqxqqx{{"2","-1"},{"1","W+","-1","1","-1"},{}};
const colour_flow qqx_qqxWqqx{{"2","-1"},{"1","-1","W+","1","-1"},{}};
const colour_flow qqx_qqxqWqx{{"2","-1"},{"1","-1","1","W+","-1"},{}};
HEJ::Mixmax rng;
void verify_colour(colour_flow configuration, std::string line,
bool const expectation = true
){
configuration.colour = std::move(line);
auto const event = configuration.to_event();
if(event.is_leading_colour() != expectation){
std::cerr << "Expected "<< (expectation?"":"non-") <<"leading colour\n"
<< event
<< "\nwith connection: " << configuration.colour << "\n";
throw std::logic_error("Colour verification failed");
}
}
int find_colour(HEJ::Event const & evt, int const colour){
if(evt.incoming()[0].colour->second==colour)
return -1;
if(evt.incoming()[1].colour->second==colour)
return -2;
for(size_t i=0;i<evt.outgoing().size();++i){
HEJ::Particle const & out = evt.outgoing()[i];
if(!out.colour)
continue;
if(out.colour->first == colour)
return i;
}
return -3;
}
int find_anticolour(HEJ::Event const & evt, int const anticolour){
if(evt.incoming()[0].colour->first==anticolour)
return -1;
if(evt.incoming()[1].colour->first==anticolour)
return -2;
for(size_t i=0;i<evt.outgoing().size();++i){
HEJ::Particle const & out = evt.outgoing()[i];
if(!out.colour)
continue;
if(out.colour->second == anticolour)
return i;
}
return -3;
}
bool matching_colours(HEJ::Event const & evt1, HEJ::Event const & evt2){
ASSERT(evt1.outgoing().size()==evt2.outgoing().size());
for(size_t i=0; i<2;++i){
HEJ::optional<HEJ::Colour> col1 = evt1.incoming()[i].colour;
HEJ::optional<HEJ::Colour> col2 = evt2.incoming()[i].colour;
ASSERT(col1);
ASSERT(col2);
int idx1 = find_colour(evt1, col1->first);
int idx2 = find_colour(evt2, col2->first);
if(idx1 == -3 || idx2 == -3)
return false;
if(idx1!=idx2){
return false;
}
idx1 = find_anticolour(evt1, col1->second);
idx2 = find_anticolour(evt2, col2->second);
if(idx1 == -3 || idx2 == -3)
return false;
if(idx1!=idx2)
return false;
}
for(size_t i=0; i<evt1.outgoing().size();++i){
HEJ::optional<HEJ::Colour> col1 = evt1.outgoing()[i].colour;
HEJ::optional<HEJ::Colour> col2 = evt2.outgoing()[i].colour;
if(!col1){
if(!col2) continue;
return false;
}
if(!col2)
return false;
int idx1 = find_colour(evt1, col1->second);
int idx2 = find_colour(evt2, col2->second);
if(idx1 == -3 || idx2 == -3)
return false;
if(idx1!=idx2)
return false;
idx1 = find_anticolour(evt1, col1->first);
idx2 = find_anticolour(evt2, col2->first);
if(idx1 == -3 || idx2 == -3)
return false;
if(idx1!=idx2)
return false;
}
return true;
}
void all_colours_possible(
colour_flow momenta, std::vector<std::string> allowed
){
std::vector<HEJ::Event> possible;
for(auto const & line: allowed){
momenta.colour = line;
possible.push_back(momenta.to_event());
if(!possible.back().is_leading_colour()){
std::cerr << "Expected leading colour\n"
<< possible.back()
<< "\nwith connection: " << momenta.colour << "\n";
throw std::logic_error("Colour verification failed");
}
}
momenta.colour = "";
ASSERT(possible.size()<16); // sooo magic
std::bitset<16> missing = (1<<(possible.size()))-1;
size_t max_tries = possible.size()*10;
// brute force generation of specific colour flow
while(missing != 0 && max_tries>0){
--max_tries;
HEJ::Event test_evt = momenta.to_event();
test_evt.generate_colours(rng);
size_t i=0;
for(; i<possible.size();++i){
if(matching_colours(test_evt, possible[i])){
missing[i]=0;
break;
}
}
if(i==possible.size()){
std::cerr << "Could not find allowed connection for\n"
<< test_evt << std::endl;
throw std::logic_error("Unknown colour flow");
}
}
if(max_tries<=0){
std::cerr << "Not all connections found missing:" << std::endl;
for(size_t i=0; i<16; ++i){
if(missing[i])
std::cerr << allowed[i] << "\n" << possible[i] << std::endl;
}
throw std::logic_error("Missing colour flow");
}
}
}
int main() {
// FKL
all_colours_possible(FKL_ggg, {"a012ba","a01b2a","a02b1a","a0b21a",
"a12b0a","a1b20a","a2b10a","ab210a"});
all_colours_possible(FKL_qgq, {"a12_b0_a", "a2_b10_a"});
all_colours_possible(FKL_qxgqx, {"a_01b_2a", "a_0b_21a"});
all_colours_possible(FKL_qhgq, {"a23_b0_a", "a3_b20_a"});
// uno
all_colours_possible(uno_gqgq, {"a023_b1_a","a03_b21_a",
"a23_b01_a","a3_b201_a"}); // u-channel
all_colours_possible(uno_qgqg, {"a12_b30_a","a2_b310_a",
"a132_b0_a","a32_b10_a"}); // u-channel
all_colours_possible(uno_Wgqq, {"a13_b2_a","a3_b12_a"});
all_colours_possible(uno_gWqq, {"a03_b2_a","a3_b02_a"});
all_colours_possible(uno_gqWq, {"a03_b1_a","a3_b01_a"});
all_colours_possible(uno_qWqg, {"a2_b30_a","a32_b0_a"});
all_colours_possible(uno_qqWg, {"a1_b30_a","a31_b0_a"});
all_colours_possible(uno_qqgW, {"a1_b20_a","a21_b0_a"});
// extremal qqx
all_colours_possible(qqx_qgqqx, {"a12_3b0_a","a2_3b10_a",
"a1b2_30_a","ab2_310_a"}); // u-channel
all_colours_possible(qqx_qgqxq, {"a1b3_20_a", "ab3_210_a",
"a13_2b0_a","a3_2b10_a"}); // u-channel
all_colours_possible(qqx_qxqgq, {"a23_b1_0a","a3_b21_0a",
"a1_023_ba","a1_03_b2a"}); // u-channel
all_colours_possible(qqx_Wqxqq, {"a3_b2_1a","a2_13_ba"});
all_colours_possible(qqx_qxWqq, {"a3_b2_0a","a2_03_ba"});
all_colours_possible(qqx_qxqWq, {"a3_b1_0a","a1_03_ba"});
all_colours_possible(qqx_qWqqx, {"a2_3b0_a","ab2_30_a"});
all_colours_possible(qqx_qqWqx, {"a1_3b0_a","ab1_30_a"});
all_colours_possible(qqx_qqqxW, {"a1_2b0_a","ab1_20_a"});
// central qqx
all_colours_possible(qqx_gqqxq, {"a01_23_ba","a1_23_b0a",
"a03_b1_2a","a3_b1_20a"}); // u-channel
all_colours_possible(qqx_qqxqg, {"a3b2_10_a","ab32_10_a",
"a2_13b0_a","a2_1b30_a"}); // u-channel
all_colours_possible(qqx_qqxqqx, {"ab_32_10_a","a2_1b_30_a"});
all_colours_possible(qqx_qWqxqqx, {"ab_43_20_a","a3_2b_40_a"});
all_colours_possible(qqx_qqxWqqx, {"ab_43_10_a","a3_1b_40_a"});
all_colours_possible(qqx_qqxqWqx, {"ab_42_10_a","a2_1b_40_a"});
// forbidden
// crossed FKL
verify_colour(FKL_ggg, "a021ba",false);
verify_colour(FKL_ggg, "a0b12a",false);
verify_colour(FKL_ggg, "a10b2a",false);
verify_colour(FKL_ggg, "a1b02a",false);
verify_colour(FKL_ggg, "a20b1a",false);
verify_colour(FKL_ggg, "a21b0a",false);
verify_colour(FKL_ggg, "a2b01a",false);
verify_colour(FKL_ggg, "ab120a",false);
// quark with anti-colour
verify_colour(FKL_qgq, "a_01b_2a",false);
verify_colour(FKL_qgq, "a_0b_21a",false);
verify_colour(FKL_qxgqx, "a12_b0_a",false);
verify_colour(FKL_qxgqx, "a2_b10_a",false);
// higgs with colour
verify_colour(FKL_qhgq, "a123_b0_a",false);
verify_colour(FKL_qhgq, "a3_1_b20_a",false);
verify_colour(FKL_qhgq, "a3_11_b20_a",false);
// not-connected
verify_colour(FKL_ggg, "a012a",false);
verify_colour(FKL_ggg, "a012aa",false);
verify_colour(FKL_ggg, "a01ba",false);
verify_colour(FKL_ggg, "a0b2a",false);
verify_colour(FKL_ggg, "a_01b2a",false);
verify_colour(FKL_ggg, "a01_b2a",false);
verify_colour(FKL_ggg, "a0b_12a",false);
verify_colour(FKL_ggg, "a012b_a",false);
verify_colour(uno_gqgq, "a_1023_ba",false);
// uno
verify_colour(uno_gqgq, "a203_b1_a",false);
verify_colour(uno_qgqg, "a312_b0_a",false);
// extremal qqx
verify_colour(qqx_qgqqx, "a10_3b2_a",false);
verify_colour(qqx_qgqqx, "a2_31b0_a",false);
verify_colour(qqx_qgqqx, "a2_31b0_a",false);
verify_colour(qqx_qgqxq, "ab13_20_a",false);
verify_colour(qqx_qxqgq, "a3_b1_02a",false);
verify_colour(qqx_qxqgq, "a21_b3_0a",false);
// central qqx
verify_colour(qqx_gqqxq, "a1_203_ba",false);
verify_colour(qqx_gqqxq, "a3_21_b0a",false);
verify_colour(qqx_qqxqg, "ab2_130_a",false);
verify_colour(qqx_qqxqg, "a3b0_12_a",false);
verify_colour(qqx_qqxqqx, "a0_1b_32_a",false);
verify_colour(qqx_qqxqqx, "a0_3b_12_a",false);
verify_colour(qqx_qqxqqx, "a2_3b_10_a",false);
verify_colour(qqx_qqxqqx, "ab_12_30_a",false);
return EXIT_SUCCESS;
}
diff --git a/t/test_decay.cc b/t/test_decay.cc
index f913188..6d7019e 100644
--- a/t/test_decay.cc
+++ b/t/test_decay.cc
@@ -1,135 +1,149 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*
* \brief Test classification for (invalid) W decays
*/
-#include "HEJ/Event.hh"
-
#include "hej_test.hh"
+#include <array>
+#include <initializer_list>
+#include <iostream>
+#include <memory>
+#include <stdlib.h>
+#include <string>
+#include <utility>
+
+#include "fastjet/JetDefinition.hh"
+#include "fastjet/PseudoJet.hh"
+
+#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+#include "HEJ/Particle.hh"
+#include "HEJ/PDG_codes.hh"
+
namespace {
const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4};
const double min_jet_pt{30.};
HEJ::Event::EventData new_event() {
HEJ::Event::EventData ev;
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -11, -96, -76, 123}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -15, -70, -22, 75}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { 68, 93, -20, 117}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -12, 95, 56, 111}, {}});
ev.outgoing.push_back({HEJ::ParticleID::gluon, { -30, -22, 25, 45}, {}});
ev.incoming[0] = {HEJ::ParticleID::gluon, { 0, 0, -254, 254}, {}};
ev.incoming[1] = {HEJ::ParticleID::gluon, { 0, 0, 217, 217}, {}};
return ev;
}
bool test_event(HEJ::Event::EventData data, bool const valid
){
using namespace HEJ::event_type;
EventType const expected{ valid?FKL:bad_final_state };
shuffle_particles(data);
auto const ev = std::move(data).cluster(jet_def, min_jet_pt);
if(ev.type() != expected){
std::cerr << "Event does not match expectation, expected "
<< name(expected) << "\n" << ev << std::endl;
return false;
}
return true;
}
} // namespace anonymous
int main() {
using namespace HEJ::pid;
auto ev = new_event();
// basic FKL
test_event(ev, true);
// W position shouldn't matter
for(auto const W_type: {Wp, Wm}){
for(size_t w_pos = 1; w_pos<ev.outgoing.size()-1; ++w_pos){
ev = new_event();
ev.outgoing[w_pos].type = W_type;
ev.outgoing.back().type = (W_type==Wp)?d:u;
ev.incoming.back().type = (W_type==Wp)?u:d;
// no decay
if(!test_event(ev, false))
return EXIT_FAILURE;
// working decay
ev.decays[w_pos] = decay_W(ev.outgoing[w_pos]);
if(!test_event(ev, true))
return EXIT_FAILURE;
// swapped W+ <-> W-
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
-ev.decays[w_pos].at(0).type );
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
-ev.decays[w_pos].at(1).type );
if(!test_event(ev, false))
return EXIT_FAILURE;
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
-ev.decays[w_pos].at(0).type );
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
-ev.decays[w_pos].at(1).type );
// replace e -> mu (normal)
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type+2 );
if(!test_event(ev, false))
return EXIT_FAILURE;
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type-2 );
// replace e -> mu (anti)
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
ev.decays[w_pos].at(1).type-2 );
if(!test_event(ev, false))
return EXIT_FAILURE;
// all mu
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type+2 );
if(!test_event(ev, true))
return EXIT_FAILURE;
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type-2 );
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
ev.decays[w_pos].at(1).type+2 );
// partonic
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type-10 );
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
ev.decays[w_pos].at(1).type+10 );
if(!test_event(ev, false))
return EXIT_FAILURE;
ev.decays[w_pos].at(0).type = static_cast<ParticleID>(
ev.decays[w_pos].at(0).type+10 );
ev.decays[w_pos].at(1).type = static_cast<ParticleID>(
ev.decays[w_pos].at(1).type-10 );
// double check that we undid all changes
if(!test_event(ev, true))
return EXIT_FAILURE;
// 1->3 decay
ev.decays[w_pos].emplace_back(
HEJ::Particle{photon, fastjet::PseudoJet(0,0,0,0), {}}
);
if(!test_event(ev, false))
return EXIT_FAILURE;
ev.decays[w_pos].pop_back();
// second decay
ev.decays[0] = decay_W(ev.outgoing[0]);
ev.decays[0].at(0).type = ev.outgoing[0].type;
ev.decays[0].at(1).type = gluon;
if(!test_event(ev, false))
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
diff --git a/t/test_descriptions.cc b/t/test_descriptions.cc
index aceb483..6fe5b1e 100644
--- a/t/test_descriptions.cc
+++ b/t/test_descriptions.cc
@@ -1,65 +1,70 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
#include <iostream>
-#include <cstddef>
+#include <stdlib.h>
+#include <utility>
#include "HEJ/Event.hh"
-#include "HEJ/EventReweighter.hh"
+#include "HEJ/Parameters.hh"
+#include "HEJ/PDG_codes.hh"
#include "HEJ/ScaleFunction.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.hh"
+#include "fastjet/PseudoJet.hh"
int main() {
constexpr double mu = 125.;
HEJ::ScaleFunction fun{"125", HEJ::FixedScale{mu}};
ASSERT(fun.name() == "125");
HEJ::ScaleGenerator scale_gen{
{std::move(fun)}, {0.5, 1, 2.}, 2.1
};
HEJ::Event::EventData tmp;
tmp.outgoing.push_back(
{HEJ::ParticleID::gluon, fastjet::PtYPhiM(50., -1., 0.3, 0.), {}}
);
tmp.outgoing.push_back(
{HEJ::ParticleID::gluon, fastjet::PtYPhiM(30., 1., -0.3, 0.), {}}
);
HEJ::Event ev{
tmp.cluster(
fastjet::JetDefinition{fastjet::kt_algorithm, 0.4}, 20.
)
};
auto rescaled = scale_gen(std::move(ev));
ASSERT(rescaled.central().description->scale_name == "125");
for(auto const & var: rescaled.variations()) {
ASSERT(var.description->scale_name == "125");
}
ASSERT(rescaled.central().description->mur_factor == 1.);
ASSERT(rescaled.central().description->muf_factor == 1.);
ASSERT(rescaled.variations(0).description->mur_factor == 1.);
ASSERT(rescaled.variations(0).description->muf_factor == 1.);
ASSERT(rescaled.variations(1).description->mur_factor == 0.5);
ASSERT(rescaled.variations(1).description->muf_factor == 0.5);
ASSERT(rescaled.variations(2).description->mur_factor == 0.5);
ASSERT(rescaled.variations(2).description->muf_factor == 1.);
ASSERT(rescaled.variations(3).description->mur_factor == 1.);
ASSERT(rescaled.variations(3).description->muf_factor == 0.5);
ASSERT(rescaled.variations(4).description->mur_factor == 1.);
ASSERT(rescaled.variations(4).description->muf_factor == 2.);
ASSERT(rescaled.variations(5).description->mur_factor == 2.);
ASSERT(rescaled.variations(5).description->muf_factor == 1.);
ASSERT(rescaled.variations(6).description->mur_factor == 2.);
ASSERT(rescaled.variations(6).description->muf_factor == 2.);
return EXIT_SUCCESS;
}
diff --git a/t/test_hdf5.cc b/t/test_hdf5.cc
index 3f370e3..ff2912a 100644
--- a/t/test_hdf5.cc
+++ b/t/test_hdf5.cc
@@ -1,46 +1,50 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
#include <iostream>
+#include <stdlib.h>
+#include <utility>
#include "HEJ/EventReader.hh"
+#include "LHEF/LHEF.h"
+
int main(int argc, char** argv) {
if(argc != 2) {
std::cerr << "Usage: " << argv[0] << " file.hdf5\n";
return EXIT_FAILURE;
}
auto reader = HEJ::make_reader(argv[1]);
if(
reader->heprup().EBMUP != std::make_pair(7000., 7000.)
|| reader->heprup().PDFSUP != std::make_pair(13000, 13000)
) {
std::cerr << "Read incorrect init parameters\n";
return EXIT_FAILURE;
}
int nevent = 0;
while(reader->read_event()) {
++nevent;
if(reader->hepeup().NUP != 13) {
std::cerr << "Read wrong number of particles: "
<< reader->hepeup().NUP << " != 13 in event " << nevent;
return EXIT_FAILURE;
}
for(size_t i = 0; i < 2; ++i) {
for(size_t j = 0; j < 2; ++j) {
if(reader->hepeup().PUP[i][j] != 0) {
std::cerr << "Non-vanishing transverse momentum in incoming particle"
" in event " << nevent;
return EXIT_FAILURE;
}
}
}
}
if(nevent != 51200) {
std::cerr << "Wrong number of events " << nevent << " != 51200\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
diff --git a/t/test_hdf5_write.cc b/t/test_hdf5_write.cc
index 1f03a36..1997adc 100644
--- a/t/test_hdf5_write.cc
+++ b/t/test_hdf5_write.cc
@@ -1,77 +1,87 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+#include <algorithm>
+#include <array>
#include <iostream>
-#include <unistd.h>
+#include <memory>
+#include <stdlib.h>
+#include <vector>
#include "HEJ/Event.hh"
+#include "HEJ/EventReader.hh"
+#include "HEJ/EventWriter.hh"
#include "HEJ/HDF5Reader.hh"
#include "HEJ/make_writer.hh"
+#include "HEJ/output_formats.hh"
+#include "HEJ/Parameters.hh"
+#include "HEJ/Particle.hh"
#include "HEJ/utility.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.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 7208eec..47cd834 100644
--- a/t/test_jet_cuts.cc
+++ b/t/test_jet_cuts.cc
@@ -1,302 +1,311 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
+#include <array>
#include <iostream>
+#include <stdlib.h>
+#include <string>
+#include <vector>
-#include "hej_test.hh"
+#include "HEJ/Event.hh"
+#include "HEJ/event_types.hh"
+
+#include "fastjet/JetDefinition.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_parameters.cc b/t/test_parameters.cc
index ec1eafe..e4ec2cd 100644
--- a/t/test_parameters.cc
+++ b/t/test_parameters.cc
@@ -1,73 +1,73 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
-#include <iostream>
-#include <stdexcept>
+#include "hej_test.hh"
-#include "HEJ/Parameters.hh"
+#include <memory>
+#include <stdlib.h>
-#include "hej_test.hh"
+#include "HEJ/Parameters.hh"
namespace {
bool same_description(
HEJ::EventParameters const & par1, HEJ::EventParameters const & par2
){
if( par1.mur!=par2.mur || par1.muf!=par2.muf ) return false;
auto const & des1 = par1.description;
auto const & des2 = par2.description;
if(bool(des1) != bool(des2)) return false; // only one set
if(!des1) return true; // both not set
return (des1->mur_factor == des2->mur_factor)
&& (des1->muf_factor == des2->muf_factor)
&& (des1->scale_name == des2->scale_name);
}
void same_description(
HEJ::Parameters<HEJ::EventParameters> const & par1,
HEJ::Parameters<HEJ::EventParameters> const & par2
){
ASSERT(same_description(par1.central, par2.central));
ASSERT(par1.variations.size() == par2.variations.size());
for(size_t i=0; i<par1.variations.size(); ++i)
ASSERT( same_description(par1.variations[i], par2.variations[i]) );
}
}
int main() {
HEJ::Parameters<HEJ::EventParameters> ev_param;
ev_param.central = HEJ::EventParameters{ 1,1,1.1,
std::make_shared<HEJ::ParameterDescription>("a", 1.,1.) };
ev_param.variations.emplace_back(
HEJ::EventParameters{ 2,2,2.2,
std::make_shared<HEJ::ParameterDescription>("b", 2.,2.)
});
ev_param.variations.emplace_back(
HEJ::EventParameters{ 3,3,3.3,
std::make_shared<HEJ::ParameterDescription>("c", 3.,3.)
});
HEJ::Weights weights;
weights.central = 4.4;
weights.variations.push_back(5.5);
weights.variations.push_back(6.6);
HEJ::Parameters<HEJ::EventParameters> mult_param;
mult_param = ev_param*weights;
same_description(ev_param, mult_param);
ASSERT(mult_param.central.weight == ev_param.central.weight*weights.central);
for(size_t i=0; i<weights.variations.size(); ++i)
ASSERT(mult_param.variations[i].weight
== ev_param.variations[i].weight*weights.variations[i]);
HEJ::Parameters<HEJ::EventParameters> div_param;
div_param = ev_param/weights;
same_description(ev_param, div_param);
ASSERT(div_param.central.weight == ev_param.central.weight/weights.central);
for(size_t i=0; i<weights.variations.size(); ++i)
ASSERT(div_param.variations[i].weight
== ev_param.variations[i].weight/weights.variations[i]);
return EXIT_SUCCESS;
}
diff --git a/t/test_psp.cc b/t/test_psp.cc
index c62e0c4..5680995 100644
--- a/t/test_psp.cc
+++ b/t/test_psp.cc
@@ -1,68 +1,74 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
-#include "LHEF/LHEF.h"
+#include "hej_test.hh"
+
+#include <stdlib.h>
+#include <iostream>
+
#include "HEJ/stream.hh"
#include "HEJ/Config.hh"
#include "HEJ/event_types.hh"
#include "HEJ/Event.hh"
#include "HEJ/PhaseSpacePoint.hh"
#include "HEJ/Ranlux64.hh"
-#include "hej_test.hh"
+#include "LHEF/LHEF.h"
+
+#include "fastjet/JetDefinition.hh"
namespace{
constexpr int max_trials = 100;
constexpr double max_ext_soft_pt_fraction = 0.1;
const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4};
constexpr double min_jet_pt = 50;
}
int main(int argn, char** argv) {
if(argn != 2){
std::cerr << "Usage: " << argv[0] << " eventfile";
return EXIT_FAILURE;
}
HEJ::istream in{argv[1]};
LHEF::Reader reader{in};
LHEF::Writer writer{std::cerr};
writer.heprup = reader.heprup;
HEJ::PhaseSpacePointConfig conf;
conf.jet_param = HEJ::JetParameters{jet_def, min_jet_pt};
conf.max_ext_soft_pt_fraction = max_ext_soft_pt_fraction;
HEJ::Ranlux64 ran{};
while(reader.readEvent()){
HEJ::Event::EventData ev_data{ reader.hepeup };
shuffle_particles(ev_data);
const HEJ::Event ev{ ev_data( jet_def, min_jet_pt ) };
for(int trial = 0; trial < max_trials; ++trial){
HEJ::PhaseSpacePoint psp{ev, conf, ran};
if(psp.weight() != 0){
HEJ::Event::EventData tmp_ev;
tmp_ev.incoming = psp.incoming();
tmp_ev.outgoing = psp.outgoing();
tmp_ev.parameters.central = {0,0,0};
shuffle_particles(tmp_ev);
HEJ::Event out_ev{ tmp_ev(jet_def, min_jet_pt) };
if(out_ev.type() != ev.type()){
using HEJ::event_type::name;
std::cerr << "Wrong class of phase space point:\n"
"original event of class " << name(ev.type()) << ":\n";
writer.hepeup = reader.hepeup;
writer.writeEvent();
std::cerr << "Phase space point of class " << name(out_ev.type()) << ":\n";
writer.hepeup = to_HEPEUP(out_ev, &writer.heprup);
writer.writeEvent();
return EXIT_FAILURE;
}
}
}
}
return EXIT_SUCCESS;
}
diff --git a/t/test_scale_arithmetics.cc b/t/test_scale_arithmetics.cc
index 19bb514..1c9cd2c 100644
--- a/t/test_scale_arithmetics.cc
+++ b/t/test_scale_arithmetics.cc
@@ -1,95 +1,102 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
-#include <fstream>
-#include <algorithm>
+#include "hej_test.hh"
-#include "LHEF/LHEF.h"
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <stdlib.h>
+#include <utility>
+#include "HEJ/Config.hh"
+#include "HEJ/Event.hh"
#include "HEJ/EventReweighter.hh"
#include "HEJ/make_RNG.hh"
-#include "HEJ/Event.hh"
-#include "HEJ/YAMLreader.hh"
+#include "HEJ/Parameters.hh"
+#include "HEJ/RNG.hh"
#include "HEJ/stream.hh"
+#include "HEJ/YAMLreader.hh"
-#include "hej_test.hh"
+#include "LHEF/LHEF.h"
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_scale_import.cc b/t/test_scale_import.cc
index 2fc7a28..d5d1f29 100644
--- a/t/test_scale_import.cc
+++ b/t/test_scale_import.cc
@@ -1,35 +1,40 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
- * \date 2019
+ * \date 2019-2020
* \copyright GPLv2 or later
*/
-#include <stdexcept>
-#include <iostream>
+#include <stdlib.h>
-#include "HEJ/YAMLreader.hh"
+#include "fastjet/JetDefinition.hh"
+#include "fastjet/PseudoJet.hh"
+
+#include "HEJ/Config.hh"
#include "HEJ/Event.hh"
+#include "HEJ/exceptions.hh"
+#include "HEJ/PDG_codes.hh"
+#include "HEJ/YAMLreader.hh"
int main(int argc, char** argv) {
constexpr double ep = 1e-7;
if (argc != 2) {
throw std::logic_error{"wrong number of args"};
}
const HEJ::Config config = HEJ::load_config(argv[1]);
HEJ::Event::EventData tmp;
tmp.outgoing.push_back(
{HEJ::ParticleID::gluon, fastjet::PtYPhiM(50., -1., 0.3, 0.), {}}
);
tmp.outgoing.push_back(
{HEJ::ParticleID::gluon, fastjet::PtYPhiM(30., 1., -0.3, 0.), {}}
);
HEJ::Event ev{
tmp(fastjet::JetDefinition{fastjet::kt_algorithm, 0.4}, 20.)
};
const double softest_pt = config.scales.base[0](ev);
if(std::abs(softest_pt-30.) > ep){
throw std::logic_error{"wrong softest pt"};
}
return EXIT_SUCCESS;
}
diff --git a/t/test_unweighter.cc b/t/test_unweighter.cc
index 9f39a55..75b3f77 100644
--- a/t/test_unweighter.cc
+++ b/t/test_unweighter.cc
@@ -1,152 +1,159 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
+#include "hej_test.hh"
+
+#include <cmath>
+#include <iostream>
+#include <stdlib.h>
+#include <string>
+#include <vector>
#include "HEJ/CrossSectionAccumulator.hh"
#include "HEJ/Event.hh"
#include "HEJ/EventReader.hh"
#include "HEJ/Mixmax.hh"
#include "HEJ/Unweighter.hh"
-#include "hej_test.hh"
+#include "fastjet/JetDefinition.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);
+ const size_t size_sample = std::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;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 3, 6:59 AM (17 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4983225
Default Alt Text
(558 KB)

Event Timeline