Internal
mutable struct SyntheticHemisphere
runNumber::Int64
eventNumber::Int64
jet::Vector{LorentzVectorCyl{Float64}}
photon::Vector{LorentzVectorCyl{Float64}}
normal_vector::Vector{Float64} #This is the normal vector to the plane we used to split the hemispheres
jet_EMFrac::Vector{Float64}
jet_fastDIPS::Vector{Float64}
@. ATLAS_f_4p(x, p) = p[1]*(1-x)^p[2] * x^(-(p[3] + p[4]*log(x)))
@. ATLAS_f_5p(x, p) = p[1]*(1-x)^p[2] * x^(-(p[3] + p[4]*log(x) + p[5]*abs2(log(x))))
antiparallel_rotation_matrix(v::AbstractVector, u::AbstractVector)
Returns a rotation matrix `M` such that `M*v` is antiparallel to `u`.
calc_db(b::Number, c::Number, u::Number; fc=0.018) -> Float64
calc_db(b::Vector, c::Vector, u::Vector; fc=0.018) -> Vector{Float64}
Compute the b-tagging discriminants, see https://cds.cern.ch/record/2862499 for more detail
The output is:
Note
The log operation usese safeLogRatio
to prevent producing Inf
and NaN
.
chi2(os, cs, σs) -> Float64
Calculate chi2 given three arrays of data points, model points and errors.
os
: observed data pointscs
: calculated model pointsσs
: errors of the observed data points
extract_dsid(path::String) -> String
Extract the DSID from the path of a file. It works by using a regular expression to match the DSID (\d{6}
) in the path.
get_fill_wgt(evt, pmgWeight, sumWeight;)
Get the fill weight for the event, if the evt
has the mcEventWeight
property, the weight is calculated as evt.mcEventWeight * (pmgWeight / sumWeight)
, otherwise the weight is 1.0
for real data.
get_pmg_weight(dsid::Union{Integer, AbstractString};
md_path = "/cvmfs/atlas.cern.ch/repo/sw/database/GroupData/dev/PMGTools/PMGxsecDB_mc23.txt")
Get the PMG weight from cvmfs database for a given dsid: returns crossSection_pb * genFiltEff * kFactor
Example
julia> signal_dsid = 510392
510392
julia> BjetTLA.get_pmg_weight(signal_dsid)
0.7831930217100002
julia> BjetTLA.get_pmg_weight("510392")
0.7831930217100002
get_shortname(dsid, map)
Convert dsid to a short name, following the naming map.
hem_axis_rotation_matrix(b_vec_1::AbstractVector, b_vec_2::, normal_vector::AbstractVector, ref_angle)
Returns a rotation matrix `M` that will rotate b_vec_2 about the axis defined by normal_vector such that the angle
between b_vec_1 and b_vec_2 equals ref_angle.
load_trees(input_path::String, pattern::String = "*.root";
split::Bool=false, tree_name::String="TreeAlgo_noCalib/nominal",)
Load ROOT trees, chaining multiple files with same Tree name.
nonoverlap_mask(photons, jets; cutoff=0.4) -> Vector{Bool}
Create a mask that can be used to get non-overlapping jets.
Example
mask = nonoverlap_mask(photons, jets)
clean_jets = @view jets[mask]
optim_fit(func::Callable, hist0::FHist.Hist1D, p0; sqrt_s=13600)
Fit a χ² fit using a func
to a 1D histogram.
func
: a function that takes two arguments,x
andp
, and returns a model value.hist0
: a 1D histogram to fit.p0
: initial parameters.
Note
The func
should be defined with @.
, and the p0
should be a vector. See ATLAS_f_4p
for an example.
Also, the x-axis (bincenters()
) of the hist0
will be divided by sqrt_s
to normalize the x-axis.
It returns the result of the optimization.
Example
julia> hist_mjj12_mask
edges: [130.0, 132.0, 134.0, 136.0, 138.0, 140.0, 142.0, 144.0, 146.0, 148.0 … 482.0, 484.0, 486.0, 488.0, 490.0, 492.0, 494.0, 496.0, 498.0, 500.0]
bin counts: [650.0, 603.0, 649.0, 566.0, 595.0, 593.0, 535.0, 487.0, 492.0, 466.0 … 2.0, 10.0, 13.0, 9.0, 13.0, 10.0, 10.0, 8.0, 15.0, 8.0]
total count: 22293.0
julia> sol4 = optim_fit(ATLAS_f_4p, hist_mjj12_mask, ones(4))
retcode: Success
u: 4-element Vector{Float64}:
0.0001086906699655396
91.12405771249554l
1.2345307604384157
-0.03547447344008578
julia> sol4.u
4-element Vector{Float64}:
0.0001086906699655396
91.12405771249554
1.2345307604384157
-0.03547447344008578
parse_input(input_path::String, pattern::String = "*.root"; split::Bool=false)
parse input into a list of files: input_path
can be a path to a file or directory, and a wildcarded pattern can be passed via the pattern
option if input_path
is a folder. If split
is set to true, a dicitonary of files splitted by dsid is returned
pass_filter_jets(evt, hlt_pfjets, photons; min_pt = 20.0, dR_cutoff=0.4, min_Jvt = 0.2, sort=true)
Filter the HLT jets collection and related variables by doing the following:
require
pt > min_pt
require
abs(eta) < dR_cutoff
remove jets that overlap with the photons collection by
dR < dR_cutoff
require
jvt > min_Jvt
(jvt = jet vertex tagger)
Finally, return the following as a NamedTuple
so you can select one or more at call site:
clean_jets
: the cleaned jets collectionclean_jets_btags
: the cleaned jets b-tagging valuesclean_jets_emfracs
: the cleaned jets EM fractionsclean_jets_jvts
: the cleaned jets JVT values
Note
If sort
is true
, all the returnred arrays are sorted by the jet pt
in descending order.
clean_photons(hlt_photons; eta_cuts = true, min_pt = 20.0) -> vector of photon lorentz vectors
Clean photons from the HLT photon collection. The cleaning is done by removing photons that satisfy the any of the following conditions:
pt < min_pt
abs(eta) > 2.37
1.37 < abs(eta) < 1.52
plane_bisector(v::AbstractVector, u::AbstractVector)
Returns a vector perpendicular to the plane which bisects the angle formed by the vectors v and u.
private_main(path::String, main::Function; isMC=false, kw...)
Single root file entry point for the main analysis loop. This function is used to parse the metadata histogram and get the sum of weights for the MC samples.
It prepares the following weigts:
sumWeight
: extracted fromROOTFile["MetaData_EventCount"]
histogram.pmgWeight
: the weight for the PMG samples, seeget_pmg_weight
kw...
: additional keyword arguments to be passed to themain
function as keyword arguments.
Finally, it will run and return main(tree; sumWeight, pmgWeight, kw...)
.
Note
Currently it's assumed TTree is stored in the TreeAlgo_noCalib/nominal
inside the root file. Systematics is not handled in this function.
safeLogRatio(num,den)
Safe log computation, to emulate athena implementation:
If
iszero(den)
, return15.0
If
iszero(num)
, return-10.0
otherwise, return
log(num/den)
as expected
sensitivity_study(tree::LazyTree; sumWeight = 1.0, pmgWeight = 1.0) -> NamedTuple of Histograms
Main analysis loop for the sensitivity study. It should be used with the private_main
function.
Example
julia> npath = "/data/jiling/TLA/eos_ntuples/mc23a/Zprime_bb/nv2/user.sfranche.510392.MGPy8EG_S1_qqa_Ph25_mRp125_gASp1_qContentUDSC.e8514_s4162_r15315.nv2_tree.root/merged.root"
julia> mc_hists_sensitivity = BjetTLA.private_main(npath, BjetTLA.sensitivity_study; isMC=true);
pass_JZ_cleaning(j_pt, jz, eps=0.2)
Apply cleaning filter for JZ slices, checks if the average pT of the two leading jets is in the expected range for slice jz
, within a eps
tolerance.
split_by_dsid(strings::Vector{String})
Splits a vector of strings into a dictionary based on the dsid, a sequence of six digits preceeded and followed by ".". If that criteria fails, looks for a run number instead (sequence of eight digits).
split_hemispheres(evt) -> Tuple{SyntheticHemisphere, SyntheticHemisphere}
Given a single event, this function will split the event into two hemispheres based on the two leading b-jets in the event. The function returns two SyntheticHemisphere
objects.
@cutflow counter hist name_list name wgt
Convinient macro to generate cutflow histogram filling code. It will increment the counter
by 1
, check if the name_list[counter]
is equal to expected name
, and then push the counter
to the hist
with the given wgt
.
BjetTLA.@cutflow counter hist name_list name wgt
is equivalent to:
counter += 1
@assert name_list[counter] == name
atomic_push!(hist, counter, wgt)
@fourmomenta evt obj_prefix
Creates the fourmomenta for a given object type in the event. Returns a StructArray of LorentzVectorCyl.
It assumes that the event is a structure containig all the projections of the 4-vectors: evt.obj_pt, evt.obj_eta, evt.obj_phi, evt.obj_m (or evt.obj_E for jets).
Concretely,
@fourmomenta evt ph
would expand to:
StructArray(LorentzVectorCyl(
evt.ph_pt,
evt.ph_eta,
evt.ph_phi,
evt.ph_m
))
For jets, it will call fromPtEtaPhiE
and the return type stays the same.
Example
julia> for evt in tree
hlt_jets = @fourmomenta evt jet
count(>(25), hlt_jets.pt) # count how many jets have pt > 25 GeV
...
end