Page MenuHomeHEPForge

No OneTemporary

diff --git a/include/RHEJ/JetSplitter.hh b/include/RHEJ/JetSplitter.hh
new file mode 100644
index 0000000..385870c
--- /dev/null
+++ b/include/RHEJ/JetSplitter.hh
@@ -0,0 +1,57 @@
+/**
+ * \file
+ * \brief Declaration of the JetSplitter class
+ */
+
+#pragma once
+
+#include "RHEJ/utility.hh"
+#include "RHEJ/RNG.hh"
+
+namespace RHEJ {
+ //! Class to split jets into their constituents
+ class JetSplitter {
+
+ public:
+ struct SplitResult {
+ std::vector<fastjet::PseudoJet> constituents;
+ double weight;
+ };
+
+ //! Constructor
+ /**
+ * @param jet_def Jet definition
+ * @param min_pt Minimum jet transverse momentum
+ * @param ran Random number generator
+ */
+ JetSplitter(
+ fastjet::JetDefinition jet_def, double min_pt,
+ RHEJ::RNG & ran
+ ):
+ R_{jet_def.R()},
+ min_jet_pt_{min_pt},
+ jet_def_{jet_def},
+ ran_{ran}
+ {}
+
+ //! Split a get into constituents
+ /**
+ * @param j2split Jet to be split
+ * @param ncons Number of constituents
+ * @returns The constituent momenta
+ * together with the associated weight
+ */
+ SplitResult split(fastjet::PseudoJet const & j2split, int ncons) const;
+
+ //! Maximum distance of constituents to jet axis
+ static constexpr double R_factor = 5./3.;
+ private:
+ SplitResult Split2(fastjet::PseudoJet const & j2split) const;
+ double sample_distance_2p(double & wt) const;
+
+ double R_;
+ double min_jet_pt_;
+ fastjet::JetDefinition jet_def_;
+ std::reference_wrapper<RHEJ::RNG> ran_;
+ };
+}
diff --git a/include/RHEJ/PhaseSpacePoint.hh b/include/RHEJ/PhaseSpacePoint.hh
index 7df62aa..03f3917 100644
--- a/include/RHEJ/PhaseSpacePoint.hh
+++ b/include/RHEJ/PhaseSpacePoint.hh
@@ -1,149 +1,148 @@
/** \file
* \brief Contains the PhaseSpacePoint Class
*/
#pragma once
#include <vector>
#include "RHEJ/utility.hh"
#include "RHEJ/config.hh"
#include "RHEJ/Event.hh"
-#include "RHEJ/Splitter.hh"
+#include "RHEJ/JetSplitter.hh"
#include "RHEJ/RNG.hh"
namespace RHEJ{
//! A point in resummation phase space
class PhaseSpacePoint{
public:
//! Default PhaseSpacePoint Constructor
PhaseSpacePoint() = default;
//! PhaseSpacePoint Constructor
/**
* @param ev Clustered Jet Event
* @param conf Configuration parameters
* @param ran Random number generator
*/
PhaseSpacePoint(
Event const & ev,
PhaseSpacePointConfig conf,
RHEJ::RNG & ran
);
//! Get phase space point weight
double weight() const{
return weight_;
}
//! Access incoming particles
std::array<Particle, 2> const & incoming() const{
return incoming_;
}
//! Access outgoing particles
std::vector<Particle> const & outgoing() const{
return outgoing_;
}
//! Particle decays
/**
* The key in the returned map corresponds to the index in the
* vector returned by outgoing()
*/
std::unordered_map<int, std::vector<Particle>> const & decays() const{
return decays_;
}
static constexpr int ng_max = 1000; //< maximum number of extra gluons
private:
std::vector<fastjet::PseudoJet> cluster_jets(
std::vector<fastjet::PseudoJet> const & partons
) const;
bool pass_resummation_cuts(
std::vector<fastjet::PseudoJet> const & jets
) const;
bool pass_extremal_cuts(
fastjet::PseudoJet const & ext_parton,
fastjet::PseudoJet const & jet
) const;
int sample_ng(std::vector<fastjet::PseudoJet> const & Born_jets);
int sample_ng_jets(int ng, std::vector<fastjet::PseudoJet> const & Born_jets);
double probability_in_jet(
std::vector<fastjet::PseudoJet> const & Born_jets
) const;
std::vector<fastjet::PseudoJet> gen_non_jet(
int ng_non_jet,
double ptmin, double ptmax
);
void rescale_rapidities(
std::vector<fastjet::PseudoJet> & partons,
double ymin, double ymax
);
std::vector<fastjet::PseudoJet> reshuffle(
std::vector<fastjet::PseudoJet> const & Born_jets,
fastjet::PseudoJet const & q
);
bool jets_ok(
std::vector<fastjet::PseudoJet> const & Born_jets,
std::vector<fastjet::PseudoJet> const & partons
) const;
void reconstruct_incoming(std::array<Particle, 2> const & Born_incoming);
double phase_space_normalisation(
int num_Born_jets,
int num_res_partons
) const;
std::vector<fastjet::PseudoJet> split(
std::vector<fastjet::PseudoJet> const & jets, int ng_jets
);
std::vector<int> distribute_jet_partons(
int ng_jets, std::vector<fastjet::PseudoJet> const & jets
);
std::vector<fastjet::PseudoJet> split(
std::vector<fastjet::PseudoJet> const & jets,
std::vector<int> const & np_in_jet
);
bool split_preserved_jets(
std::vector<fastjet::PseudoJet> const & jets,
std::vector<fastjet::PseudoJet> const & jet_partons
) const;
template<class Particle>
Particle const & most_backward_FKL(
std::vector<Particle> const & partons
) const;
template<class Particle>
Particle const & most_forward_FKL(
std::vector<Particle> const & partons
) const;
template<class Particle>
Particle & most_backward_FKL(std::vector<Particle> & partons) const;
template<class Particle>
Particle & most_forward_FKL(std::vector<Particle> & partons) const;
bool extremal_ok(
std::vector<fastjet::PseudoJet> const & partons
) const;
void copy_AWZH_boson_from(Event const & event);
bool momentum_conserved() const;
bool unob_, unof_;
double weight_;
PhaseSpacePointConfig param_;
std::array<Particle, 2> incoming_;
std::vector<Particle> outgoing_;
//! \internal Particle decays in the format {outgoing index, decay products}
std::unordered_map<int, std::vector<Particle>> decays_;
std::reference_wrapper<RHEJ::RNG> ran_;
- HejSplit splitter_;
};
}
diff --git a/include/RHEJ/Splitter.hh b/include/RHEJ/Splitter.hh
deleted file mode 100644
index 8e838b2..0000000
--- a/include/RHEJ/Splitter.hh
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * \file
- * \brief Declaration of the HejSplit class
- */
-
-#pragma once
-
-#include "RHEJ/utility.hh"
-#include "RHEJ/RNG.hh"
-
-//! Class to split jets into their constituents
-class HejSplit {
-
- public:
- //! Constructor
- /**
- * @param rpin Jet radius parameter
- * @param jetdefin Jet definition
- * @param ptmin Minimum jet transverse momentum
- * @param ran Random number generator
- */
- HejSplit(
- double rpin, fastjet::JetDefinition jetdefin, double ptmin,
- RHEJ::RNG & ran
- ):
- ran_{ran},
- rp{rpin},
- jetptmin{ptmin},
- jet_def{jetdefin}
- {}
-
- //! Split a get into constituents
- /**
- * @param j2split Jet to be split
- * @param ncons Number of constituents
- * @returns The weight factor associated with the split
- */
- double Split(fastjet::PseudoJet const & j2split, int ncons);
- void PrintConstituentInfo(std::vector<fastjet::PseudoJet> jets);
-
- double getweight();
- int gettrials();
-
- double get_qwt(){return qwt;}
- double get_swt(){return swt;}
-
- //! Get constituents of the last split jet
- std::vector<fastjet::PseudoJet> const & get_jcons() const{return jcons;}
-
- void clear_jcons(){jcons.clear();}
- void reset_qwt(){qwt=1.;}
- void reset_swt(){qwt=1.;}
-
- //! Maximum distance of constituents to jet axis
- static constexpr double R_factor = 5./3.;
- private:
- double Split2(fastjet::PseudoJet const & j2split);
- double sample_distance_2p(double & wt);
-
- std::reference_wrapper<RHEJ::RNG> ran_;
- double rp;
- double jetptmin;
- fastjet::JetDefinition jet_def;
- double qwt,swt;
- std::vector<fastjet::PseudoJet> jcons;
-
-};
diff --git a/src/JetSplitter.cc b/src/JetSplitter.cc
new file mode 100644
index 0000000..5ddc229
--- /dev/null
+++ b/src/JetSplitter.cc
@@ -0,0 +1,174 @@
+#include "RHEJ/JetSplitter.hh"
+
+#include <numeric>
+
+#include "RHEJ/Constants.hh"
+#include "RHEJ/PhaseSpacePoint.hh"
+
+namespace RHEJ {
+ namespace{
+ constexpr double ccut=RHEJ::CMINPT; // min parton pt
+
+ template<class Iterator>
+ bool same_pt_and_rapidity(
+ Iterator begin, Iterator end,
+ fastjet::PseudoJet const & jet
+ ){
+ static constexpr double ep = 1e-2;
+ const fastjet::PseudoJet reconstructed_jet = std::accumulate(
+ begin, end, fastjet::PseudoJet{}
+ );
+ return
+ (std::abs(reconstructed_jet.pt() - jet.pt()) < ep)
+ && (std::abs(reconstructed_jet.rapidity() - jet.rapidity()) < ep)
+ ;
+ }
+
+ bool all_in_one_jet(
+ std::vector<fastjet::PseudoJet> const & partons,
+ fastjet::JetDefinition jet_def, double min_jet_pt
+ ){
+ fastjet::ClusterSequence ev(partons, jet_def);
+ const std::vector<fastjet::PseudoJet> testjet = ev.inclusive_jets(min_jet_pt);
+ return testjet.size() == 1u
+ && testjet[0].constituents().size() == partons.size();
+ }
+ }
+
+ using SplitResult = JetSplitter::SplitResult;
+
+ SplitResult JetSplitter::split(
+ fastjet::PseudoJet const & j2split, int ncons
+ ) const{
+ if(ncons <= 0) {
+ throw std::invalid_argument{
+ "number of requested jet constituents less than 1"
+ };
+ }
+ double swt = 1.;
+
+ std::vector<fastjet::PseudoJet> jcons;
+ if(ncons == 1){
+ jcons.emplace_back(j2split);
+ jcons.back().set_user_index(0);
+ return {jcons, swt};
+ }
+ if(ncons == 2){
+ return Split2(j2split);
+ }
+ const double R_max = R_factor*R_;
+ assert(R_max < M_PI);
+
+ double pt_remaining = j2split.pt();
+ const double phi_jet = j2split.phi();
+ const double y_jet = j2split.rapidity();
+ for(int i = 0; i < ncons - 1; ++i){
+ /**
+ * Generate rapidity and azimuthal angle with a distance
+ * R = sqrt(delta_y^2 + delta_phi^2) < R_max
+ * from the jet centre
+ */
+ const double R = R_max*ran_.get().flat();
+ const double theta = 2*M_PI*ran_.get().flat();
+ const double delta_phi = R*cos(theta);
+ const double delta_y = R*sin(theta);
+
+ /**
+ * Generate pt such that the total contribution of all partons
+ * along the jet pt axis does not exceed the jet pt
+ */
+ const double pt_max = pt_remaining/cos(delta_phi);
+ assert(pt_max > 0);
+ if(pt_max < ccut) return {}; // no pt remaining for this parton
+ const double pt = (pt_max - ccut)*ran_.get().flat() + ccut;
+ pt_remaining -= pt*cos(delta_phi);
+
+ jcons.emplace_back(
+ pt*cos(phi_jet + delta_phi), pt*sin(phi_jet + delta_phi),
+ pt*sinh(y_jet + delta_y), pt*cosh(y_jet + delta_y)
+ );
+ jcons.back().set_user_index(i);
+ swt *= 2*M_PI*R*R_max*pt*(pt_max - ccut);
+ }
+
+ const fastjet::PseudoJet p_total = std::accumulate(
+ jcons.begin(), jcons.end(), fastjet::PseudoJet{}
+ );
+
+ // Calculate the pt of the last parton
+ const double last_px = j2split.px() - p_total.px();
+ const double last_py = j2split.py() - p_total.py();
+ const double last_pt = sqrt(last_px*last_px + last_py*last_py);
+ if(last_pt < ccut) return {};
+
+ // Calculate the rapidity of the last parton using the requirement that the
+ // new jet must have the same rapidity as the LO jet.
+ const double exp_2y_jet = (j2split.e() + j2split.pz())/(j2split.e() - j2split.pz());
+ const double bb = (p_total.e()+p_total.pz()) - exp_2y_jet*(p_total.e()-p_total.pz());
+ const double lasty = log((-bb+sqrt(bb*bb+4.*exp_2y_jet*last_pt*last_pt))/(2.*last_pt));
+
+ jcons.emplace_back(
+ last_px, last_py, last_pt*sinh(lasty), last_pt*cosh(lasty)
+ );
+ jcons.back().set_user_index(ncons-1);
+ assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
+
+ // Test that the last parton is not too far away from the jet centre.
+ if (jcons.back().delta_R(j2split) > R_max) return {};
+
+ if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {};
+
+ return {jcons, swt};
+ }
+
+ //! sample y-phi distance to jet pt axis for a jet splitting into two partons
+ /**
+ * @param wt Multiplied by the weight of the sampling point
+ * @returns The distance in units of the jet radius
+ */
+ double JetSplitter::sample_distance_2p(double & wt) const{
+ static constexpr double x_small = 0.1;
+ static constexpr double p_small = 0.4;
+
+ const double pR = ran_.get().flat();
+ if(pR < p_small){
+ wt *= x_small/p_small;
+ return x_small/p_small*pR;
+ }
+ wt *= (1-x_small)/(1-p_small);
+ return (1-x_small)/(1-p_small)*(pR-p_small) + x_small;
+ }
+
+ // split jet into two partons
+ SplitResult JetSplitter::Split2(fastjet::PseudoJet const & j2split) const{
+ static constexpr size_t ncons = 2;
+ std::vector<fastjet::PseudoJet> jcons(ncons);
+ std::array<double, ncons> R, phi, y, pt;
+ double wt = 1;
+
+ const double theta = 2*M_PI*ran_.get().flat(); // angle in y-phi plane
+ // empiric observation: we are always within the jet radius
+ R[0] = sample_distance_2p(wt)*R_;
+ R[1] = -sample_distance_2p(wt)*R_;
+ for(size_t i = 0; i <= 1; ++i){
+ phi[i] = j2split.phi() + R[i]*cos(theta);
+ y[i] = j2split.rapidity() + R[i]*sin(theta);
+ }
+ for(size_t i = 0; i <= 1; ++i){
+ pt[i] = (j2split.py() - tan(phi[1-i])*j2split.px())/
+ (sin(phi[i]) - tan(phi[1-i])*cos(phi[i]));
+ if(pt[i] < ccut) return {};
+ jcons[i].reset_PtYPhiM(pt[i], y[i], phi[i]);
+ jcons[i].set_user_index(i);
+ }
+ assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
+
+ if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {};
+ wt *= 2*M_PI*pt[0]*R[0]*R_*R_;
+ // from transformation of delta(R[1] - ...) to delta(pt[0] - ...)
+ const double dphi0 = phi[0] - j2split.phi();
+ const double ptJ = j2split.pt();
+ const double jacobian = cos(theta)*pt[1]*pt[1]/(ptJ*sin(dphi0));
+ return {jcons, jacobian*wt};
+ }
+}
diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc
index d63ed7c..3ac02ab 100644
--- a/src/PhaseSpacePoint.cc
+++ b/src/PhaseSpacePoint.cc
@@ -1,535 +1,537 @@
#include "RHEJ/PhaseSpacePoint.hh"
#include <random>
#include <CLHEP/Random/Randomize.h>
#include <CLHEP/Random/RanluxEngine.h>
#include "RHEJ/Constants.hh"
#include "RHEJ/resummation_jet_momenta.hh"
#include "RHEJ/Jacobian.hh"
#include "RHEJ/uno.hh"
#include "RHEJ/utility.hh"
#include "RHEJ/kinematics.hh"
namespace RHEJ{
namespace {
constexpr int max_jet_user_idx = PhaseSpacePoint::ng_max;
bool is_nonjet_parton(fastjet::PseudoJet const & parton){
assert(parton.user_index() != -1);
return parton.user_index() > max_jet_user_idx;
}
bool is_jet_parton(fastjet::PseudoJet const & parton){
assert(parton.user_index() != -1);
return parton.user_index() <= max_jet_user_idx;
}
// user indices for partons with extremal rapidity
constexpr int unob_idx = -5;
constexpr int unof_idx = -4;
constexpr int backward_FKL_idx = -3;
constexpr int forward_FKL_idx = -2;
}
namespace {
double estimate_ng_mean(std::vector<fastjet::PseudoJet> const & Born_jets){
const double delta_y =
Born_jets.back().rapidity() - Born_jets.front().rapidity();
assert(delta_y > 0);
// Formula derived from fit in reversed HEJ intro paper
return 0.975052*delta_y;
}
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::cluster_jets(
std::vector<fastjet::PseudoJet> const & partons
) const{
fastjet::ClusterSequence cs(partons, param_.jet_param.def);
return cs.inclusive_jets(param_.jet_param.min_pt);
}
bool PhaseSpacePoint::pass_resummation_cuts(
std::vector<fastjet::PseudoJet> const & jets
) const{
return cluster_jets(jets).size() == jets.size();
}
int PhaseSpacePoint::sample_ng(std::vector<fastjet::PseudoJet> const & Born_jets){
const double ng_mean = estimate_ng_mean(Born_jets);
std::poisson_distribution<int> dist(ng_mean);
const int ng = dist(ran_.get());
assert(ng >= 0);
assert(ng < ng_max);
weight_ *= std::tgamma(ng + 1)*std::exp(ng_mean)*std::pow(ng_mean, -ng);
return ng;
}
void PhaseSpacePoint::copy_AWZH_boson_from(Event const & event){
auto const & from = event.outgoing();
const auto AWZH_boson = std::find_if(
begin(from), end(from),
[](Particle const & p){ return is_AWZH_boson(p); }
);
if(AWZH_boson == end(from)) return;
auto insertion_point = std::lower_bound(
begin(outgoing_), end(outgoing_), *AWZH_boson, rapidity_less{}
);
outgoing_.insert(insertion_point, *AWZH_boson);
// copy decay products
const int idx = std::distance(begin(from), AWZH_boson);
const auto decay_it = event.decays().find(idx);
if(decay_it != end(event.decays())){
const int new_idx = std::distance(begin(outgoing_), insertion_point);
assert(outgoing_[new_idx].type == AWZH_boson->type);
decays_.emplace(new_idx, decay_it->second);
}
assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{}));
}
PhaseSpacePoint::PhaseSpacePoint(
Event const & ev, PhaseSpacePointConfig conf, RHEJ::RNG & ran
):
unob_{has_unob_gluon(ev.incoming(), ev.outgoing())},
unof_{!unob_ && has_unof_gluon(ev.incoming(), ev.outgoing())},
param_{std::move(conf)},
- ran_{ran},
- splitter_{param_.jet_param.def.R(), param_.jet_param.def, param_.jet_param.min_pt, ran}
+ ran_{ran}
{
weight_ = 1;
const auto Born_jets = sorted_by_rapidity(ev.jets());
const int ng = sample_ng(Born_jets);
weight_ /= std::tgamma(ng + 1);
const int ng_jets = sample_ng_jets(ng, Born_jets);
std::vector<fastjet::PseudoJet> out_partons = gen_non_jet(
ng - ng_jets, CMINPT, param_.jet_param.min_pt
);
{
const auto qperp = std::accumulate(
begin(out_partons), end(out_partons),
fastjet::PseudoJet{}
);
const auto jets = reshuffle(Born_jets, qperp);
if(weight_ == 0.) return;
if(! pass_resummation_cuts(jets)){
weight_ = 0.;
return;
}
std::vector<fastjet::PseudoJet> jet_partons = split(jets, ng_jets);
if(weight_ == 0.) return;
rescale_rapidities(
out_partons,
most_backward_FKL(jet_partons).rapidity(),
most_forward_FKL(jet_partons).rapidity()
);
if(! cluster_jets(out_partons).empty()){
weight_ = 0.;
return;
}
std::sort(begin(out_partons), end(out_partons), rapidity_less{});
assert(
std::is_sorted(begin(jet_partons), end(jet_partons), rapidity_less{})
);
const auto first_jet_parton = out_partons.insert(
end(out_partons), begin(jet_partons), end(jet_partons)
);
std::inplace_merge(
begin(out_partons), first_jet_parton, end(out_partons), rapidity_less{}
);
}
if(! jets_ok(Born_jets, out_partons)){
weight_ = 0.;
return;
}
weight_ *= phase_space_normalisation(Born_jets.size(), out_partons.size());
outgoing_.reserve(out_partons.size() + 1); // one slot for possible A, W, Z, H
for(auto & p: out_partons){
outgoing_.emplace_back(Particle{pid::gluon, std::move(p)});
}
most_backward_FKL(outgoing_).type = ev.incoming().front().type;
most_forward_FKL(outgoing_).type = ev.incoming().back().type;
copy_AWZH_boson_from(ev);
assert(!outgoing_.empty());
reconstruct_incoming(ev.incoming());
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_non_jet(
int count, double ptmin, double ptmax
){
// heuristic parameters for pt sampling
const double ptpar = 1.3 + count/5.;
const double temp1 = atan((ptmax - ptmin)/ptpar);
std::vector<fastjet::PseudoJet> partons(count);
for(size_t i = 0; i < (size_t) count; ++i){
const double r1 = ran_.get().flat();
const double pt = ptmin + ptpar*tan(r1*temp1);
const double temp2 = cos(r1*temp1);
const double phi = 2*M_PI*ran_.get().flat();
weight_ *= 2.0*M_PI*pt*ptpar*temp1/(temp2*temp2);
// we don't know the allowed rapidity span yet,
// set a random value to be rescaled later on
const double y = ran_.get().flat();
partons[i].reset_PtYPhiM(pt, y, phi);
// Set user index higher than any jet-parton index
// in order to assert that these are not inside jets
partons[i].set_user_index(i + 1 + ng_max);
assert(ptmin-1e-5 <= partons[i].pt() && partons[i].pt() <= ptmax+1e-5);
}
assert(std::all_of(partons.cbegin(), partons.cend(), is_nonjet_parton));
return partons;
}
void PhaseSpacePoint::rescale_rapidities(
std::vector<fastjet::PseudoJet> & partons,
double ymin, double ymax
){
constexpr double ep = 1e-7;
for(auto & parton: partons){
assert(0 <= parton.rapidity() && parton.rapidity() <= 1);
const double dy = ymax - ymin - 2*ep;
const double y = ymin + ep + dy*parton.rapidity();
parton.reset_momentum_PtYPhiM(parton.pt(), y, parton.phi());
weight_ *= dy;
assert(ymin <= parton.rapidity() && parton.rapidity() <= ymax);
}
}
namespace {
template<typename T, typename... Rest>
auto min(T const & a, T const & b, Rest&&... r) {
using std::min;
return min(a, min(b, std::forward<Rest>(r)...));
}
}
double PhaseSpacePoint::probability_in_jet(
std::vector<fastjet::PseudoJet> const & Born_jets
) const{
assert(std::is_sorted(begin(Born_jets), end(Born_jets), rapidity_less{}));
assert(Born_jets.size() >= 2);
const double dy =
Born_jets.back().rapidity() - Born_jets.front().rapidity();
const double R = param_.jet_param.def.R();
const int njets = Born_jets.size();
const double p_J_y_large = (njets-1)*R*R/(2.*dy);
const double p_J_y0 = njets*R/M_PI;
return min(p_J_y_large, p_J_y0, 1.);
}
int PhaseSpacePoint::sample_ng_jets(
int ng, std::vector<fastjet::PseudoJet> const & Born_jets
){
const double p_J = probability_in_jet(Born_jets);
std::binomial_distribution<> bin_dist(ng, p_J);
const int ng_J = bin_dist(ran_.get());
weight_ *= std::pow(p_J, -ng_J)*std::pow(1 - p_J, ng_J - ng);
return ng_J;
}
std::vector<fastjet::PseudoJet>
PhaseSpacePoint::reshuffle(
std::vector<fastjet::PseudoJet> const & Born_jets,
fastjet::PseudoJet const & q
){
if(q == fastjet::PseudoJet{0, 0, 0, 0}) return Born_jets;
std::vector<fastjet::PseudoJet> jets = resummation_jet_momenta(Born_jets, q);
if(jets.empty()){
weight_ = 0;
return {};
}
// transform delta functions to integration over resummation momenta
weight_ /= Jacobian(jets, q);
return jets;
}
std::vector<int> PhaseSpacePoint::distribute_jet_partons(
int ng_jets, std::vector<fastjet::PseudoJet> const & jets
){
size_t first_valid_jet = 0;
size_t num_valid_jets = jets.size();
const double R_eff = 5./3.*param_.jet_param.def.R();
// if there is an unordered jet too far away from the FKL jets
// then extra gluon constituents of the unordered jet would
// violate the FKL rapidity ordering
if(unob_ && jets[0].delta_R(jets[1]) > R_eff){
++first_valid_jet;
--num_valid_jets;
}
else if(unof_ && jets[jets.size()-1].delta_R(jets[jets.size()-2]) > R_eff){
--num_valid_jets;
}
std::vector<int> np(jets.size(), 1);
for(int i = 0; i < ng_jets; ++i){
++np[first_valid_jet + ran_.get().flat() * num_valid_jets];
}
weight_ *= std::pow(num_valid_jets, ng_jets);
return np;
}
#ifndef NDEBUG
namespace{
bool tagged_FKL_backward(
std::vector<fastjet::PseudoJet> const & jet_partons
){
return std::find_if(
begin(jet_partons), end(jet_partons),
[](fastjet::PseudoJet const & p){
return p.user_index() == backward_FKL_idx;
}
) != end(jet_partons);
}
bool tagged_FKL_forward(
std::vector<fastjet::PseudoJet> const & jet_partons
){
// the most forward FKL parton is most likely near the end of jet_partons;
// start search from there
return std::find_if(
jet_partons.rbegin(), jet_partons.rend(),
[](fastjet::PseudoJet const & p){
return p.user_index() == forward_FKL_idx;
}
) != jet_partons.rend();
}
bool tagged_FKL_extremal(
std::vector<fastjet::PseudoJet> const & jet_partons
){
return tagged_FKL_backward(jet_partons) && tagged_FKL_forward(jet_partons);
}
} // namespace anonymous
#endif
std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
std::vector<fastjet::PseudoJet> const & jets,
int ng_jets
){
return split(jets, distribute_jet_partons(ng_jets, jets));
}
bool PhaseSpacePoint::pass_extremal_cuts(
fastjet::PseudoJet const & ext_parton,
fastjet::PseudoJet const & jet
) const{
if(ext_parton.pt() < param_.min_extparton_pt) return false;
return (ext_parton - jet).pt()/jet.pt() < param_.max_ext_soft_pt_fraction;
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
std::vector<fastjet::PseudoJet> const & jets,
std::vector<int> const & np
){
assert(! jets.empty());
assert(jets.size() == np.size());
assert(pass_resummation_cuts(jets));
const size_t most_backward_FKL_idx = 0 + unob_;
const size_t most_forward_FKL_idx = jets.size() - 1 - unof_;
+ const auto & jet = param_.jet_param;
+ const JetSplitter jet_splitter{jet.def, jet.min_pt, ran_};
std::vector<fastjet::PseudoJet> jet_partons;
// randomly distribute jet gluons among jets
for(size_t i = 0; i < jets.size(); ++i){
- weight_ *= splitter_.Split(jets[i], np[i]);
+ auto split_res = jet_splitter.split(jets[i], np[i]);
+ weight_ *= split_res.weight;
if(weight_ == 0) return {};
assert(
std::all_of(
- begin(splitter_.get_jcons()), end(splitter_.get_jcons()),
+ begin(split_res.constituents), end(split_res.constituents),
is_jet_parton
)
);
const auto first_new_parton = jet_partons.insert(
end(jet_partons),
- begin(splitter_.get_jcons()), end(splitter_.get_jcons())
+ begin(split_res.constituents), end(split_res.constituents)
);
// mark uno and extremal FKL emissions here so we can check
// their position once all emissions are generated
auto extremal = end(jet_partons);
if((unob_ && i == 0) || i == most_backward_FKL_idx){
// unordered or FKL backward emission
extremal = std::min_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(
(i == most_backward_FKL_idx)?backward_FKL_idx:unob_idx
);
}
else if((unof_ && i == jets.size() - 1) || i == most_forward_FKL_idx){
// unordered or FKL forward emission
extremal = std::max_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(
(i == most_forward_FKL_idx)?forward_FKL_idx:unof_idx
);
}
if(
extremal != end(jet_partons)
&& !pass_extremal_cuts(*extremal, jets[i])
){
weight_ = 0;
return {};
}
}
assert(tagged_FKL_extremal(jet_partons));
std::sort(begin(jet_partons), end(jet_partons), rapidity_less{});
if(
!extremal_ok(jet_partons)
|| !split_preserved_jets(jets, jet_partons)
){
weight_ = 0.;
return {};
}
return jet_partons;
}
bool PhaseSpacePoint::extremal_ok(
std::vector<fastjet::PseudoJet> const & partons
) const{
assert(std::is_sorted(begin(partons), end(partons), rapidity_less{}));
if(unob_ && partons.front().user_index() != unob_idx) return false;
if(unof_ && partons.back().user_index() != unof_idx) return false;
return
most_backward_FKL(partons).user_index() == backward_FKL_idx
&& most_forward_FKL(partons).user_index() == forward_FKL_idx;
}
bool PhaseSpacePoint::split_preserved_jets(
std::vector<fastjet::PseudoJet> const & jets,
std::vector<fastjet::PseudoJet> const & jet_partons
) const{
assert(std::is_sorted(begin(jets), end(jets), rapidity_less{}));
const auto split_jets = sorted_by_rapidity(cluster_jets(jet_partons));
// this can happen if two overlapping jets
// are both split into more than one parton
if(split_jets.size() != jets.size()) return false;
for(size_t i = 0; i < split_jets.size(); ++i){
// this can happen if there are two overlapping jets
// and a parton is assigned to the "wrong" jet
if(!nearby_ep(jets[i].rapidity(), split_jets[i].rapidity(), 1e-2)){
return false;
}
}
return true;
}
template<class Particle>
Particle const & PhaseSpacePoint::most_backward_FKL(
std::vector<Particle> const & partons
) const{
return partons[0 + unob_];
}
template<class Particle>
Particle const & PhaseSpacePoint::most_forward_FKL(
std::vector<Particle> const & partons
) const{
const size_t idx = partons.size() - 1 - unof_;
assert(idx < partons.size());
return partons[idx];
}
template<class Particle>
Particle & PhaseSpacePoint::most_backward_FKL(
std::vector<Particle> & partons
) const{
return partons[0 + unob_];
}
template<class Particle>
Particle & PhaseSpacePoint::most_forward_FKL(
std::vector<Particle> & partons
) const{
const size_t idx = partons.size() - 1 - unof_;
assert(idx < partons.size());
return partons[idx];
}
namespace {
bool contains_idx(
fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton
){
auto const & constituents = jet.constituents();
const int idx = parton.user_index();
return std::find_if(
begin(constituents), end(constituents),
[idx](fastjet::PseudoJet const & con){return con.user_index() == idx;}
) != end(constituents);
}
}
/**
* final jet test:
* - number of jets must match Born kinematics
* - no partons designated as nonjet may end up inside jets
* - all other outgoing partons *must* end up inside jets
* - the extremal (in rapidity) partons must be inside the extremal jets
* - rapidities must be the same (by construction)
*/
bool PhaseSpacePoint::jets_ok(
std::vector<fastjet::PseudoJet> const & Born_jets,
std::vector<fastjet::PseudoJet> const & partons
) const{
fastjet::ClusterSequence cs(partons, param_.jet_param.def);
const auto jets = sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt));
if(jets.size() != Born_jets.size()) return false;
int in_jet = 0;
for(size_t i = 0; i < jets.size(); ++i){
assert(jets[i].has_constituents());
for(auto && parton: jets[i].constituents()){
if(is_nonjet_parton(parton)) return false;
}
in_jet += jets[i].constituents().size();
}
const int expect_in_jet = std::count_if(
partons.cbegin(), partons.cend(), is_jet_parton
);
if(in_jet != expect_in_jet) return false;
// note that PseudoJet::contains does not work here
if(! (
contains_idx(most_backward_FKL(jets), most_backward_FKL(partons))
&& contains_idx(most_forward_FKL(jets), most_forward_FKL(partons))
)) return false;
if(unob_ && !contains_idx(jets.front(), partons.front())) return false;
if(unof_ && !contains_idx(jets.back(), partons.back())) return false;
for(size_t i = 0; i < jets.size(); ++i){
assert(nearby_ep(jets[i].rapidity(), Born_jets[i].rapidity(), 1e-2));
}
return true;
}
void PhaseSpacePoint::reconstruct_incoming(
std::array<Particle, 2> const & Born_incoming
){
std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_);
for(size_t i = 0; i < incoming_.size(); ++i){
incoming_[i].type = Born_incoming[i].type;
}
assert(momentum_conserved());
}
double PhaseSpacePoint::phase_space_normalisation(
int num_Born_jets, int num_out_partons
) const{
return pow(16*pow(M_PI,3), num_Born_jets - num_out_partons);
}
bool PhaseSpacePoint::momentum_conserved() const{
fastjet::PseudoJet diff;
for(auto const & in: incoming()) diff += in.p;
const double norm = diff.E();
for(auto const & out: outgoing()) diff -= out.p;
return nearby(diff, fastjet::PseudoJet{}, norm);
}
} //namespace RHEJ
diff --git a/src/Splitter.cc b/src/Splitter.cc
deleted file mode 100644
index 73e4cf7..0000000
--- a/src/Splitter.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * Name: splitter.cc
- * Author: Tuomas Hapola
- *
- * double Sample2jetPartons(int * np);
- * bool Split(fastjet::PseudoJet j2split, int ncons);
- * bool DistributeQperp(fastjet::PseudoJet qperp, int np, double miny, double maxy);
- * void PrintConstituentInfo(std::vector<fastjet::PseudoJet> jets);
- *
- */
-
-#include <numeric>
-
-#include "RHEJ/Constants.hh"
-#include "RHEJ/Splitter.hh"
-#include "RHEJ/PhaseSpacePoint.hh"
-
-namespace{
- constexpr double ccut=RHEJ::CMINPT; // min parton pt
-
- template<class Iterator>
- bool same_pt_and_rapidity(
- Iterator begin, Iterator end,
- fastjet::PseudoJet const & jet
- ){
- static constexpr double ep = 1e-2;
- const fastjet::PseudoJet reconstructed_jet = std::accumulate(
- begin, end, fastjet::PseudoJet{}
- );
- return
- (std::abs(reconstructed_jet.pt() - jet.pt()) < ep)
- && (std::abs(reconstructed_jet.rapidity() - jet.rapidity()) < ep)
- ;
- }
-
- bool all_in_one_jet(
- std::vector<fastjet::PseudoJet> const & partons,
- fastjet::JetDefinition jet_def, double min_jet_pt
- ){
- fastjet::ClusterSequence ev(partons, jet_def);
- const std::vector<fastjet::PseudoJet> testjet = ev.inclusive_jets(min_jet_pt);
- return testjet.size() == 1u
- && testjet[0].constituents().size() == partons.size();
- }
-}
-
-double HejSplit::Split(fastjet::PseudoJet const & j2split, int ncons){
- assert(ncons >= 1);
-
- double swt = 1.;
-
- jcons.clear();
- jcons.reserve(ncons);
- if(ncons == 1){
- jcons.emplace_back(j2split);
- jcons.back().set_user_index(0);
- return swt;
- }
- if(ncons == 2){
- return Split2(j2split);
- }
- const double R_max = R_factor*rp;
- assert(R_max < M_PI);
-
- double pt_remaining = j2split.pt();
- const double phi_jet = j2split.phi();
- const double y_jet = j2split.rapidity();
- for(int i = 0; i < ncons - 1; ++i){
- /**
- * Generate rapidity and azimuthal angle with a distance
- * R = sqrt(delta_y^2 + delta_phi^2) < R_max
- * from the jet centre
- */
- const double R = R_max*ran_.get().flat();
- const double theta = 2*M_PI*ran_.get().flat();
- const double delta_phi = R*cos(theta);
- const double delta_y = R*sin(theta);
-
- /**
- * Generate pt such that the total contribution of all partons
- * along the jet pt axis does not exceed the jet pt
- */
- const double pt_max = pt_remaining/cos(delta_phi);
- assert(pt_max > 0);
- if(pt_max < ccut) return false; // no pt remaining for this parton
- const double pt = (pt_max - ccut)*ran_.get().flat() + ccut;
- pt_remaining -= pt*cos(delta_phi);
-
- jcons.emplace_back(
- pt*cos(phi_jet + delta_phi), pt*sin(phi_jet + delta_phi),
- pt*sinh(y_jet + delta_y), pt*cosh(y_jet + delta_y)
- );
- jcons.back().set_user_index(i);
- swt *= 2*M_PI*R*R_max*pt*(pt_max - ccut);
- }
-
- const fastjet::PseudoJet p_total = std::accumulate(
- jcons.begin(), jcons.end(), fastjet::PseudoJet{}
- );
-
- // Calculate the pt of the last parton
- const double last_px = j2split.px() - p_total.px();
- const double last_py = j2split.py() - p_total.py();
- const double last_pt = sqrt(last_px*last_px + last_py*last_py);
- if(last_pt < ccut) return 0.;
-
- // Calculate the rapidity of the last parton using the requirement that the
- // new jet must have the same rapidity as the LO jet.
- const double exp_2y_jet = (j2split.e() + j2split.pz())/(j2split.e() - j2split.pz());
- const double bb = (p_total.e()+p_total.pz()) - exp_2y_jet*(p_total.e()-p_total.pz());
- const double lasty = log((-bb+sqrt(bb*bb+4.*exp_2y_jet*last_pt*last_pt))/(2.*last_pt));
-
- jcons.emplace_back(
- last_px, last_py, last_pt*sinh(lasty), last_pt*cosh(lasty)
- );
- jcons.back().set_user_index(ncons-1);
- assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
-
- // Test that the last parton is not too far away from the jet centre.
- if (jcons.back().delta_R(j2split) > R_max) return 0.;
-
- if(! all_in_one_jet(jcons, jet_def, jetptmin)) return 0.;
-
- return swt;
-}
-
-//! sample y-phi distance to jet pt axis for a jet splitting into two partons
-/**
- * @param wt Multiplied by the weight of the sampling point
- * @returns The distance in units of the jet radius
- */
-double HejSplit::sample_distance_2p(double & wt){
- static constexpr double x_small = 0.1;
- static constexpr double p_small = 0.4;
-
- const double pR = ran_.get().flat();
- if(pR < p_small){
- wt *= x_small/p_small;
- return x_small/p_small*pR;
- }
- wt *= (1-x_small)/(1-p_small);
- return (1-x_small)/(1-p_small)*(pR-p_small) + x_small;
-}
-
-// split jet into two partons
-double HejSplit::Split2(fastjet::PseudoJet const & j2split){
- static constexpr size_t ncons = 2;
- jcons.resize(ncons);
- std::array<double, ncons> R, phi, y, pt;
- double wt = 1;
-
- const double theta = 2*M_PI*ran_.get().flat(); // angle in y-phi plane
- // empiric observation: we are always within the jet radius
- R[0] = sample_distance_2p(wt)*rp;
- R[1] = -sample_distance_2p(wt)*rp;
- for(size_t i = 0; i <= 1; ++i){
- phi[i] = j2split.phi() + R[i]*cos(theta);
- y[i] = j2split.rapidity() + R[i]*sin(theta);
- }
- for(size_t i = 0; i <= 1; ++i){
- pt[i] = (j2split.py() - tan(phi[1-i])*j2split.px())/
- (sin(phi[i]) - tan(phi[1-i])*cos(phi[i]));
- if(pt[i] < ccut) return 0.;
- jcons[i].reset_PtYPhiM(pt[i], y[i], phi[i]);
- jcons[i].set_user_index(i);
- }
- assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
-
- if(! all_in_one_jet(jcons, jet_def, jetptmin)) return 0.;
- wt *= 2*M_PI*pt[0]*R[0]*rp*rp;
- // from transformation of delta(R[1] - ...) to delta(pt[0] - ...)
- const double dphi0 = phi[0] - j2split.phi();
- const double ptJ = j2split.pt();
- const double jacobian = cos(theta)*pt[1]*pt[1]/(ptJ*sin(dphi0));
- return jacobian*wt;
-}
-
-
-void HejSplit::PrintConstituentInfo(std::vector<fastjet::PseudoJet> jets){
-
- // tell the user what was done
- // - the description of the algorithm used
- // - extract the inclusive jets with pt > 5 GeV
- // show the output as
- // {index, rap, phi, pt, number of constituents}
- //----------------------------------------------------------
- std::cout << "Ran " << (jet_def).description() << std::endl << std::endl;
-
- // label the columns
- printf("%5s %15s %15s %15s %15s\n","jet #", "rapidity", "phi", "pt", "n constituents");
- printf(" indices of constituents\n\n");
- //njj[jets.size()] += 1;
- // print out the details for each jet
- for (unsigned int i = 0; i < jets.size(); i++) {
- // get the constituents of the jet
- std::vector<fastjet::PseudoJet> constituents = jets[i].constituents();
- //nco[jets.size()] += int(constituents.size());
- //if (int(constituents.size())>maxco)
- //maxco = int(constituents.size());
- printf("%5u %15.8f %15.8f %15.8f %8u\n",
- i, jets[i].rap(), jets[i].phi(),
- jets[i].perp(), (unsigned int) constituents.size());
-
- printf(" ");
- for (unsigned int j=0; j<constituents.size(); j++){
- printf("%4u ", constituents[j].user_index());
- if (j%10==9) printf("\n ");
- }
- printf("\n\n");
- }
-}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jan 20, 9:14 PM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4242445
Default Alt Text
(40 KB)

Event Timeline