Skip to content

Commit b51be3b

Browse files
final changes
1 parent 8991c9a commit b51be3b

8 files changed

Lines changed: 181 additions & 124 deletions

File tree

src/ClusterSequence.jl

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,29 +92,17 @@ end
9292
9393
Reset and initialise reusable clustering-history storage for `particles`.
9494
95-
The vector's logical length is reset to the number of initial particles.
96-
Capacity is retained for the complete clustering history.
95+
The vector's logical length is reset to the number of initial particles while
96+
retaining any capacity already owned by the vector.
9797
9898
The returned history vector is borrowed storage owned by the caller.
9999
"""
100100
function initial_history!(history::Vector{HistoryElement},
101101
particles)
102102
N = length(particles)
103103

104-
# Remove the previous event's logical history entries.
105-
#
106-
# This changes length(history) to zero, but retained vector capacity is
107-
# preserved.
108-
empty!(history)
109-
110-
# A complete hadron-collider clustering sequence contains:
111-
#
112-
# N initial history entries
113-
# + N recombination/finalisation entries
114-
# = 2N total entries.
115-
_sizehint_for_reuse!(history, 2 * N)
116-
117-
# Establish exactly N active initial-history slots.
104+
# Establish exactly N active initial-history slots. All slots are
105+
# overwritten below, and `resize!` retains any existing excess capacity.
118106
resize!(history, N)
119107

120108
Qtot::Float64 = 0.0

src/JetUtils.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,68 @@ function kt_scale(jet1::T, jet2::T) where {T <: FourMomentum}
138138
pt2 = JetReconstruction.pt(jet2)
139139
return min(pt1, pt2) * deltar(jet1, jet2)
140140
end
141+
142+
"""
143+
construct_reco_jets(particles, ::Type{J}, preprocess) where {J <: FourMomentum}
144+
145+
Create independently owned, reconstruction-ready jets of type `J` from
146+
`particles`.
147+
148+
When `preprocess` is `nothing`, inputs that already have type `J` are copied
149+
directly and other inputs are converted to `J`. Otherwise `preprocess` is called
150+
for every input particle.
151+
"""
152+
function construct_reco_jets(particles::AbstractVector{P},
153+
::Type{J},
154+
preprocess) where {P, J <: FourMomentum}
155+
TargetNumericalType = eltype(particles[1])
156+
if TargetNumericalType <: Real
157+
TargetJetType = concretize_return_type(J, TargetNumericalType)
158+
else
159+
TargetJetType = typeof(J(particles[1]))
160+
end
161+
162+
recombination_particles = Vector{TargetJetType}()
163+
sizehint!(recombination_particles, 2 * length(particles))
164+
165+
return construct_reco_jets!(recombination_particles,
166+
particles,
167+
preprocess)
168+
end
169+
170+
"""
171+
construct_reco_jets!(recombination_particles, particles, preprocess)
172+
173+
Reset and fill reusable reconstruction-jet storage. The destination must not
174+
alias the input collection.
175+
"""
176+
function construct_reco_jets!(recombination_particles::Vector{J},
177+
particles::AbstractVector{P},
178+
preprocess) where {P, J <: FourMomentum}
179+
Base.mightalias(recombination_particles, particles) &&
180+
throw(ArgumentError("reusable jet storage must not alias the input particles"))
181+
182+
N = length(particles)
183+
empty!(recombination_particles)
184+
_sizehint_for_reuse!(recombination_particles, 2 * N)
185+
186+
if isnothing(preprocess)
187+
if P === J
188+
append!(recombination_particles, particles)
189+
else
190+
for (i, particle) in enumerate(particles)
191+
push!(recombination_particles,
192+
J(particle; cluster_hist_index = i))
193+
end
194+
end
195+
else
196+
for (i, particle) in enumerate(particles)
197+
push!(recombination_particles,
198+
preprocess(particle,
199+
J;
200+
cluster_hist_index = i))
201+
end
202+
end
203+
204+
return recombination_particles
205+
end

src/TiledAlgoLL.jl

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -220,20 +220,6 @@ function set_nearest_neighbours!(tiling::Tiling,
220220
return nothing
221221
end
222222

223-
function set_nearest_neighbours!(clusterseq::ClusterSequence,
224-
tiling::Tiling,
225-
tiledjets::Vector{TiledJet})
226-
NNs = similar(clusterseq.jets, TiledJet)
227-
diJ = similar(clusterseq.jets, Float64)
228-
229-
set_nearest_neighbours!(tiling,
230-
tiledjets,
231-
NNs,
232-
diJ)
233-
234-
NNs, diJ
235-
end
236-
237223
"""
238224
do_iB_recombination_step!(clusterseq::ClusterSequence, jet_i, diB)
239225
@@ -361,31 +347,9 @@ function tiled_jet_reconstruct(particles::AbstractVector{T};
361347
# Get consistent algorithm power
362348
p = get_algorithm_power(p = p, algorithm = algorithm)
363349

364-
if isnothing(preprocess)
365-
if T == PseudoJet
366-
# If we don't have a preprocessor, we just need to copy to our own
367-
# PseudoJet objects
368-
recombination_particles = copy(particles)
369-
sizehint!(recombination_particles, length(particles) * 2)
370-
else
371-
# We assume a constructor for PseudoJet that can ingest the appropriate
372-
# type of particle
373-
recombination_particles = PseudoJet[]
374-
sizehint!(recombination_particles, length(particles) * 2)
375-
for (i, particle) in enumerate(particles)
376-
push!(recombination_particles, PseudoJet(particle; cluster_hist_index = i))
377-
end
378-
end
379-
else
380-
# We have a preprocessor function that we need to call to modify the
381-
# input particles
382-
recombination_particles = PseudoJet[]
383-
sizehint!(recombination_particles, length(particles) * 2)
384-
for (i, particle) in enumerate(particles)
385-
push!(recombination_particles,
386-
preprocess(particle, PseudoJet; cluster_hist_index = i))
387-
end
388-
end
350+
recombination_particles = construct_reco_jets(particles,
351+
PseudoJet,
352+
preprocess)
389353

390354
_tiled_jet_reconstruct!(recombination_particles; algorithm = algorithm, p = p, R = R,
391355
recombine = recombine)
@@ -454,9 +418,7 @@ function _tiled_jet_reconstruct!(particles::AbstractVector{PseudoJet};
454418
tiledjets = scratch.tiledjets
455419

456420
if isnothing(history_buffer)
457-
# Preserve the owning path's eager complete-history allocation. This is
458-
# especially important on Julia < 1.11, where initial_history!'s
459-
# no-shrink size hint is intentionally unavailable.
421+
# Preserve the owning path's eager complete-history allocation.
460422
history_buffer = Vector{HistoryElement}(undef, N)
461423
sizehint!(history_buffer, 2 * N)
462424
end
@@ -631,9 +593,9 @@ function _n2tiled_reconstruct_with_workspace!(workspace::N2TiledWorkspace,
631593
resolved_power = get_algorithm_power(p = p,
632594
algorithm = algorithm)
633595

634-
jets = prepare_recombination_jets!(workspace,
635-
particles,
636-
preprocess = preprocess)
596+
jets = construct_reco_jets!(workspace.jets,
597+
particles,
598+
preprocess)
637599

638600
return _tiled_jet_reconstruct!(jets;
639601
algorithm = algorithm,

src/TiledAlgoLLStructs.jl

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -261,51 +261,6 @@ function TiledScratch()
261261
Dict{Tuple{Int, Int}, TilingArrays}())
262262
end
263263

264-
"""
265-
Reset and fill reusable recombination-jet storage.
266-
267-
This mirrors the preprocessing behaviour of `tiled_jet_reconstruct`.
268-
269-
When `isnothing(preprocess)`:
270-
271-
- PseudoJet inputs are copied directly;
272-
- other supported input types are converted to PseudoJet.
273-
274-
Otherwise, the supplied preprocessing function is called for every particle.
275-
"""
276-
function _prepare_recombination_jets!(jets::Vector{PseudoJet},
277-
particles::AbstractVector{T};
278-
preprocess = preprocess_escheme) where {T}
279-
jets === particles &&
280-
throw(ArgumentError("reusable jet storage must not alias the input particle vector"))
281-
282-
N = length(particles)
283-
284-
empty!(jets)
285-
_sizehint_for_reuse!(jets, 2 * N)
286-
287-
if isnothing(preprocess)
288-
if T == PseudoJet
289-
append!(jets, particles)
290-
else
291-
for (i, particle) in enumerate(particles)
292-
push!(jets,
293-
PseudoJet(particle;
294-
cluster_hist_index = i))
295-
end
296-
end
297-
else
298-
for (i, particle) in enumerate(particles)
299-
push!(jets,
300-
preprocess(particle,
301-
PseudoJet;
302-
cluster_hist_index = i))
303-
end
304-
end
305-
306-
return jets
307-
end
308-
309264
"""
310265
Reusable state for one full-semantics N2Tiled reconstruction worker.
311266
@@ -359,14 +314,6 @@ function release_n2tiled_workspace_capacity!(workspace::N2TiledWorkspace)
359314
return nothing
360315
end
361316

362-
function prepare_recombination_jets!(workspace::N2TiledWorkspace,
363-
particles::AbstractVector;
364-
preprocess = preprocess_escheme)
365-
return _prepare_recombination_jets!(workspace.jets,
366-
particles;
367-
preprocess = preprocess)
368-
end
369-
370317
"""
371318
Resize the event-sized vectors in `scratch` for an event with `N` particles.
372319

src/Utils.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,42 @@ fast_findmin(dij, n) = begin
125125
end
126126
dij_min, best
127127
end
128+
129+
"""
130+
concretize_return_type(return_type, numerical_type)
131+
132+
Return `return_type` unchanged when it is already a `DataType`, or apply the
133+
single type parameter of a `UnionAll` type using `numerical_type`.
134+
"""
135+
function concretize_return_type(return_type::Type,
136+
numerical_type::Type{<:Real})
137+
return_type isa DataType && return return_type
138+
139+
if return_type isa UnionAll
140+
n_parameters = count_typevars(return_type)
141+
n_parameters == 1 ||
142+
throw(ArgumentError("Type $return_type requires $n_parameters type parameters, " *
143+
"but only 1 can be inferred"))
144+
145+
typevar = return_type.var
146+
if !(typevar.lb <: numerical_type <: typevar.ub)
147+
throw(ArgumentError("Cannot parameterize $return_type with type $numerical_type"))
148+
end
149+
150+
return return_type{numerical_type}
151+
end
152+
153+
throw(ArgumentError("Unexpected type specified: $return_type"))
154+
end
155+
156+
function count_typevars(type::UnionAll)
157+
count = 0
158+
current = type
159+
160+
while current isa UnionAll
161+
count += 1
162+
current = current.body
163+
end
164+
165+
return count
166+
end

test/test-jet-utils.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,44 @@ end
9595
@test finaljets_pj[i] finaljets_lorentzhep[i] finaljets_lorentzhepcyl[i]
9696
end
9797
end
98+
99+
@testset "Reconstruction-jet construction" begin
100+
raw_jets = [PseudoJet(1.0, 2.0, 3.0, 4.0; cluster_hist_index = 11),
101+
PseudoJet(2.0, 3.0, 4.0, 6.0; cluster_hist_index = 17)]
102+
103+
owned_jets = JetReconstruction.construct_reco_jets(raw_jets,
104+
PseudoJet,
105+
nothing)
106+
@test owned_jets == raw_jets
107+
@test owned_jets !== raw_jets
108+
@test JetReconstruction.cluster_hist_index.(owned_jets) == [11, 17]
109+
110+
lorentz_particles = lorentzvector.(raw_jets)
111+
converted_jets = JetReconstruction.construct_reco_jets(lorentz_particles,
112+
PseudoJet,
113+
nothing)
114+
@test JetReconstruction.cluster_hist_index.(converted_jets) == [1, 2]
115+
@test lorentzvector.(converted_jets) == lorentz_particles
116+
117+
preprocessed_jets = JetReconstruction.construct_reco_jets(raw_jets,
118+
PseudoJet,
119+
preprocess_ptscheme)
120+
@test JetReconstruction.cluster_hist_index.(preprocessed_jets) == [1, 2]
121+
122+
reusable_jets = PseudoJet[]
123+
returned_jets = JetReconstruction.construct_reco_jets!(reusable_jets,
124+
raw_jets,
125+
nothing)
126+
@test returned_jets === reusable_jets
127+
@test returned_jets == raw_jets
128+
129+
JetReconstruction.construct_reco_jets!(reusable_jets,
130+
raw_jets[1:1],
131+
nothing)
132+
@test length(reusable_jets) == 1
133+
@test reusable_jets[1] == raw_jets[1]
134+
135+
@test_throws ArgumentError JetReconstruction.construct_reco_jets!(raw_jets,
136+
raw_jets,
137+
nothing)
138+
end

test/test-n2tiled-kernel.jl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ end
4949
@testset "Exact owning/workspace equivalence" begin
5050
workspace = N2TiledWorkspace()
5151

52-
cases = ((PseudoJet[], JetAlgorithm.AntiKt, nothing, 0.4),
53-
(small_event[1:1], JetAlgorithm.AntiKt, nothing, 0.8),
52+
cases = ((small_event[1:1], JetAlgorithm.AntiKt, nothing, 0.8),
5453
(small_event, JetAlgorithm.AntiKt, nothing, 0.4),
5554
(large_event, JetAlgorithm.AntiKt, nothing, 0.2),
5655
(small_event, JetAlgorithm.Kt, nothing, 0.4),
@@ -65,19 +64,20 @@ end
6564
preprocess = nothing)
6665
expected_inclusive = inclusive_jets(expected; ptmin = 5.0)
6766

68-
with_n2tiled_reconstruction(workspace,
69-
event;
70-
algorithm = algorithm,
71-
p = power,
72-
R = distance,
73-
preprocess = nothing) do actual
67+
output = with_n2tiled_reconstruction(workspace,
68+
event;
69+
algorithm = algorithm,
70+
p = power,
71+
R = distance,
72+
preprocess = nothing) do actual
7473
@test actual.jets === workspace.jets
7574
@test actual.history === workspace.history
75+
@test length(actual.history) == 2 * length(event)
7676
test_exact_clustersequence_equality(actual, expected)
7777

78-
output = inclusive_jets(actual; ptmin = 5.0)
79-
@test output == expected_inclusive
78+
return inclusive_jets(actual; ptmin = 5.0)
8079
end
80+
@test output == expected_inclusive
8181
end
8282
end
8383

0 commit comments

Comments
 (0)