Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implementation of substructure modules #87

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions examples/jetreco-substructure.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#! /usr/bin/env julia
using JetReconstruction

input_file = joinpath(dirname(pathof(JetReconstruction)),
"..", "test", "data", "events.pp13TeV.hepmc3.gz")
events = read_final_state_particles(input_file)

# Event to pick
event_no = 1

cluster_seq = jet_reconstruct(events[event_no], p = 0, R = 1.0)
jets = inclusive_jets(cluster_seq; ptmin = 5.0, T = PseudoJet)

μ = 0.67
y = 0.09

tagger = MassDropTagger(μ, y)

@info "Mass Drop Tagging: μ = $μ, y = $y"
for jet in jets
tagged = massDrop(jet, cluster_seq, tagger)
println("Original jet: pt = $(JetReconstruction.pt(jet)), eta = $(JetReconstruction.eta(jet)), phi = $(JetReconstruction.phi(jet)), E = $(jet.E)")
println("Tagged jet: pt = $(JetReconstruction.pt(tagged)), eta = $(JetReconstruction.eta(tagged)), phi = $(JetReconstruction.phi(tagged)), E = $(tagged.E)\n")
end

r = 0.3 # recluster radius
f = 0.3 # trim fraction
m = 0 # recluster method (0 = C/A, 1 = kT)

trim = Trim(r, f, m)

@info "Jet Trimming: recluster radius = $r, trim fraction = $f, recluster method = $(m == 0 ? "C/A" : "kT")"
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
for jet in jets
trimmed = jetTrim(jet, cluster_seq, trim)

println("Original jet: pt = $(JetReconstruction.pt(jet)), eta = $(JetReconstruction.eta(jet)), phi = $(JetReconstruction.phi(jet)), E = $(jet.E)")
println("Trimmed jet: pt = $(JetReconstruction.pt(trimmed)), eta = $(JetReconstruction.eta(trimmed)), phi = $(JetReconstruction.phi(trimmed)), E = $(trimmed.E)\n")

end
4 changes: 4 additions & 0 deletions src/JetReconstruction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,8 @@ export jetsplot, animatereco
include("JSONresults.jl")
export FinalJet, FinalJets

# Substructure modules
include("Substructure.jl")
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
export MassDropTagger, SoftDropTagger, Filter, Trim, massDrop, softDrop, jetFilter, jetTrim
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved

end
313 changes: 313 additions & 0 deletions src/Substructure.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
"""
has_parents(p, history) -> (boolean, Int64, Int64)

Checks if the jet `p` is a child of two other jets, after clustering

# Arguments
- `p`: The jet to check.
- `history`: The vector of history element obtained from the cluster sequence after clustering.

# Returns
- (boolean, Int64, Int64): true or false depending on if the jet has a parent or not. If the jet has a parent, returns the indices of the parent jets in the history element. Otherwise, returns -2 (NonexistentParent).
"""
function has_parents(p::PseudoJet, history::Vector{HistoryElement})

Check warning on line 13 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L13

Added line #L13 was not covered by tests
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved

N = p._cluster_hist_index
p1 = history[N].parent1
p2 = history[N].parent2

Check warning on line 17 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L15-L17

Added lines #L15 - L17 were not covered by tests

p1 == p2 == NonexistentParent ? result = false : result = true
return (result, p1, p2)

Check warning on line 20 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L19-L20

Added lines #L19 - L20 were not covered by tests

end

"""
delta_R(jet1, jet2) -> Float64

Function to calculate the distance in the y-ϕ plane between two jets `jet1` and `jet2`

# Arguments
- `jet1`: The first jet.
- `jet2`: The second jet.

# Returns
- Float64: The Euclidean distance in the y-ϕ plane for the two jets.
"""
function delta_R(jet1::PseudoJet, jet2::PseudoJet)

Check warning on line 36 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L36

Added line #L36 was not covered by tests
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved

