UnROOT.jl is a reader for the CERN ROOT file format written entirely in Julia, without any dependence on ROOT or Python.
Important API changes in v0.9.0
Click to expand example for RNTuple
We decided to alter the behaviour of getindex(f::ROOTfile, s::AbstractString)
which is essentially
the method called called when f["foo/bar"]
is used. Before v0.9.0
, UnROOT
tried to do a best guess
and return a tree/branch or even fully parsed data. This lead to two bigger issues.
- Errors prevented any further exploration once
UnROOT
bumped into something it could not interpret, although it might not even be requested by the user (e.g. the interpretation of a single branch in a tree, while others would work fine) - Unpredictable behaviour (type instability): the path dictates which type of data is returned.
Starting from v0.9.0
we introduce an interface where f["..."]
always returns genuine ROOT datatypes (or custom ones if you provide interpretations) and only performs the actual parsing when explicitly requested by the user via helper methods like LazyBranch(f, "...")
.
Long story short, the following pattern can be used to fix your code when upgrading to v0.9.0
:
f("foo/bar") => LazyBranch(f, "foo/bar")
The f["foo/bar"]
accessor should now work on almost all files and is a handy utility to explore the ROOT data structures.
See PR199 for more details.
Installation Guide
- Download the latest Julia release
- Open up Julia REPL (hit
]
once to enter Pkg mode, hit backspace to exit it)
julia>]
(v1.8) pkg> add UnROOT
docs for more)
Quick Start (seeTTree
julia> using UnROOT
julia> f = ROOTFile("test/samples/NanoAODv5_sample.root")
ROOTFile with 2 entries and 21 streamers.
test/samples/NanoAODv5_sample.root
├─ Events (TTree)
│ ├─ "run"
│ ├─ "luminosityBlock"
│ ├─ "event"
│ ├─ "⋮"
│ ├─ "L1_UnpairedBunchBptxPlus"
│ ├─ "L1_ZeroBias"
│ └─ "L1_ZeroBias_copy"
└─ untagged (TObjString)
julia> mytree = LazyTree(f, "Events", ["Electron_dxy", "nMuon", r"Muon_(pt|eta)$"])
Row │ Electron_dxy nMuon Muon_pt Muon_eta
│ SubArray{Float3 UInt32 SubArray{Float3 SubArray{Float3
─────┼────────────────────────────────────────────────────────────────────────────
1 │ [0.000371] 0 [] []
2 │ [-0.00982] 2 [19.9, 15.3] [0.53, 0.229]
3 │ [] 0 [] []
4 │ [-0.00157] 0 [] []
5 │ [] 0 [] []
6 │ [-0.00126] 0 [] []
7 │ [0.0612, 0.000642] 2 [22.2, 4.43] [-1.13, 1.98]
8 │ [0.00587, 0.000549, -0.00617] 0 [] []
⋮ │ ⋮ ⋮ ⋮ ⋮
992 rows omitted
RNTuple
Click to expand example for RNTuple
julia> using UnROOT
julia> f = ROOTFile("./test/samples/RNTuple/test_ntuple_stl_containers.root");
julia> f["ntuple"]
UnROOT.RNTuple with 5 rows, 13 fields, and metadata:
header:
name: "ntuple"
ntuple_description: ""
writer_identifier: "ROOT v6.29/01"
schema:
RNTupleSchema with 13 top fields
├─ :lorentz_vector ⇒ Struct
├─ :vector_tuple_int32_string ⇒ Vector
├─ :string ⇒ String
├─ :vector_string ⇒ Vector
├─ :vector_vector_int32 ⇒ Vector
├─ :vector_variant_int64_string ⇒ Vector
├─ :vector_vector_string ⇒ Vector
├─ :variant_int32_string ⇒ Union
├─ :array_float ⇒ StdArray{3}
├─ :tuple_int32_string ⇒ Struct
├─ :array_lv ⇒ StdArray{3}
├─ :pair_int32_string ⇒ Struct
└─ :vector_int32 ⇒ Vector
footer:
cluster_summaries: UnROOT.ClusterSummary[ClusterSummary(num_first_entry=0, num_entries=5)]
julia> LazyTree(f, "ntuple")
Row │ string vector_int32 array_float vector_vector_i vector_string vector_vector_s variant_int32_s vector_variant_ ⋯
│ String Vector{Int32} StaticArraysCor Vector{Vector{I Vector{String} Vector{Vector{S Union{Int32, St Vector{Union{In ⋯
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ one [1] [1.0, 1.0, 1.0] Vector{Int32}[Int3 ["one"] [["one"]] 1 Union{Int64, Strin ⋯
2 │ two [1, 2] [2.0, 2.0, 2.0] Vector{Int32}[Int3 ["one", "two"] [["one"], ["two"]] two Union{Int64, Strin ⋯
3 │ three [1, 2, 3] [3.0, 3.0, 3.0] Vector{Int32}[Int3 ["one", "two", "th [["one"], ["two"], three Union{Int64, Strin ⋯
4 │ four [1, 2, 3, 4] [4.0, 4.0, 4.0] Vector{Int32}[Int3 ["one", "two", "th [["one"], ["two"], 4 Union{Int64, Strin ⋯
5 │ five [1, 2, 3, 4, 5] [5.0, 5.0, 5.0] Vector{Int32}[Int3 ["one", "two", "th [["one"], ["two"], 5 Union{Int64, Strin ⋯
5 columns omitted
LazyTree as unified table / iteration interface
You can iterate through a LazyTree
:
julia> for event in mytree
@show event.Electron_dxy
break
end
event.Electron_dxy = Float32[0.00037050247]
julia> Threads.@threads :static for event in mytree # multi-threading
...
end
Only one basket per branch will be cached so you don't have to worry about running out of RAM.
At the same time, event
inside the for-loop is not materialized until a field is accessed. This means you should avoid double-access,
see performance tips
XRootD is also supported, depending on the protocol:
- the "url" has to start with
http://
orhttps://
: - (1.6+ only) or the "url" has to start with
root://
and have another//
to separate server and file path
julia> r = ROOTFile("https://scikit-hep.org/uproot3/examples/Zmumu.root")
ROOTFile with 1 entry and 18 streamers.
https://scikit-hep.org/uproot3/examples/Zmumu.root
└─ events (TTree)
├─ "Type"
├─ "Run"
├─ "Event"
├─ "⋮"
├─ "phi2"
├─ "Q2"
└─ "M"
julia> r = ROOTFile("root://eospublic.cern.ch//eos/root-eos/cms_opendata_2012_nanoaod/Run2012B_DoubleMuParked.root")
ROOTFile with 1 entry and 19 streamers.
root://eospublic.cern.ch//eos/root-eos/cms_opendata_2012_nanoaod/Run2012B_DoubleMuParked.root
└─ Events (TTree)
├─ "run"
├─ "luminosityBlock"
├─ "event"
├─ "⋮"
├─ "Electron_dxyErr"
├─ "Electron_dz"
└─ "Electron_dzErr"
TBranch of custom struct
We provide an experimental interface for hooking up UnROOT with your custom types
that only takes 2 steps, as explained in the docs.
As a show case for this functionality, the TLorentzVector
support in UnROOT is implemented
with the said plug-in system.
Support & Contributiing
- Use Github issues for any bug reporting or feature request; feel free to make PRs, bug fixing, feature tuning, quality of life, docs, examples etc.
- See
CONTRIBUTING.md
for more information and recommended workflows in contributing to this package.
Acknowledgements
Special thanks to Jim Pivarski (@jpivarski) from the Scikit-HEP project, who is the main author of uproot, a native Python library to read and write ROOT files, which was and is a great source of inspiration and information for reverse engineering the ROOT binary structures.
Contributors ✨
Thanks goes to these wonderful people (emoji key):
Tamas Gal 💻 📖 🚇 🔣 |
Jerry Ling 💻 |
Johannes Schumann 💻 |
Nick Amin 💻 |
Mosè Giordano 🚇 |
Oliver Schulz 🤔 |
Misha Mikhasenko 🔣 |
Yuan-Ru Lin |
This project follows the all-contributors specification. Contributions of any kind welcome!