Skip to content

Commit 04faf06

Browse files
LorenzVectorBase interfaces (#223)
Delegate to 4-vector and 4-momentum calculations to LorentzVectorBase package. Support generic conversion from LorentzVectorBase. Rename CommonJetStructs to CommonJet. Add LorentzVectorBase package and define ::FourMomentum (EEJet and PseudoJet) as PxPyPzE coordinate systems. Update getter functions to call the delegated function from LorentzVectorBase. For some functions where edge case behaviour differs from previous behaviour the output is modified to match what happened before: - phi should be in the range [0, 2pi) - rapidity and pseudorapidity should have a maximum cut off for stability Small documentation improvements for PseudoJet accessors which use internal cached values for efficiency. For PseudoJet and EEJet update the the constructor from ::Any to test that the LorentzVectorBase interface is supported and construct from those accessors. Tests are added (test-jet-constructors.jl) to check the conversion works and that it also fails with ArgumentError when a type does not support the interface. Test explicitly construction from LorentzVector and LorentzVectorCyl. Documentation on input particles is updated. The CommonJetStructs source had a lot more in it than just struct definitions, so the name was misleading, renamed to CommonJet, Fix a minor inconsistency in the cross constructors for PseudoJet and EEJet. Use the feature that bare identifiers imply the keyword argument name.
1 parent 3838eb2 commit 04faf06

File tree

11 files changed

+206
-112
lines changed

11 files changed

+206
-112
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "JetReconstruction"
22
uuid = "44e8cb2c-dfab-4825-9c70-d4808a591196"
3-
authors = ["Atell Krasnopolski <delta_atell@protonmail.com>", "Graeme A Stewart <graeme.andrew.stewart@cern.ch", "Philippe Gras <philippe.gras@cern.ch>"]
43
version = "1.0.0-dev"
4+
authors = ["Atell Krasnopolski <delta_atell@protonmail.com>", "Graeme A Stewart <graeme.andrew.stewart@cern.ch", "Philippe Gras <philippe.gras@cern.ch>"]
55

66
[deps]
77
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
@@ -10,6 +10,7 @@ CodecZstd = "6b39b394-51ab-5f42-8807-6242bab2b4c2"
1010
EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
1111
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
1212
LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890"
13+
LorentzVectorBase = "592a752d-6533-4762-a71b-738712ea30ec"
1314
LorentzVectorHEP = "f612022c-142a-473f-8cfd-a09cf3793c6c"
1415
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"
1516
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
@@ -34,6 +35,7 @@ EnumX = "1.0.4"
3435
JSON = "0.21, 1.2"
3536
Logging = "1.10"
3637
LoopVectorization = "0.12.170"
38+
LorentzVectorBase = "0.1.0"
3739
LorentzVectorHEP = "0.1.6"
3840
Makie = "0.20, 0.21, 0.22, 0.23, 0.24"
3941
MuladdMacro = "0.2.4"

docs/src/particles.md

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
# Input Particle Types
22

33
For the `particles` input to the reconstruction any one dimensional
4-
`AbstractArray{T, 1}` can be used, where the type `T` has to implement methods
5-
to extract the 4-vector components, viz, the following are required:
4+
`AbstractArray{T, 1}` can be used, in general the type `T` should implement the
5+
[LorentzVectorBase](https://github.com/JuliaHEP/LorentzVectorBase.jl)
6+
interface. That is the `T` should be a valid coordinate system and the
7+
following methods are required:
68

7-
- `JetReconstuction.px(particle::T)`
8-
- `JetReconstuction.py(particle::T)`
9-
- `JetReconstuction.pz(particle::T)`
10-
- `JetReconstuction.energy(particle::T)`
9+
- `LorentzVectorBase.px(particle::T)`
10+
- `LorentzVectorBase.py(particle::T)`
11+
- `LorentzVectorBase.pz(particle::T)`
12+
- `LorentzVectorBase.energy(particle::T)`
1113

12-
Currently built-in supported types are
13-
[`LorentzVectorHEP`](https://github.com/JuliaHEP/LorentzVectorHEP.jl), the
14-
`PseudoJet` and `EEJet`s from this package, and `ReconstructedParticles` from
15-
[EDM4hep Inputs](@ref).
14+
In addition, direct support for the construction of particles from
15+
`LorentzVector` and `LorentzVectorCyl` types is provided.
1616

17-
If you require support for a different input collection type then ensure you
18-
define the `px()`, etc. methods *for your specific type* and *in the
19-
`JetReconstruction` package*. This use of what might be considered type piracy
20-
is blessed as long as you are en *end user* of the jet reconstruction package.
21-
22-
If your type is used in several places or by different users, please consider
23-
writing a package extension that will support your type, following the model for
24-
EDM4hep in `ext/EDM4hepJets.jl`.
17+
Direct use of the `PseudoJet` and `EEJet`s from this package, and
18+
`ReconstructedParticles` from [EDM4hep Inputs](@ref) is supported.
Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,22 @@
44
Interface for composite types that includes fields px, py, py, and E
55
that represents the components of a four-momentum vector. All concrete
66
jets that are used in the package are subtypes of this type.
7+
8+
These types are also added as LorentzVectorBase `PxPyPzE` coordinate systems.
79
"""
810
abstract type FourMomentum end
911

12+
"""
13+
LorentzVectorBase.coordinate_system(::FourMomentum)
14+
15+
All of this package's FourMomentum types are `PxPyPzE` coordinate systems.
16+
"""
17+
LorentzVectorBase.coordinate_system(::FourMomentum) = LorentzVectorBase.PxPyPzE()
18+
LorentzVectorBase.px(v::FourMomentum) = v.px
19+
LorentzVectorBase.py(v::FourMomentum) = v.py
20+
LorentzVectorBase.pz(v::FourMomentum) = v.pz
21+
LorentzVectorBase.E(v::FourMomentum) = v.E
22+
1023
# Define here all common functions that can be used for all jet types <: FourMomentum
1124
"""
1225
px(j::FourMomentum)
@@ -50,21 +63,28 @@ cluster_hist_index(j::FourMomentum) = j._cluster_hist_index
5063
5164
Return the squared momentum of the four-momentum vector of `j`.
5265
"""
53-
p2(j::FourMomentum) = j.px^2 + j.py^2 + j.pz^2
66+
p2(j::FourMomentum) = LorentzVectorBase.spatial_magnitude2(j)
5467

5568
"""
5669
p(j::FourMomentum)
5770
5871
Return the momentum of the four-momentum vector of `j`.
5972
"""
60-
p(j::FourMomentum) = sqrt(p2(j))
73+
p(j::FourMomentum) = LorentzVectorBase.spatial_magnitude(j)
74+
75+
"""
76+
mag(j::FourMomentum)
77+
78+
Return the spatial magnitude (alias for `p()`).
79+
"""
80+
const mag = p
6181

6282
"""
6383
pt2(j::FourMomentum)
6484
6585
Return the squared transverse momentum of the four-momentum vector of `j`.
6686
"""
67-
pt2(j::FourMomentum) = j.px^2 + j.py^2
87+
pt2(j::FourMomentum) = LorentzVectorBase.transverse_momentum2(j)
6888

6989
"""Alias for `pt2` function"""
7090
const kt2 = pt2
@@ -73,14 +93,14 @@ const kt2 = pt2
7393
pt(j::FourMomentum)
7494
Return the momentum of the four-momentum vector of `j`.
7595
"""
76-
pt(j::FourMomentum) = sqrt(pt2(j))
96+
pt(j::FourMomentum) = LorentzVectorBase.transverse_momentum(j)
7797

7898
"""
7999
mass2(j::FourMomentum)
80100
81101
Return the invariant mass squared of the four-momentum vector `j`.
82102
"""
83-
mass2(j::FourMomentum) = energy(j)^2 - p2(j)
103+
mass2(j::FourMomentum) = LorentzVectorBase.mass2(j)
84104

85105
"""Alias for `mass2` function"""
86106
const m2 = mass2
@@ -91,7 +111,7 @@ const m2 = mass2
91111
Return the invariant mass of a four momentum `j`. By convention if ``m^2 < 0``,
92112
then ``-sqrt{(-m^2)}`` is returned.
93113
"""
94-
mass(j::FourMomentum) = m2(j) < 0.0 ? -sqrt(-m2(j)) : sqrt(m2(j))
114+
mass(j::FourMomentum) = LorentzVectorBase.mass(j)
95115

96116
"""Alias for `mass` function"""
97117
const m = mass
@@ -101,12 +121,9 @@ const m = mass
101121
102122
Return the azimuthal angle, ϕ, of the four momentum `j` in the range [0, 2π).
103123
"""
104-
phi(j::FourMomentum) = begin
105-
phi = pt2(j) == 0.0 ? 0.0 : atan(j.py, j.px)
106-
if phi < 0.0
107-
phi += 2π
108-
end
109-
phi
124+
function phi(j::FourMomentum)
125+
phi = LorentzVectorBase.phi(j)
126+
return phi < 0.0 ? phi + 2π : phi
110127
end
111128

112129
# Alternative name for [0, 2π) range
@@ -120,35 +137,16 @@ const _MaxRap = 1e5
120137
rapidity(j::FourMomentum)
121138
122139
Return the rapidity of the four momentum `j`.
140+
141+
In this package, for numerical stability, we clamp the rapidity, but in a way
142+
that allows for predictability, based on pz.
123143
"""
124144
function rapidity(j::FourMomentum)
125-
if energy(j) == abs(pz(j)) && iszero(pt2(j))
126-
MaxRapHere = _MaxRap + abs(pz(j))
127-
rapidity = (pz(j) >= 0.0) ? MaxRapHere : -MaxRapHere
128-
else
129-
# get the rapidity in a way that's modestly insensitive to roundoff
130-
# error when things pz,E are large (actually the best we can do without
131-
# explicit knowledge of mass)
132-
effective_m2 = max(0.0, m2(j)) # force non tachyonic mass
133-
E_plus_pz = energy(j) + abs(pz(j)) # the safer of p+, p-
134-
rapidity = 0.5 * log((pt2(j) + effective_m2) / (E_plus_pz * E_plus_pz))
135-
if pz(j) > 0
136-
rapidity = -rapidity
137-
end
138-
end
139-
rapidity
145+
rap = LorentzVectorBase.rapidity(j)
146+
MaxRapHere = _MaxRap + abs(pz(j))
147+
return clamp(rap, -MaxRapHere, MaxRapHere)
140148
end
141149

142-
"""
143-
mag(jet::T) where {T <: FourMomentum}
144-
145-
Return the magnitude of the momentum of a jet, `|p|`.
146-
147-
# Returns
148-
The magnitude of the jet.
149-
"""
150-
mag(jet::T) where {T <: FourMomentum} = sqrt(muladd(jet.px, jet.px, jet.py^2) + jet.pz^2)
151-
152150
"""
153151
CosTheta(jet::T) where {T <: FourMomentum}
154152
@@ -157,29 +155,18 @@ Compute the cosine of the angle between the momentum vector of `jet` and the z-a
157155
# Returns
158156
- The cosine of the angle between the jet and the z-axis.
159157
"""
160-
@inline function CosTheta(jet::T) where {T <: FourMomentum}
161-
fZ = jet.pz
162-
ptot = mag(jet)
163-
return ifelse(ptot == 0.0, 1.0, fZ / ptot)
164-
end
158+
CosTheta(j::FourMomentum) = LorentzVectorBase.cos_theta(j)
165159

166160
"""
167161
eta(jet::T) where {T <: FourMomentum}
168162
169-
Compute the pseudorapidity (η) of a jet.
170-
171-
# Returns
172-
- The pseudorapidity (η) of the jet.
173-
"""
174-
function eta(jet::T) where {T <: FourMomentum}
175-
cosTheta = CosTheta(jet)
176-
(cosTheta^2 < 1.0) && return -0.5 * log((1.0 - cosTheta) / (1.0 + cosTheta))
177-
fZ = jet.pz
178-
iszero(fZ) && return 0.0
179-
# Warning("PseudoRapidity","transverse momentum = 0! return +/- 10e10");
180-
fZ > 0.0 ? _MaxRap : -_MaxRap
163+
Returns the pseudorapidity (η or eta) of a jet. For this package we
164+
clamp the return value to our package's `_MaxRap`.
165+
"""
166+
function eta(j::FourMomentum)
167+
eta = LorentzVectorBase.eta(j)
168+
clamp(eta, -_MaxRap, _MaxRap)
181169
end
182-
183170
"""
184171
const η = eta
185172

src/EEJet.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,36 +83,49 @@ end
8383
EEJet(jet::LorentzVector; cluster_hist_index::Int = 0)
8484
8585
Construct a EEJet from a `LorentzVector` object with optional cluster index.
86+
87+
The `cluster_hist_index` is optional, but needed if the `jet` is part of a
88+
reconstruction sequence. If not provided, it defaults to `0` as an "invalid"
89+
value.
8690
"""
8791
function EEJet(jet::LorentzVector; cluster_hist_index::Int = 0)
88-
EEJet(jet.x, jet.y, jet.z, jet.t; cluster_hist_index = cluster_hist_index)
92+
EEJet(jet.x, jet.y, jet.z, jet.t; cluster_hist_index)
8993
end
9094

9195
"""
9296
EEJet(jet::Any; cluster_hist_index::Int = 0)
9397
94-
Construct a EEJet from a generic object `jet` with the given cluster index.
95-
This functions also for `LorentzVectorCyl` objects.
98+
Construct an EEJet from a generic object `jet` with the given cluster index.
99+
These generic jets must implement the LorentzVectorBase interface from
100+
`LorentzVectorBase.jl`.
96101
97102
# Details
98103
99-
This function is used to convert a generic object `jet` into an `EEJet`, for
100-
this to work the object must have the methods `px`, `py`, `pz`, and `energy`
101-
defined, which are used to extract the four-momentum components of the object.
104+
This function is used to convert a generic object `jet` into a `EEJet`.
105+
These generic jets should implement the LorentzVectorBase interface: the
106+
`LorentzVectorBase.{px,py,pz,energy}()` methods will be called to retrieve
107+
the four momentum components.
102108
103109
The `cluster_hist_index` is optional, but needed if the `jet` is part of a
104110
reconstruction sequence. If not provided, it defaults to `0` as an "invalid"
105111
value.
106112
"""
107113
function EEJet(jet::Any; cluster_hist_index::Int = 0)
108-
EEJet(px(jet), py(jet), pz(jet), energy(jet);
109-
cluster_hist_index = cluster_hist_index)
114+
# Check that the interface is implemented
115+
if hasmethod(LorentzVectorBase.coordinate_system, (typeof(jet),))
116+
return EEJet(LorentzVectorBase.px(jet), LorentzVectorBase.py(jet),
117+
LorentzVectorBase.pz(jet), LorentzVectorBase.energy(jet);
118+
cluster_hist_index)
119+
else
120+
throw(ArgumentError("EEJet cannot be constructed from object of type '$(typeof(jet))'"))
121+
end
110122
end
111123

112124
"""
113125
p2(eej::EEJet)
114126
115-
Return the squared momentum of the `EEJet` object `eej`.
127+
Return the squared momentum of the `EEJet` object `eej`. This accessor uses the
128+
pre-calculated value that the struct has.
116129
"""
117130
p2(eej::EEJet) = eej._p2
118131

src/JetReconstruction.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ examples in this package for more information.
1515
"""
1616
module JetReconstruction
1717

18+
using LorentzVectorBase
1819
using LorentzVectorHEP
1920
using MuladdMacro
2021
using StructArrays
@@ -48,7 +49,7 @@ mass(p::LorentzVectorCyl) = LorentzVectorHEP.mass(p)
4849
const max_allowable_R = 1000.0
4950

5051
# Pseudojet and EEJet types
51-
include("CommonJetStructs.jl")
52+
include("CommonJet.jl")
5253
include("PseudoJet.jl")
5354
include("EEJet.jl")
5455
include("JetUtils.jl")

src/JetUtils.jl

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
# Functions that create each jet type from the other need to be defined here,
44
# after the structs are defined.
55
"""
6-
PseudoJet(jet::EEJet)
6+
PseudoJet(jet::EEJet; cluster_hist_index::Int=0)
77
8-
Constructs a `PseudoJet` object from an `EEJet` object, with the same four
9-
momentum and cluster history index.
8+
Constructs a `PseudoJet` from an `EEJet`.
9+
10+
# Details
11+
12+
The `cluster_hist_index` is set to the value of the `cluster_hist_index` of the
13+
EEJet if `0` is passed. Otherwise it is set to the value, `>0`, passed in.
1014
"""
11-
function PseudoJet(eej::EEJet)
15+
function PseudoJet(eej::EEJet; cluster_hist_index::Int = 0)
16+
cluster_hist_index = cluster_hist_index == 0 ? eej._cluster_hist_index :
17+
cluster_hist_index
1218
PseudoJet(px(eej), py(eej), pz(eej), energy(eej);
13-
cluster_hist_index = cluster_hist_index(eej))
19+
cluster_hist_index)
1420
end
1521

1622
"""
@@ -24,10 +30,10 @@ The `cluster_hist_index` is set to the value of the `cluster_hist_index` of the
2430
PseudoJet if `0` is passed. Otherwise it is set to the value, `>0`, passed in.
2531
"""
2632
function EEJet(jet::PseudoJet; cluster_hist_index::Int = 0)
27-
new_cluster_hist_index = cluster_hist_index == 0 ? jet._cluster_hist_index :
28-
cluster_hist_index
33+
cluster_hist_index = cluster_hist_index == 0 ? jet._cluster_hist_index :
34+
cluster_hist_index
2935
EEJet(px(jet), py(jet), pz(jet), energy(jet);
30-
cluster_hist_index = new_cluster_hist_index)
36+
cluster_hist_index)
3137
end
3238

3339
# Functions to convert jets to types from other packages

0 commit comments

Comments
 (0)