eta1, phi1 = rapidity(jet1), phi(jet1)
eta2, phi2 = rapidity(jet2), phi(jet2)

Check warning on line 39 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L38-L39

Added lines #L38 - L39 were not covered by tests

d_eta = eta1 - eta2
d_phi = phi1 - phi2
d_phi = abs(d_phi) > π ? 2π - abs(d_phi) : d_phi

Check warning on line 43 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L41-L43

Added lines #L41 - L43 were not covered by tests

return sqrt(d_eta^2 + d_phi^2)

Check warning on line 45 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L45

Added line #L45 was not covered by tests

end

# Function to calculate kt distance between two pseudojets
function kt_distance(jet1::PseudoJet, jet2::PseudoJet, R = 1)
p1 = pt2(jet1)
p2 = pt2(jet2)

Check warning on line 52 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L50-L52

Added lines #L50 - L52 were not covered by tests

d_R = delta_R(jet1, jet2)
return min(p1, p2) * (d_R^2 / R^2)

Check warning on line 55 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L54-L55

Added lines #L54 - L55 were not covered by tests
end;
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved

"""
recluster(jet, clusterseq, rad, mtd) -> ClusterSequence

Reclusters the constituents of a given jet `jet` with a different clustering method `mtd` and different jet radius `rad`.

# Arguments
- `jet`: The jet whose constituents are to be reclustered.
- `clusterseq`: The cluster sequence from which the original jet is obtained.
- `rad`: The new jet radius.
- `mtd`: The new clustering method.

# Returns
- ClusterSequence: The new cluster sequence.
"""
function recluster(jet::PseudoJet, clusterseq::ClusterSequence, rad = 1.0, mtd = 0)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
cons = constituents(jet, clusterseq)
new_clusterseq = jet_reconstruct(cons; p = mtd, R = rad, strategy = RecoStrategy.N2Plain)

Check warning on line 74 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L72-L74

Added lines #L72 - L74 were not covered by tests

return new_clusterseq

Check warning on line 76 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L76

Added line #L76 was not covered by tests
end

function sort_jets!(event_jet_array)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still find the need for this function questionable. You can just replace it with

sort!(event_jet_array, by = pt2, rev = true)

so it's really not needed.

jet_pt(jet) = pt2(jet)
sort!(event_jet_array, by = jet_pt, rev = true)

Check warning on line 81 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L79-L81

Added lines #L79 - L81 were not covered by tests
# println(event_jet_array)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
end

function join(jets::Vector{PseudoJet})
final = PseudoJet(0.0, 0.0, 0.0, 0.0)

Check warning on line 86 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L85-L86

Added lines #L85 - L86 were not covered by tests

for jet in jets
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
final = final + jet
end

Check warning on line 90 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L88-L90

Added lines #L88 - L90 were not covered by tests

final

Check warning on line 92 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L92

Added line #L92 was not covered by tests
end

# Defining suitable structures to be used for jet grooming and tagging
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can cut this line - say everything in the doctoring

"""
struct MassDropTagger

Used for tagging jets that undergo mass drop, a common technique in jet substructure.

Fields:
- mu: Maximum allowed mass ratio for a jet to pass tagging
- y: Minimum kT distance threshold for parent separation
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line. (It may even cause trouble?)

mutable struct MassDropTagger
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
mu::Float64
y::Float64
end

"""
struct SoftDropTagger

Applies a soft-drop condition on jets, trimming away soft, wide-angle radiation.

Fields:
- zcut: Minimum allowed energy fraction for subjets
- b: Angular exponent controlling soft radiation suppression
"""

mutable struct SoftDropTagger
zcut::Float64
b::Float64
end

"""
struct Filter

Filters jets based on radius and number of hardest subjets, reducing contamination.

Fields:
- filterRadius: Radius parameter to recluster subjets
- numHardestJets: Number of hardest jets to retain in the filtered result
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line

mutable struct Filter
filterRadius::Float64
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
numHardestJets::Int
end

