Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F8723746
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
40 KB
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Jan 20, 9:14 PM (23 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4242445
Default Alt Text
(40 KB)
Attached To
rHEJ HEJ
Event Timeline
Log In to Comment