Skip to content

Commit d3c97f3

Browse files
Rationalise final jets (JuliaHEP#198)
* First attempt at parametrising final_jets * Fix return values from jet selectors The default return values from exclusive and inclusive jet selectors were wrong, as the LorentzVectorCyl was created mistakenly with rapidity(jet) instead of eta(jet). This went unnoticed as the FinalJets reversed the mistake, treating eta() as if it was rapidity(). In testing, there is quite a precision loss involved in conversion to a LorentzVectorCyl as this involves converting from (px,py,py,E) to (pT,eta,phi,m), then re-extracting (rapidity,phi,pT). It is the rapidity<->phi which is problematic. So, the default return type has been changed to be a LorentzVector as this matches better the internal representation of PseudoJet and EEJet. Also now import eta() and pt() methods for LorentzVectorCyl and LorentzVector. Decrease the tolerance on the pT test from 1e-6 to 1e-7 (for Float64). * Formatting * Add jet selector tests Test if different struct selectors work for selected jets, both inclusive and exclusive. Add mass and mass2 "imports" for LorentzVector and LorentzVectorHEP. * Broaden test coverage * Add new tests to runtests.jl * Format! * Test mass and eta as well * Better isapprox for FinalJet Pass kwargs properly and don't rely on the ≈ alias. Use the default equality implementation.
1 parent 58aac77 commit d3c97f3

File tree

10 files changed

+143
-65
lines changed

10 files changed

+143
-65
lines changed

src/ClusterSequence.jl

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -235,18 +235,17 @@ It iterates over the clustering history and checks the transverse momentum of
235235
each parent jet. If the transverse momentum is greater than or equal to `ptmin`,
236236
the jet is added to the array of inclusive jets.
237237
238-
Valid return types are `LorentzVectorCyl` and the jet type of the input `clusterseq`
239-
(`U` - either `PseudoJet` or `EEJet` depending which algorithm was used)
240-
(N.B. this will evolve in the future to be any subtype of `FourMomentumBase`;
241-
currently unrecognised types will return `LorentzVectorCyl`).
238+
Valid return types are `LorentzVector` `LorentzVectorCyl` or the jet type of the
239+
input `clusterseq` (`U` - either `PseudoJet` or `EEJet` depending which
240+
algorithm was used).
242241
243242
# Example
244243
```julia
245244
inclusive_jets(clusterseq; ptmin = 10.0)
246245
```
247246
"""
248247
function inclusive_jets(clusterseq::ClusterSequence{U},
249-
::Type{T} = LorentzVectorCyl{Float64};
248+
::Type{T} = LorentzVector{Float64};
250249
ptmin = 0.0) where {T, U}
251250
pt2min = ptmin * ptmin
252251
jets_local = T[]
@@ -263,25 +262,29 @@ function inclusive_jets(clusterseq::ClusterSequence{U},
263262
@debug "Added inclusive jet index $iparent_jet"
264263
if T == U
265264
push!(jets_local, jet)
265+
elseif T <: LorentzVectorCyl
266+
push!(jets_local, lorentzvector_cyl(jet))
267+
elseif T <: LorentzVector
268+
push!(jets_local, lorentzvector(jet))
266269
else
267-
push!(jets_local,
268-
LorentzVectorCyl(pt(jet), rapidity(jet), phi(jet), mass(jet)))
270+
error("Unsupported return type $T for inclusive jets")
269271
end
270272
end
271273
end
272274
jets_local
273275
end
274276

275277
"""
276-
exclusive_jets(clusterseq::ClusterSequence{U}, ::Type{T} = LorentzVectorCyl{Float64}; dcut = nothing, njets = nothing) where {T, U}
278+
exclusive_jets(clusterseq::ClusterSequence{U}, ::Type{T} = LorentzVector{Float64}; dcut = nothing, njets = nothing) where {T, U}
277279
278280
Return all exclusive jets of a ClusterSequence, with either a specific number of
279281
jets or a cut on the maximum distance parameter.
280282
281283
# Arguments
282284
- `clusterseq::ClusterSequence`: The `ClusterSequence` object containing the
283285
clustering history and jets.
284-
- `::Type{T} = LorentzVectorCyl{Float64}`: The return type used for the selected jets.
286+
- `::Type{T} = LorentzVectorCyl{Float64}`: The return type used for the selected
287+
jets.
285288
- `dcut::Union{Nothing, Real}`: The distance parameter used to define the
286289
exclusive jets. If `dcut` is provided, the number of exclusive jets will be
287290
calculated based on this parameter.
@@ -294,10 +297,9 @@ jets or a cut on the maximum distance parameter.
294297
# Returns
295298
- An array of `T` objects representing the exclusive jets.
296299
297-
Valid return types are `LorentzVectorCyl` and the jet type of the input `clusterseq`
298-
(`U` - either `PseudoJet` or `EEJet` depending which algorithm was used)
299-
(N.B. this will evolve in the future to be any subtype of `FourMomentumBase`;
300-
currently unrecognised types will return `LorentzVectorCyl`).
300+
Valid return types are `LorentzVector` `LorentzVectorCyl` or the jet type of the
301+
input `clusterseq` (`U` - either `PseudoJet` or `EEJet` depending which
302+
algorithm was used).
301303
302304
# Exceptions
303305
- `ArgumentError`: If neither `dcut` nor `njets` is provided.
@@ -313,7 +315,7 @@ exclusive_jets(clusterseq, PseudoJet, njets = 3)
313315
```
314316
"""
315317
function exclusive_jets(clusterseq::ClusterSequence{U},
316-
::Type{T} = LorentzVectorCyl{Float64};
318+
::Type{T} = LorentzVector{Float64};
317319
dcut = nothing, njets = nothing,) where {T, U}
318320
if isnothing(dcut) && isnothing(njets)
319321
throw(ArgumentError("Must pass either a dcut or an njets value"))
@@ -355,14 +357,16 @@ function exclusive_jets(clusterseq::ClusterSequence{U},
355357
jet = clusterseq.jets[clusterseq.history[parent].jetp_index]
356358
if T == U
357359
push!(excl_jets, jet)
360+
elseif T <: LorentzVectorCyl
361+
push!(excl_jets, lorentzvector_cyl(jet))
362+
elseif T <: LorentzVector
363+
push!(excl_jets, lorentzvector(jet))
358364
else
359-
push!(excl_jets,
360-
LorentzVectorCyl(pt(jet), rapidity(jet), phi(jet), mass(jet)))
365+
error("Unsupported return type $T for inclusive jets")
361366
end
362367
end
363368
end
364369
end
365-
366370
excl_jets
367371
end
368372

src/CommonJetStructs.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ phi(j::FourMomentum) = begin
109109
phi
110110
end
111111

112+
# Alternative name for [0, 2π) range
113+
const phi02pi = phi
114+
112115
"""Used to protect against parton-level events where pt can be zero
113116
for some partons, giving rapidity=infinity. KtJet fails in those cases."""
114117
const _MaxRap = 1e5

src/JSONresults.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ struct FinalJet
1515
pt::Float64
1616
end
1717

18+
"""
19+
FinalJet(jet::T)
20+
21+
Convert a jet of type `T` to a `FinalJet` object. `T` must be a type that
22+
supports the methods `rapidity`, `phi`, and `pt`.
23+
"""
24+
FinalJet(jet::T) where {T} = FinalJet(rapidity(jet), phi(jet), pt(jet))
25+
26+
import Base: isapprox
27+
28+
"""
29+
isapprox(fj1::FinalJet, fj2::FinalJet; kwargs...)
30+
31+
Compare two `FinalJet` objects for approximate equality based on their rapidity,
32+
azimuthal angle, and transverse momentum. `kwargs` are passed to the `isapprox`
33+
function, e.g., `tol`.
34+
"""
35+
function isapprox(fj1::FinalJet, fj2::FinalJet; kwargs...)
36+
isapprox(fj1.rap, fj2.rap; kwargs...) && isapprox(fj1.phi, fj2.phi; kwargs...) &&
37+
isapprox(fj1.pt, fj2.pt; kwargs...)
38+
end
39+
1840
"""
1941
struct FinalJets
2042

src/JetReconstruction.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,28 @@ using StructArrays
2121

2222
# Import from LorentzVectorHEP methods for those 4-vector types
2323
pt2(p::LorentzVector) = LorentzVectorHEP.pt2(p)
24+
pt(p::LorentzVector) = LorentzVectorHEP.pt(p)
2425
phi(p::LorentzVector) = LorentzVectorHEP.phi(p)
2526
rapidity(p::LorentzVector) = LorentzVectorHEP.rapidity(p)
27+
eta(p::LorentzVector) = LorentzVectorHEP.eta(p)
2628
px(p::LorentzVector) = LorentzVectorHEP.px(p)
2729
py(p::LorentzVector) = LorentzVectorHEP.py(p)
2830
pz(p::LorentzVector) = LorentzVectorHEP.pz(p)
2931
energy(p::LorentzVector) = LorentzVectorHEP.energy(p)
32+
mass2(p::LorentzVector) = LorentzVectorHEP.mass2(p)
33+
mass(p::LorentzVector) = LorentzVectorHEP.mass(p)
3034

3135
pt2(p::LorentzVectorCyl) = LorentzVectorHEP.pt2(p)
36+
pt(p::LorentzVectorCyl) = LorentzVectorHEP.pt(p)
3237
phi(p::LorentzVectorCyl) = LorentzVectorHEP.phi(p)
3338
rapidity(p::LorentzVectorCyl) = LorentzVectorHEP.rapidity(p)
39+
eta(p::LorentzVectorCyl) = LorentzVectorHEP.eta(p)
3440
px(p::LorentzVectorCyl) = LorentzVectorHEP.px(p)
3541
py(p::LorentzVectorCyl) = LorentzVectorHEP.py(p)
3642
pz(p::LorentzVectorCyl) = LorentzVectorHEP.pz(p)
3743
energy(p::LorentzVectorCyl) = LorentzVectorHEP.energy(p)
44+
mass2(p::LorentzVectorCyl) = LorentzVectorHEP.mass2(p)
45+
mass(p::LorentzVectorCyl) = LorentzVectorHEP.mass(p)
3846

3947
# Some useful constants/limits that are used internally
4048
const max_allowable_R = 1000.0

src/Utils.jl

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -73,64 +73,28 @@ function read_final_state_particles(fname, ::Type{T} = PseudoJet; maxevents = -1
7373
end
7474

7575
"""
76-
final_jets(jets::Vector{PseudoJet}, ptmin::AbstractFloat=0.0)
76+
final_jets(jets::Vector{T}, ptmin::AbstractFloat=0.0)
7777
78-
This function takes a vector of `PseudoJet` objects and a minimum transverse
79-
momentum `ptmin` as input. It returns a vector of `FinalJet` objects that
80-
satisfy the transverse momentum condition.
78+
This function takes a vector of `T` objects, which should be jets, and a minimum
79+
transverse momentum `ptmin` as input. It returns a vector of `FinalJet` objects
80+
that satisfy the transverse momentum condition.
8181
8282
# Arguments
83-
- `jets::Vector{PseudoJet}`: A vector of `PseudoJet` objects representing the
84-
input jets.
83+
- `jets::Vector{T}`: A vector of `T` objects representing the input jets.
8584
- `ptmin::AbstractFloat=0.0`: The minimum transverse momentum required for a jet
8685
to be included in the final jets vector.
8786
8887
# Returns
8988
A vector of `FinalJet` objects that satisfy the transverse momentum condition.
9089
"""
91-
function final_jets(jets::Vector{PseudoJet}, ptmin::AbstractFloat = 0.0)
90+
function final_jets(jets::Vector{T}, ptmin::AbstractFloat = 0.0) where {T}
9291
count = 0
9392
final_jets = Vector{FinalJet}()
94-
sizehint!(final_jets, 6)
95-
for jet in jets
96-
dcut = ptmin^2
97-
if pt2(jet) > dcut
98-
count += 1
99-
push!(final_jets, FinalJet(rapidity(jet), phi(jet), sqrt(pt2(jet))))
100-
end
101-
end
102-
final_jets
103-
end
104-
105-
"""Specialisation for final jets from LorentzVectors (TODO: merge into more general function)"""
106-
function final_jets(jets::Vector{<:LorentzVector}, ptmin::AbstractFloat = 0.0)
107-
count = 0
108-
final_jets = Vector{FinalJet}()
109-
sizehint!(final_jets, 6)
11093
dcut = ptmin^2
11194
for jet in jets
112-
if LorentzVectorHEP.pt(jet)^2 > dcut
113-
count += 1
114-
push!(final_jets,
115-
FinalJet(LorentzVectorHEP.rapidity(jet), LorentzVectorHEP.phi(jet),
116-
LorentzVectorHEP.pt(jet)))
117-
end
118-
end
119-
final_jets
120-
end
121-
122-
"""Specialisation for final jets from LorentzVectorCyl (TODO: merge into more general function)"""
123-
function final_jets(jets::Vector{<:LorentzVectorCyl}, ptmin::AbstractFloat = 0.0)
124-
count = 0
125-
final_jets = Vector{FinalJet}()
126-
sizehint!(final_jets, 6)
127-
dcut = ptmin^2
128-
for jet in jets
129-
if LorentzVectorHEP.pt(jet)^2 > dcut
95+
if pt2(jet) > dcut
13096
count += 1
131-
push!(final_jets,
132-
FinalJet(LorentzVectorHEP.eta(jet), LorentzVectorHEP.phi(jet),
133-
LorentzVectorHEP.pt(jet)))
97+
push!(final_jets, FinalJet(jet))
13498
end
13599
end
136100
final_jets

test/_common.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function run_reco_test(test::ComparisonTest; testname = nothing)
139139
normalised_phi = jet.phi < 0.0 ? jet.phi + 2π : jet.phi
140140
@test jet.rapfastjet_jets[ievt]["jets"][ijet]["rap"] atol=1e-7
141141
@test normalised_phifastjet_jets[ievt]["jets"][ijet]["phi"] atol=1e-7
142-
@test jet.ptfastjet_jets[ievt]["jets"][ijet]["pt"] rtol=1e-6
142+
@test jet.ptfastjet_jets[ievt]["jets"][ijet]["pt"] rtol=1e-7
143143
end
144144
end
145145
end

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ function main()
3131
# Utility support tests
3232
include("test-utils.jl")
3333

34+
# Check jet selectors to different jet types
35+
include("test-jet-selectors.jl")
36+
3437
# Substructure tests
3538
include("test-substructure.jl")
3639

test/test-compare-types.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ function do_test_compare_types(strategy::RecoStrategy.Strategy;
4949
@test size(event.jets) == size(event_lv.jets)
5050
# Test each jet in turn
5151
for (jet, jet_lv) in zip(event.jets, event_lv.jets)
52-
@test jet.rapjet_lv.rap atol=1e-7
53-
@test jet.phijet_lv.phi atol=1e-7
54-
@test jet.ptjet_lv.pt rtol=1e-6
52+
@test jet == jet_lv
5553
end
5654
end
5755
end

test/test-jet-selectors.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Test that final jets can be retrieved with different
2+
# output types
3+
4+
include("common.jl")
5+
6+
"""
7+
test_jet_equality(ref, test)
8+
9+
Test two jet representations for equality.
10+
"""
11+
function test_jet_equality(ref, test)
12+
for (j1, j2) in zip(ref, test)
13+
@test JetReconstruction.pt(j1) JetReconstruction.pt(j2)
14+
test_phi = JetReconstruction.phi02pi(j2)
15+
test_phi = test_phi < 0.0 ? test_phi + 2π : test_phi
16+
@test JetReconstruction.phi(j1) test_phi
17+
@test JetReconstruction.rapidity(j1) JetReconstruction.rapidity(j2)
18+
@test JetReconstruction.eta(j1) JetReconstruction.eta(j2)
19+
@test JetReconstruction.px(j1) JetReconstruction.px(j2)
20+
@test JetReconstruction.py(j1) JetReconstruction.py(j2)
21+
@test JetReconstruction.pz(j1) JetReconstruction.pz(j2)
22+
@test JetReconstruction.energy(j1) JetReconstruction.energy(j2)
23+
@test JetReconstruction.mass2(j1) JetReconstruction.mass2(j2)
24+
@test JetReconstruction.mass(j1) JetReconstruction.mass(j2)
25+
end
26+
nothing
27+
end
28+
29+
@testset "Jet selections to types" begin
30+
pp_event = first(JetReconstruction.read_final_state_particles(events_file_pp;
31+
maxevents = 1))
32+
ee_event = first(JetReconstruction.read_final_state_particles(events_file_ee;
33+
maxevents = 1))
34+
35+
pp_cs = jet_reconstruct(pp_event; algorithm = JetAlgorithm.Kt)
36+
ee_cs = jet_reconstruct(ee_event; algorithm = JetAlgorithm.Durham)
37+
38+
pp_ref_jets = inclusive_jets(pp_cs, PseudoJet; ptmin = 10.0)
39+
for T in (LorentzVectorCyl, LorentzVector)
40+
test_jet_equality(pp_ref_jets, inclusive_jets(pp_cs, T; ptmin = 10.0))
41+
end
42+
43+
ee_ref_jets = exclusive_jets(ee_cs, EEJet; njets = 2)
44+
for T in (LorentzVectorCyl, LorentzVector)
45+
test_jet_equality(ee_ref_jets, exclusive_jets(ee_cs, T; njets = 2))
46+
end
47+
48+
# Also check that for an unknown type we throw
49+
@test_throws ErrorException begin
50+
inclusive_jets(pp_cs, Float64; ptmin = 10.0)
51+
end
52+
@test_throws ErrorException begin
53+
exclusive_jets(ee_cs, Float64; njets = 2)
54+
end
55+
end

test/test-jet-utils.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,24 @@ eej2 = EEJet(-1.0, 3.2, -1.2, 39.0)
4141
@test LorentzVectorHEP.mass(lvc_pj1) JetReconstruction.mass(eej1)
4242
JetReconstruction.mass(eej1) LorentzVectorHEP.mass(lvc_eej1)
4343
end
44+
45+
@testset "Final jets extraction" begin
46+
# Test final jets extraction from different jet types
47+
vec_pj = [pj1, pj2]
48+
finaljets_pj = JetReconstruction.final_jets(vec_pj)
49+
50+
vec_lorentzhep = [JetReconstruction.lorentzvector(pj1),
51+
JetReconstruction.lorentzvector(pj2)]
52+
finaljets_lorentzhep = JetReconstruction.final_jets(vec_lorentzhep)
53+
54+
vec_lorentzhepcyl = [JetReconstruction.lorentzvector_cyl(pj1),
55+
JetReconstruction.lorentzvector_cyl(pj2)]
56+
finaljets_lorentzhepcyl = JetReconstruction.final_jets(vec_lorentzhepcyl)
57+
58+
@test length(finaljets_pj) == 2
59+
@test length(finaljets_lorentzhep) == 2
60+
@test length(finaljets_lorentzhepcyl) == 2
61+
for i in 1:2
62+
@test finaljets_pj[i] finaljets_lorentzhep[i] finaljets_lorentzhepcyl[i]
63+
end
64+
end

0 commit comments

Comments
 (0)