Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F8723756
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Subscribers
None
View Options
diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh
index a090e92..65df4f5 100644
--- a/FixedOrderGen/include/PhaseSpacePoint.hh
+++ b/FixedOrderGen/include/PhaseSpacePoint.hh
@@ -1,149 +1,153 @@
/** \file PhaseSpacePoint.hh
* \brief Contains the PhaseSpacePoint Class
*/
#pragma once
#include <vector>
#include "fastjet/JetDefinition.hh"
#include "RHEJ/utility.hh"
#include "RHEJ/Event.hh"
#include "RHEJ/PDG_codes.hh"
#include "RHEJ/PDF.hh"
#include "RHEJ/RNG.hh"
#include "Status.hh"
#include "HiggsProperties.hh"
namespace HEJFOG{
class Process;
using RHEJ::Particle;
//! A point in resummation phase space
class PhaseSpacePoint{
public:
//! Default PhaseSpacePoint Constructor
PhaseSpacePoint() = default;
//! PhaseSpacePoint Constructor
/**
* @param proc The process to generate
* @param jet_def The Jet Definition Used
* @param jetptmin Minimum Jet Transverse Momentum
* @param rapmax Maximum parton rapidity
* @param pdf The pdf set (used for sampling)
* @param uno_chance Chance to turn a potentially unordered
* emission into an actual one
*
* Initially, only FKL phase space points are generated. If the most
* extremal emission in any direction is a quark or anti-quark and the
* next emission is a gluon, uno_chance is the chance to turn this into
* an unordered emission event by swapping the two flavours. At most one
* unordered emission will be generated in this way.
*/
PhaseSpacePoint(
Process const & proc,
fastjet::JetDefinition jet_def,double jetptmin, double rapmax,
RHEJ::PDF & pdf, double E_beam,
double uno_chance,
HiggsProperties const & higgs_properties,
RHEJ::RNG & ran
);
//! Get Weight Function
/**
* @returns Weight of Event
*/
double weight() const{
return weight_;
}
Status status() const{
return status_;
}
//! Get Incoming Function
/**
* @returns Incoming Particles
*/
std::array<Particle, 2> const & incoming() const{
return incoming_;
}
//! Get Outgoing Function
/**
* @returns Outgoing Particles
*/
std::vector<Particle> const & outgoing() const{
return outgoing_;
}
std::unordered_map<int, std::vector<Particle>> const & decays() const{
return decays_;
}
private:
std::vector<fastjet::PseudoJet> gen_LO_partons(
int count, double ptmin, double ptmax, double rapmax,
RHEJ::RNG & ran
);
Particle gen_boson(
RHEJ::ParticleID bosonid, double mass, double width,
RHEJ::RNG & ran
);
bool jets_ok(
std::vector<fastjet::PseudoJet> const & Born_jets,
std::vector<fastjet::PseudoJet> const & partons
) const;
void reconstruct_incoming(
std::array<RHEJ::ParticleID, 2> const & ids,
RHEJ::PDF & pdf, double E_beam,
RHEJ::RNG & ran
);
RHEJ::ParticleID generate_incoming_id(
double x, double uf, RHEJ::PDF & pdf,
RHEJ::RNG & ran
);
bool momentum_conserved(double ep) const;
RHEJ::Particle const & most_backward_FKL(
std::vector<RHEJ::Particle> const & partons
) const;
RHEJ::Particle const & most_forward_FKL(
std::vector<RHEJ::Particle> const & partons
) const;
RHEJ::Particle & most_backward_FKL(std::vector<RHEJ::Particle> & partons) const;
RHEJ::Particle & most_forward_FKL(std::vector<RHEJ::Particle> & partons) const;
bool extremal_FKL_ok(
std::vector<fastjet::PseudoJet> const & partons
) const;
double random_normal(double stddev, RHEJ::RNG & ran);
void maybe_turn_to_uno(double chance, RHEJ::RNG & ran);
std::vector<Particle> decay_boson(
RHEJ::Particle const & parent,
std::vector<Decay> const & decays,
RHEJ::RNG & ran
);
Decay select_decay_channel(
std::vector<Decay> const & decays,
RHEJ::RNG & ran
);
+ double gen_hard_pt(
+ int count, double ptmin, double ptmax, double y,
+ RHEJ::RNG & ran
+ );
double weight_;
Status status_;
double jetptmin_;
fastjet::JetDefinition jet_def_;
std::array<Particle, 2> incoming_;
std::vector<Particle> outgoing_;
//! Particle decays in the format {outgoing index, decay products}
std::unordered_map<int, std::vector<Particle>> decays_;
};
RHEJ::UnclusteredEvent to_UnclusteredEvent(PhaseSpacePoint const & psp);
}
diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc
index f922cd4..25989a2 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,419 +1,427 @@
#include "PhaseSpacePoint.hh"
#include <random>
#include "RHEJ/kinematics.hh"
#include "RHEJ/utility.hh"
#include "RHEJ/exceptions.hh"
#include "Process.hh"
#include <CLHEP/Random/Randomize.h>
#include <CLHEP/Random/RanluxEngine.h>
using namespace RHEJ;
namespace HEJFOG{
static_assert(
std::numeric_limits<double>::has_quiet_NaN,
"no quiet NaN for double"
);
constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
RHEJ::UnclusteredEvent to_UnclusteredEvent(PhaseSpacePoint const & psp){
RHEJ::UnclusteredEvent result;
result.incoming = psp.incoming();
std::sort(
begin(result.incoming), end(result.incoming),
[](Particle o1, Particle o2){return o1.p.pz()<o2.p.pz();}
);
assert(result.incoming.size() == 2);
result.outgoing = psp.outgoing();
assert(
std::is_sorted(
begin(result.outgoing), end(result.outgoing),
RHEJ::rapidity_less{}
)
);
assert(result.outgoing.size() >= 2);
result.decays = psp.decays();
result.central.mur = NaN;
result.central.muf = NaN;
result.central.weight = psp.weight();
return result;
}
namespace{
bool can_swap_to_uno(
RHEJ::Particle const & p1, RHEJ::Particle const & p2
){
return is_parton(p1)
&& p1.type != RHEJ::pid::gluon
&& p2.type == RHEJ::pid::gluon;
}
}
void PhaseSpacePoint::maybe_turn_to_uno(
double chance,
RHEJ::RNG & ran
){
assert(outgoing_.size() >= 2);
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]
);
if(!can_be_uno_backward && !can_be_uno_forward) return;
if(ran.flat() < chance){
weight_ /= chance;
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);
}
}
else weight_ /= 1 - chance;
}
PhaseSpacePoint::PhaseSpacePoint(
Process const & proc,
fastjet::JetDefinition jet_def,double jetptmin, double rapmax,
RHEJ::PDF & pdf, double E_beam,
double uno_chance,
HiggsProperties const & h,
RHEJ::RNG & ran
):
jetptmin_{jetptmin},
jet_def_{jet_def}
{
assert(proc.njets >= 2);
const int nout = proc.njets + (proc.boson?1:0);
status_ = good;
weight_ = 1;
weight_ /= std::tgamma(nout);
outgoing_.reserve(nout);
for(auto&& p_out: gen_LO_partons(proc.njets, jetptmin_, E_beam, rapmax, ran)){
outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out)});
}
if(status_ != good) return;
if(proc.boson && *proc.boson == pid::Higgs){
// The Higgs
auto Hparticle=gen_boson(pid::higgs, h.mass, h.width, ran);
auto pos=std::upper_bound(
begin(outgoing_),end(outgoing_),Hparticle,rapidity_less{}
);
outgoing_.insert(pos, Hparticle);
if(! h.decays.empty()){
const int boson_idx = std::distance(begin(outgoing_), pos);
decays_.emplace(
boson_idx,
decay_boson(outgoing_[boson_idx], h.decays, ran)
);
}
}
reconstruct_incoming(proc.incoming, pdf, E_beam, ran);
if(status_ != good) return;
// set outgoing states
most_backward_FKL(outgoing_).type = incoming_[0].type;
most_forward_FKL(outgoing_).type = incoming_[1].type;
if(proc.njets > 2) maybe_turn_to_uno(uno_chance, ran);
}
- std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_LO_partons(
- int np , double ptmin, double ptmax, double rapmax,
+ double PhaseSpacePoint::gen_hard_pt(
+ int np , double ptmin, double ptmax, double y,
RHEJ::RNG & ran
- ){
- if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"};
+ ) {
// heuristic parameters for pt sampling
const double ptpar = ptmin + np/5.;
const double arg_small_y = atan((ptmax - ptmin)/ptpar);
const double y_cut = 3.;
+ const double r1 = ran.flat();
+ if(y < y_cut){
+ const double pt = ptmin + ptpar*tan(r1*arg_small_y);
+ const double temp = cos(r1*arg_small_y);
+ weight_ *= pt*ptpar*arg_small_y/(temp*temp);
+ return pt;
+ }
+
+ const double ptpar2 = ptpar/(1 + 5*(y-y_cut));
+ const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2);
+ const double pt = ptmin - ptpar2*std::log(1-r1*temp);
+ weight_ *= pt*ptpar2*temp/(1-r1*temp);
+ return pt;
+ }
+
+ std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_LO_partons(
+ int np , double ptmin, double ptmax, double rapmax,
+ RHEJ::RNG & ran
+ ){
+ if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"};
+
weight_ /= pow(16.*pow(M_PI,3),np-2);
std::vector<fastjet::PseudoJet> partons;
partons.reserve(np);
for(int i = 0; i < np; ++i){
const double y = -rapmax + 2*rapmax*ran.flat();
weight_ *= 2*rapmax;
const double phi = 2*M_PI*ran.flat();
weight_ *= 2.0*M_PI;
- double pt;
- const double r1 = ran.flat();
- if(y < y_cut){
- pt = ptmin + ptpar*tan(r1*arg_small_y);
- const double temp = cos(r1*arg_small_y);
- weight_ *= pt*ptpar*arg_small_y/(temp*temp);
- }
- else{
- const double ptpar2 = ptpar/(1 + 5*(y-y_cut));
- const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2);
- pt = ptmin - ptpar2*std::log(1-r1*temp);
- weight_ *= pt*ptpar2*temp/(1-r1*temp);
- }
+ const double pt = gen_hard_pt(np, ptmin, ptmax, y, ran);
partons.emplace_back(fastjet::PtYPhiM(pt, y, phi));
assert(ptmin-1e-5 <= partons[i].pt() && partons[i].pt() <= ptmax+1e-5);
}
// Need to check that at LO, the number of jets = number of partons;
fastjet::ClusterSequence cs(partons, jet_def_);
auto cluster_jets=cs.inclusive_jets(jetptmin_);
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(
RHEJ::ParticleID bosonid, double mass, double width,
RHEJ::RNG & ran
){
std::array<double,2> ptrans{0.,0.};
for (auto const & parton:outgoing_) {
ptrans[0]-=parton.px();
ptrans[1]-=parton.py();
}
// The Higgs:
// Generate a y Gaussian distributed around 0
const double y = random_normal(1.6, ran);
const double r1 = ran.flat();
const double sH = mass*(
mass + width*tan(M_PI/2.*r1 + (r1-1.)*atan(mass/width))
);
const double mHperp=sqrt(ptrans[0]*ptrans[0]+ptrans[1]*ptrans[1]+sH);
const double pz=mHperp*sinh(y);
const double E=mHperp*cosh(y);
return Particle{bosonid,fastjet::PseudoJet{ptrans[0],ptrans[1],pz,E}};
}
Particle const & PhaseSpacePoint::most_backward_FKL(
std::vector<Particle> const & partons
) const{
if(!RHEJ::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(!RHEJ::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(!RHEJ::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(!RHEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
return partons[last_idx];
}
void PhaseSpacePoint::reconstruct_incoming(
std::array<RHEJ::ParticleID, 2> const & ids,
RHEJ::PDF & pdf, double E_beam,
RHEJ::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].p.e()-incoming_[0].p.pz())/sqrts;
const double xb=(incoming_[1].p.e()+incoming_[1].p.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;
}
// pick pdfs
/** TODO:
* ufa, ufb don't correspond to our final scale choice.
* The reversed HEJ scale generators currently expect a full event as input,
* so fixing this is not completely trivial
*/
if(ids[0] == pid::proton || ids[0] == pid::p_bar){
const double ufa=jetptmin_;
incoming_[0].type = generate_incoming_id(xa, ufa, pdf, ran);
}
else {
incoming_[0].type = ids[0];
}
if(ids[1] == pid::proton || ids[1] == pid::p_bar){
const double ufb=jetptmin_;
incoming_[1].type = generate_incoming_id(xb, ufb, pdf, ran);
}
else {
incoming_[1].type = ids[1];
}
assert(momentum_conserved(1e-7));
}
RHEJ::ParticleID PhaseSpacePoint::generate_incoming_id(
double x, double uf, RHEJ::PDF & pdf,
RHEJ::RNG & ran
){
const double pdfg=fabs(pdf.pdfpt(0,x,uf,pid::gluon));
const double pdfu=fabs(pdf.pdfpt(0,x,uf,pid::up));
const double pdfd=fabs(pdf.pdfpt(0,x,uf,pid::down));
const double pdfux=fabs(pdf.pdfpt(0,x,uf,pid::u_bar));
const double pdfdx=fabs(pdf.pdfpt(0,x,uf,pid::d_bar));
const double pdfc=fabs(pdf.pdfpt(0,x,uf,pid::charm));
const double pdfs=fabs(pdf.pdfpt(0,x,uf,pid::strange));
const double pdfsx=fabs(pdf.pdfpt(0,x,uf,pid::s_bar));
const double pdfb=fabs(pdf.pdfpt(0,x,uf,pid::b));
const double pdftot=pdfg+4.0/9.0*(pdfu + pdfd + pdfux + pdfdx +pdfs +pdfsx + 2.0*(pdfc +pdfb ));
const double r1=pdftot*ran.flat();
double sum;
if (r1<(sum=pdfg)) {
weight_*=pdftot/pdfg;
return pid::gluon;
}
if (r1<(sum+=(4./9.)*pdfu)) {
weight_*=pdftot/((4./9.)*pdfu);
return pid::up;
}
else if (r1<(sum+=(4./9.)*pdfd)) {
weight_*=pdftot/((4./9.)*pdfd);
return pid::down;
}
else if (r1<(sum+=(4./9.)*pdfux)) {
weight_*=pdftot/((4./9.)*pdfux);
return pid::u_bar;
}
else if (r1<(sum+=(4./9.)*pdfdx)) {
weight_*=pdftot/((4./9.)*pdfdx);
return pid::d_bar;
}
else if (r1<(sum+=(4./9.)*pdfc)) {
weight_*=pdftot/((4./9.)*pdfc);
return pid::c;
}
else if (r1<(sum+=(4./9.)*pdfc)){
weight_*=pdftot/((4./9.)*pdfc);
return pid::c_bar;
}
else if (r1<(sum+=(4./9.)*pdfs)) {
weight_*=pdftot/((4./9.)*pdfs);
return pid::s;
}
else if (r1<(sum+=(4./9.)*pdfsx)) {
weight_*=pdftot/((4./9.)*pdfsx);
return pid::s_bar;
}
else if (r1<(sum+=(4./9.)*pdfb)) {
weight_*=pdftot/((4./9.)*pdfb);
return pid::b;
}
else if (r1<=(sum+=(4./9.)*pdfb)) {
weight_*=pdftot/((4./9.)*pdfb);
return pid::b_bar;
}
std::cout << "Error in choosing incoming parton: "<<x<<" "<<uf<<" "<<sum<<" "<<pdftot<<" "<<r1;
std::cout << " "<<pdfg+4./9.*(pdfu+pdfux+pdfd+pdfdx+pdfs+pdfsx+2.*(pdfc+pdfb))<<std::endl;
throw std::logic_error{"Failed to choose parton flavour"};
}
double PhaseSpacePoint::random_normal(
double stddev,
RHEJ::RNG & ran
){
const double r1 = ran.flat();
const double r2 = ran.flat();
const double lninvr1 = -log(r1);
const double result = stddev*sqrt(2.*lninvr1)*cos(2.*M_PI*r2);
weight_ *= exp(result*result/(2*stddev*stddev))*sqrt(2.*M_PI)*stddev;
return result;
}
bool PhaseSpacePoint::momentum_conserved(double ep) const{
fastjet::PseudoJet diff;
for(auto const & in: incoming()) diff += in.p;
for(auto const & out: outgoing()) diff -= out.p;
return nearby_ep(diff, fastjet::PseudoJet{}, ep);
}
Decay PhaseSpacePoint::select_decay_channel(
std::vector<Decay> const & decays,
RHEJ::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;
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(
RHEJ::Particle const & parent,
std::vector<Decay> const & decays,
RHEJ::RNG & ran
){
const auto channel = select_decay_channel(decays, ran);
if(channel.products.size() != 2){
throw RHEJ::not_implemented{
"only decays into two particles are implemented"
};
}
std::vector<Particle> decay_products(channel.products.size());
for(size_t i = 0; i < channel.products.size(); ++i){
decay_products[i].type = channel.products[i];
}
// choose polar and azimuth angle in parent rest frame
const double E = parent.m()/2;
const double theta = 2.*M_PI*ran.flat();
const double cos_phi = 2.*ran.flat()-1.;
const double sin_phi = sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi
const double px = E*cos(theta)*sin_phi;
const double py = E*sin(theta)*sin_phi;
const double pz = E*cos_phi;
decay_products[0].p.reset(px, py, pz, E);
decay_products[1].p.reset(-px, -py, -pz, E);
for(auto & particle: decay_products) particle.p.boost(parent.p);
return decay_products;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Jan 20, 9:15 PM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4242294
Default Alt Text
(19 KB)
Attached To
rHEJ HEJ
Event Timeline
Log In to Comment