Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F19251007
Event.cc
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
32 KB
Referenced Files
None
Subscribers
None
Event.cc
View Options
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2019
* \copyright GPLv2 or later
*/
#include
"HEJ/Event.hh"
#include
<algorithm>
#include
<assert.h>
#include
<iterator>
#include
<numeric>
#include
<unordered_set>
#include
<utility>
#include
"LHEF/LHEF.h"
#include
"fastjet/JetDefinition.hh"
#include
"HEJ/Constants.hh"
#include
"HEJ/exceptions.hh"
#include
"HEJ/PDG_codes.hh"
namespace
HEJ
{
namespace
{
constexpr
int
status_in
=
-
1
;
constexpr
int
status_decayed
=
2
;
constexpr
int
status_out
=
1
;
//! true if leptonic W decay
bool
valid_W_decay
(
int
const
w_type
,
// sign of W
std
::
vector
<
Particle
>
const
&
decays
){
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_type
)
// correct charge
return
false
;
// leptonic decay (only check first, second follows from pidsum)
if
(
w_type
==
1
)
// W+
return
is_antilepton
(
decays
[
0
])
||
is_neutrino
(
decays
[
0
]);
// W-
return
is_lepton
(
decays
[
0
])
||
is_antineutrino
(
decays
[
0
]);
}
/// @name helper functions to determine event type
//@{
/**
* \brief check if final state valid for HEJ
*
* check if there is at most one photon, W, H, Z in the final state
* and all the rest are quarks or gluons
*/
bool
final_state_ok
(
Event
const
&
ev
){
std
::
vector
<
Particle
>
const
&
outgoing
=
ev
.
outgoing
();
if
(
ev
.
decays
().
size
()
>
1
)
// at most one decay
return
false
;
bool
has_AWZH_boson
=
false
;
for
(
size_t
i
=
0
;
i
<
outgoing
.
size
();
++
i
){
auto
const
&
out
{
outgoing
[
i
]
};
if
(
is_AWZH_boson
(
out
.
type
)){
// at most one boson
if
(
has_AWZH_boson
)
return
false
;
has_AWZH_boson
=
true
;
// valid decay for W
if
(
std
::
abs
(
out
.
type
)
==
ParticleID
::
Wp
){
// exactly 1 decay of W
if
(
ev
.
decays
().
size
()
!=
1
||
ev
.
decays
().
cbegin
()
->
first
!=
i
)
return
false
;
if
(
!
valid_W_decay
(
out
.
type
>
0
?+
1
:-
1
,
ev
.
decays
().
cbegin
()
->
second
)
)
return
false
;
}
}
else
if
(
!
is_parton
(
out
.
type
))
return
false
;
}
return
true
;
}
/**
* returns all EventTypes implemented in HEJ
*/
size_t
implemented_types
(
std
::
vector
<
Particle
>
const
&
bosons
){
using
namespace
event_type
;
if
(
bosons
.
empty
())
return
FKL
|
unob
|
unof
|
qqxexb
|
qqxexf
;
// pure jets
if
(
bosons
.
size
()
>
1
)
return
non_resummable
;
// multi boson
switch
(
bosons
[
0
].
type
)
{
case
ParticleID
::
Wp
:
case
ParticleID
::
Wm
:
return
FKL
|
unob
|
unof
|
qqxexb
|
qqxexf
|
qqxmid
;
case
ParticleID
::
h
:
return
FKL
|
unob
|
unof
;
default
:
return
non_resummable
;
}
}
/**
* \brief function which determines if type change is consistent with Wp emission.
* @param in incoming Particle id
* @param out outgoing Particle id
* @param qqx Current both incoming/both outgoing?
*
* \see is_Wm_Change
*/
bool
is_Wp_Change
(
ParticleID
in
,
ParticleID
out
,
bool
qqx
){
if
(
!
qqx
&&
(
in
==-
1
||
in
==
2
||
in
==-
3
||
in
==
4
))
return
out
==
(
in
-
1
);
if
(
qqx
&&
(
in
==
1
||
in
==-
2
||
in
==
3
||
in
==-
4
))
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 qqx Current both incoming/both outgoing?
*
* Ensures that change type of quark line is possible by a flavour changing
* Wm emission. Allows checking of qqx currents also.
*/
bool
is_Wm_Change
(
ParticleID
in
,
ParticleID
out
,
bool
qqx
){
if
(
!
qqx
&&
(
in
==
1
||
in
==-
2
||
in
==
3
||
in
==-
4
))
return
out
==
(
in
+
1
);
if
(
qqx
&&
(
in
==-
1
||
in
==
2
||
in
==-
3
||
in
==
4
))
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 qqx Current both incoming/outgoing?
*/
bool
no_flavour_change
(
ParticleID
in
,
ParticleID
out
,
bool
qqx
){
const
int
qqxCurrent
=
qqx
?-
1
:
1
;
if
(
abs
(
in
)
<=
6
||
in
==
pid
::
gluon
)
return
(
in
==
out
*
qqxCurrent
);
else
return
false
;
}
bool
has_2_jets
(
Event
const
&
event
){
return
event
.
jets
().
size
()
>=
2
;
}
/**
* \brief check if we have a valid Impact factor
* @param in incoming Particle
* @param out outgoing Particle
* @param qqx Current both incoming/outgoing?
* @param qqx returns +1 if Wp, -1 if Wm, else 0
*/
bool
is_valid_impact_factor
(
ParticleID
in
,
ParticleID
out
,
bool
qqx
,
int
&
W_change
){
if
(
no_flavour_change
(
in
,
out
,
qqx
)
){
return
true
;
}
if
(
is_Wp_Change
(
in
,
out
,
qqx
)
)
{
W_change
+=
1
;
return
true
;
}
if
(
is_Wm_Change
(
in
,
out
,
qqx
)
)
{
W_change
-=
1
;
return
true
;
}
return
false
;
}
//! 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
;
assert
(
boson
.
size
()
<
2
);
// keep track of all states that we don't test
size_t
not_tested
=
qqxmid
;
if
(
backward
)
not_tested
|=
unof
|
qqxexf
;
else
not_tested
|=
unob
|
qqxexb
;
// Is this LL current?
if
(
is_valid_impact_factor
(
incoming_id
,
begin_out
->
type
,
false
,
W_change
)
){
++
begin_out
;
return
not_tested
|
FKL
;
}
// or NLL current?
// -> needs two partons in two different jets
if
(
std
::
distance
(
begin_out
,
end_out
)
>=
2
){
// Is this unordered emisson?
if
(
incoming_id
!=
pid
::
gluon
&&
begin_out
->
type
==
pid
::
gluon
){
if
(
is_valid_impact_factor
(
incoming_id
,
(
begin_out
+
1
)
->
type
,
false
,
W_change
)
){
// veto Higgs inside uno
assert
((
begin_out
+
1
)
<
end_out
);
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
){
if
(
(
backward
&&
boson
.
front
().
rapidity
()
<
(
begin_out
+
1
)
->
rapidity
())
||
(
!
backward
&&
boson
.
front
().
rapidity
()
>
(
begin_out
+
1
)
->
rapidity
()))
return
non_resummable
;
}
begin_out
+=
2
;
return
not_tested
|
(
backward
?
unob
:
unof
);
}
}
// Is this QQbar?
else
if
(
incoming_id
==
pid
::
gluon
){
if
(
is_valid_impact_factor
(
begin_out
->
type
,
(
begin_out
+
1
)
->
type
,
true
,
W_change
)
){
// veto Higgs inside qqx
assert
((
begin_out
+
1
)
<
end_out
);
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
){
if
(
(
backward
&&
boson
.
front
().
rapidity
()
<
(
begin_out
+
1
)
->
rapidity
())
||
(
!
backward
&&
boson
.
front
().
rapidity
()
>
(
begin_out
+
1
)
->
rapidity
()))
return
non_resummable
;
}
begin_out
+=
2
;
return
not_tested
|
(
backward
?
qqxexb
:
qqxexf
);
}
}
}
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
;
assert
(
boson
.
size
()
<
2
);
// 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
=
unob
|
unof
|
qqxexb
|
qqxexf
;
// Find the first non-gluon/non-FKL
while
(
(
begin_out
->
type
==
pid
::
gluon
)
&&
(
begin_out
<
end_out
)
){
++
begin_out
;
}
// end of chain -> FKL
if
(
begin_out
==
end_out
){
return
possible
|
FKL
;
}
// is this a qqbar-pair?
// needs two partons in two separate jets
if
(
is_valid_impact_factor
(
begin_out
->
type
,
(
begin_out
+
1
)
->
type
,
true
,
W_change
)
){
// veto Higgs inside qqx
if
(
!
boson
.
empty
()
&&
boson
.
front
().
type
==
ParticleID
::
h
&&
boson
.
front
().
rapidity
()
>
begin_out
->
rapidity
()
&&
boson
.
front
().
rapidity
()
<
(
begin_out
+
1
)
->
rapidity
()
){
return
non_resummable
;
}
begin_out
+=
2
;
// remaining chain should be pure gluon/FKL
for
(;
begin_out
<
end_out
;
++
begin_out
){
if
(
begin_out
->
type
!=
pid
::
gluon
)
return
non_resummable
;
}
return
possible
|
qqxmid
;
}
return
non_resummable
;
}
/**
* \brief Checks for all event types
* @param ev Event
* @returns Event Type
*
*/
event_type
::
EventType
classify
(
Event
const
&
ev
){
using
namespace
event_type
;
if
(
!
has_2_jets
(
ev
))
return
no_2_jets
;
// currently we can't handle multiple boson states in the ME. So they are
// considered "bad_final_state" even though the "classify" could work with
// them.
if
(
!
final_state_ok
(
ev
))
return
bad_final_state
;
// initialise variables
auto
const
&
in
=
ev
.
incoming
();
auto
const
&
out
=
filter_partons
(
ev
.
outgoing
());
assert
(
std
::
distance
(
begin
(
in
),
end
(
in
))
==
2
);
assert
(
out
.
size
()
>=
2
);
assert
(
std
::
distance
(
begin
(
out
),
end
(
out
))
>=
2
);
assert
(
std
::
is_sorted
(
begin
(
out
),
end
(
out
),
rapidity_less
{}));
auto
const
boson
{
filter_AWZH_bosons
(
ev
.
outgoing
())
};
// we only allow one boson through final_state_ok
assert
(
boson
.
size
()
<=
1
);
// keep track of potential W couplings, at the end the sum should be 0
int
remaining_Wp
=
0
;
int
remaining_Wm
=
0
;
if
(
!
boson
.
empty
()
&&
abs
(
boson
.
front
().
type
)
==
ParticleID
::
Wp
){
if
(
boson
.
front
().
type
>
0
)
++
remaining_Wp
;
else
++
remaining_Wm
;
}
int
W_change
=
0
;
// range for current checks
auto
begin_out
{
out
.
cbegin
()};
auto
end_out
{
out
.
crbegin
()};
size_t
final_type
=
~
(
no_2_jets
|
bad_final_state
);
// check forward impact factor
final_type
&=
possible_impact_factors
(
in
.
front
().
type
,
begin_out
,
end_out
.
base
(),
W_change
,
boson
,
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
;
W_change
=
0
;
// check backward impact factor
final_type
&=
possible_impact_factors
(
in
.
back
().
type
,
end_out
,
std
::
make_reverse_iterator
(
begin_out
),
W_change
,
boson
,
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
;
W_change
=
0
;
// check central emissions
final_type
&=
possible_central
(
begin_out
,
end_out
.
base
(),
W_change
,
boson
);
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
(
boson
))
!=
0
)
return
non_resummable
;
return
static_cast
<
EventType
>
(
final_type
);
}
//@}
Particle
extract_particle
(
LHEF
::
HEPEUP
const
&
hepeup
,
int
i
){
const
ParticleID
id
=
static_cast
<
ParticleID
>
(
hepeup
.
IDUP
[
i
]);
const
fastjet
::
PseudoJet
momentum
{
hepeup
.
PUP
[
i
][
0
],
hepeup
.
PUP
[
i
][
1
],
hepeup
.
PUP
[
i
][
2
],
hepeup
.
PUP
[
i
][
3
]
};
if
(
is_parton
(
id
))
return
Particle
{
id
,
std
::
move
(
momentum
),
hepeup
.
ICOLUP
[
i
]
};
return
Particle
{
id
,
std
::
move
(
momentum
),
{}
};
}
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 anonymous
Event
::
EventData
::
EventData
(
LHEF
::
HEPEUP
const
&
hepeup
){
parameters
.
central
=
EventParameters
{
hepeup
.
scales
.
mur
,
hepeup
.
scales
.
muf
,
hepeup
.
weight
()
};
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
]
==
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
));
}
}
Event
::
Event
(
UnclusteredEvent
const
&
ev
,
fastjet
::
JetDefinition
const
&
jet_def
,
double
const
min_jet_pt
)
:
Event
(
Event
::
EventData
{
ev
.
incoming
,
ev
.
outgoing
,
ev
.
decays
,
Parameters
<
EventParameters
>
{
ev
.
central
,
ev
.
variations
}
}.
cluster
(
jet_def
,
min_jet_pt
)
)
{}
//! @TODO remove in HEJ 2.2.0
UnclusteredEvent
::
UnclusteredEvent
(
LHEF
::
HEPEUP
const
&
hepeup
){
Event
::
EventData
const
evData
{
hepeup
};
incoming
=
evData
.
incoming
;
outgoing
=
evData
.
outgoing
;
decays
=
evData
.
decays
;
central
=
evData
.
parameters
.
central
;
variations
=
evData
.
parameters
.
variations
;
}
void
Event
::
EventData
::
sort
(){
// sort particles
std
::
sort
(
begin
(
incoming
),
end
(
incoming
),
[](
Particle
o1
,
Particle
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
{
Particle
reconstruct_boson
(
std
::
vector
<
Particle
>
const
&
leptons
)
{
Particle
decayed_boson
;
decayed_boson
.
p
=
leptons
[
0
].
p
+
leptons
[
1
].
p
;
const
int
pidsum
=
leptons
[
0
].
type
+
leptons
[
1
].
type
;
if
(
pidsum
==
+
1
)
{
assert
(
is_antilepton
(
leptons
[
0
]));
if
(
is_antineutrino
(
leptons
[
0
]))
{
throw
not_implemented
{
"lepton-flavour violating final state"
};
}
assert
(
is_neutrino
(
leptons
[
1
]));
// charged antilepton + neutrino means we had a W+
decayed_boson
.
type
=
pid
::
Wp
;
}
else
if
(
pidsum
==
-
1
)
{
assert
(
is_antilepton
(
leptons
[
0
]));
if
(
is_neutrino
(
leptons
[
1
]))
{
throw
not_implemented
{
"lepton-flavour violating final state"
};
}
assert
(
is_antineutrino
(
leptons
[
0
]));
// charged lepton + antineutrino means we had a W-
decayed_boson
.
type
=
pid
::
Wm
;
}
else
{
throw
not_implemented
{
"final state with leptons "
+
name
(
leptons
[
0
].
type
)
+
" and "
+
name
(
leptons
[
1
].
type
)
};
}
return
decayed_boson
;
}
}
void
Event
::
EventData
::
reconstruct_intermediate
()
{
const
auto
begin_leptons
=
std
::
partition
(
begin
(
outgoing
),
end
(
outgoing
),
[](
Particle
const
&
p
)
{
return
!
is_anylepton
(
p
);}
);
if
(
begin_leptons
==
end
(
outgoing
))
return
;
assert
(
is_anylepton
(
*
begin_leptons
));
std
::
vector
<
Particle
>
leptons
(
begin_leptons
,
end
(
outgoing
));
outgoing
.
erase
(
begin_leptons
,
end
(
outgoing
));
if
(
leptons
.
size
()
!=
2
)
{
throw
not_implemented
{
"Final states with one or more than two leptons"
};
}
std
::
sort
(
begin
(
leptons
),
end
(
leptons
),
[](
Particle
const
&
p0
,
Particle
const
&
p1
)
{
return
p0
.
type
<
p1
.
type
;
}
);
outgoing
.
emplace_back
(
reconstruct_boson
(
leptons
));
decays
.
emplace
(
outgoing
.
size
()
-
1
,
std
::
move
(
leptons
));
}
Event
Event
::
EventData
::
cluster
(
fastjet
::
JetDefinition
const
&
jet_def
,
double
const
min_jet_pt
){
sort
();
Event
ev
{
std
::
move
(
incoming
),
std
::
move
(
outgoing
),
std
::
move
(
decays
),
std
::
move
(
parameters
),
jet_def
,
min_jet_pt
};
assert
(
std
::
is_sorted
(
begin
(
ev
.
outgoing_
),
end
(
ev
.
outgoing_
),
rapidity_less
{}));
ev
.
type_
=
classify
(
ev
);
return
ev
;
}
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_
));
}
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
;
}
}
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
);
for
(
auto
const
&
part
:
outgoing
()){
// reasonable colour
if
(
!
correct_colour
(
part
))
return
false
;
if
(
!
is_parton
(
part
))
// skip colour neutral particles
continue
;
// if possible connect to line
if
(
line_colour
.
first
==
part
.
colour
->
second
)
line_colour
.
first
=
part
.
colour
->
first
;
else
if
(
line_colour
.
second
==
part
.
colour
->
first
)
line_colour
.
second
=
part
.
colour
->
second
;
else
return
false
;
// 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
{
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
;
return
;
}
}
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
);
for
(
auto
&
part
:
outgoing_
){
assert
(
colour
>
0
||
anti_colour
>
0
);
if
(
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
){
part
.
colour
=
std
::
make_pair
(
colour
+
2
,
colour
);
colour
+=
2
;
}
else
{
part
.
colour
=
std
::
make_pair
(
anti_colour
,
anti_colour
+
2
);
anti_colour
+=
2
;
}
}
else
if
(
colour
>
0
){
// on q line => connect to available colour
part
.
colour
=
std
::
make_pair
(
colour
+
2
,
colour
);
colour
+=
2
;
}
else
{
assert
(
colour
<
0
&&
anti_colour
>
0
);
// on qx line => connect to available anti-colour
part
.
colour
=
std
::
make_pair
(
anti_colour
,
anti_colour
+
2
);
anti_colour
+=
2
;
}
}
else
if
(
is_quark
(
part
))
{
// quark
assert
(
anti_colour
>
0
);
if
(
colour
>
0
){
// on g line => connect and remove anti-colour
part
.
colour
=
std
::
make_pair
(
anti_colour
,
0
);
anti_colour
+=
2
;
anti_colour
*=-
1
;
}
else
{
// on qx line => new colour
colour
*=-
1
;
part
.
colour
=
std
::
make_pair
(
colour
,
0
);
}
}
else
if
(
is_antiquark
(
part
))
{
// anti-quark
assert
(
colour
>
0
);
if
(
anti_colour
>
0
){
// on g line => connect and remove colour
part
.
colour
=
std
::
make_pair
(
0
,
colour
);
colour
+=
2
;
colour
*=-
1
;
}
else
{
// on q line => new anti-colour
anti_colour
*=-
1
;
part
.
colour
=
std
::
make_pair
(
0
,
anti_colour
);
}
}
else
{
// not a parton
assert
(
!
is_parton
(
part
));
part
.
colour
=
{};
}
}
// 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
max_ext_soft_pt_fraction
,
double
const
min_extparton_pt
){
// TODO code overlap with PhaseSpacePoint::pass_extremal_cuts
if
(
min_extparton_pt
>
parton
.
pt
())
return
false
;
if
(
idx
<
0
)
return
false
;
assert
((
int
)
jets
.
size
()
>=
idx
);
auto
const
&
jet
{
jets
[
idx
]
};
if
(
(
parton
.
p
-
jet
).
pt
()
/
jet
.
pt
()
>
max_ext_soft_pt_fraction
)
return
false
;
return
true
;
}
}
// this should work with multiple types
bool
Event
::
valid_hej_state
(
double
const
max_frac
,
double
const
min_pt
)
const
{
using
namespace
event_type
;
if
(
!
is_resummable
(
type
()))
return
false
;
auto
const
&
jet_idx
{
particle_jet_indices
()
};
auto
idx_begin
{
jet_idx
.
cbegin
()
};
auto
idx_end
{
jet_idx
.
crbegin
()
};
auto
part_begin
{
cbegin_partons
()
};
auto
part_end
{
crbegin_partons
()
};
// always seperate extremal jets
if
(
!
valid_parton
(
jets
(),
*
part_begin
,
*
idx_begin
,
max_frac
,
min_pt
)
)
return
false
;
++
part_begin
;
++
idx_begin
;
if
(
!
valid_parton
(
jets
(),
*
part_end
,
*
idx_end
,
max_frac
,
min_pt
)
)
return
false
;
++
part_end
;
++
idx_end
;
// unob -> second parton in own jet
if
(
type
()
&
(
unob
|
qqxexb
)
){
if
(
!
valid_parton
(
jets
(),
*
part_begin
,
*
idx_begin
,
max_frac
,
min_pt
)
)
return
false
;
++
part_begin
;
++
idx_begin
;
}
if
(
type
()
&
(
unof
|
qqxexf
)
){
if
(
!
valid_parton
(
jets
(),
*
part_end
,
*
idx_end
,
max_frac
,
min_pt
)
)
return
false
;
++
part_end
;
++
idx_end
;
}
if
(
type
()
&
qqxmid
){
// find qqx pair
auto
begin_qqx
{
std
::
find_if
(
part_begin
,
part_end
.
base
(),
[](
Particle
const
&
part
)
->
bool
{
return
part
.
type
!=
ParticleID
::
gluon
;
}
)};
assert
(
begin_qqx
!=
part_end
.
base
());
long
int
qqx_pos
{
std
::
distance
(
part_begin
,
begin_qqx
)
};
assert
(
qqx_pos
>=
0
);
idx_begin
+=
qqx_pos
;
if
(
!
(
valid_parton
(
jets
(),
*
begin_qqx
,
*
idx_begin
,
max_frac
,
min_pt
)
&&
valid_parton
(
jets
(),
*
(
++
begin_qqx
),
*
(
++
idx_begin
),
max_frac
,
min_pt
)
))
return
false
;
}
return
true
;
}
Event
::
ConstPartonIterator
Event
::
begin_partons
()
const
{
return
cbegin_partons
();
};
Event
::
ConstPartonIterator
Event
::
cbegin_partons
()
const
{
return
boost
::
make_filter_iterator
(
static_cast
<
bool
(
*
)(
Particle
const
&
)
>
(
is_parton
),
cbegin
(
outgoing
()),
cend
(
outgoing
())
);
};
Event
::
ConstPartonIterator
Event
::
end_partons
()
const
{
return
cend_partons
();
};
Event
::
ConstPartonIterator
Event
::
cend_partons
()
const
{
return
boost
::
make_filter_iterator
(
static_cast
<
bool
(
*
)(
Particle
const
&
)
>
(
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
()
);
};
namespace
{
void
print_momentum
(
std
::
ostream
&
os
,
fastjet
::
PseudoJet
const
&
part
){
const
std
::
streamsize
orig_prec
=
os
.
precision
();
os
<<
std
::
scientific
<<
std
::
setprecision
(
6
)
<<
"["
<<
std
::
setw
(
13
)
<<
std
::
right
<<
part
.
px
()
<<
", "
<<
std
::
setw
(
13
)
<<
std
::
right
<<
part
.
py
()
<<
", "
<<
std
::
setw
(
13
)
<<
std
::
right
<<
part
.
pz
()
<<
", "
<<
std
::
setw
(
13
)
<<
std
::
right
<<
part
.
E
()
<<
"]"
<<
std
::
fixed
;
os
.
precision
(
orig_prec
);
}
void
print_colour
(
std
::
ostream
&
os
,
optional
<
Colour
>
const
&
col
){
if
(
!
col
)
os
<<
"(no color)"
;
// American spelling for better alignment
else
os
<<
"("
<<
std
::
setw
(
3
)
<<
std
::
right
<<
col
->
first
<<
", "
<<
std
::
setw
(
3
)
<<
std
::
right
<<
col
->
second
<<
")"
;
}
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
Event
const
&
ev
){
const
std
::
streamsize
orig_prec
=
os
.
precision
();
os
<<
std
::
setprecision
(
4
)
<<
std
::
fixed
;
os
<<
"########## "
<<
event_type
::
name
(
ev
.
type
())
<<
" ##########"
<<
std
::
endl
;
os
<<
"Incoming particles:
\n
"
;
for
(
auto
const
&
in
:
ev
.
incoming
()){
os
<<
std
::
setw
(
3
)
<<
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
(
3
)
<<
out
.
type
<<
": "
;
print_colour
(
os
,
out
.
colour
);
os
<<
" "
;
print_momentum
(
os
,
out
.
p
);
os
<<
" => rapidity="
<<
std
::
setw
(
7
)
<<
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
(
7
)
<<
std
::
right
<<
jet
.
rapidity
()
<<
std
::
endl
;
}
if
(
ev
.
decays
().
size
()
>
0
){
os
<<
"
\n
Decays: "
<<
ev
.
decays
().
size
()
<<
"
\n
"
;
for
(
auto
const
&
decay
:
ev
.
decays
()){
os
<<
std
::
setw
(
3
)
<<
ev
.
outgoing
()[
decay
.
first
].
type
<<
" ("
<<
decay
.
first
<<
") to:
\n
"
;
for
(
auto
const
&
out
:
decay
.
second
){
os
<<
" "
<<
std
::
setw
(
3
)
<<
out
.
type
<<
": "
;
print_momentum
(
os
,
out
.
p
);
os
<<
" => rapidity="
<<
std
::
setw
(
7
)
<<
std
::
right
<<
out
.
rapidity
()
<<
std
::
endl
;
}
}
}
os
<<
std
::
defaultfloat
;
os
.
precision
(
orig_prec
);
return
os
;
}
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
(
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
)
?
status_decayed
:
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
(
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
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Sep 30, 5:46 AM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
6506116
Default Alt Text
Event.cc (32 KB)
Attached To
Mode
rHEJ HEJ
Attached
Detach File
Event Timeline
Log In to Comment