Page MenuHomeHEPForge

No OneTemporary

diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh
index fe0266f..04699ee 100644
--- a/FixedOrderGen/include/PhaseSpacePoint.hh
+++ b/FixedOrderGen/include/PhaseSpacePoint.hh
@@ -1,231 +1,272 @@
/** \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 <cstddef>
#include <unordered_map>
#include <vector>
+#include "boost/iterator/filter_iterator.hpp"
+
#include "HEJ/Event.hh"
#include "HEJ/Particle.hh"
#include "HEJ/PDG_codes.hh"
#include "fastjet/PseudoJet.hh"
#include "Decay.hh"
#include "Status.hh"
#include "Subleading.hh"
namespace HEJ {
class EWConstants;
class PDF;
class RNG;
}
namespace HEJFOG {
class JetParameters;
class Process;
//! A point in resummation phase space
class PhaseSpacePoint{
public:
+
+ //! Iterator over partons
+ using ConstPartonIterator = boost::filter_iterator<
+ bool (*)(HEJ::Particle const &),
+ std::vector<HEJ::Particle>::const_iterator
+ >;
+ //! Reverse Iterator over partons
+ using ConstReversePartonIterator = std::reverse_iterator<
+ ConstPartonIterator>;
+
//! 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,
Subleading 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<HEJ::Particle, 2> const & incoming() const{
return incoming_;
}
//! Get Outgoing Function
/**
* @returns Outgoing Particles
*/
std::vector<HEJ::Particle> const & outgoing() const{
return outgoing_;
}
std::unordered_map<std::size_t, std::vector<HEJ::Particle>> const & decays() const{
return decays_;
}
+ //! 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;
+
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
);
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, Subleading subl_channels,
HEJ::PDF & pdf, double E_beam,
double uf,
HEJ::RNG & ran
);
HEJ::ParticleID generate_incoming_id(
std::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, Subleading channels,
Process const & proc, HEJ::RNG & ran);
void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward,
HEJ::RNG & ran);
void turn_to_qqx(bool allow_strange, HEJ::RNG & ran);
//! decay where we select the decay channel
std::vector<HEJ::Particle> decay_boson(
HEJ::Particle const & parent,
std::vector<Decay> const & decays,
HEJ::RNG & ran
);
//! generate decay products of a 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
);
+ //! Iterator over partons (non-const)
+ using PartonIterator = boost::filter_iterator<
+ bool (*)(HEJ::Particle const &),
+ std::vector<HEJ::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();
+
double weight_;
Status status_;
std::array<HEJ::Particle, 2> incoming_;
std::vector<HEJ::Particle> outgoing_;
//! Particle decays in the format {outgoing index, decay products}
std::unordered_map<std::size_t, std::vector<HEJ::Particle>> decays_;
};
//! Extract HEJ::Event::EventData from PhaseSpacePoint
HEJ::Event::EventData to_EventData(PhaseSpacePoint psp);
}
diff --git a/FixedOrderGen/src/EventGenerator.cc b/FixedOrderGen/src/EventGenerator.cc
index c956a97..5c9c779 100644
--- a/FixedOrderGen/src/EventGenerator.cc
+++ b/FixedOrderGen/src/EventGenerator.cc
@@ -1,99 +1,101 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "EventGenerator.hh"
#include <cstddef>
#include <utility>
#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"
namespace HEJFOG {
EventGenerator::EventGenerator(
Process process,
Beam beam,
HEJ::ScaleGenerator scale_gen,
JetParameters jets,
int pdf_id,
double subl_change,
Subleading 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()))
+ if(!is_resummable(ev.type())){
+ std::cerr << ev << std::endl;
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(std::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 38831f1..4ecb68c 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,735 +1,776 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "PhaseSpacePoint.hh"
#include <algorithm>
#include <cassert>
#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"
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 {
Event::EventData to_EventData(PhaseSpacePoint psp){
//! @TODO Same function already in HEJ
Event::EventData result;
result.incoming = std::move(psp).incoming_;
assert(result.incoming.size() == 2);
result.outgoing = std::move(psp).outgoing_;
// technically Event::EventData doesn't have to be sorted,
// but PhaseSpacePoint should be anyway
assert(
std::is_sorted(
begin(result.outgoing), end(result.outgoing),
rapidity_less{}
)
);
assert(result.outgoing.size() >= 2);
result.decays = std::move(psp).decays_;
result.parameters.central = {NaN, NaN, psp.weight()};
return result;
}
+ PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::begin_partons() const {
+ return cbegin_partons();
+ }
+ PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cbegin_partons() const {
+ return boost::make_filter_iterator(
+ static_cast<bool (*)(HEJ::Particle const &)>(HEJ::is_parton),
+ cbegin(outgoing()),
+ cend(outgoing())
+ );
+ }
+
+ PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::end_partons() const {
+ return cend_partons();
+ }
+ PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cend_partons() const {
+ return boost::make_filter_iterator(
+ static_cast<bool (*)(HEJ::Particle const &)>(HEJ::is_parton),
+ cend(outgoing()),
+ cend(outgoing())
+ );
+ }
+
+ PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rbegin_partons() const {
+ return crbegin_partons();
+ }
+ PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crbegin_partons() const {
+ return std::reverse_iterator<ConstPartonIterator>( cend_partons() );
+ }
+
+ PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rend_partons() const {
+ return crend_partons();
+ }
+ PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crend_partons() const {
+ return std::reverse_iterator<ConstPartonIterator>( cbegin_partons() );
+ }
+
+ PhaseSpacePoint::PartonIterator PhaseSpacePoint::begin_partons() {
+ return boost::make_filter_iterator(
+ static_cast<bool (*)(HEJ::Particle const &)>(HEJ::is_parton),
+ begin(outgoing_),
+ end(outgoing_)
+ );
+ }
+
+ PhaseSpacePoint::PartonIterator PhaseSpacePoint::end_partons() {
+ return boost::make_filter_iterator(
+ static_cast<bool (*)(HEJ::Particle const &)>(HEJ::is_parton),
+ end(outgoing_),
+ end(outgoing_)
+ );
+ }
+
+ PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rbegin_partons() {
+ return std::reverse_iterator<PartonIterator>( end_partons() );
+ }
+
+ PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rend_partons() {
+ return std::reverse_iterator<PartonIterator>( begin_partons() );
+ }
+
namespace {
bool can_swap_to_uno(
Particle const & p1, Particle const & p2
){
- return is_parton(p1)
- && p1.type != pid::gluon
- && p2.type == pid::gluon;
+ assert(is_parton(p1) && is_parton(p2));
+ return p1.type != pid::gluon
+ && p2.type == pid::gluon;
}
- size_t count_gluons(std::vector<Particle>::const_iterator first,
- std::vector<Particle>::const_iterator last
+ size_t count_gluons(PhaseSpacePoint::ConstPartonIterator first,
+ PhaseSpacePoint::ConstPartonIterator 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
*/
Subleading possible_qqx(
- std::vector<Particle>::const_iterator first,
- std::vector<Particle>::const_iterator last
+ PhaseSpacePoint::ConstPartonIterator first,
+ PhaseSpacePoint::ConstPartonIterator last
){
using namespace subleading;
+ assert(std::distance(first,last)>2);
Subleading channels(~0l);
channels.reset(qqx);
channels.reset(eqqx);
channels.reset(cqqx);
auto const ngluon = count_gluons(first,last);
if(ngluon < 2) return channels;
return channels.set(qqx);
//! @TODO stub
if(ngluon == 2 && first->type==pid::gluon){
//! backwards qqx?
return channels.set(eqqx);
}
channels.set(cqqx);
return channels.set(eqqx);
}
bool is_AWZ_proccess(Process const & proc){
return proc.boson && is_AWZ_boson(*proc.boson);
}
bool is_up_type(Particle const & part){
return is_anyquark(part) && !(std::abs(part.type)%2);
}
bool is_down_type(Particle const & part){
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 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,
Subleading channels,
Process const & proc,
RNG & ran
){
if(proc.njets <= 2) return;
assert(outgoing_.size() >= 2);
// decide what kind of subleading process is allowed
- const size_t nout = outgoing_.size();
- const bool can_be_uno_backward = can_swap_to_uno(outgoing_[0], outgoing_[1]);
- const bool can_be_uno_forward = can_swap_to_uno(outgoing_[nout-1], outgoing_[nout-2]);
+ bool can_be_uno_backward = can_swap_to_uno(
+ *cbegin_partons(), *(++cbegin_partons()) );
+ bool can_be_uno_forward = can_swap_to_uno(
+ *crbegin_partons(), *(++crbegin_partons()) );
+ // Special case: Higgs can not be outside of uno
+ if(proc.boson && *proc.boson==pid::Higgs){
+ if(outgoing_.begin()->type == pid::Higgs
+ || (++outgoing_.begin())->type==pid::Higgs){
+ can_be_uno_backward = false;
+ }
+ if(outgoing_.rbegin()->type == pid::Higgs
+ || (++outgoing_.rbegin())->type==pid::Higgs){
+ can_be_uno_forward = false;
+ }
+ }
if(channels[subleading::uno]){
channels.set(subleading::uno, can_be_uno_backward || can_be_uno_forward);
}
- channels &= possible_qqx(outgoing_.cbegin(), outgoing_.cend());
+ channels &= possible_qqx(cbegin_partons(), cend_partons());
bool allow_strange = true;
if(is_AWZ_proccess(proc)) {
- if(std::none_of(outgoing_.cbegin(), outgoing_.cend(),
+ if(std::none_of(cbegin_partons(), cend_partons(),
[&proc](Particle const & p){
return can_couple_to_W(p, *proc.boson);})) {
// enforce qqx if A/W/Z can't couple somewhere else
// this is ensured to work through filter_partons in reconstruct_incoming
channels.reset(subleading::uno);
assert(channels.any());
chance = 1.;
// strange not allowed for W
if(std::abs(*proc.boson)== pid::Wp) allow_strange = false;
}
}
// no subleading
std::size_t const nchannels = channels.count();
if(nchannels==0) return;
if(ran.flat() >= chance){
weight_ /= 1 - chance;
return;
}
// select channel
weight_ /= chance;
double const step = 1./nchannels;
weight_*=nchannels;
unsigned selected = subleading::first;
double rnd = nchannels>1?ran.flat():0.;
for(; selected<=subleading::last; ++selected){
assert(rnd>=0);
if(channels[selected]){
if(rnd<step) break;
rnd-=step;
}
}
switch(selected){
case subleading::uno:
return turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
case subleading::qqx:
case subleading::cqqx:
case subleading::eqqx:
return turn_to_qqx(allow_strange, ran);
default:
throw std::logic_error{"unreachable"};
}
}
void PhaseSpacePoint::turn_to_uno(
const bool can_be_uno_backward, const bool can_be_uno_forward,
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){
+ weight_ *= 2.;
if(ran.flat() < 0.5){
- std::swap(outgoing_[0].type, outgoing_[1].type);
- } else {
- std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type);
+ return std::swap(begin_partons()->type, (++begin_partons())->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);
+ return std::swap(rbegin_partons()->type, (++rbegin_partons())->type);
}
+ if(can_be_uno_backward){
+ return std::swap(begin_partons()->type, (++begin_partons())->type);
+ }
+ assert(can_be_uno_forward);
+ std::swap(rbegin_partons()->type, (++rbegin_partons())->type);
}
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;
+ std::vector<std::vector<Particle>::iterator> FKL_gluons;
for(auto p = first; p!=outgoing_.end(); ++p){
- if(p->type == pid::gluon) FKL_gluons.push_back(&*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 = 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 = 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<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,
PDF & pdf, double E_beam,
double const subl_chance,
Subleading const subl_channels,
ParticlesDecayMap const & particle_decays,
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
auto const & 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);
auto const & 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_ *= 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;
+ begin_partons()->type = incoming_[0].type;
+ rbegin_partons()->type = incoming_[1].type;
maybe_turn_to_subl(subl_chance, subl_channels, proc, ran);
if(proc.boson) couple_boson(*proc.boson, ran);
}
// pt generation, see eq:pt_sampling in developer manual
double PhaseSpacePoint::gen_hard_pt(
const int np , const double ptmin, const double ptmax, const double /* y */,
RNG & ran
) {
// heuristic parameter for pt sampling, see eq:pt_par in developer manual
const double ptpar = ptmin + np/5.;
const double arctan = std::atan((ptmax - ptmin)/ptpar);
const double xpt = ran.flat();
const double pt = ptmin + ptpar*std::tan(xpt*arctan);
const double cosine = std::cos(xpt*arctan);
weight_ *= pt*ptpar*arctan/(cosine*cosine);
return pt;
}
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,
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,
RNG & ran
){
if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"};
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(
ParticleID bosonid, double mass, double width,
RNG & ran
){
// Usual phase space measure
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*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(std::abs(bosonid) == pid::Wp){
weight_/=M_PI*M_PI*16.; //Corrects B-W factors, see git issue 132
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(!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(!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(!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(!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 : std::abs(id)*2;
}
using part_mask = std::bitset<11>; //!< Selection mask for partons
part_mask init_allowed(ParticleID const id){
if(std::abs(id) == pid::proton)
return ~0;
part_mask 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
part_mask allowed_quarks(ParticleID const boson){
part_mask allowed = ~0;
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;
}
/**
* @brief Returns list of all allowed initial states partons
*
* 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<part_mask,2> filter_partons(
Process const & proc, Subleading const subl_channels, RNG & ran
){
std::array<part_mask,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.reset(0);
// possible states per leg
std::array<part_mask,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, Subleading const subl_channels,
PDF & pdf, double E_beam,
double uf,
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<part_mask,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));
}
ParticleID PhaseSpacePoint::generate_incoming_id(
size_t const beam_idx, double const x, double const uf,
PDF & pdf, part_mask allowed_partons, RNG & ran
){
std::array<double,11> pdf_wt;
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.*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(
ParticleID const boson, RNG & ran
){
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_){
+ std::vector<PartonIterator> allowed_parts;
+ for(auto part_it=begin_partons(); part_it!=end_partons(); ++part_it){
// 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 ( can_couple_to_W(*part_it, boson) )
+ allowed_parts.push_back(part_it);
}
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 = 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,
RNG & ran
){
const double r1 = ran.flat();
const double r2 = ran.flat();
const double lninvr1 = -std::log(r1);
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,
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(
Particle const & parent,
std::vector<Decay> const & decays,
RNG & ran
){
const auto channel = select_decay_channel(decays, ran);
return decay_boson(parent, channel.products, ran);
}
std::vector<Particle> PhaseSpacePoint::decay_boson(
Particle const & parent,
std::vector<ParticleID> const & decays,
RNG & ran
){
if(decays.size() != 2){
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.; // Jacobian Factors for W in line 418
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/t/CMakeLists.txt b/FixedOrderGen/t/CMakeLists.txt
index b16fb1a..241ceaf 100644
--- a/FixedOrderGen/t/CMakeLists.txt
+++ b/FixedOrderGen/t/CMakeLists.txt
@@ -1,108 +1,108 @@
set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}")
foreach(tst W_reconstruct_enu W_2j_classify W_nj_classify)
add_executable(test_${tst} ${tst_dir}/${tst}.cc)
target_link_libraries(test_${tst} hejfog_lib)
add_test(NAME ${tst} COMMAND test_${tst} WORKING_DIRECTORY ${tst_dir})
endforeach()
# this only tests if the runcard actually works, not if the result is correct
add_test(
NAME main_2j
COMMAND HEJFOG ${tst_dir}/config_2j.yml
)
add_test(
NAME main_h2j
COMMAND HEJFOG ${tst_dir}/config_h2j.yml
)
add_test(
NAME main_h2j_decay
COMMAND HEJFOG ${tst_dir}/config_h2j_decay.yml
)
add_test(
NAME peakpt
COMMAND HEJFOG ${tst_dir}/config_2j_peak.yml
)
# check that uno emission doesn't change FKL xs
add_executable(FKL_uno FKL_uno.cc)
target_link_libraries(FKL_uno hejfog_lib)
add_test(
NAME FKL_uno
# calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
COMMAND FKL_uno ${tst_dir}/config_h3j_uno.yml 0.0243548 0.000119862
)
# xs tests
add_executable(xs_gen xs_gen.cc)
target_link_libraries(xs_gen hejfog_lib)
## Higgs
add_test(
NAME xs_h2j
# calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
COMMAND xs_gen ${tst_dir}/config_h2j.yml 2.04928 0.00956022
)
add_test(
NAME xs_h3j
# calculated with HEJ revision bd4388fe55cbc3f5a7b6139096456c551294aa31
COMMAND xs_gen ${tst_dir}/config_h3j.yml 1.07807 0.0132409
)
add_test(
NAME xs_h5j
# calculated with HEJ revision dbde2ffbb3b383ae6709b2424d8f0f9d5658270b
COMMAND xs_gen ${tst_dir}/config_h5j.yml 0.0112504 0.000199633
)
add_test(
NAME xs_h3j_uno
# calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
COMMAND xs_gen ${tst_dir}/config_h3j_uno.yml 0.00347538 3.85875e-05
)
add_test(
NAME xs_h2j_decay
# calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
COMMAND xs_gen ${tst_dir}/config_h2j_decay.yml 0.00466994 2.20995e-05
)
## pure jets
add_test(
NAME xs_2j
# calculated with "combined" HEJ svn r3480
COMMAND xs_gen ${tst_dir}/config_2j.yml 86.42031848e06 590570
)
add_test(
NAME xs_3j_uno
# calculated with HEJ revision b40e0212b144cdd267df19333b574cc7b91847f1
COMMAND xs_gen ${tst_dir}/config_3j_uno.yml 520697 8948.18
)
add_test(
NAME xs_3j_qqx
# calculated with HEJ revision b40e0212b144cdd267df19333b574cc7b91847f1
COMMAND xs_gen ${tst_dir}/config_3j_qqx.yml 122233 1613.04
)
add_test(
NAME xs_4j_qqx
# calculated with HEJ revision b40e0212b144cdd267df19333b574cc7b91847f1
COMMAND xs_gen ${tst_dir}/config_4j_qqx.yml 29805.4 581.473
)
add_test(
NAME xs_4j
# calculated with HEJ revision 13207b5f67a5f40a2141aa7ee515b022bd4efb65
COMMAND xs_gen ${tst_dir}/config_4j.yml 915072 15402.4
)
## W
add_test(
NAME xs_W2j
# calculated with HEJ revision 987bb30a7985a24a7961f98cfbbc74d3a7992970
COMMAND xs_gen ${tst_dir}/config_Wm2j.yml 231.404 3.43798
)
add_test(
NAME xs_W3j_uno
- # calculated with HEJ revision 987bb30a7985a24a7961f98cfbbc74d3a7992970
- COMMAND xs_gen ${tst_dir}/config_Wp3j_uno.yml 0.139372 0.00248345
+ # calculated with HEJ revision 996f728a56ed67b980fccbe79948fe56914aaccd+1
+ COMMAND xs_gen ${tst_dir}/config_Wp3j_uno.yml 0.295275 0.0056667
)
add_test(
NAME xs_W3j_eqqx
# calculated with HEJ revision 987bb30a7985a24a7961f98cfbbc74d3a7992970
COMMAND xs_gen ${tst_dir}/config_Wp3j_qqx.yml 8.45323 0.103449
)
add_test(
NAME xs_W4j_qqx
# calculated with HEJ revision 987bb30a7985a24a7961f98cfbbc74d3a7992970
COMMAND xs_gen ${tst_dir}/config_Wp4j_qqx.yml 0.0851908 0.00300447
)

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jan 20, 10:31 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4242686
Default Alt Text
(45 KB)

Event Timeline