"""
struct Trim

Trims soft, large-angle components from jets based on fraction and radius.

Fields:
- trimRadius: Radius used for reclustering in trimming
- trimFraction: Minimum momentum fraction for retained subjets
- reclusterMethod: Method identifier for reclustering
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line

mutable struct Trim
Copy link
Member

@graeme-a-stewart graeme-a-stewart Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for variable names - prefer lowercase and _s if needed.

trimRadius::Float64
trimFraction::Float64
reclusterMethod::Int
end

"""
massDrop(jet, clusterseq, tag) -> PseudoJet

Identifies subjets in a jet that pass the mass drop tagging condition.
The method stops at the first jet satisfying the mass and distance thresholds.

Arguments:
- jet: PseudoJet instance representing the jet to tag
- clusterseq: ClusterSequence with jet clustering history
- tag: MassDropTagger instance providing mass drop parameters

Returns:
- PseudoJet: The jet (or subjet) satisfying the mass drop conditions, if tagging is successful, otherwise a zero-momentum PseudoJet
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line

function massDrop(jet::PseudoJet, clusterseq::ClusterSequence, tag::MassDropTagger)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
allJets = clusterseq.jets
hist = clusterseq.history

Check warning on line 175 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L173-L175

Added lines #L173 - L175 were not covered by tests

while(true)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
had_parents, p1, p2 = has_parents(jet, hist)

Check warning on line 178 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L177-L178

Added lines #L177 - L178 were not covered by tests

if (had_parents)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
parent1 = allJets[hist[p1].jetp_index]
parent2 = allJets[hist[p2].jetp_index]

Check warning on line 182 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L180-L182

Added lines #L180 - L182 were not covered by tests

if (m2(parent1) < m2(parent2))
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
p1, p2 = p2, p1
parent1, parent2 = parent2, parent1

Check warning on line 186 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L184-L186

Added lines #L184 - L186 were not covered by tests
end

if ((m2(parent1) < m2(jet)*tag.mu^2) && (kt_distance(parent1, parent2) > tag.y*m2(jet)))
return jet

Check warning on line 190 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L189-L190

Added lines #L189 - L190 were not covered by tests
else
jet = parent1

Check warning on line 192 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L192

Added line #L192 was not covered by tests
end

else
return PseudoJet(0.0, 0.0, 0.0, 0.0)

Check warning on line 196 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L196

Added line #L196 was not covered by tests
end

end

Check warning on line 199 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L199

Added line #L199 was not covered by tests

end

"""
softDrop(jet, clusterseq, rad, tag) -> PseudoJet

Applies soft-drop grooming to remove soft, wide-angle radiation from jets.
This function reclusters the jet and iteratively checks the soft-drop condition on subjets.

Arguments:
- jet: PseudoJet instance to groom
- clusterseq: ClusterSequence containing jet history
- rad: Radius parameter for reclustering
- tag: SoftDropTagger instance with soft-drop parameters

Returns:
- PseudoJet: Groomed jet or zero-momentum PseudoJet if grooming fails
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line

function softDrop(jet::PseudoJet, clusterseq::ClusterSequence, rad::Float64, tag::SoftDropTagger)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
new_clusterseq = recluster(jet, clusterseq, rad, 0)
new_jet = inclusive_jets(new_clusterseq; T = PseudoJet)[1]

Check warning on line 221 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L219-L221

Added lines #L219 - L221 were not covered by tests

allJets = new_clusterseq.jets
hist = new_clusterseq.history

Check warning on line 224 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L223-L224

Added lines #L223 - L224 were not covered by tests

while(true)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
had_parents, p1, p2 = has_parents(new_jet, hist)

Check warning on line 227 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L226-L227

Added lines #L226 - L227 were not covered by tests

if (had_parents)
parent1 = allJets[hist[p1].jetp_index]
parent2 = allJets[hist[p2].jetp_index]

Check warning on line 231 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L229-L231

Added lines #L229 - L231 were not covered by tests

pti = pt2(parent1)^0.5
ptj = pt2(parent2)^0.5

Check warning on line 234 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L233-L234

Added lines #L233 - L234 were not covered by tests

if (m2(parent1) < m2(parent2))
p1, p2 = p2, p1
parent1, parent2 = parent2, parent1

Check warning on line 238 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L236-L238

Added lines #L236 - L238 were not covered by tests
end

if (min(pti, ptj)/(pti + ptj) > tag.zcut * (delta_R(parent1, parent2)/rad) ^ tag.b)
return new_jet

Check warning on line 242 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L241-L242

Added lines #L241 - L242 were not covered by tests
else
new_jet = parent1

Check warning on line 244 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L244

Added line #L244 was not covered by tests
end

else
return PseudoJet(0.0, 0.0, 0.0, 0.0)

Check warning on line 248 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L248

Added line #L248 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not completely convinced by this return type - it might be better in this case to return nothing. But we need to check this is ok from a performance point of view. Could you check that?

end

end

Check warning on line 251 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L251

Added line #L251 was not covered by tests

end

"""
jetFilter(jet, clusterseq, filter) -> PseudoJet

