Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F9501532
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
70 KB
Subscribers
None
View Options
diff --git a/include/HEJ/Hjets.hh b/include/HEJ/Hjets.hh
index e7f666d..0a174ef 100644
--- a/include/HEJ/Hjets.hh
+++ b/include/HEJ/Hjets.hh
@@ -1,383 +1,383 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
/** \file
* \brief Functions computing the square of current contractions in H+Jets.
*
* This file contains all the H+Jet specific components to compute
* the current contractions for valid HEJ processes, to form a full
* H+Jets ME, currently one would have to use functions from the
* jets.hh header also. We have FKL and also unordered components for
* H+Jets.
*/
#pragma once
#include "CLHEP/Vector/LorentzVector.h"
namespace HEJ {
namespace currents {
using HLV = CLHEP::HepLorentzVector;
//! Square of gg->gg Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state gluon
* @param p1in Momentum of initial state gluon
* @param p2out Momentum of final state gluon
* @param p2in Momentum of intial state gluon
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for gg->gg
*
* g~p1 g~p2
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if g is backward, qH1 is forward)
*/
double ME_H_gg(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
/**
* @brief Square of gq->Hq current contraction
* @param ph Outgoing Higgs boson momentum
* @param pg Incoming gluon momentum
* @param pn Momentum of outgoing particle in FKL current
* @param pb Momentum of incoming particle in FKL current
* @param mt top mass (inf or value)
* @param inc_bottom whether to include bottom mass effects (true) or not (false)
* @param mb bottom mass (value)
* @param vev Higgs vacuum expectation value
*
* Calculates helicity-averaged || \epsilon_\mu V_H^{\mu\nu} j_\nu ||^2.
* See eq:S_gf_Hf in developer manual
*/
double ME_jgH_j(
- HLV const & ph, HLV const & pg,
+ HLV const & ph, HLV const & pa,
HLV const & pn, HLV const & pb,
double mt, bool inc_bottom, double mb, double vev
);
/**
* @brief Square of qg -> gqH current contraction
* @param pg Outgoing (unordered) gluon momentum
* @param ph Outgoing quark momentum
* @param pa Incoming quark momentum
* @param ph Outgoing Higgs boson momentum
* @param pb Incoming gluon momentum
* @param mt top mass (inf or value)
* @param inc_bottom whether to include bottom mass effects (true) or not (false)
* @param mb bottom mass (value)
* @param vev Higgs vacuum expectation value
*
* Calculates helicity-averaged || j_{uno \mu} V_H^{\mu\nu} \epsilon_\nu ||^2.
* See eq:S_gf_Hf in developer manual
*/
double ME_juno_jgH(
HLV const & pg,
HLV const & p1, HLV const & pa,
HLV const & ph, HLV const & pb,
double mt, bool inc_bottom, double mb, double vev
);
//! Square of gq->gq Higgs+Jets Scattering Current with Higgs before Gluon
/**
* @param p1out Momentum of final state gluon
* @param p1in Momentum of initial state gluon
* @param p2out Momentum of final state gluon
* @param p2in Momentum of intial state gluon
* @param pH Momentum of Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contraction
*/
double ME_H_qg(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qbarg->qbarg Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state anti-quark
* @param p1in Momentum of initial state anti-quark
* @param p2out Momentum of final state gluon
* @param p2in Momentum of intial state gluon
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for qbarg->qbarg
*
* qbar~p1 g~p2 (i.e. ALWAYS p1 for anti-quark, p2 for gluon)
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if g is backward, qH1 is forward)
*/
double ME_H_qbarg(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qQ->qQ Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state quark
* @param p1in Momentum of initial state quark
* @param p2out Momentum of final state quark
* @param p2in Momentum of intial state quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for qQ->qQ
*
* q~p1 Q~p2 (i.e. ALWAYS p1 for quark, p2 for quark)
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if Q is backward, qH1 is forward)
*/
double ME_H_qQ(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qQbar->qQbar Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state quark
* @param p1in Momentum of initial state quark
* @param p2out Momentum of final state anti-quark
* @param p2in Momentum of intial state anti-quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for qQ->qQ
*
* q~p1 Qbar~p2 (i.e. ALWAYS p1 for quark, p2 for anti-quark)
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if Qbar is backward, qH1 is forward)
*/
double ME_H_qQbar(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qbarQ->qbarQ Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state anti-quark
* @param p1in Momentum of initial state anti-quark
* @param p2out Momentum of final state quark
* @param p2in Momentum of intial state quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for qbarQ->qbarQ
*
* qbar~p1 Q~p2 (i.e. ALWAYS p1 for anti-quark, p2 for quark)
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if Q is backward, qH1 is forward)
*/
double ME_H_qbarQ(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qbarQbar->qbarQbar Higgs+Jets Scattering Current
/**
* @param p1out Momentum of final state anti-quark
* @param p1in Momentum of initial state anti-quark
* @param p2out Momentum of final state anti-quark
* @param p2in Momentum of intial state anti-quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for
* qbarQbar->qbarQbar
*
* qbar~p1 Qbar~p2 (i.e. ALWAYS p1 for anti-quark, p2 for anti-quark)
* should be called with qH1 meant to be contracted with p2 in first part of
* vertex (i.e. if Qbar is backward, qH1 is forward)
*/
double ME_H_qbarQbar(HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! @name Unordered backwards
//! @{
//! Square of qbarQ->qbarQg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state anti-quark
* @param p1in Momentum of initial state anti-quark
* @param p2out Momentum of final state quark
* @param p2in Momentum of intial state quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for
* qbarQ->qbarQg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_qbarQ(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qQ->qQg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state quark
* @param p1in Momentum of initial state quark
* @param p2out Momentum of final state quark
* @param p2in Momentum of intial state quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for qQ->qQg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_qQ(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qQbar->qQbarg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state quark
* @param p1in Momentum of initial state quark
* @param p2out Momentum of final state anti-quark
* @param p2in Momentum of intial state anti-quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for
* qQbar->qQbarg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_qQbar(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of qbarQbar->qbarQbarg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state anti-quark
* @param p1in Momentum of initial state anti-quark
* @param p2out Momentum of final state anti-quark
* @param p2in Momentum of intial state anti-quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for
* qbarQbar->qbarQbarg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_qbarQbar(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of gQbar->gQbarg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state gluon
* @param p1in Momentum of initial state gluon
* @param p2out Momentum of final state anti-quark
* @param p2in Momentum of intial state anti-quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for
* gQbar->gQbarg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_gQbar(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! Square of gQ->gQg Higgs+Jets Unordered b Scattering Current
/**
* @param pg Momentum of unordered b gluon
* @param p1out Momentum of final state gluon
* @param p1in Momentum of initial state gluon
* @param p2out Momentum of final state quark
* @param p2in Momentum of intial state quark
* @param qH1 Momentum of t-channel propagator before Higgs
* @param qH2 Momentum of t-channel propagator after Higgs
* @param mt Top quark mass
* @param include_bottom Specifies whether bottom corrections are included
* @param mb Bottom quark mass
* @param vev Vacuum expectation value
* @returns Square of the current contractions for gQ->gQg
*
* This construction is taking rapidity order: p1out >> p2out > pg
*/
double ME_H_unob_gQ(HLV const & pg, HLV const & p1out,
HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1,
HLV const & qH2,
double mt,
bool include_bottom, double mb, double vev);
//! @}
} // namespace currents
} // namespace HEJ
diff --git a/src/Hjets.cc b/src/Hjets.cc
index 64a9ecd..5369b21 100644
--- a/src/Hjets.cc
+++ b/src/Hjets.cc
@@ -1,539 +1,537 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/jets.hh"
#include "HEJ/Hjets.hh"
#include <array>
#include <cassert>
#include <cmath>
#include <complex>
#include <limits>
#include "HEJ/ConfigFlags.hh"
#include "HEJ/Constants.hh"
#include "HEJ/LorentzVector.hh"
#include "HEJ/utility.hh"
// generated headers
#include "HEJ/currents/j_h_j.hh"
#include "HEJ/currents/jgh_j.hh"
#include "HEJ/currents/juno_h_j.hh"
#include "HEJ/currents/juno_jgh.hh"
#ifdef HEJ_BUILD_WITH_QCDLOOP
#include "qcdloop/qcdloop.h"
#else
#include "HEJ/exceptions.hh"
#endif
namespace HEJ {
namespace currents {
namespace {
// short hand for math functions
using std::norm;
- using std::abs;
using std::conj;
- using std::pow;
- using std::sqrt;
constexpr double infinity = std::numeric_limits<double>::infinity(); // NOLINT
// Loop integrals
#ifdef HEJ_BUILD_WITH_QCDLOOP
- const COM LOOPRWFACTOR = (COM(0.,1.)*M_PI*M_PI)/pow((2.*M_PI),4);
+ const COM LOOPRWFACTOR = (COM(0.,1.)*M_PI*M_PI)/std::pow((2.*M_PI),4);
COM B0DD(HLV const & q, double mq)
{
static std::vector<std::complex<double>> result(3);
static auto ql_B0 = [](){
ql::Bubble<std::complex<double>,double,double> ql_B0;
ql_B0.setCacheSize(100);
return ql_B0;
}();
static std::vector<double> masses(2);
static std::vector<double> momenta(1);
for(auto & m: masses) m = mq*mq;
momenta.front() = q.m2();
ql_B0.integral(result, 1, masses, momenta);
return result[0];
}
COM C0DD(HLV const & q1, HLV const & q2, double mq)
{
static std::vector<std::complex<double>> result(3);
static auto ql_C0 = [](){
ql::Triangle<std::complex<double>,double,double> ql_C0;
ql_C0.setCacheSize(100);
return ql_C0;
}();
static std::vector<double> masses(3);
static std::vector<double> momenta(3);
for(auto & m: masses) m = mq*mq;
momenta[0] = q1.m2();
momenta[1] = q2.m2();
momenta[2] = (q1+q2).m2();
ql_C0.integral(result, 1, masses, momenta);
return result[0];
}
// Kallen lambda functions, see eq:lambda in developer manual
double lambda(const double s1, const double s2, const double s3) {
return s1*s1 + s2*s2 + s3*s3 - 2*s1*s2 - 2*s1*s3 - 2*s2*s3;
}
// eq: T_1 in developer manual
COM T1(HLV const & q1, HLV const & q2, const double m) {
const double q12 = q1.m2();
const double q22 = q2.m2();
const HLV ph = q1 - q2;
const double ph2 = ph.m2();
const double lam = lambda(q12, q22, ph2);
assert(m > 0.);
const double m2 = m*m;
return
- C0DD(q1, -q2, m)*(2.*m2 + 1./2.*(q12 + q22 - ph2) + 2.*q12*q22*ph2/lam)
- (B0DD(q2, m) - B0DD(ph, m))*(q22 - q12 - ph2)*q22/lam
- (B0DD(q1, m) - B0DD(ph, m))*(q12 - q22 - ph2)*q12/lam
- 1.;
}
// eq: T_2 in developer manual
COM T2(HLV const & q1, HLV const & q2, const double m) {
const double q12 = q1.m2();
const double q22 = q2.m2();
const HLV ph = q1 - q2;
const double ph2 = ph.m2();
const double lam = lambda(q12, q22, ph2);
assert(m > 0.);
const double m2 = m*m;
return
C0DD(q1, -q2, m)*(
4.*m2/lam*(ph2 - q12 - q22) - 1. - 4.*q12*q22/lam*(
1 + 3.*ph2*(q12 + q22 - ph2)/lam
)
)
- (B0DD(q2, m) - B0DD(ph, m))*(1. + 6.*q12/lam*(q22 - q12 + ph2))*2.*q22/lam
- (B0DD(q1, m) - B0DD(ph, m))*(1. + 6.*q22/lam*(q12 - q22 + ph2))*2.*q12/lam
- 2.*(q12 + q22 - ph2)/lam;
}
#else // no QCDloop
COM T1(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){
throw std::logic_error{"T1 called without QCDloop support"};
}
COM T2(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){
throw std::logic_error{"T2 called without QCDloop support"};
}
#endif
// prefactors of g^{\mu \nu} and q_2^\mu q_1^\nu in Higgs boson emission vertex
// see eq:VH in developer manual, but *without* global factor \alpha_s
std::array<COM, 2> TT(
HLV const & qH1, HLV const & qH2,
const double mt, const bool inc_bottom,
const double mb, const double vev
) {
if(mt == infinity) {
std::array<COM, 2> res = {qH1.dot(qH2), 1.};
for(auto & tt: res) tt /= (3.*M_PI*vev);
return res;
}
std::array<COM, 2> res = {T1(qH1, qH2, mt), T2(qH1, qH2, mt)};
for(auto & tt: res) tt *= mt*mt;
if(inc_bottom) {
res[0] += mb*mb*T1(qH1, qH2, mb);
res[1] += mb*mb*T2(qH1, qH2, mb);
}
for(auto & tt: res) tt /= M_PI*vev;
return res;
}
/**
* @brief Higgs+Jets FKL Contributions, function to handle all incoming types.
* @param p1out Outgoing Particle 1
* @param p1in Incoming Particle 1
* @param p2out Outgoing Particle 2
* @param p2in Incoming Particle 2
* @param qH1 t-channel momenta into higgs vertex
* @param qH2 t-channel momenta out of higgs vertex
* @param mt top mass (inf or value)
* @param inc_bottom whether to include bottom mass effects (true) or not (false)
* @param mb bottom mass (value)
* @param vev Higgs vacuum expectation value
*
* Calculates j^\mu H j_\mu. FKL with higgs vertex somewhere in the FKL chain.
* Handles all possible incoming states.
*/
double j_h_j(
HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
const double mt, const bool inc_bottom, const double mb, const double vev
){
using helicity::plus;
using helicity::minus;
const auto qqH1 = split_into_lightlike(qH1);
const HLV qH11 = qqH1.first;
const HLV qH12 = -qqH1.second;
const auto qqH2 = split_into_lightlike(qH2);
const HLV qH21 = qqH2.first;
const HLV qH22 = -qqH2.second;
// since qH1.m2(), qH2.m2() < 0 the following assertions are always true
assert(qH11.e() > 0);
assert(qH12.e() > 0);
assert(qH21.e() > 0);
assert(qH22.e() > 0);
const auto T_pref = TT(qH1, qH2, mt, inc_bottom, mb, vev);
const COM amp_mm = HEJ::j_h_j<minus, minus>(
p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1]
);
const COM amp_mp = HEJ::j_h_j<minus, plus>(
p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1]
);
const COM amp_pm = HEJ::j_h_j<plus, minus>(
p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1]
);
const COM amp_pp = HEJ::j_h_j<plus, plus>(
p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1]
);
static constexpr double num_hel = 4.;
// square of amplitudes, averaged over helicities
const double amp2 = (norm(amp_mm) + norm(amp_mp) + norm(amp_pm) + norm(amp_pp))/num_hel;
return amp2/((p1in-p1out).m2()*(p2in-p2out).m2()*qH1.m2()*qH2.m2());
}
// }
} // namespace
double ME_H_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt,
bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_qbarQ(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev)
* K_g(p2out,p2in)/C_A;
}
double ME_H_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev)
* K_g(p2out,p2in)/C_A;
}
double ME_H_gg(HLV const & p1out, HLV const & p1in, HLV const & p2out,
HLV const & p2in, HLV const & qH1, HLV const & qH2,
double mt, bool include_bottom, double mb, double vev
){
return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev)
* K_g(p2out,p2in)/C_A * K_g(p1out,p1in)/C_A;
}
//@}
double ME_jgH_j(
HLV const & ph, HLV const & pa,
HLV const & pn, HLV const & pb,
const double mt, const bool inc_bottom, const double mb, const double vev
){
using helicity::plus;
using helicity::minus;
const auto pH = split_into_lightlike(ph);
const HLV ph1 = pH.first;
const HLV ph2 = pH.second;
// since pH.m2() > 0 the following assertions are always true
assert(ph1.e() > 0);
assert(ph2.e() > 0);
const auto T_pref = TT(pa, pa-ph, mt, inc_bottom, mb, vev);
// only distinguish between same and different helicities,
// the other two combinations just add a factor of 2
const COM amp_mm = HEJ::jgh_j<minus, minus>(
pa, pb, pn, ph1, ph2, T_pref[0], T_pref[1]
);
const COM amp_mp = HEJ::jgh_j<minus, plus>(
pa, pb, pn, ph1, ph2, T_pref[0], T_pref[1]
);
constexpr double hel_factor = 2.;
// sum over squares of helicity amplitudes
return hel_factor*(norm(amp_mm) + norm(amp_mp));
}
namespace {
template<Helicity h1, Helicity h2, Helicity hg>
double amp_juno_jgh(
HLV const & pg, HLV const & p1, HLV const & pa,
HLV const & ph1, HLV const & ph2, HLV const & pb,
std::array<COM, 2> const & T_pref
) {
// TODO: code duplication with Wjets and pure jets
const COM u1 = U1_jgh<h1, h2, hg>(pg, p1, pa, ph1, ph2, pb, T_pref[0], T_pref[1]);
const COM u2 = U2_jgh<h1, h2, hg>(pg, p1, pa, ph1, ph2, pb, T_pref[0], T_pref[1]);
const COM l = L_jgh<h1, h2, hg>(pg, p1, pa, ph1, ph2, pb, T_pref[0], T_pref[1]);
return C_F*std::norm(u1+u2) - C_A*std::real((u1-l)*std::conj(u2+l));
}
- }
+ } // namespace
double ME_juno_jgH(
HLV const & pg,
HLV const & p1, HLV const & pa,
HLV const & ph, HLV const & pb,
const double mt, const bool inc_bottom, const double mb, const double vev
) {
using Helicity::plus;
using Helicity::minus;
const auto T_pref = TT(pb, pb-ph, mt, inc_bottom, mb, vev);
const auto pH = split_into_lightlike(ph);
const HLV ph1 = pH.first;
const HLV ph2 = pH.second;
// since pH.m2() > 0 the following assertions are always true
assert(ph1.e() > 0);
assert(ph2.e() > 0);
// only 4 out of the 8 helicity amplitudes are independent
// we still compute all of them for better numerical stability (mirror check)
MultiArray<double, 2, 2, 2> amp;
+// NOLINTNEXTLINE
#define ASSIGN_HEL(RES, J, H1, H2, HG) \
RES[H1][H2][HG] = J<H1, H2, HG>( \
pg, p1, pa, ph1, ph2, pb, T_pref \
- ) // NOLINT
+ )
ASSIGN_HEL(amp, amp_juno_jgh, minus, minus, minus);
ASSIGN_HEL(amp, amp_juno_jgh, minus, minus, plus);
ASSIGN_HEL(amp, amp_juno_jgh, minus, plus, minus);
ASSIGN_HEL(amp, amp_juno_jgh, minus, plus, plus);
ASSIGN_HEL(amp, amp_juno_jgh, plus, minus, minus);
ASSIGN_HEL(amp, amp_juno_jgh, plus, minus, plus);
ASSIGN_HEL(amp, amp_juno_jgh, plus, plus, minus);
ASSIGN_HEL(amp, amp_juno_jgh, plus, plus, plus);
#undef ASSIGN_HEL
double ampsq = 0.;
for(Helicity h1: {minus, plus}) {
for(Helicity h2: {minus, plus}) {
for(Helicity hg: {minus, plus}) {
ampsq += amp[h1][h2][hg];
}
}
}
return ampsq;
}
namespace {
template<Helicity h1, Helicity h2, Helicity hg>
double amp_juno_h_j(
HLV const & pa, HLV const & pb,
HLV const & pg, HLV const & p1, HLV const & p2,
HLV const & qH11, HLV const & qH12, HLV const & qH21, HLV const & qH22,
std::array<COM, 2> const & T_pref
) {
// TODO: code duplication with Wjets and pure jets
const COM u1 = U1_h_j<h1, h2, hg>(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]);
const COM u2 = U2_h_j<h1, h2, hg>(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]);
const COM l = L_h_j<h1, h2, hg>(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]);
return 2.*C_F*std::real((l-u1)*std::conj(l+u2))
+ 2.*C_F*C_F/3.*std::norm(u1+u2)
;
}
//@{
/**
* @brief Higgs+Jets Unordered Contributions, function to handle all incoming types.
* @param pg Unordered Gluon momenta
* @param p1out Outgoing Particle 1
* @param p1in Incoming Particle 1
* @param p2out Outgoing Particle 2
* @param p2in Incoming Particle 2
* @param qH1 t-channel momenta into higgs vertex
* @param qH2 t-channel momenta out of higgs vertex
* @param mt top mass (inf or value)
* @param inc_bottom whether to include bottom mass effects (true) or not (false)
* @param mb bottom mass (value)
*
* Calculates j_{uno}^\mu H j_\mu. Unordered with higgs vertex
* somewhere in the FKL chain. Handles all possible incoming states.
*/
double juno_h_j(
HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in,
HLV const & qH1, HLV const & qH2,
const double mt, const bool incBot, const double mb, const double vev
){
using helicity::plus;
using helicity::minus;
const auto qqH1 = split_into_lightlike(qH1);
const HLV qH11 = qqH1.first;
const HLV qH12 = -qqH1.second;
const auto qqH2 = split_into_lightlike(qH2);
const HLV qH21 = qqH2.first;
const HLV qH22 = -qqH2.second;
// since qH1.m2(), qH2.m2() < 0 the following assertions are always true
assert(qH11.e() > 0);
assert(qH12.e() > 0);
assert(qH21.e() > 0);
assert(qH22.e() > 0);
const auto T_pref = TT(qH1, qH2, mt, incBot, mb, vev);
// only 4 out of the 8 helicity amplitudes are independent
// we still compute all of them for better numerical stability (mirror check)
MultiArray<double, 2, 2, 2> amp{};
// NOLINTNEXTLINE
#define ASSIGN_HEL(RES, J, H1, H2, HG) \
RES[H1][H2][HG] = J<H1, H2, HG>( \
p1in, p2in, pg, p1out, p2out, qH11, qH12, qH21, qH22, T_pref \
)
ASSIGN_HEL(amp, amp_juno_h_j, minus, minus, minus);
ASSIGN_HEL(amp, amp_juno_h_j, minus, minus, plus);
ASSIGN_HEL(amp, amp_juno_h_j, minus, plus, minus);
ASSIGN_HEL(amp, amp_juno_h_j, minus, plus, plus);
ASSIGN_HEL(amp, amp_juno_h_j, plus, minus, minus);
ASSIGN_HEL(amp, amp_juno_h_j, plus, minus, plus);
ASSIGN_HEL(amp, amp_juno_h_j, plus, plus, minus);
ASSIGN_HEL(amp, amp_juno_h_j, plus, plus, plus);
#undef ASSIGN_HEL
const HLV q1 = p1in-p1out; // Top End
const HLV q2 = p2out-p2in; // Bottom End
const HLV qg = p1in-p1out-pg; // Extra bit post-gluon
double ampsq = 0.;
for(Helicity h1: {minus, plus}) {
for(Helicity h2: {minus, plus}) {
for(Helicity hg: {minus, plus}) {
ampsq += amp[h1][h2][hg];
}
}
}
ampsq /= 16.*qg.m2()*qH1.m2()*qH2.m2()*q2.m2();
// Factor of (Cf/Ca) for each quark to match ME_H_qQ.
ampsq*=C_F*C_F/C_A/C_A;
return ampsq;
}
} // namespace
double ME_H_unob_qQ(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_unob_qbarQ(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_unob_qQbar(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_unob_qbarQbar(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev);
}
double ME_H_unob_gQ(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev)
*K_g(p2out,p2in)/C_F;
}
double ME_H_unob_gQbar(HLV const & pg, HLV const & p1out, HLV const & p1in,
HLV const & p2out, HLV const & p2in, HLV const & qH1,
HLV const & qH2, double mt, bool include_bottom, double mb,
double vev
){
return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev)
*K_g(p2out,p2in)/C_F;
}
//@}
} // namespace currents
} // namespace HEJ
diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc
index 4ff0bd1..3f549cb 100644
--- a/src/PhaseSpacePoint.cc
+++ b/src/PhaseSpacePoint.cc
@@ -1,956 +1,955 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2020
* \copyright GPLv2 or later
*/
#include "HEJ/PhaseSpacePoint.hh"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
#include <random>
#include <tuple>
#include "fastjet/ClusterSequence.hh"
#include "fastjet/JetDefinition.hh"
#include "HEJ/Constants.hh"
#include "HEJ/Event.hh"
#include "HEJ/JetSplitter.hh"
#include "HEJ/PDG_codes.hh"
#include "HEJ/RNG.hh"
#include "HEJ/event_types.hh"
#include "HEJ/kinematics.hh"
#include "HEJ/resummation_jet.hh"
#include "HEJ/utility.hh"
namespace HEJ {
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;
}
namespace user_idx {
//! user indices for partons with extremal rapidity
enum ID: int {
qqbar_mid1 = -9,
qqbar_mid2 = -8,
qqbarb = -7,
qqbarf = -6,
unob = -5,
unof = -4,
backward_fkl = -3,
forward_fkl = -2,
};
} // namespace user_idx
using UID = user_idx::ID;
double phase_space_normalisation(
int num_Born_jets, int num_out_partons
){
return std::pow(16.*std::pow(M_PI,3), num_Born_jets - num_out_partons);
}
} // namespace
Event::EventData to_EventData(PhaseSpacePoint psp){
Event::EventData result;
result.incoming = std::move(psp).incoming_; // NOLINT(bugprone-use-after-move)
result.outgoing = std::move(psp).outgoing_; // NOLINT(bugprone-use-after-move)
// 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);
static_assert(
std::numeric_limits<double>::has_quiet_NaN,
"no quiet NaN for double"
);
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
result.decays = std::move(psp).decays_; // NOLINT(bugprone-use-after-move)
result.parameters.central = {nan, nan, psp.weight()}; // NOLINT(bugprone-use-after-move)
return result;
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::cluster_jets(
std::vector<fastjet::PseudoJet> const & partons
) const{
fastjet::ClusterSequence cs(partons, param_.jet_param.def);
return sorted_by_rapidity(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();
}
namespace {
// find iterators to central qqbar emission
auto get_central_qqbar(Event const & ev) {
// find born quarks (ignore extremal partons)
auto const firstquark = std::find_if(
std::next(ev.begin_partons()), std::prev(ev.end_partons(), 2),
[](Particle const & s){ return (is_anyquark(s)); }
);
// assert that it is a q-q_bar pair.
assert(std::distance(firstquark, ev.end_partons()) != 2);
assert(
( is_quark(*firstquark) && is_antiquark(*std::next(firstquark)) )
|| ( is_antiquark(*firstquark) && is_quark(*std::next(firstquark)) )
);
return std::make_pair(firstquark, std::next(firstquark));
}
//! returns index of most backward q-qbar jet
template<class Iterator>
int get_back_quark_jet(Event const & ev, Iterator firstquark){
// find jets at FO corresponding to the quarks
// technically this isn't necessary for LO
std::vector<int> const born_indices{ ev.particle_jet_indices() };
const auto firstquark_idx = std::distance(ev.begin_partons(), firstquark);
int const firstjet_idx = born_indices[firstquark_idx];
assert(firstjet_idx>0);
assert( born_indices[firstquark_idx+1] == firstjet_idx+1 );
return firstjet_idx;
}
//! returns index of most backward q-qbar jet
int getBackQuarkJet(Event const & ev){
const auto firstquark = get_central_qqbar(ev).first;
return get_back_quark_jet(ev, firstquark);
}
} // namespace
double PhaseSpacePoint::estimate_emission_rapidity_range(
Event const & ev
) const {
assert(std::is_sorted(begin(ev.jets()), end(ev.jets()), rapidity_less{}));
const double ymin = is_backward_g_to_h(ev)?
ev.outgoing().front().rapidity():
most_backward_FKL(ev.jets()).rapidity();
const double ymax = is_forward_g_to_h(ev)?
ev.outgoing().back().rapidity():
most_forward_FKL(ev.jets()).rapidity();
double delta_y = ymax - ymin;
// neglect tiny probability for emission between central qqbar pair
if(ev.type() == event_type::central_qqbar) {
const int qjet = getBackQuarkJet(ev);
delta_y -= ev.jets()[qjet+1].rapidity() - ev.jets()[qjet].rapidity();
}
assert(delta_y >= 0);
return delta_y;
}
double PhaseSpacePoint::estimate_ng_mean(Event const & ev) const {
// Formula derived from fit in arXiv:1805.04446 (see Fig. 2)
constexpr double GLUONS_PER_RAPIDITY = 0.975052;
return GLUONS_PER_RAPIDITY*estimate_emission_rapidity_range(ev);
}
int PhaseSpacePoint::sample_ng(Event const & event, RNG & ran){
const double ng_mean = estimate_ng_mean(event);
std::poisson_distribution<int> dist(ng_mean);
const int ng = dist(ran);
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::boost_AWZH_boson_from(
fastjet::PseudoJet const & boosted_boson, Event const & event
){
auto const & from = event.outgoing();
const auto original_boson = std::find_if(
begin(from), end(from),
[](Particle const & p){ return is_AWZH_boson(p); }
);
if(original_boson == end(from)) return;
auto insertion_point = std::lower_bound(
begin(outgoing_), end(outgoing_), *original_boson, rapidity_less{}
);
// copy AWZH particle
outgoing_.insert(insertion_point,
{original_boson->type, boosted_boson, original_boson->colour}
);
assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{}));
// copy & boost decay products
const int idx = std::distance(begin(from), original_boson);
assert(idx >= 0);
const auto decay_it = event.decays().find(idx);
if(decay_it == end(event.decays()))
return;
const int new_idx = std::distance(begin(outgoing_), insertion_point);
assert(new_idx >= 0);
assert(outgoing_[new_idx].type == original_boson->type);
auto decayparticles=decay_it->second;
// change the momenta of the decay products.
for(auto & particle: decayparticles){
auto & p = particle.p;
// boost _to_ rest frame of input boson
p.unboost(original_boson->p);
// then boost _from_ rest frame of shuffled boson
p.boost(boosted_boson);
}
decays_.emplace(new_idx, decayparticles);
}
namespace {
template<class ConstIterator, class Iterator>
void label_extremal_qqbar(
ConstIterator born_begin, ConstIterator born_end,
Iterator first_out
){
// find born quarks
const auto firstquark = std::find_if(
born_begin, born_end-1,
[](Particle const & s){ return (is_anyquark(s)); }
);
assert(firstquark != born_end-1);
const auto secondquark = std::find_if(
firstquark+1, born_end,
[](Particle const & s){ return (is_anyquark(s)); }
);
assert(secondquark != born_end);
assert( ( is_quark(*firstquark) && is_antiquark(*secondquark) )
|| ( is_antiquark(*firstquark) && is_quark(*secondquark) ));
assert(first_out->type == ParticleID::gluon);
assert((first_out+1)->type == ParticleID::gluon);
// copy type from born
first_out->type = firstquark->type;
(first_out+1)->type = secondquark->type;
}
} // namespace
void PhaseSpacePoint::label_qqbar(Event const & event){
assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{}));
assert(filter_partons(outgoing_).size() == outgoing_.size());
if(qqbarb_){
label_extremal_qqbar(event.outgoing().cbegin(), event.outgoing().cend(),
outgoing_.begin() );
return;
}
if(qqbarf_){ // same as qqbarb with reversed order
label_extremal_qqbar( event.outgoing().crbegin(), event.outgoing().crend(),
outgoing_.rbegin() );
return;
}
// central qqbar
const auto firstquark = get_central_qqbar(event).first;
// find jets at FO corresponding to the quarks
// technically this isn't necessary for LO
const auto firstjet_idx = get_back_quark_jet(event, firstquark);
// find corresponding jets after resummation
fastjet::ClusterSequence cs{to_PseudoJet(outgoing_), param_.jet_param.def};
auto const jets = fastjet::sorted_by_rapidity(
cs.inclusive_jets( param_.jet_param.min_pt ));
std::vector<int> const resum_indices{ cs.particle_jet_indices({jets}) };
// assert that jets didn't move
assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx )->rapidity(),
jets[ firstjet_idx ].rapidity(), 1e-2) );
assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx+1 )->rapidity(),
jets[ firstjet_idx+1 ].rapidity(), 1e-2) );
// find last partons in first (central) jet
size_t idx_out = 0;
for(size_t i=resum_indices.size()-2; i>0; --i)
if(resum_indices[i] == firstjet_idx){
idx_out = i;
break;
}
assert(idx_out != 0);
// check that there is sufficient pt in jets from the quarks
const double minpartonjetpt = 1. - param_.soft_pt_regulator;
if (outgoing_[idx_out].p.pt()<minpartonjetpt*( event.jets().cbegin()+firstjet_idx )->pt()){
weight_=0.;
status_ = StatusCode::wrong_jets;
return;
}
if (outgoing_[idx_out+1].p.pt()<minpartonjetpt*( event.jets().cbegin()+firstjet_idx+1 )->pt()){
weight_=0.;
status_ = StatusCode::wrong_jets;
return;
}
// check that no additional emission between jets
// such configurations are possible if we have an gluon gets generated
// inside the rapidities of the qqbar chain, but clusted to a
// differnet/outside jet. Changing this is non trivial
if(resum_indices[idx_out+1] != resum_indices[idx_out]+1){
weight_=0.;
status_ = StatusCode::gluon_in_qqbar;
return;
}
outgoing_[idx_out].type = firstquark->type;
outgoing_[idx_out+1].type = std::next(firstquark)->type;
}
void PhaseSpacePoint::label_quarks(Event const & ev){
const auto WZEmit = std::find_if(
begin(ev.outgoing()), end(ev.outgoing()),
[](Particle const & s){ return (std::abs(s.type) == pid::Wp || s.type == pid::Z_photon_mix); }
);
if (WZEmit != end(ev.outgoing())){
if(!qqbarb_) {
const size_t backward_FKL_idx = unob_?1:0;
const auto backward_FKL = std::next(ev.begin_partons(), backward_FKL_idx);
outgoing_[backward_FKL_idx].type = backward_FKL->type;
}
if(!qqbarf_) {
const size_t forward_FKL_idx = unof_?1:0;
const auto forward_FKL = std::prev(ev.end_partons(), 1+forward_FKL_idx);
outgoing_.rbegin()[unof_].type = forward_FKL->type; // NOLINT
}
} else {
if(!is_backward_g_to_h(ev)) {
most_backward_FKL(outgoing_).type = ev.incoming().front().type;
}
if(!is_forward_g_to_h(ev)) {
most_forward_FKL(outgoing_).type = ev.incoming().back().type;
}
}
if(qqbar_mid_||qqbarb_||qqbarf_){
label_qqbar(ev);
}
}
PhaseSpacePoint::PhaseSpacePoint(
Event const & ev, PhaseSpacePointConfig conf, RNG & ran
):
unob_{ev.type() == event_type::unob},
unof_{ev.type() == event_type::unof},
qqbarb_{ev.type() == event_type::qqbar_exb},
qqbarf_{ev.type() == event_type::qqbar_exf},
qqbar_mid_{ev.type() == event_type::qqbar_mid},
param_{std::move(conf)},
status_{unspecified}
{
// legacy code: override new variable with old
if(param_.max_ext_soft_pt_fraction){
param_.soft_pt_regulator = *param_.max_ext_soft_pt_fraction;
param_.max_ext_soft_pt_fraction = {};
}
weight_ = 1;
auto const & Born_jets = ev.jets();
const int ng = sample_ng(ev, ran);
weight_ /= std::tgamma(ng + 1);
const int ng_jets = sample_ng_jets(ev, ng, ran);
std::vector<fastjet::PseudoJet> out_partons = gen_non_jet(
ng - ng_jets, CMINPT, param_.jet_param.min_pt, ran
);
const auto qperp = std::accumulate(
begin(out_partons), end(out_partons),
fastjet::PseudoJet{}
);
std::vector<fastjet::PseudoJet> jets;
optional<fastjet::PseudoJet> boson;
std::tie(jets, boson) = reshuffle(ev, qperp);
if(weight_ == 0.) {
status_ = failed_reshuffle;
return;
}
if(! pass_resummation_cuts(jets)){
status_ = failed_resummation_cuts;
weight_ = 0.;
return;
}
// split jets in multiple partons
std::vector<fastjet::PseudoJet> jet_partons = split(
ev, jets, ng_jets, ran
);
if(weight_ == 0.) {
status_ = StatusCode::failed_split;
return;
}
const double ymin = is_backward_g_to_h(ev)?
ev.outgoing().front().rapidity():
most_backward_FKL(jet_partons).rapidity()
;
const double ymax = is_forward_g_to_h(ev)?
ev.outgoing().back().rapidity():
most_forward_FKL(jet_partons).rapidity()
;
if(qqbar_mid_){
const int qqbar_backjet = getBackQuarkJet(ev);
rescale_qqbar_rapidities(
out_partons, jets,
ymin, ymax,
qqbar_backjet
);
}
else{
rescale_rapidities(out_partons, ymin, ymax);
}
if(! cluster_jets(out_partons).empty()){
weight_ = 0.;
status_ = StatusCode::empty_jets;
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(ev, out_partons)){
weight_ = 0.;
status_ = StatusCode::wrong_jets;
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 it = std::make_move_iterator(out_partons.begin());
it != std::make_move_iterator(out_partons.end());
++it
){
outgoing_.emplace_back( Particle{pid::gluon, *it, {}});
}
assert(!outgoing_.empty());
label_quarks(ev);
if(weight_ == 0.) {
//! @TODO optimise s.t. this is not possible
// status is handled internally
return;
}
// reattach boson & decays
if(boson){
boost_AWZH_boson_from(*boson, ev);
}
reconstruct_incoming(ev.incoming());
status_ = StatusCode::good;
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_non_jet(
int const ng_non_jet, double const ptmin, double const ptmax, RNG & ran
){
// heuristic parameters for pt sampling
const double ptpar = 1.3 + ng_non_jet/5.;
const double temp1 = std::atan((ptmax - ptmin)/ptpar);
std::vector<fastjet::PseudoJet> partons(ng_non_jet);
for(int i = 0; i < ng_non_jet; ++i){
const double r1 = ran.flat();
const double pt = ptmin + ptpar*std::tan(r1*temp1);
const double temp2 = std::cos(r1*temp1);
const double phi = 2*M_PI*ran.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.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 sorted_by_rapidity(partons);
}
void PhaseSpacePoint::rescale_qqbar_rapidities(
std::vector<fastjet::PseudoJet> & out_partons,
std::vector<fastjet::PseudoJet> const & jets,
const double ymin1, const double ymax2,
const int qqbar_backjet
){
const double ymax1 = jets[qqbar_backjet].rapidity();
const double ymin2 = jets[qqbar_backjet+1].rapidity();
constexpr double ep = 1e-7;
const double tot_y = ymax1 - ymin1 + ymax2 - ymin2;
std::vector<std::reference_wrapper<fastjet::PseudoJet>> refpart(
out_partons.begin(), out_partons.end());
double ratio = (ymax1 - ymin1)/tot_y;
const auto gap{ std::find_if(refpart.begin(), refpart.end(),
[ratio](fastjet::PseudoJet const & p){
return (p.rapidity()>=ratio);} ) };
double ymin = ymin1;
double ymax = ymax1;
double dy = ymax - ymin - 2*ep;
double offset = 0.;
for(auto it_part=refpart.begin(); it_part<refpart.end(); ++it_part){
if(it_part == gap){
ymin = ymin2;
ymax = ymax2;
dy = ymax - ymin - 2*ep;
offset = ratio;
ratio = 1-ratio;
}
fastjet::PseudoJet & part = *it_part;
assert(offset <= part.rapidity() && part.rapidity() < ratio+offset);
const double y = ymin + ep + dy*((part.rapidity()-offset)/ratio);
part.reset_momentum_PtYPhiM(part.pt(), y, part.phi());
weight_ *= tot_y-4.*ep;
assert(ymin <= part.rapidity() && part.rapidity() <= ymax);
}
assert(is_sorted(begin(out_partons), end(out_partons), rapidity_less{}));
}
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(Event const & ev) const{
const double dy = estimate_emission_rapidity_range(ev);
const double R = param_.jet_param.def.R();
// jets into which we predominantly emit
const int njets = ev.jets().size() - unof_ - unob_ - qqbarb_ - qqbarf_; //NOLINT
assert(njets >= 1);
const size_t nextremal_jets = std::min(njets, 2);
const double p_J_y_large = (njets - nextremal_jets/2.)*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(Event const & event, int ng, RNG & ran){
const double p_J = probability_in_jet(event);
std::binomial_distribution<> bin_dist(ng, p_J);
const int ng_J = bin_dist(ran);
weight_ *= std::pow(p_J, -ng_J)*std::pow(1 - p_J, ng_J - ng);
return ng_J;
}
std::pair< std::vector<fastjet::PseudoJet>,
optional<fastjet::PseudoJet> >
PhaseSpacePoint::reshuffle(
Event const & ev,
fastjet::PseudoJet const & q
){
// Create a copy of the outgoing momenta not containing decay products
std::vector<fastjet::PseudoJet const *> born_momenta;
born_momenta.reserve(ev.jets().size());
std::transform(ev.jets().cbegin(), ev.jets().cend(),
back_inserter(born_momenta),
[](fastjet::PseudoJet const & t) { return &t; });
// check if there is one (or more) bosons in the event.
const auto AWZH_boson = std::find_if(
begin(ev.outgoing()), end(ev.outgoing()),
[](Particle const & p){ return is_AWZH_boson(p); }
);
optional<fastjet::PseudoJet> boson;
if(AWZH_boson != end(ev.outgoing())){
boson = AWZH_boson->p;
}
// reshuffle all momenta
if(q == fastjet::PseudoJet{0, 0, 0, 0}) return {ev.jets(), boson};
// add boson to reshuffling
if(boson) {
born_momenta.push_back(&*boson);
}
auto shuffle_momenta = resummation_jet_momenta(born_momenta, q);
if(shuffle_momenta.empty()){
weight_ = 0;
return {};
}
// additional Jacobian to ensure Born integration over delta gives 1
weight_ *= resummation_jet_weight(born_momenta, q);
// take out boson again
optional<fastjet::PseudoJet> shuffle_boson;
if(boson){
shuffle_boson = std::move(shuffle_momenta.back());
shuffle_momenta.pop_back();
}
return {shuffle_momenta, shuffle_boson};
}
std::vector<int> PhaseSpacePoint::distribute_jet_partons(
int ng_jets, std::vector<fastjet::PseudoJet> const & jets, RNG & ran
){
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_||qqbarb_) && jets[0].delta_R(jets[1]) > R_eff){
++first_valid_jet;
--num_valid_jets;
}
else if((unof_||qqbarf_) && 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.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() == UID::backward_fkl;
}
) != 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() == UID::forward_fkl;
}
) != jet_partons.rend();
}
} // namespace
#endif
std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
Event const & Born_event,
std::vector<fastjet::PseudoJet> const & jets,
int ng_jets
, RNG & ran
){
return split(
Born_event,
jets,
distribute_jet_partons(ng_jets, jets, ran),
ran
);
}
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_.soft_pt_regulator;
}
std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
Event const & Born_event,
std::vector<fastjet::PseudoJet> const & jets,
std::vector<int> const & np,
RNG & ran
){
assert(! jets.empty());
assert(jets.size() == np.size());
assert(pass_resummation_cuts(jets));
constexpr auto no_such_jet_idx = std::numeric_limits<std::size_t>::max();
const size_t most_backward_FKL_idx = is_backward_g_to_h(Born_event)?
no_such_jet_idx: // we have backward Higgs instead of FKL jet
(0 + unob_ + qqbarb_); // NOLINT
const size_t most_forward_FKL_idx = is_forward_g_to_h(Born_event)?
no_such_jet_idx: // we have forward Higgs instead of FKL jet
(jets.size() - 1 - unof_ - qqbarf_); // NOLINT
const size_t qqbar_jet_idx = qqbar_mid_?
getBackQuarkJet(Born_event):
no_such_jet_idx;
auto const & jet = param_.jet_param;
const JetSplitter jet_splitter{jet.def, jet.min_pt};
std::vector<fastjet::PseudoJet> jet_partons;
// randomly distribute jet gluons among jets
for(size_t i = 0; i < jets.size(); ++i){
auto split_res = jet_splitter.split(jets[i], np[i], ran);
weight_ *= split_res.weight;
if(weight_ == 0) return {};
assert(
std::all_of(
begin(split_res.constituents), end(split_res.constituents),
is_jet_parton
)
);
const auto first_new_parton = jet_partons.insert(
end(jet_partons),
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
// also mark qqbar_mid partons, and apply appropriate pt cut.
auto extremal = end(jet_partons);
if (i == most_backward_FKL_idx){ //FKL backward emission
extremal = std::min_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(UID::backward_fkl);
}
else if(((unob_ || qqbarb_) && i == 0)){
// unordered/qqbarb
extremal = std::min_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index((unob_)?UID::unob:UID::qqbarb);
}
else if (i == most_forward_FKL_idx){
extremal = std::max_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(UID::forward_fkl);
}
else if(((unof_ || qqbarf_) && i == jets.size() - 1)){
// unordered/qqbarf
extremal = std::max_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index((unof_)?UID::unof:UID::qqbarf);
}
else if((qqbar_mid_ && i == qqbar_jet_idx)){
extremal = std::max_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(UID::qqbar_mid1);
}
else if((qqbar_mid_ && i == qqbar_jet_idx+1)){
extremal = std::min_element(
first_new_parton, end(jet_partons), rapidity_less{}
);
extremal->set_user_index(UID::qqbar_mid2);
}
if(
extremal != end(jet_partons)
&& !pass_extremal_cuts(*extremal, jets[i])
){
weight_ = 0;
return {};
}
}
assert(is_backward_g_to_h(Born_event) || tagged_FKL_backward(jet_partons));
assert(is_forward_g_to_h(Born_event) || tagged_FKL_forward(jet_partons));
std::sort(begin(jet_partons), end(jet_partons), rapidity_less{});
if(
!extremal_ok(Born_event, jet_partons)
|| !split_preserved_jets(jets, jet_partons)
){
weight_ = 0.;
return {};
}
return jet_partons;
}
bool PhaseSpacePoint::extremal_ok(
Event const & Born_event,
std::vector<fastjet::PseudoJet> const & partons
) const{
assert(std::is_sorted(begin(partons), end(partons), rapidity_less{}));
if(unob_ && partons.front().user_index() != UID::unob) return false;
if(unof_ && partons.back().user_index() != UID::unof) return false;
if(qqbarb_ && partons.front().user_index() != UID::qqbarb) return false;
if(qqbarf_ && partons.back().user_index() != UID::qqbarf) return false;
if(is_backward_g_to_h(Born_event)) {
if(partons.front().rapidity() < Born_event.outgoing().front().rapidity()){
return false;
}
} else if(most_backward_FKL(partons).user_index() != UID::backward_fkl) {
return false;
}
if(is_forward_g_to_h(Born_event)) {
return partons.back().rapidity() <= Born_event.outgoing().back().rapidity();
- } else {
- return most_forward_FKL(partons).user_index() == UID::forward_fkl;
}
+ return most_forward_FKL(partons).user_index() == UID::forward_fkl;
}
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 = 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_ + qqbarb_];
}
template<class Particle>
Particle const & PhaseSpacePoint::most_forward_FKL(
std::vector<Particle> const & partons
) const{
const size_t idx = partons.size() - 1 - unof_ - qqbarf_;
assert(idx < partons.size());
return partons[idx];
}
template<class Particle>
Particle & PhaseSpacePoint::most_backward_FKL(
std::vector<Particle> & partons
) const{
return partons[0 + unob_ + qqbarb_];
}
template<class Particle>
Particle & PhaseSpacePoint::most_forward_FKL(
std::vector<Particle> & partons
) const{
const size_t idx = partons.size() - 1 - unof_ - qqbarf_;
assert(idx < partons.size());
return partons[idx];
}
bool PhaseSpacePoint::contains_idx(
fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton
) const {
auto const & constituents = jet.constituents();
const int idx = parton.user_index();
const bool injet = std::find_if(
begin(constituents), end(constituents),
[idx](fastjet::PseudoJet const & con){return con.user_index() == idx;}
) != end(constituents);
const double minpartonjetpt = 1. - param_.soft_pt_regulator;
return ((parton.pt()>minpartonjetpt*jet.pt())&&injet);
}
bool PhaseSpacePoint::jets_ok(
Event const & Born_event,
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_event.jets().size()) return false;
int in_jet = 0;
for(auto const & jet : jets){
assert(jet.has_constituents());
for(auto && parton: jet.constituents()){
if(is_nonjet_parton(parton)) return false;
}
in_jet += jet.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(
!is_backward_g_to_h(Born_event) &&
!contains_idx(most_backward_FKL(jets), most_backward_FKL(partons))
) return false;
if(
!is_forward_g_to_h(Born_event)
&& !contains_idx(most_forward_FKL(jets), most_forward_FKL(partons))
) return false;
if(unob_ && !contains_idx(jets.front(), partons.front())) return false;
if(qqbarb_ && !contains_idx(jets.front(), partons.front())) return false;
if(unof_ && !contains_idx(jets.back(), partons.back())) return false;
if(qqbarf_ && !contains_idx(jets.back(), partons.back())) return false;
#ifndef NDEBUG
for(size_t i = 0; i < jets.size(); ++i){
assert(nearby_ep(jets[i].rapidity(), Born_event.jets()[i].rapidity(), 1e-2));
}
#endif
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());
}
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 HEJ
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 2:37 PM (11 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4470302
Default Alt Text
(70 KB)
Attached To
rHEJ HEJ
Event Timeline
Log In to Comment