Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F19251270
Event.cc
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
49 KB
Referenced Files
None
Subscribers
None
Event.cc
View Options
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019-2023
* \copyright GPLv2 or later
*/
#include
"HEJ/Event.hh"
#include
<algorithm>
#include
<boost/rational.hpp>
#include
<cassert>
#include
<cstdlib>
#include
<iomanip>
#include
<iterator>
#include
<memory>
#include
<numeric>
#include
<optional>
#include
<ostream>
#include
<sstream>
#include
<string>
#include
<utility>
#include
"HEJ/event_types.hh"
#include
"fastjet/ClusterSequence.hh"
#include
"fastjet/JetDefinition.hh"
#include
"fastjet/PseudoJet.hh"
#include
"LHEF/LHEF.h"
#include
"HEJ/Constants.hh"
#include
"HEJ/EWConstants.hh"
#include
"HEJ/PDG_codes.hh"
#include
"HEJ/RNG.hh"
#include
"HEJ/exceptions.hh"
#include
"HEJ/LorentzVector.hh"
#include
"HEJ/utility.hh"
namespace
HEJ
{
/**
* returns all EventTypes implemented in HEJ
*/
size_t
implemented_types
(
std
::
vector
<
Particle
>
const
&
bosons
){
using
namespace
event_type
;
// no bosons
if
(
bosons
.
empty
())
return
FKL
|
UNO
|
QQBAR
;
// 1 boson
if
(
bosons
.
size
()
==
1
)
{
switch
(
bosons
[
0
].
type
)
{
case
ParticleID
::
Wp
:
case
ParticleID
::
Wm
:
case
ParticleID
::
Z_photon_mix
:
return
FKL
|
UNO
|
QQBAR
;
case
ParticleID
::
h
:
return
FKL
|
UNO
;
default
:
return
non_resummable
;
}
}
// 2 bosons
if
(
bosons
.
size
()
==
2
)
{
// Resum only samesign W events
if
(
bosons
[
0
].
type
==
ParticleID
::
Wp
&&
bosons
[
1
].
type
==
ParticleID
::
Wp
)
{
return
FKL
;
}
else
if
(
bosons
[
0
].
type
==
ParticleID
::
Wm
&&
bosons
[
1
].
type
==
ParticleID
::
Wm
)
{
return
FKL
;
}
}
return
non_resummable
;
}
namespace
{
using
std
::
size_t
;
//! LHE status codes
namespace
lhe_status
{
enum
Status
:
int
{
in
=
-
1
,
decay
=
2
,
out
=
1
,
};
}
using
LHE_Status
=
lhe_status
::
Status
;
//! true if leptonic W decay
bool
valid_W_decay
(
int
const
w_charge
,
std
::
vector
<
Particle
>
const
&
decays
){
assert
(
std
::
abs
(
w_charge
)
==
1
);
if
(
decays
.
size
()
!=
2
)
// no 1->2 decay
return
false
;
const
int
pidsum
=
decays
[
0
].
type
+
decays
[
1
].
type
;
if
(
std
::
abs
(
pidsum
)
!=
1
||
pidsum
!=
w_charge
)
// correct charge
return
false
;
// leptonic decay (only check first, second follows from pidsum)
if
(
w_charge
==
1
)
// W+
return
is_charged_antilepton
(
decays
[
0
])
||
is_neutrino
(
decays
[
0
]);
// W-
return
is_charged_lepton
(
decays
[
0
])
||
is_antineutrino
(
decays
[
0
]);
}
//! true for Z decay to charged leptons
bool
valid_Z_decay
(
std
::
vector
<
Particle
>
const
&
decays
){
if
(
decays
.
size
()
!=
2
)
// no 1->2 decay
return
false
;
if
(
decays
[
0
].
type
!=
anti
(
decays
[
1
].
type
))
{
return
false
;
}
// leptonic decay (only check first, second follows from above)
return
is_charged_anylepton
(
decays
[
0
]);
}
//! true if supported decay
bool
valid_decay
(
std
::
vector
<
Particle
>
const
&
decays
){
return
valid_W_decay
(
+
1
,
decays
)
||
// Wp
valid_W_decay
(
-
1
,
decays
)
||
// Wm
valid_Z_decay
(
decays
)
// Z/gamma
;
}
/// @name helper functions to determine event type
//@{
/**
* \brief function which determines if type change is consistent with Wp emission.
* @param in incoming Particle id
* @param out outgoing Particle id
* @param is_qqbar Current both incoming/both outgoing?
*
* \see is_Wm_Change
*/
bool
is_Wp_Change
(
ParticleID
in
,
ParticleID
out
,
bool
is_qqbar
){
using
namespace
pid
;
if
(
!
is_qqbar
&&
(
in
==
d_bar
||
in
==
u
||
in
==
s_bar
||
in
==
c
))
return
out
==
(
in
-
1
);
if
(
is_qqbar
&&
(
in
==
d
||
in
==
u_bar
||
in
==
s
||
in
==
c_bar
))
return
out
==
-
(
in
+
1
);
return
false
;
}
/**
* \brief function which determines if type change is consistent with Wm emission.
* @param in incoming Particle id
* @param out outgoing Particle id
* @param is_qqbar Current both incoming/both outgoing?
*
* Ensures that change type of quark line is possible by a flavour changing
* Wm emission. Allows checking of is_qqbar currents also.
*/
bool
is_Wm_Change
(
ParticleID
in
,
ParticleID
out
,
bool
is_qqbar
){
using
namespace
pid
;
if
(
!
is_qqbar
&&
(
in
==
d
||
in
==
u_bar
||
in
==
s
||
in
==
c_bar
))
return
out
==
(
in
+
1
);
if
(
is_qqbar
&&
(
in
==
d_bar
||
in
==
u
||
in
==
s_bar
||
in
==
c
))
return
out
==
-
(
in
-
1
);
return
false
;
}
/**
* \brief checks if particle type remains same from incoming to outgoing
* @param in incoming Particle
* @param out outgoing Particle
* @param is_qqbar Current both incoming/outgoing?
*/
bool
no_flavour_change
(
ParticleID
in
,
ParticleID
out
,
bool
is_qqbar
){
const
int
qqbarCurrent
=
is_qqbar
?-
1
:
1
;
if
(
std
::
abs
(
in
)
<=
pid
::
top
||
in
==
pid
::
gluon
)
return
(
in
==
out
*
qqbarCurrent
);
return
false
;
}
bool
is_gluon_to_Higgs
(
const
ParticleID
in
,
const
ParticleID
out
)
{
return
in
==
pid
::
gluon
&&
out
==
pid
::
Higgs
;
}
/**
* \brief check if we have a valid Impact factor
* @param in incoming Particle
* @param out outgoing Particle
* @param is_qqbar Current both incoming/outgoing?
* @param W_change returns +1 if Wp, -1 if Wm, else 0
*/
bool
is_valid_impact_factor
(
ParticleID
in
,
ParticleID
out
,
bool
is_qqbar
,
int
&
W_change
){
if
(
no_flavour_change
(
in
,
out
,
is_qqbar
)
||
is_gluon_to_Higgs
(
in
,
out
))
{
return
true
;
}
if
(
is_Wp_Change
(
in
,
out
,
is_qqbar
)
)
{
W_change
+=
1
;
return
true
;
}
if
(
is_Wm_Change
(
in
,
out
,
is_qqbar
)
)
{
W_change
-=
1
;
return
true
;
}
return
false
;
}
bool
is_extremal_higgs_off_quark
(
const
ParticleID
in
,
const
ParticleID
extremal_out
,
const
ParticleID
out
)
{
return
in
==
out
&&
extremal_out
==
pid
::
higgs
&&
is_anyquark
(
in
);
}
//! Returns all possible classifications from the impact factors
// the beginning points are changed s.t. after the the classification they
// point to the beginning of the (potential) FKL chain
// sets W_change: + if Wp change
// 0 if no change
// - if Wm change
// This function can be used with forward & backwards iterators
template
<
class
OutIterator
>
size_t
possible_impact_factors
(
ParticleID
incoming_id
,
// incoming
OutIterator
&
begin_out
,
OutIterator
const
&
end_out
,
// outgoing
int
&
W_change
,
std
::
vector
<
Particle
>
const
&
boson
,
bool
const
backward
// backward?
){
using
namespace
event_type
;
if
(
begin_out
==
end_out
)
return
non_resummable
;
// keep track of all states that we don't test
size_t
not_tested
=
qqbar_mid
;
if
(
backward
)
not_tested
|=
unof
|
qqbar_exf
;
else
not_tested
|=
unob
|
qqbar_exb
;
// Is this LL current?
if
(
is_valid_impact_factor
(
incoming_id
,
begin_out
->
type
,
false
,
W_change
)
){
++
begin_out
;
return
not_tested
|
FKL
;
}
// q -> H q and qbar -> H qbar are technically not LL,
// but we treat them as such anyway
const
auto
next
=
std
::
next
(
begin_out
);
if
(
// first ensure that the next particle is not part of the *other* impact factor
next
!=
end_out
&&
is_extremal_higgs_off_quark
(
incoming_id
,
begin_out
->
type
,
next
->
type
)
)
{
std
::
advance
(
begin_out
,
2
);
return
not_tested
|
FKL
;
}
// or NLL current?
// -> needs two partons in two different jets
if
(
std
::
distance
(
begin_out
,
end_out
)
>=
2
){
auto
next
=
std
::
next
(
begin_out
);
// Is this unordered emisson?
if
(
incoming_id
!=
pid
::
gluon
&&
begin_out
->
type
==
pid
::
gluon
){
if
(
is_valid_impact_factor
(
incoming_id
,
next
->
type
,
false
,
W_change
)
){
// veto Higgs inside uno
assert
(
next
!=
end_out
);
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
){
if
(
(
backward
&&
boson
.
front
().
rapidity
()
<
next
->
rapidity
())
||
(
!
backward
&&
boson
.
front
().
rapidity
()
>
next
->
rapidity
()))
return
non_resummable
;
}
begin_out
=
std
::
next
(
next
);
return
not_tested
|
(
backward
?
unob
:
unof
);
}
}
// Is this QQbar?
else
if
(
incoming_id
==
pid
::
gluon
){
if
(
is_valid_impact_factor
(
begin_out
->
type
,
next
->
type
,
true
,
W_change
)
){
// veto Higgs inside qqbar
assert
(
next
!=
end_out
);
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
){
if
(
(
backward
&&
boson
.
front
().
rapidity
()
<
next
->
rapidity
())
||
(
!
backward
&&
boson
.
front
().
rapidity
()
>
next
->
rapidity
()))
return
non_resummable
;
}
begin_out
=
std
::
next
(
next
);
return
not_tested
|
(
backward
?
qqbar_exb
:
qqbar_exf
);
}
}
}
return
non_resummable
;
}
//! Returns all possible classifications from central emissions
// the beginning points are changed s.t. after the the classification they
// point to the end of the emission chain
// sets W_change: + if Wp change
// 0 if no change
// - if Wm change
template
<
class
OutIterator
>
size_t
possible_central
(
OutIterator
&
begin_out
,
OutIterator
const
&
end_out
,
int
&
W_change
,
std
::
vector
<
Particle
>
const
&
boson
){
using
namespace
event_type
;
// if we already passed the central chain,
// then it is not a valid all-order state
if
(
std
::
distance
(
begin_out
,
end_out
)
<
0
)
return
non_resummable
;
// keep track of all states that we don't test
size_t
possible
=
UNO
|
EXTREMAL_QQBAR
;
// Find the first quark or antiquark emission
begin_out
=
std
::
find_if
(
begin_out
,
end_out
,
[](
Particle
const
&
p
)
{
return
is_anyquark
(
p
);
}
);
// end of chain -> FKL
if
(
begin_out
==
end_out
){
return
possible
|
FKL
;
}
// is this a qqbar-pair?
// needs two partons in two separate jets
auto
next
=
std
::
next
(
begin_out
);
if
(
next
!=
end_out
&&
is_valid_impact_factor
(
begin_out
->
type
,
next
->
type
,
true
,
W_change
)
){
// veto Higgs inside qqbar
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
&&
boson
.
front
().
rapidity
()
>
begin_out
->
rapidity
()
&&
boson
.
front
().
rapidity
()
<
next
->
rapidity
()
){
return
non_resummable
;
}
begin_out
=
std
::
next
(
next
);
// remaining chain should be pure FKL (gluon or higgs)
if
(
std
::
any_of
(
begin_out
,
end_out
,
[](
Particle
const
&
p
)
{
return
is_anyquark
(
p
);
}
))
{
return
non_resummable
;
}
return
possible
|
qqbar_mid
;
}
return
non_resummable
;
}
namespace
{
bool
is_parton_or_higgs
(
Particle
const
&
p
)
{
return
is_parton
(
p
)
||
p
.
type
==
pid
::
higgs
;
}
bool
decay_conserves_charge
(
Particle
const
&
parent
,
std
::
vector
<
Particle
>
const
&
products
)
{
auto
charge_diff
=
charge
(
parent
);
for
(
auto
const
&
p
:
products
)
{
charge_diff
-=
charge
(
p
);
}
return
charge_diff
==
0
;
}
bool
charge_conserved
(
Event
const
&
ev
)
{
boost
::
rational
<
int
>
charge_diff
{
0
};
for
(
auto
const
&
in
:
ev
.
incoming
())
{
charge_diff
+=
charge
(
in
);
}
for
(
auto
const
&
out
:
ev
.
outgoing
())
{
charge_diff
-=
charge
(
out
);
}
if
(
charge_diff
!=
0
)
return
false
;
return
std
::
all_of
(
ev
.
decays
().
begin
(),
ev
.
decays
().
end
(),
[
&
ev
](
auto
const
&
decay
)
{
auto
const
&
[
parent
,
products
]
=
decay
;
return
decay_conserves_charge
(
ev
.
outgoing
()[
parent
],
products
);
});
}
bool
decay_conserves_momentum
(
Particle
const
&
parent
,
std
::
vector
<
Particle
>
const
&
products
,
const
double
tolerance
)
{
fastjet
::
PseudoJet
total_p
;
for
(
auto
const
&
p
:
products
)
total_p
+=
p
.
p
;
return
nearby_ep
(
parent
.
p
,
total_p
,
tolerance
);
}
bool
event_momentum_conserved
(
Event
const
&
ev
,
const
double
tolerance
)
{
return
momentum_conserved
(
ev
,
tolerance
)
&&
std
::
all_of
(
ev
.
decays
().
begin
(),
ev
.
decays
().
end
(),
[
&
ev
,
tolerance
](
auto
const
&
decay
)
{
auto
const
&
[
parent
,
products
]
=
decay
;
return
decay_conserves_momentum
(
ev
.
outgoing
()[
parent
],
products
,
tolerance
);
});
}
template
<
class
Container
>
bool
massless_particles_onshell
(
Container
const
&
c
,
const
double
tolerance
)
{
return
std
::
all_of
(
c
.
begin
(),
c
.
end
(),
[
tolerance
](
Particle
const
&
p
)
{
return
is_massive
(
p
)
||
p
.
m
()
<
tolerance
*
std
::
max
(
p
.
E
(),
1.0
);
}
);
}
bool
all_massless_particles_onshell
(
Event
const
&
ev
,
const
double
tolerance
)
{
return
massless_particles_onshell
(
ev
.
incoming
(),
tolerance
)
&&
massless_particles_onshell
(
ev
.
outgoing
(),
tolerance
)
&&
std
::
all_of
(
ev
.
decays
().
begin
(),
ev
.
decays
().
end
(),
[
tolerance
](
auto
const
&
decay
)
{
return
massless_particles_onshell
(
decay
.
second
,
tolerance
);
});
}
bool
no_incoming_pt
(
Event
const
&
ev
,
const
double
tolerance
)
{
return
std
::
all_of
(
ev
.
incoming
().
cbegin
(),
ev
.
incoming
().
end
(),
[
tolerance
](
Particle
const
&
p
)
{
return
std
::
abs
(
p
.
px
())
<
tolerance
&&
std
::
abs
(
p
.
py
())
<
tolerance
;
});
}
bool
is_invalid
(
Event
const
&
ev
,
const
double
tolerance
)
{
return
!
(
charge_conserved
(
ev
)
&&
event_momentum_conserved
(
ev
,
tolerance
)
&&
no_incoming_pt
(
ev
,
tolerance
)
&&
all_massless_particles_onshell
(
ev
,
tolerance
)
);
}
// TODO: choose reasonable value or make configurable
constexpr
double
TOLERANCE
=
1e-3
;
bool
incoming_are_partons
(
Event
const
&
ev
)
{
return
std
::
all_of
(
ev
.
incoming
().
begin
(),
ev
.
incoming
().
end
(),
[](
Particle
const
&
p
)
{
return
is_parton
(
p
);
}
);
}
bool
known_outgoing
(
Event
const
&
ev
)
{
return
std
::
all_of
(
ev
.
outgoing
().
begin
(),
ev
.
outgoing
().
end
(),
[](
Particle
const
&
p
)
{
return
is_parton
(
p
)
||
p
.
type
==
pid
::
Higgs
||
std
::
abs
(
p
.
type
)
==
pid
::
Wp
||
p
.
type
==
pid
::
Z_photon_mix
;
});
}
bool
is_same_sign_WW
(
std
::
vector
<
Particle
>
const
&
particles
)
{
return
particles
.
size
()
==
2
&&
std
::
abs
(
particles
.
front
().
type
)
==
pid
::
Wp
&&
particles
.
front
().
type
==
particles
.
back
().
type
;
}
bool
all_W_Zphoton_decay
(
Event
const
&
ev
)
{
auto
const
&
out
=
ev
.
outgoing
();
for
(
std
::
size_t
i
=
0
;
i
<
out
.
size
();
++
i
)
{
if
(
(
std
::
abs
(
out
[
i
].
type
)
==
pid
::
Wp
||
out
[
i
].
type
==
pid
::
Z_photon_mix
)
&&
ev
.
decays
().
count
(
i
)
==
0
)
{
return
false
;
}
}
return
true
;
}
bool
decay_known
(
Particle
const
&
parent
,
std
::
vector
<
Particle
>
const
&
products
)
{
if
(
parent
.
type
==
pid
::
Higgs
)
return
true
;
if
(
parent
.
type
==
pid
::
Z_photon_mix
)
return
valid_Z_decay
(
products
);
if
(
std
::
abs
(
parent
.
type
)
==
pid
::
Wp
)
{
assert
(
charge
(
parent
).
denominator
()
==
1
);
return
valid_W_decay
(
charge
(
parent
).
numerator
(),
products
);
}
return
false
;
}
bool
all_decays_known
(
Event
const
&
ev
)
{
return
std
::
all_of
(
ev
.
decays
().
begin
(),
ev
.
decays
().
end
(),
[
&
ev
](
auto
const
&
decay
)
{
auto
const
&
[
parent
,
products
]
=
decay
;
return
decay_known
(
ev
.
outgoing
()[
parent
],
products
);
});
}
bool
is_known_process_type
(
Event
const
&
ev
)
{
if
(
!
incoming_are_partons
(
ev
))
return
false
;
if
(
!
known_outgoing
(
ev
))
return
false
;
if
(
!
all_W_Zphoton_decay
(
ev
))
return
false
;
if
(
!
all_decays_known
(
ev
))
return
false
;
auto
const
bosons
=
filter_AWZH_bosons
(
ev
.
outgoing
());
if
(
bosons
.
size
()
>
2
)
return
false
;
if
(
bosons
.
size
()
==
2
&&
!
is_same_sign_WW
(
bosons
))
{
return
false
;
}
if
(
bosons
.
size
()
==
1
&&
bosons
.
front
().
type
==
pid
::
Higgs
)
{
return
!
ev
.
jets
().
empty
();
}
return
ev
.
jets
().
size
()
>=
2
;
}
}
/**
* \brief Checks for all event types
* @param ev Event
* @returns Event Type
*
*/
event_type
::
EventType
classify
(
Event
const
&
ev
){
using
namespace
event_type
;
if
(
is_invalid
(
ev
,
TOLERANCE
))
return
invalid
;
if
(
!
is_known_process_type
(
ev
))
return
unknown
;
// initialise variables
auto
const
&
in
=
ev
.
incoming
();
// range for current checks
auto
begin_out
=
boost
::
make_filter_iterator
(
is_parton_or_higgs
,
cbegin
(
ev
.
outgoing
()),
cend
(
ev
.
outgoing
())
);
auto
rbegin_out
=
std
::
make_reverse_iterator
(
boost
::
make_filter_iterator
(
is_parton_or_higgs
,
cend
(
ev
.
outgoing
()),
cend
(
ev
.
outgoing
())
)
);
assert
(
std
::
distance
(
begin
(
in
),
end
(
in
))
==
2
);
assert
(
std
::
distance
(
begin_out
,
rbegin_out
.
base
())
>=
2
);
assert
(
std
::
is_sorted
(
begin_out
,
rbegin_out
.
base
(),
rapidity_less
{}));
auto
const
bosons
{
filter_AWZH_bosons
(
ev
.
outgoing
())
};
// keep track of potential W couplings, at the end the sum should be 0
int
remaining_Wp
=
0
;
int
remaining_Wm
=
0
;
for
(
auto
const
&
boson
:
bosons
){
if
(
boson
.
type
==
ParticleID
::
Wp
)
++
remaining_Wp
;
else
if
(
boson
.
type
==
ParticleID
::
Wm
)
++
remaining_Wm
;
}
size_t
final_type
=
VALID
;
// check forward impact factor
int
W_change
=
0
;
final_type
&=
possible_impact_factors
(
in
.
front
().
type
,
begin_out
,
rbegin_out
.
base
(),
W_change
,
bosons
,
true
);
if
(
final_type
==
non_resummable
)
return
non_resummable
;
if
(
W_change
>
0
)
remaining_Wp
-=
W_change
;
else
if
(
W_change
<
0
)
remaining_Wm
+=
W_change
;
// check backward impact factor
W_change
=
0
;
final_type
&=
possible_impact_factors
(
in
.
back
().
type
,
rbegin_out
,
std
::
make_reverse_iterator
(
begin_out
),
W_change
,
bosons
,
false
);
if
(
final_type
==
non_resummable
)
return
non_resummable
;
if
(
W_change
>
0
)
remaining_Wp
-=
W_change
;
else
if
(
W_change
<
0
)
remaining_Wm
+=
W_change
;
// check central emissions
W_change
=
0
;
final_type
&=
possible_central
(
begin_out
,
rbegin_out
.
base
(),
W_change
,
bosons
);
if
(
final_type
==
non_resummable
)
return
non_resummable
;
if
(
W_change
>
0
)
remaining_Wp
-=
W_change
;
else
if
(
W_change
<
0
)
remaining_Wm
+=
W_change
;
// Check whether the right number of Ws are present
if
(
remaining_Wp
!=
0
||
remaining_Wm
!=
0
)
return
non_resummable
;
// result has to be unique
if
(
(
final_type
&
(
final_type
-
1
))
!=
0
)
return
non_resummable
;
// check that each sub processes is implemented
// (has to be done at the end)
if
(
(
final_type
&
~
implemented_types
(
bosons
))
!=
0
)
return
non_resummable
;
return
static_cast
<
EventType
>
(
final_type
);
}
//@}
Particle
extract_particle
(
LHEF
::
HEPEUP
const
&
hepeup
,
size_t
i
){
auto
id
=
static_cast
<
ParticleID
>
(
hepeup
.
IDUP
[
i
]);
auto
colour
=
is_parton
(
id
)
?
hepeup
.
ICOLUP
[
i
]
:
std
::
optional
<
Colour
>
();
return
{
id
,
{
hepeup
.
PUP
[
i
][
0
],
hepeup
.
PUP
[
i
][
1
],
hepeup
.
PUP
[
i
][
2
],
hepeup
.
PUP
[
i
][
3
]
},
colour
};
}
bool
is_decay_product
(
std
::
pair
<
int
,
int
>
const
&
mothers
){
if
(
mothers
.
first
==
0
)
return
false
;
return
mothers
.
second
==
0
||
mothers
.
first
==
mothers
.
second
;
}
}
// namespace
Event
::
EventData
::
EventData
(
LHEF
::
HEPEUP
const
&
hepeup
){
parameters
.
central
=
EventParameters
{
hepeup
.
scales
.
mur
,
hepeup
.
scales
.
muf
,
hepeup
.
XWGTUP
};
size_t
in_idx
=
0
;
for
(
int
i
=
0
;
i
<
hepeup
.
NUP
;
++
i
)
{
// skip decay products
// we will add them later on, but we have to ensure that
// the decayed particle is added before
if
(
is_decay_product
(
hepeup
.
MOTHUP
[
i
]))
continue
;
auto
particle
=
extract_particle
(
hepeup
,
i
);
// needed to identify mother particles for decay products
particle
.
p
.
set_user_index
(
i
+
1
);
if
(
hepeup
.
ISTUP
[
i
]
==
LHE_Status
::
in
){
if
(
in_idx
>
incoming
.
size
())
{
throw
std
::
invalid_argument
{
"Event has too many incoming particles"
};
}
incoming
[
in_idx
++
]
=
std
::
move
(
particle
);
}
else
outgoing
.
emplace_back
(
std
::
move
(
particle
));
}
// add decay products
for
(
int
i
=
0
;
i
<
hepeup
.
NUP
;
++
i
)
{
if
(
!
is_decay_product
(
hepeup
.
MOTHUP
[
i
]))
continue
;
const
int
mother_id
=
hepeup
.
MOTHUP
[
i
].
first
;
const
auto
mother
=
std
::
find_if
(
begin
(
outgoing
),
end
(
outgoing
),
[
mother_id
](
Particle
const
&
particle
){
return
particle
.
p
.
user_index
()
==
mother_id
;
}
);
if
(
mother
==
end
(
outgoing
)){
throw
std
::
invalid_argument
{
"invalid decay product parent"
};
}
const
int
mother_idx
=
std
::
distance
(
begin
(
outgoing
),
mother
);
assert
(
mother_idx
>=
0
);
decays
[
mother_idx
].
emplace_back
(
extract_particle
(
hepeup
,
i
));
}
}
void
Event
::
EventData
::
sort
(){
// sort particles
std
::
sort
(
begin
(
incoming
),
end
(
incoming
),
[](
Particle
const
&
o1
,
Particle
const
&
o2
){
return
o1
.
p
.
pz
()
<
o2
.
p
.
pz
();}
);
auto
old_outgoing
=
std
::
move
(
outgoing
);
std
::
vector
<
size_t
>
idx
(
old_outgoing
.
size
());
std
::
iota
(
idx
.
begin
(),
idx
.
end
(),
0
);
std
::
sort
(
idx
.
begin
(),
idx
.
end
(),
[
&
old_outgoing
](
size_t
i
,
size_t
j
){
return
old_outgoing
[
i
].
rapidity
()
<
old_outgoing
[
j
].
rapidity
();
});
outgoing
.
clear
();
outgoing
.
reserve
(
old_outgoing
.
size
());
for
(
size_t
i
:
idx
)
{
outgoing
.
emplace_back
(
std
::
move
(
old_outgoing
[
i
]));
}
// find decays again
if
(
!
decays
.
empty
()){
auto
old_decays
=
std
::
move
(
decays
);
decays
.
clear
();
for
(
size_t
i
=
0
;
i
<
idx
.
size
();
++
i
)
{
auto
decay
=
old_decays
.
find
(
idx
[
i
]);
if
(
decay
!=
old_decays
.
end
())
decays
.
emplace
(
i
,
std
::
move
(
decay
->
second
));
}
assert
(
old_decays
.
size
()
==
decays
.
size
());
}
}
namespace
{
// use valid_X_decay to determine boson type
ParticleID
reconstruct_type
(
std
::
vector
<
Particle
>
const
&
progeny
)
{
if
(
valid_W_decay
(
+
1
,
progeny
))
{
return
ParticleID
::
Wp
;
}
if
(
valid_W_decay
(
-
1
,
progeny
))
{
return
ParticleID
::
Wm
;
}
if
(
valid_Z_decay
(
progeny
))
{
return
ParticleID
::
Z_photon_mix
;
}
throw
not_implemented
{
"final state with decay X -> "
+
name
(
progeny
[
0
].
type
)
+
" + "
+
name
(
progeny
[
1
].
type
)
};
}
// reconstruct particle with explicit ParticleID
Particle
reconstruct_boson
(
std
::
vector
<
Particle
>
const
&
progeny
,
ParticleID
const
&
type
)
{
Particle
progenitor
;
progenitor
.
p
=
progeny
[
0
].
p
+
progeny
[
1
].
p
;
progenitor
.
type
=
type
;
return
progenitor
;
}
// reconstruct via call to reconstruct_type
Particle
reconstruct_boson
(
std
::
vector
<
Particle
>
const
&
progeny
)
{
Particle
progenitor
{
reconstruct_boson
(
progeny
,
reconstruct_type
(
progeny
))};
assert
(
is_AWZH_boson
(
progenitor
));
return
progenitor
;
}
using
GroupedParticles
=
std
::
vector
<
std
::
vector
<
Particle
>
>
;
using
Decay
=
std
::
pair
<
Particle
,
std
::
vector
<
Particle
>
>
;
using
Decays
=
std
::
vector
<
Decay
>
;
// return groups of reconstructable progeny
std
::
vector
<
GroupedParticles
>
group_progeny
(
std
::
vector
<
Particle
>
&
leptons
)
{
/**
Warning: The partition in to charged/neutral leptons is valid ONLY for WW.
**/
assert
(
leptons
.
size
()
==
4
);
auto
const
begin_neutrino
=
std
::
partition
(
begin
(
leptons
),
end
(
leptons
),
[](
Particle
const
&
p
)
{
return
!
is_anyneutrino
(
p
);}
);
std
::
vector
<
Particle
>
neutrinos
(
begin_neutrino
,
end
(
leptons
));
leptons
.
erase
(
begin_neutrino
,
end
(
leptons
));
if
(
leptons
.
size
()
!=
2
||
neutrinos
.
size
()
!=
2
)
{
return
{};
}
assert
(
leptons
.
size
()
==
2
&&
neutrinos
.
size
()
==
2
);
std
::
vector
<
GroupedParticles
>
valid_groupings
;
GroupedParticles
candidate_grouping
{{
leptons
[
0
],
neutrinos
[
0
]},
{
leptons
[
1
],
neutrinos
[
1
]}};
if
(
valid_decay
(
candidate_grouping
.
front
())
&&
valid_decay
(
candidate_grouping
.
back
()))
{
valid_groupings
.
emplace_back
(
std
::
move
(
candidate_grouping
));
}
candidate_grouping
=
{{
leptons
[
1
],
neutrinos
[
0
]},
{
leptons
[
0
],
neutrinos
[
1
]}};
if
(
valid_decay
(
candidate_grouping
.
front
())
&&
valid_decay
(
candidate_grouping
.
back
()))
{
valid_groupings
.
emplace_back
(
std
::
move
(
candidate_grouping
));
}
return
valid_groupings
;
}
// 'best' decay ordering measure
double
decay_measure
(
const
Particle
&
reconstructed
,
EWConstants
const
&
params
)
{
ParticleProperties
ref
=
params
.
prop
(
reconstructed
.
type
);
return
std
::
abs
(
reconstructed
.
p
.
m
()
-
ref
.
mass
);
}
// decay_measure accumulated over decays
double
decay_measure
(
Decays
const
&
decays
,
EWConstants
const
&
params
)
{
return
std
::
accumulate
(
cbegin
(
decays
),
cend
(
decays
),
0.
,
[
&
params
]
(
double
dm
,
Decay
const
&
decay
)
->
double
{
return
dm
+
decay_measure
(
decay
.
first
,
params
);
}
);
}
// select best combination of decays for the event
Decays
select_decays
(
std
::
vector
<
Particle
>
&
leptons
,
EWConstants
const
&
ew_parameters
)
{
std
::
vector
<
GroupedParticles
>
groupings
=
group_progeny
(
leptons
);
std
::
vector
<
Decays
>
valid_decays
;
valid_decays
.
reserve
(
groupings
.
size
());
// Reconstruct all groupings
for
(
GroupedParticles
const
&
group
:
groupings
)
{
Decays
decays
;
for
(
auto
const
&
progeny
:
group
)
{
decays
.
emplace_back
(
make_pair
(
reconstruct_boson
(
progeny
),
progeny
));
}
valid_decays
.
emplace_back
(
decays
);
}
if
(
valid_decays
.
empty
())
{
throw
not_implemented
{
"No supported intermediate reconstruction available"
};
}
if
(
valid_decays
.
size
()
==
1
)
{
return
valid_decays
[
0
];
}
// select decay with smallest decay_measure
auto
selected
=
std
::
min_element
(
cbegin
(
valid_decays
),
cend
(
valid_decays
),
[
&
ew_parameters
]
(
auto
const
&
d1
,
auto
const
&
d2
)
->
bool
{
return
decay_measure
(
d1
,
ew_parameters
)
<
decay_measure
(
d2
,
ew_parameters
);
}
);
return
*
selected
;
}
}
// namespace
void
Event
::
EventData
::
reconstruct_intermediate
(
EWConstants
const
&
ew_parameters
)
{
auto
const
begin_leptons
=
std
::
partition
(
begin
(
outgoing
),
end
(
outgoing
),
[](
Particle
const
&
p
)
{
return
!
is_anylepton
(
p
);}
);
std
::
vector
<
Particle
>
leptons
(
begin_leptons
,
end
(
outgoing
));
outgoing
.
erase
(
begin_leptons
,
end
(
outgoing
));
if
(
leptons
.
empty
())
{
return
;
}
// nothing to do
if
(
leptons
.
size
()
==
2
)
{
outgoing
.
emplace_back
(
reconstruct_boson
(
leptons
));
std
::
sort
(
begin
(
leptons
),
end
(
leptons
),
type_less
{});
decays
.
emplace
(
outgoing
.
size
()
-
1
,
std
::
move
(
leptons
));
}
else
if
(
leptons
.
size
()
==
4
)
{
Decays
valid_decays
=
select_decays
(
leptons
,
ew_parameters
);
for
(
auto
&
decay
:
valid_decays
)
{
outgoing
.
emplace_back
(
decay
.
first
);
std
::
sort
(
begin
(
decay
.
second
),
end
(
decay
.
second
),
type_less
{});
decays
.
emplace
(
outgoing
.
size
()
-
1
,
std
::
move
(
decay
.
second
));
}
}
else
{
throw
not_implemented
{
std
::
to_string
(
leptons
.
size
())
+
" leptons in the final state"
};
}
}
namespace
{
void
repair_momentum
(
fastjet
::
PseudoJet
&
p
,
const
double
tolerance
)
{
if
(
p
.
e
()
>
0.
&&
p
.
m2
()
!=
0.
&&
(
p
.
m2
()
<
tolerance
*
tolerance
))
{
const
double
rescale
=
std
::
sqrt
(
p
.
modp
()
/
p
.
e
());
const
double
e
=
p
.
e
()
*
rescale
;
const
double
px
=
p
.
px
()
/
rescale
;
const
double
py
=
p
.
py
()
/
rescale
;
const
double
pz
=
p
.
pz
()
/
rescale
;
p
.
reset
(
px
,
py
,
pz
,
e
);
}
}
}
void
Event
::
EventData
::
repair_momenta
(
const
double
tolerance
)
{
for
(
auto
&
in
:
incoming
)
{
if
(
is_massless
(
in
))
{
const
double
px
=
(
std
::
abs
(
in
.
px
())
<
tolerance
)
?
0.
:
in
.
px
();
const
double
py
=
(
std
::
abs
(
in
.
py
())
<
tolerance
)
?
0.
:
in
.
py
();
in
.
p
.
reset
(
px
,
py
,
in
.
p
.
pz
(),
in
.
p
.
e
());
repair_momentum
(
in
.
p
,
tolerance
);
}
}
for
(
auto
&
out
:
outgoing
)
{
if
(
is_massless
(
out
))
repair_momentum
(
out
.
p
,
tolerance
);
}
for
(
auto
&
decay
:
decays
)
{
for
(
auto
&
out
:
decay
.
second
)
{
if
(
is_massless
(
out
))
repair_momentum
(
out
.
p
,
tolerance
);
}
}
}
Event
Event
::
EventData
::
cluster
(
fastjet
::
JetDefinition
const
&
jet_def
,
double
const
min_jet_pt
){
sort
();
return
Event
{
std
::
move
(
incoming
),
std
::
move
(
outgoing
),
std
::
move
(
decays
),
std
::
move
(
parameters
),
jet_def
,
min_jet_pt
};
}
Event
::
Event
(
std
::
array
<
Particle
,
2
>
&&
incoming
,
std
::
vector
<
Particle
>
&&
outgoing
,
std
::
unordered_map
<
size_t
,
std
::
vector
<
Particle
>>
&&
decays
,
Parameters
<
EventParameters
>
&&
parameters
,
fastjet
::
JetDefinition
const
&
jet_def
,
double
const
min_jet_pt
)
:
incoming_
{
std
::
move
(
incoming
)},
outgoing_
{
std
::
move
(
outgoing
)},
decays_
{
std
::
move
(
decays
)},
parameters_
{
std
::
move
(
parameters
)},
cs_
{
to_PseudoJet
(
filter_partons
(
outgoing_
)
),
jet_def
},
min_jet_pt_
{
min_jet_pt
}
{
jets_
=
sorted_by_rapidity
(
cs_
.
inclusive_jets
(
min_jet_pt_
));
assert
(
std
::
is_sorted
(
begin
(
outgoing_
),
end
(
outgoing_
),
rapidity_less
{}));
type_
=
classify
(
*
this
);
}
namespace
{
//! check that Particles have a reasonable colour
bool
correct_colour
(
Particle
const
&
part
){
ParticleID
id
{
part
.
type
};
if
(
!
is_parton
(
id
))
return
!
part
.
colour
;
if
(
!
part
.
colour
)
return
false
;
Colour
const
&
col
{
*
part
.
colour
};
if
(
is_quark
(
id
))
return
col
.
first
!=
0
&&
col
.
second
==
0
;
if
(
is_antiquark
(
id
))
return
col
.
first
==
0
&&
col
.
second
!=
0
;
assert
(
id
==
ParticleID
::
gluon
);
return
col
.
first
!=
0
&&
col
.
second
!=
0
&&
col
.
first
!=
col
.
second
;
}
//! Connect parton to t-channel colour line & update the line
//! returns false if connection not possible
template
<
class
OutIterator
>
bool
try_connect_t
(
OutIterator
const
&
it_part
,
Colour
&
line_colour
){
if
(
line_colour
.
first
==
it_part
->
colour
->
second
){
line_colour
.
first
=
it_part
->
colour
->
first
;
return
true
;
}
if
(
line_colour
.
second
==
it_part
->
colour
->
first
){
line_colour
.
second
=
it_part
->
colour
->
second
;
return
true
;
}
return
false
;
}
//! Connect parton to u-channel colour line & update the line
//! returns false if connection not possible
template
<
class
OutIterator
>
bool
try_connect_u
(
OutIterator
&
it_part
,
Colour
&
line_colour
){
auto
it_next
=
std
::
next
(
it_part
);
if
(
try_connect_t
(
it_next
,
line_colour
)
&&
try_connect_t
(
it_part
,
line_colour
)
){
it_part
=
it_next
;
return
true
;
}
return
false
;
}
}
// namespace
bool
Event
::
is_leading_colour
()
const
{
if
(
!
correct_colour
(
incoming
()[
0
])
||
!
correct_colour
(
incoming
()[
1
])
)
return
false
;
Colour
line_colour
=
*
incoming
()[
0
].
colour
;
std
::
swap
(
line_colour
.
first
,
line_colour
.
second
);
// reasonable colour
if
(
!
std
::
all_of
(
outgoing
().
cbegin
(),
outgoing
().
cend
(),
correct_colour
))
return
false
;
for
(
auto
it_part
=
cbegin_partons
();
it_part
!=
cend_partons
();
++
it_part
){
switch
(
type
())
{
case
event_type
::
FKL
:
if
(
!
try_connect_t
(
it_part
,
line_colour
)
)
return
false
;
break
;
case
event_type
::
unob
:
case
event_type
::
qqbar_exb
:
{
if
(
!
try_connect_t
(
it_part
,
line_colour
)
// u-channel only allowed at impact factor
&&
(
std
::
distance
(
cbegin_partons
(),
it_part
)
!=
0
||
!
try_connect_u
(
it_part
,
line_colour
)))
return
false
;
break
;
}
case
event_type
::
unof
:
case
event_type
::
qqbar_exf
:
{
if
(
!
try_connect_t
(
it_part
,
line_colour
)
// u-channel only allowed at impact factor
&&
(
std
::
distance
(
it_part
,
cend_partons
())
!=
2
||
!
try_connect_u
(
it_part
,
line_colour
)))
return
false
;
break
;
}
case
event_type
::
qqbar_mid
:{
auto
it_next
=
std
::
next
(
it_part
);
if
(
!
try_connect_t
(
it_part
,
line_colour
)
// u-channel only allowed at q-qbar/qbar-q pair
&&
(
(
!
(
is_quark
(
*
it_part
)
&&
is_antiquark
(
*
it_next
))
&&
!
(
is_antiquark
(
*
it_part
)
&&
is_quark
(
*
it_next
)))
||
!
try_connect_u
(
it_part
,
line_colour
))
)
return
false
;
break
;
}
default
:
throw
std
::
logic_error
{
"unreachable"
};
}
// no colour singlet exchange/disconnected diagram
if
(
line_colour
.
first
==
line_colour
.
second
)
return
false
;
}
return
(
incoming
()[
1
].
colour
->
first
==
line_colour
.
first
)
&&
(
incoming
()[
1
].
colour
->
second
==
line_colour
.
second
);
}
namespace
{
//! connect incoming Particle to colour flow
void
connect_incoming
(
Particle
&
in
,
int
&
colour
,
int
&
anti_colour
){
in
.
colour
=
std
::
make_pair
(
anti_colour
,
colour
);
// gluon
if
(
in
.
type
==
pid
::
gluon
)
return
;
if
(
in
.
type
>
0
){
// quark
assert
(
is_quark
(
in
));
in
.
colour
->
second
=
0
;
colour
*=-
1
;
return
;
}
// anti-quark
assert
(
is_antiquark
(
in
));
in
.
colour
->
first
=
0
;
anti_colour
*=-
1
;
}
//! connect outgoing Particle to t-channel colour flow
template
<
class
OutIterator
>
void
connect_tchannel
(
OutIterator
&
it_part
,
int
&
colour
,
int
&
anti_colour
,
RNG
&
ran
){
assert
(
colour
>
0
||
anti_colour
>
0
);
if
(
it_part
->
type
==
ParticleID
::
gluon
){
// gluon
if
(
colour
>
0
&&
anti_colour
>
0
){
// on g line => connect to colour OR anti-colour (random)
if
(
ran
.
flat
()
<
0.5
){
it_part
->
colour
=
std
::
make_pair
(
colour
+
2
,
colour
);
colour
+=
2
;
}
else
{
it_part
->
colour
=
std
::
make_pair
(
anti_colour
,
anti_colour
+
2
);
anti_colour
+=
2
;
}
}
else
if
(
colour
>
0
){
// on q line => connect to available colour
it_part
->
colour
=
std
::
make_pair
(
colour
+
2
,
colour
);
colour
+=
2
;
}
else
{
assert
(
colour
<
0
&&
anti_colour
>
0
);
// on qbar line => connect to available anti-colour
it_part
->
colour
=
std
::
make_pair
(
anti_colour
,
anti_colour
+
2
);
anti_colour
+=
2
;
}
}
else
if
(
is_quark
(
*
it_part
))
{
// quark
assert
(
anti_colour
>
0
);
if
(
colour
>
0
){
// on g line => connect and remove anti-colour
it_part
->
colour
=
std
::
make_pair
(
anti_colour
,
0
);
anti_colour
+=
2
;
anti_colour
*=-
1
;
}
else
{
// on qbar line => new colour
colour
*=-
1
;
it_part
->
colour
=
std
::
make_pair
(
colour
,
0
);
}
}
else
if
(
is_antiquark
(
*
it_part
))
{
// anti-quark
assert
(
colour
>
0
);
if
(
anti_colour
>
0
){
// on g line => connect and remove colour
it_part
->
colour
=
std
::
make_pair
(
0
,
colour
);
colour
+=
2
;
colour
*=-
1
;
}
else
{
// on q line => new anti-colour
anti_colour
*=-
1
;
it_part
->
colour
=
std
::
make_pair
(
0
,
anti_colour
);
}
}
else
{
// not a parton
assert
(
!
is_parton
(
*
it_part
));
it_part
->
colour
=
{};
}
}
//! connect to t- or u-channel colour flow
template
<
class
OutIterator
>
void
connect_utchannel
(
OutIterator
&
it_part
,
int
&
colour
,
int
&
anti_colour
,
RNG
&
ran
){
OutIterator
it_first
=
it_part
++
;
if
(
ran
.
flat
()
<
.5
)
{
// t-channel
connect_tchannel
(
it_first
,
colour
,
anti_colour
,
ran
);
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
}
else
{
// u-channel
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
connect_tchannel
(
it_first
,
colour
,
anti_colour
,
ran
);
}
}
}
// namespace
bool
Event
::
generate_colours
(
RNG
&
ran
){
// generate only for HEJ events
if
(
!
event_type
::
is_resummable
(
type
()))
return
false
;
assert
(
std
::
is_sorted
(
begin
(
outgoing
()),
end
(
outgoing
()),
rapidity_less
{}));
assert
(
incoming
()[
0
].
pz
()
<
incoming
()[
1
].
pz
());
// positive (anti-)colour -> can connect
// negative (anti-)colour -> not available/used up by (anti-)quark
int
colour
=
COLOUR_OFFSET
;
int
anti_colour
=
colour
+
1
;
// initialise first
connect_incoming
(
incoming_
[
0
],
colour
,
anti_colour
);
// reset outgoing colours
std
::
for_each
(
outgoing_
.
begin
(),
outgoing_
.
end
(),
[](
Particle
&
part
){
part
.
colour
=
{};});
for
(
auto
it_part
=
begin_partons
();
it_part
!=
end_partons
();
++
it_part
){
switch
(
type
())
{
// subleading can connect to t- or u-channel
case
event_type
::
unob
:
case
event_type
::
qqbar_exb
:
{
if
(
std
::
distance
(
begin_partons
(),
it_part
)
==
0
)
connect_utchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
else
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
break
;
}
case
event_type
::
unof
:
case
event_type
::
qqbar_exf
:
{
if
(
std
::
distance
(
it_part
,
end_partons
())
==
2
)
connect_utchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
else
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
break
;
}
case
event_type
::
qqbar_mid
:{
auto
it_next
=
std
::
next
(
it_part
);
if
(
std
::
distance
(
begin_partons
(),
it_part
)
>
0
&&
std
::
distance
(
it_part
,
end_partons
())
>
2
&&
(
(
is_quark
(
*
it_part
)
&&
is_antiquark
(
*
it_next
))
||
(
is_antiquark
(
*
it_part
)
&&
is_quark
(
*
it_next
))
)
)
connect_utchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
else
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
break
;
}
default
:
// rest has to be t-channel
connect_tchannel
(
it_part
,
colour
,
anti_colour
,
ran
);
}
}
// Connect last
connect_incoming
(
incoming_
[
1
],
anti_colour
,
colour
);
assert
(
is_leading_colour
());
return
true
;
}
// generate_colours
namespace
{
bool
valid_parton
(
std
::
vector
<
fastjet
::
PseudoJet
>
const
&
jets
,
Particle
const
&
parton
,
int
const
idx
,
double
const
soft_pt_regulator
){
// TODO code overlap with PhaseSpacePoint::pass_extremal_cuts
if
(
idx
<
0
)
return
false
;
assert
(
static_cast
<
int
>
(
jets
.
size
())
>=
idx
);
auto
const
&
jet
{
jets
[
idx
]
};
return
(
parton
.
p
-
jet
).
pt
()
/
jet
.
pt
()
<=
soft_pt_regulator
;
}
}
// namespace
bool
Event
::
valid_hej_state
(
double
const
soft_pt_regulator
)
const
{
using
namespace
event_type
;
const
auto
is_valid_parton
=
[
&
](
Particle
const
&
parton
,
int
const
jet_idx
)
{
return
valid_parton
(
jets
(),
parton
,
jet_idx
,
soft_pt_regulator
);
};
if
(
!
is_resummable
(
type
()))
return
false
;
auto
const
&
jet_indices
{
particle_jet_indices
()
};
auto
jet_idx_begin
{
jet_indices
.
cbegin
()
};
auto
jet_idx_end
{
jet_indices
.
crbegin
()
};
auto
part_begin
{
cbegin_partons
()
};
auto
part_end
{
crbegin_partons
()
};
if
(
!
is_backward_g_to_h
(
*
this
))
{
const
int
first_jet_idx
=
*
jet_idx_begin
;
if
(
!
is_valid_parton
(
*
part_begin
,
first_jet_idx
))
{
return
false
;
}
++
part_begin
;
++
jet_idx_begin
;
// unob -> second parton in own jet
if
(
type
()
&
(
unob
|
qqbar_exb
)
){
if
(
(
*
jet_idx_begin
==
first_jet_idx
)
||
!
is_valid_parton
(
*
part_begin
,
*
jet_idx_begin
)
)
{
return
false
;
}
++
part_begin
;
++
jet_idx_begin
;
}
}
if
(
!
is_forward_g_to_h
(
*
this
))
{
const
int
last_jet_idx
=
*
jet_idx_end
;
if
(
!
is_valid_parton
(
*
part_end
,
last_jet_idx
))
{
return
false
;
}
++
part_end
;
++
jet_idx_end
;
if
(
type
()
&
(
unof
|
qqbar_exf
)
){
if
(
(
*
jet_idx_end
==
last_jet_idx
)
||
!
is_valid_parton
(
*
part_end
,
*
jet_idx_end
)
)
{
return
false
;
}
++
part_end
;
// ++jet_idx_end; // last check, we don't need idx_end afterwards
}
}
if
(
type
()
&
qqbar_mid
){
// find qqbar pair
auto
begin_qqbar
{
std
::
find_if
(
part_begin
,
part_end
.
base
(),
[](
Particle
const
&
part
)
->
bool
{
return
part
.
type
!=
ParticleID
::
gluon
;
}
)};
assert
(
begin_qqbar
!=
part_end
.
base
());
long
int
qqbar_pos
{
std
::
distance
(
part_begin
,
begin_qqbar
)
};
assert
(
qqbar_pos
>=
0
);
jet_idx_begin
+=
qqbar_pos
;
const
int
next_jet_idx
=
*
std
::
next
(
jet_idx_begin
);
if
(
(
*
jet_idx_begin
==
next_jet_idx
)
||
!
is_valid_parton
(
*
begin_qqbar
,
*
jet_idx_begin
)
||
!
is_valid_parton
(
*
std
::
next
(
begin_qqbar
),
next_jet_idx
)
)
{
return
false
;
}
}
return
true
;
}
bool
Event
::
valid_incoming
()
const
{
for
(
std
::
size_t
i
=
0
;
i
<
incoming_
.
size
();
++
i
){
if
(
!
(
HEJ
::
nearby_ep
(
std
::
abs
(
incoming_
[
i
].
pz
()),
incoming_
[
i
].
E
(),
TOL
*
incoming_
[
i
].
E
())
&&
(
incoming_
[
i
].
pt
()
==
0.
)))
return
false
;
}
return
true
;
}
Event
::
ConstPartonIterator
Event
::
begin_partons
()
const
{
return
cbegin_partons
();
}
Event
::
ConstPartonIterator
Event
::
cbegin_partons
()
const
{
return
{
HEJ
::
is_parton
,
cbegin
(
outgoing
()),
cend
(
outgoing
())};
}
Event
::
ConstPartonIterator
Event
::
end_partons
()
const
{
return
cend_partons
();
}
Event
::
ConstPartonIterator
Event
::
cend_partons
()
const
{
return
{
HEJ
::
is_parton
,
cend
(
outgoing
()),
cend
(
outgoing
())};
}
Event
::
ConstReversePartonIterator
Event
::
rbegin_partons
()
const
{
return
crbegin_partons
();
}
Event
::
ConstReversePartonIterator
Event
::
crbegin_partons
()
const
{
return
std
::
reverse_iterator
<
ConstPartonIterator
>
(
cend_partons
()
);
}
Event
::
ConstReversePartonIterator
Event
::
rend_partons
()
const
{
return
crend_partons
();
}
Event
::
ConstReversePartonIterator
Event
::
crend_partons
()
const
{
return
std
::
reverse_iterator
<
ConstPartonIterator
>
(
cbegin_partons
()
);
}
Event
::
PartonIterator
Event
::
begin_partons
()
{
return
{
HEJ
::
is_parton
,
begin
(
outgoing_
),
end
(
outgoing_
)};
}
Event
::
PartonIterator
Event
::
end_partons
()
{
return
{
HEJ
::
is_parton
,
end
(
outgoing_
),
end
(
outgoing_
)};
}
Event
::
ReversePartonIterator
Event
::
rbegin_partons
()
{
return
std
::
reverse_iterator
<
PartonIterator
>
(
end_partons
()
);
}
Event
::
ReversePartonIterator
Event
::
rend_partons
()
{
return
std
::
reverse_iterator
<
PartonIterator
>
(
begin_partons
()
);
}
namespace
{
void
print_momentum
(
std
::
ostream
&
os
,
fastjet
::
PseudoJet
const
&
part
){
constexpr
int
prec
=
6
;
const
std
::
streamsize
orig_prec
=
os
.
precision
();
os
<<
std
::
scientific
<<
std
::
setprecision
(
prec
)
<<
"["
<<
std
::
setw
(
2
*
prec
+
1
)
<<
std
::
right
<<
part
.
px
()
<<
", "
<<
std
::
setw
(
2
*
prec
+
1
)
<<
std
::
right
<<
part
.
py
()
<<
", "
<<
std
::
setw
(
2
*
prec
+
1
)
<<
std
::
right
<<
part
.
pz
()
<<
", "
<<
std
::
setw
(
2
*
prec
+
1
)
<<
std
::
right
<<
part
.
E
()
<<
"]"
<<
std
::
fixed
;
os
.
precision
(
orig_prec
);
}
void
print_colour
(
std
::
ostream
&
os
,
std
::
optional
<
Colour
>
const
&
col
){
constexpr
int
width
=
3
;
if
(
!
col
)
os
<<
"(no color)"
;
// American spelling for better alignment
else
os
<<
"("
<<
std
::
setw
(
width
)
<<
std
::
right
<<
col
->
first
<<
", "
<<
std
::
setw
(
width
)
<<
std
::
right
<<
col
->
second
<<
")"
;
}
}
// namespace
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
Event
const
&
ev
){
constexpr
int
prec
=
4
;
constexpr
int
wtype
=
3
;
// width for types
const
std
::
streamsize
orig_prec
=
os
.
precision
();
os
<<
std
::
setprecision
(
prec
)
<<
std
::
fixed
;
os
<<
"########## "
<<
name
(
ev
.
type
())
<<
" ##########"
<<
std
::
endl
;
os
<<
"Incoming particles:
\n
"
;
for
(
auto
const
&
in
:
ev
.
incoming
()){
os
<<
std
::
setw
(
wtype
)
<<
in
.
type
<<
": "
;
print_colour
(
os
,
in
.
colour
);
os
<<
" "
;
print_momentum
(
os
,
in
.
p
);
os
<<
std
::
endl
;
}
os
<<
"
\n
Outgoing particles: "
<<
ev
.
outgoing
().
size
()
<<
"
\n
"
;
for
(
auto
const
&
out
:
ev
.
outgoing
()){
os
<<
std
::
setw
(
wtype
)
<<
out
.
type
<<
": "
;
print_colour
(
os
,
out
.
colour
);
os
<<
" "
;
print_momentum
(
os
,
out
.
p
);
os
<<
" => rapidity="
<<
std
::
setw
(
2
*
prec
-
1
)
<<
std
::
right
<<
out
.
rapidity
()
<<
std
::
endl
;
}
os
<<
"
\n
Forming Jets: "
<<
ev
.
jets
().
size
()
<<
"
\n
"
;
for
(
auto
const
&
jet
:
ev
.
jets
()){
print_momentum
(
os
,
jet
);
os
<<
" => rapidity="
<<
std
::
setw
(
2
*
prec
-
1
)
<<
std
::
right
<<
jet
.
rapidity
()
<<
std
::
endl
;
}
if
(
!
ev
.
decays
().
empty
()
){
os
<<
"
\n
Decays: "
<<
ev
.
decays
().
size
()
<<
"
\n
"
;
for
(
auto
const
&
decay
:
ev
.
decays
()){
os
<<
std
::
setw
(
wtype
)
<<
ev
.
outgoing
()[
decay
.
first
].
type
<<
" ("
<<
decay
.
first
<<
") to:
\n
"
;
for
(
auto
const
&
out
:
decay
.
second
){
os
<<
" "
<<
std
::
setw
(
wtype
)
<<
out
.
type
<<
": "
;
print_momentum
(
os
,
out
.
p
);
os
<<
" => rapidity="
<<
std
::
setw
(
2
*
prec
-
1
)
<<
std
::
right
<<
out
.
rapidity
()
<<
std
::
endl
;
}
}
}
os
<<
std
::
defaultfloat
;
os
.
precision
(
orig_prec
);
return
os
;
}
std
::
string
to_string
(
Event
const
&
ev
){
std
::
stringstream
ss
;
ss
<<
ev
;
return
ss
.
str
();
}
double
shat
(
Event
const
&
ev
){
return
(
ev
.
incoming
()[
0
].
p
+
ev
.
incoming
()[
1
].
p
).
m2
();
}
LHEF
::
HEPEUP
to_HEPEUP
(
Event
const
&
event
,
LHEF
::
HEPRUP
*
heprup
){
LHEF
::
HEPEUP
result
;
result
.
heprup
=
heprup
;
result
.
weights
=
{{
event
.
central
().
weight
,
nullptr
}};
for
(
auto
const
&
var
:
event
.
variations
()){
result
.
weights
.
emplace_back
(
var
.
weight
,
nullptr
);
}
size_t
num_particles
=
event
.
incoming
().
size
()
+
event
.
outgoing
().
size
();
for
(
auto
const
&
decay
:
event
.
decays
())
num_particles
+=
decay
.
second
.
size
();
result
.
NUP
=
num_particles
;
// the following entries are pretty much meaningless
result
.
IDPRUP
=
event
.
type
();
// event type
result
.
AQEDUP
=
1.
/
128.
;
// alpha_EW
//result.AQCDUP = 0.118 // alpha_QCD
// end meaningless part
result
.
XWGTUP
=
event
.
central
().
weight
;
result
.
SCALUP
=
event
.
central
().
muf
;
result
.
scales
.
muf
=
event
.
central
().
muf
;
result
.
scales
.
mur
=
event
.
central
().
mur
;
result
.
scales
.
SCALUP
=
event
.
central
().
muf
;
result
.
pdfinfo
.
p1
=
event
.
incoming
().
front
().
type
;
result
.
pdfinfo
.
p2
=
event
.
incoming
().
back
().
type
;
result
.
pdfinfo
.
scale
=
event
.
central
().
muf
;
result
.
IDUP
.
reserve
(
num_particles
);
// PID
result
.
ISTUP
.
reserve
(
num_particles
);
// status (in, out, decay)
result
.
PUP
.
reserve
(
num_particles
);
// momentum
result
.
MOTHUP
.
reserve
(
num_particles
);
// index mother particle
result
.
ICOLUP
.
reserve
(
num_particles
);
// colour
// incoming
std
::
array
<
Particle
,
2
>
incoming
{
event
.
incoming
()
};
// First incoming should be positive pz according to LHE standard
// (or at least most (everyone?) do it this way, and Pythia assumes it)
if
(
incoming
[
0
].
pz
()
<
incoming
[
1
].
pz
())
std
::
swap
(
incoming
[
0
],
incoming
[
1
]);
for
(
Particle
const
&
in
:
incoming
){
result
.
IDUP
.
emplace_back
(
in
.
type
);
result
.
ISTUP
.
emplace_back
(
LHE_Status
::
in
);
result
.
PUP
.
push_back
({
in
.
p
[
0
],
in
.
p
[
1
],
in
.
p
[
2
],
in
.
p
[
3
],
in
.
p
.
m
()});
result
.
MOTHUP
.
emplace_back
(
0
,
0
);
assert
(
in
.
colour
);
result
.
ICOLUP
.
emplace_back
(
*
in
.
colour
);
}
// outgoing
for
(
size_t
i
=
0
;
i
<
event
.
outgoing
().
size
();
++
i
){
Particle
const
&
out
=
event
.
outgoing
()[
i
];
result
.
IDUP
.
emplace_back
(
out
.
type
);
const
int
status
=
event
.
decays
().
count
(
i
)
!=
0u
?
LHE_Status
::
decay
:
LHE_Status
::
out
;
result
.
ISTUP
.
emplace_back
(
status
);
result
.
PUP
.
push_back
({
out
.
p
[
0
],
out
.
p
[
1
],
out
.
p
[
2
],
out
.
p
[
3
],
out
.
p
.
m
()});
result
.
MOTHUP
.
emplace_back
(
1
,
2
);
if
(
out
.
colour
)
result
.
ICOLUP
.
emplace_back
(
*
out
.
colour
);
else
{
result
.
ICOLUP
.
emplace_back
(
std
::
make_pair
(
0
,
0
));
}
}
// decays
for
(
auto
const
&
decay
:
event
.
decays
()){
for
(
auto
const
&
out
:
decay
.
second
){
result
.
IDUP
.
emplace_back
(
out
.
type
);
result
.
ISTUP
.
emplace_back
(
LHE_Status
::
out
);
result
.
PUP
.
push_back
({
out
.
p
[
0
],
out
.
p
[
1
],
out
.
p
[
2
],
out
.
p
[
3
],
out
.
p
.
m
()});
const
size_t
mother_idx
=
1
+
event
.
incoming
().
size
()
+
decay
.
first
;
result
.
MOTHUP
.
emplace_back
(
mother_idx
,
mother_idx
);
result
.
ICOLUP
.
emplace_back
(
0
,
0
);
}
}
assert
(
result
.
ICOLUP
.
size
()
==
num_particles
);
static
constexpr
double
unknown_spin
=
9.
;
//per Les Houches accord
result
.
VTIMUP
=
std
::
vector
<
double
>
(
num_particles
,
unknown_spin
);
result
.
SPINUP
=
result
.
VTIMUP
;
return
result
;
}
}
// namespace HEJ
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Sep 30, 5:49 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
6477927
Default Alt Text
Event.cc (49 KB)
Attached To
Mode
rHEJ HEJ
Attached
Detach File
Event Timeline
Log In to Comment