Filters a jet to retain only the hardest subjets based on a specified radius and number.

Arguments:
- jet: PseudoJet instance representing the jet to filter
- clusterseq: ClusterSequence containing jet history
- filter: Filter instance specifying radius and number of subjets

Returns:
- PseudoJet: Filtered jet composed of the hardest subjets

"""
function jetFilter(jet::PseudoJet, clusterseq::ClusterSequence, filter::Filter)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
rad = filter.filterRadius;
new_clusterseq = recluster(jet, clusterseq, rad, 0)
reclustered = sort_jets!(inclusive_jets(new_clusterseq; T = PseudoJet))

Check warning on line 272 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L269-L272

Added lines #L269 - L272 were not covered by tests

n = length(reclustered) <= filter.numHardestJets ? length(reclustered) : filter.numHardestJets
hard = reclustered[1:n]

Check warning on line 275 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L274-L275

Added lines #L274 - L275 were not covered by tests

filtered = join(hard)

Check warning on line 277 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L277

Added line #L277 was not covered by tests

filtered

Check warning on line 279 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L279

Added line #L279 was not covered by tests
end

"""
jetTrim(jet, clusterseq, trim) -> PseudoJet

Trims a jet by removing subjets with transverse momentum below a specified fraction.

Arguments:
- jet: PseudoJet instance representing the jet to trim
- clusterseq: ClusterSequence containing jet history
- trim: Trim instance specifying trimming parameters

Returns:
- PseudoJet: Trimmed jet composed of retained subjets
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank

function jetTrim(jet::PseudoJet, clusterseq::ClusterSequence, trim::Trim)
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
rad = trim.trimRadius;
mtd = trim.reclusterMethod
frac2 = trim.trimFraction ^ 2

Check warning on line 299 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L296-L299

Added lines #L296 - L299 were not covered by tests

new_clusterseq = recluster(jet, clusterseq, rad, mtd)
reclustered = sort_jets!(inclusive_jets(new_clusterseq; T = PseudoJet))

Check warning on line 302 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L301-L302

Added lines #L301 - L302 were not covered by tests

hard = Vector{PseudoJet}(undef, 0)
for item in reclustered
if pt2(item) >= frac2 * pt2(jet)
push!(hard, item)

Check warning on line 307 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L304-L307

Added lines #L304 - L307 were not covered by tests
end
end
trimmed = join(hard)

Check warning on line 310 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L309-L310

Added lines #L309 - L310 were not covered by tests

trimmed

Check warning on line 312 in src/Substructure.jl

View check run for this annotation

Codecov / codecov/patch

src/Substructure.jl#L312

Added line #L312 was not covered by tests
end;
graeme-a-stewart marked this conversation as resolved.
Show resolved Hide resolved
Loading