Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F8724139
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
45 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rHEJ HEJ
Event Timeline
Log In to Comment