Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F19251840
PhaseSpacePoint.cc
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
31 KB
Referenced Files
None
Subscribers
None
PhaseSpacePoint.cc
View Options
/**
* \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
<utility>
#include
"CLHEP/Vector/LorentzVector.h"
#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
{
qqxmid1
=
-
9
,
qqxmid2
=
-
8
,
qqxb
=
-
7
,
qqxf
=
-
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
);
}
}
double
PhaseSpacePoint
::
estimate_emission_rapidity_range
(
Event
const
&
ev
)
const
{
assert
(
std
::
is_sorted
(
begin
(
ev
.
jets
()),
end
(
ev
.
jets
()),
rapidity_less
{}));
double
delta_y
=
most_forward_FKL
(
ev
.
jets
()).
rapidity
()
-
most_backward_FKL
(
ev
.
jets
()).
rapidity
();
// neglect tiny probability for emission between central qqbar pair
if
(
ev
.
type
()
==
event_type
::
central_qqx
)
{
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
boostedbosonm
,
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
{}
);
// copy awzh particle
auto
bAWZH_boson
=
*
AWZH_boson
;
bAWZH_boson
.
p
=
boostedbosonm
;
outgoing_
.
insert
(
insertion_point
,
bAWZH_boson
);
// copy decay products
const
int
idx
=
std
::
distance
(
begin
(
from
),
AWZH_boson
);
assert
(
idx
>=
0
);
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
(
new_idx
>=
0
);
assert
(
outgoing_
[
new_idx
].
type
==
AWZH_boson
->
type
);
// change the momenta of the decay products.
// boost to rest frame of input boson, then from rest frame of shuffled boson
auto
decayparticles
=
decay_it
->
second
;
auto
boost1
=
CLHEP
::
HepLorentzVector
((
*
AWZH_boson
).
px
(),(
*
AWZH_boson
).
py
(),
(
*
AWZH_boson
).
pz
(),(
*
AWZH_boson
).
E
());
auto
boost2
=
CLHEP
::
HepLorentzVector
(
boostedbosonm
.
px
(),
boostedbosonm
.
py
(),
boostedbosonm
.
pz
(),
boostedbosonm
.
E
());
auto
vec1
=
CLHEP
::
HepLorentzVector
(
decayparticles
[
0
].
p
.
px
(),
decayparticles
[
0
].
p
.
py
(),
decayparticles
[
0
].
p
.
pz
(),
decayparticles
[
0
].
p
.
E
());
auto
vec2
=
CLHEP
::
HepLorentzVector
(
decayparticles
[
1
].
p
.
px
(),
decayparticles
[
1
].
p
.
py
(),
decayparticles
[
1
].
p
.
pz
(),
decayparticles
[
1
].
p
.
E
());
auto
rvec1
=
vec1
.
boost
(
boost1
.
findBoostToCM
());
auto
rvec2
=
vec2
.
boost
(
boost1
.
findBoostToCM
());
auto
bvec1
=
vec1
.
boost
(
-
boost2
.
findBoostToCM
());
auto
bvec2
=
vec2
.
boost
(
-
boost2
.
findBoostToCM
());
decayparticles
[
0
].
p
=
fastjet
::
PseudoJet
(
bvec1
.
px
(),
bvec1
.
py
(),
bvec1
.
pz
(),
bvec1
.
e
());
decayparticles
[
1
].
p
=
fastjet
::
PseudoJet
(
bvec2
.
px
(),
bvec2
.
py
(),
bvec2
.
pz
(),
bvec2
.
e
());
decays_
.
emplace
(
new_idx
,
decayparticles
);
}
assert
(
std
::
is_sorted
(
begin
(
outgoing_
),
end
(
outgoing_
),
rapidity_less
{}));
}
namespace
{
template
<
class
ConstIterator
,
class
Iterator
>
void
label_extremal_qqx
(
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_qqx
(
Event
const
&
event
){
assert
(
std
::
is_sorted
(
begin
(
outgoing_
),
end
(
outgoing_
),
rapidity_less
{}));
assert
(
filter_partons
(
outgoing_
).
size
()
==
outgoing_
.
size
());
if
(
qqxb_
){
label_extremal_qqx
(
event
.
outgoing
().
cbegin
(),
event
.
outgoing
().
cend
(),
outgoing_
.
begin
()
);
return
;
}
if
(
qqxf_
){
// same as qqxb with reversed order
label_extremal_qqx
(
event
.
outgoing
().
crbegin
(),
event
.
outgoing
().
crend
(),
outgoing_
.
rbegin
()
);
return
;
}
// central qqx
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 qqx 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_qqx
;
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
(
!
qqxb_
)
{
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
(
!
qqxf_
)
{
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
{
most_backward_FKL
(
outgoing_
).
type
=
ev
.
incoming
().
front
().
type
;
most_forward_FKL
(
outgoing_
).
type
=
ev
.
incoming
().
back
().
type
;
}
if
(
qqxmid_
||
qqxb_
||
qqxf_
){
label_qqx
(
ev
);
}
}
PhaseSpacePoint
::
PhaseSpacePoint
(
Event
const
&
ev
,
PhaseSpacePointConfig
conf
,
RNG
&
ran
)
:
unob_
{
ev
.
type
()
==
event_type
::
unob
},
unof_
{
ev
.
type
()
==
event_type
::
unof
},
qqxb_
{
ev
.
type
()
==
event_type
::
qqxexb
},
qqxf_
{
ev
.
type
()
==
event_type
::
qqxexf
},
qqxmid_
{
ev
.
type
()
==
event_type
::
qqxmid
},
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
);
int
qqxbackjet
(
-
1
);
if
(
qqxmid_
){
qqxbackjet
=
getBackQuarkJet
(
ev
);
}
// reshuffle soft momenta
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
(
jets
,
ng_jets
,
qqxbackjet
,
ran
);
if
(
weight_
==
0.
)
{
status_
=
StatusCode
::
failed_split
;
return
;
}
// rescale rapidity interval
if
(
qqxmid_
){
rescale_qqx_rapidities
(
out_partons
,
jets
,
most_backward_FKL
(
jet_partons
).
rapidity
(),
most_forward_FKL
(
jet_partons
).
rapidity
(),
qqxbackjet
);
}
else
{
rescale_rapidities
(
out_partons
,
most_backward_FKL
(
jet_partons
).
rapidity
(),
most_forward_FKL
(
jet_partons
).
rapidity
()
);
}
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
(
Born_jets
,
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_qqx_rapidities
(
std
::
vector
<
fastjet
::
PseudoJet
>
&
out_partons
,
std
::
vector
<
fastjet
::
PseudoJet
>
const
&
jets
,
const
double
ymin1
,
const
double
ymax2
,
const
int
qqxbackjet
){
const
double
ymax1
=
jets
[
qqxbackjet
].
rapidity
();
const
double
ymin2
=
jets
[
qqxbackjet
+
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_
-
qqxb_
-
qqxf_
;
assert
(
njets
>=
2
);
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
(
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_
||
qqxb_
)
&&
jets
[
0
].
delta_R
(
jets
[
1
])
>
R_eff
){
++
first_valid_jet
;
--
num_valid_jets
;
}
else
if
((
unof_
||
qqxf_
)
&&
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
();
}
bool
tagged_FKL_extremal
(
std
::
vector
<
fastjet
::
PseudoJet
>
const
&
jet_partons
){
return
tagged_FKL_backward
(
jet_partons
)
&&
tagged_FKL_forward
(
jet_partons
);
}
}
// namespace
#endif
std
::
vector
<
fastjet
::
PseudoJet
>
PhaseSpacePoint
::
split
(
std
::
vector
<
fastjet
::
PseudoJet
>
const
&
jets
,
int
ng_jets
,
size_t
qqxbackjet
,
RNG
&
ran
){
return
split
(
jets
,
distribute_jet_partons
(
ng_jets
,
jets
,
ran
),
qqxbackjet
,
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
(
std
::
vector
<
fastjet
::
PseudoJet
>
const
&
jets
,
std
::
vector
<
int
>
const
&
np
,
size_t
qqxbackjet
,
RNG
&
ran
){
assert
(
!
jets
.
empty
());
assert
(
jets
.
size
()
==
np
.
size
());
assert
(
pass_resummation_cuts
(
jets
));
const
size_t
most_backward_FKL_idx
=
0
+
unob_
+
qqxb_
;
// NOLINT
const
size_t
most_forward_FKL_idx
=
jets
.
size
()
-
1
-
unof_
-
qqxf_
;
// NOLINT
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 qqxmid 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_
||
qqxb_
)
&&
i
==
0
)){
// unordered/qqxb
extremal
=
std
::
min_element
(
first_new_parton
,
end
(
jet_partons
),
rapidity_less
{}
);
extremal
->
set_user_index
((
unob_
)
?
UID
::
unob
:
UID
::
qqxb
);
}
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_
||
qqxf_
)
&&
i
==
jets
.
size
()
-
1
)){
// unordered/qqxf
extremal
=
std
::
max_element
(
first_new_parton
,
end
(
jet_partons
),
rapidity_less
{}
);
extremal
->
set_user_index
((
unof_
)
?
UID
::
unof
:
UID
::
qqxf
);
}
else
if
((
qqxmid_
&&
i
==
qqxbackjet
)){
extremal
=
std
::
max_element
(
first_new_parton
,
end
(
jet_partons
),
rapidity_less
{}
);
extremal
->
set_user_index
(
UID
::
qqxmid1
);
}
else
if
((
qqxmid_
&&
i
==
qqxbackjet
+
1
)){
extremal
=
std
::
min_element
(
first_new_parton
,
end
(
jet_partons
),
rapidity_less
{}
);
extremal
->
set_user_index
(
UID
::
qqxmid2
);
}
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
()
!=
UID
::
unob
)
return
false
;
if
(
unof_
&&
partons
.
back
().
user_index
()
!=
UID
::
unof
)
return
false
;
if
(
qqxb_
&&
partons
.
front
().
user_index
()
!=
UID
::
qqxb
)
return
false
;
if
(
qqxf_
&&
partons
.
back
().
user_index
()
!=
UID
::
qqxf
)
return
false
;
return
most_backward_FKL
(
partons
).
user_index
()
==
UID
::
backward_fkl
&&
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_
+
qqxb_
];
}
template
<
class
Particle
>
Particle
const
&
PhaseSpacePoint
::
most_forward_FKL
(
std
::
vector
<
Particle
>
const
&
partons
)
const
{
const
size_t
idx
=
partons
.
size
()
-
1
-
unof_
-
qqxf_
;
assert
(
idx
<
partons
.
size
());
return
partons
[
idx
];
}
template
<
class
Particle
>
Particle
&
PhaseSpacePoint
::
most_backward_FKL
(
std
::
vector
<
Particle
>
&
partons
)
const
{
return
partons
[
0
+
unob_
+
qqxb_
];
}
template
<
class
Particle
>
Particle
&
PhaseSpacePoint
::
most_forward_FKL
(
std
::
vector
<
Particle
>
&
partons
)
const
{
const
size_t
idx
=
partons
.
size
()
-
1
-
unof_
-
qqxf_
;
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
(
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
(
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
(
!
(
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
(
qqxb_
&&
!
contains_idx
(
jets
.
front
(),
partons
.
front
()))
return
false
;
if
(
unof_
&&
!
contains_idx
(
jets
.
back
(),
partons
.
back
()))
return
false
;
if
(
qqxf_
&&
!
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_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-c
Expires
Tue, Sep 30, 6:11 AM (17 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
6566456
Default Alt Text
PhaseSpacePoint.cc (31 KB)
Attached To
Mode
rHEJ HEJ
Attached
Detach File
Event Timeline
Log In to Comment