SkillAgentSearch skills...

UnROOT.jl

Native Julia I/O package to work with CERN ROOT files objects (TTree and RNTuple)

Install / Use

/learn @JuliaHEP/UnROOT.jl
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/src/assets/unroot_dark.svg"> <img alt="UnROOT.jl Logo" src="docs/src/assets/unroot.svg"> </picture> <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

All Contributors

<!-- ALL-CONTRIBUTORS-BADGE:END -->

JOSS Dev CI pkgeval Aqua QA Codecov

UnROOT.jl is a reader for the CERN ROOT file format written entirely in Julia, without any dependence on ROOT or Python.

Important and Breaking Changes

Breaking API changes in v0.11.0

<details><summary>XRootD and HTTP are now available via extensions</summary> <p>

UnROOT.jl supports opening files remotely via XRootD which requires XRootD.jl and via HTTP using HTTP.jl seamlessly up to version 0.10.38. Starting with v0.11, this behaviour has changed to reduce default dependencies, since not everyone uses these features. To continue opening remote files via XRootD or HTTP, the corresponding Julia package (XRootD.jl, respectively HTTP.jl) now needs to be installed and loaded.

UnROOT.jl provides extensions for both which are loaded automatically so ROOTFile(url) will work just like before.

Long story short, when passing a url to ROOTFile(...), make sure to load appropriate package:

using UnROOT
using XRootD  # this is now required for XRootD URLs

ROOTFile("xroot://...")

or

using UnROOT
using HTTP  # this is now required for HTTP/HTTPS URLs

ROOTFile("https://...")

See PR396 for more details.

</p> </details>

Important API changes in v0.9.0

<details><summary>`getindex` now behaves differently</summary> <p>

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.

  1. 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)
  2. 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.

</p> </details>

Installation Guide

  1. Download the latest Julia release
  2. Open up Julia REPL (hit ] once to enter Pkg mode, hit backspace to exit it)
julia>]
(v1.8) pkg> add UnROOT

Quick Start (see docs for more)

TTree

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

<details><summary>Click to expand example for RNTuple</summary> <p>
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
</p> </details>

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 via extensions, depending on the protocol, either using XRootD or using HTTP:

  • the "url" has to start with http:// or https://:
  • (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.

View on GitHub
GitHub Stars111
CategoryDevelopment
Updated14d ago
Forks19

Languages

Julia

Security Score

100/100

Audited on Mar 18, 2026

No findings