From f0e346539257f080921fcf2f34f2655a26099310 Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 13 Oct 2026 11:08:54 +0200 Subject: [PATCH 1/4] [ntuple] add pythonization for RNTupleWriter_Append(RFile) --- .../pythonizations/python/ROOT/_pythonization/_rntuple.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py index abb435adbe1b0..d18220d9198de 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py @@ -234,6 +234,9 @@ def _RNTupleWriter_Append(model, *args): model = model.Clone() import ROOT + if len(args) > 1 and isinstance(args[1], ROOT.Experimental.RFile): + return RNTupleContextWrapper(ROOT.Experimental.RNTupleWriter_Append(model, *args), "RNTupleWriter", on_ctx_exit = _RNTupleWriter_exit) + return RNTupleContextWrapper(ROOT.RNTupleWriter._Append(model, *args), "RNTupleWriter", on_ctx_exit = _RNTupleWriter_exit) From 476228d4ddbf600bc2429ddff63327f9455b617d Mon Sep 17 00:00:00 2001 From: silverweed Date: Mon, 12 Oct 2026 11:30:29 +0200 Subject: [PATCH 2/4] [rfile] move rfile tutorials to experimental/rfile --- tutorials/io/experimental/{ => rfile}/rfile001_basics.C | 0 tutorials/io/experimental/{ => rfile}/rfile001_basics.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tutorials/io/experimental/{ => rfile}/rfile001_basics.C (100%) rename tutorials/io/experimental/{ => rfile}/rfile001_basics.py (100%) diff --git a/tutorials/io/experimental/rfile001_basics.C b/tutorials/io/experimental/rfile/rfile001_basics.C similarity index 100% rename from tutorials/io/experimental/rfile001_basics.C rename to tutorials/io/experimental/rfile/rfile001_basics.C diff --git a/tutorials/io/experimental/rfile001_basics.py b/tutorials/io/experimental/rfile/rfile001_basics.py similarity index 100% rename from tutorials/io/experimental/rfile001_basics.py rename to tutorials/io/experimental/rfile/rfile001_basics.py From 9143aa836aa4d0b2a007cefaa45f5c9e000772ee Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 13 Oct 2026 11:08:50 +0200 Subject: [PATCH 3/4] [rfile] Add tutorials with RNTuple and TTree --- tutorials/CMakeLists.txt | 2 + .../io/experimental/rfile/rfile002_rntuple.C | 62 +++++++++++++++++++ .../io/experimental/rfile/rfile002_rntuple.py | 57 +++++++++++++++++ .../io/experimental/rfile/rfile003_ttree.C | 50 +++++++++++++++ .../io/experimental/rfile/rfile003_ttree.py | 44 +++++++++++++ tutorials/io/index.md | 4 ++ 6 files changed, 219 insertions(+) create mode 100644 tutorials/io/experimental/rfile/rfile002_rntuple.C create mode 100644 tutorials/io/experimental/rfile/rfile002_rntuple.py create mode 100644 tutorials/io/experimental/rfile/rfile003_ttree.C create mode 100644 tutorials/io/experimental/rfile/rfile003_ttree.py diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 95c26b38c6983..b584e74307c0e 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -154,6 +154,8 @@ if (NOT dataframe) # RNTuple tutorials depending on RDataFrame list(APPEND dataframe_veto io/ntuple/ntpl004_dimuon.C) list(APPEND dataframe_veto io/ntuple/ntpl008_import.C) + # RFile tutorials depending on RDataFrame + list(APPEND dataframe_veto io/experimental/rfile/rfile003_ttree.C) endif() if(NOT sqlite) diff --git a/tutorials/io/experimental/rfile/rfile002_rntuple.C b/tutorials/io/experimental/rfile/rfile002_rntuple.C new file mode 100644 index 0000000000000..f0c61fba7618c --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile002_rntuple.C @@ -0,0 +1,62 @@ +/// \file +/// \ingroup ROOT7 tutorial_io +/// Demonstrate how to read/write an RNTuple with RFile +/// +/// \macro_code +/// \macro_output +/// +/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +/// is welcome! +/// +/// \date 2026-10-12 +/// \author Giacomo Parolini + +void write_rntuple_to_rfile(const char *fileName) +{ + // Create a new ROOT file for writing + auto file = ROOT::Experimental::RFile::Recreate(fileName); + + // Define the RNTuple schema + auto model = ROOT::RNTupleModel::Create(); + auto pInt = model->MakeField("intField"); + + // Create an RNTupleWriter to append to the RFile + auto writer = ROOT::Experimental::RNTupleWriter_Append(std::move(model), "ntuple", *file); + + // Fill the RNTuple with data + for (int i = 0; i < 10; ++i) { + *pInt = i; + writer->Fill(); + } + + // `writer` will write the data to file upon destruction. Then `file` will write itself to disk in turn. +} + +void read_rntuple_from_rfile(const char *fileName) +{ + // Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + auto file = ROOT::Experimental::RFile::Open(fileName); + + // Get back the RNTuple anchor from the file + auto rntuple = file->Get("ntuple"); + R__ASSERT(rntuple); // we just wrote the RNTuple to the file so we expect to find it + + // Create an RNTupleReader from the anchor + auto reader = ROOT::RNTupleReader::Open(*rntuple); + auto pInt = reader->GetModel().GetDefaultEntry().GetPtr("intField"); + + for (auto i : reader->GetEntryRange()) { + reader->LoadEntry(i); + std::cout << "value[" << i << "] = " << *pInt << "\n"; + } +} + +void rfile002_rntuple() +{ + const char *const fileName = "rfile_rntuple.root"; + + write_rntuple_to_rfile(fileName); + read_rntuple_from_rfile(fileName); + + gSystem->Unlink(fileName); +} diff --git a/tutorials/io/experimental/rfile/rfile002_rntuple.py b/tutorials/io/experimental/rfile/rfile002_rntuple.py new file mode 100644 index 0000000000000..743f89ef297a5 --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile002_rntuple.py @@ -0,0 +1,57 @@ +# \file +# \ingroup ROOT7 tutorial_io +# Demonstrate how to read/write an RNTuple with RFile +# +# \macro_code +# \macro_output +# +# \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +# is welcome! +# +# \date 2026-10-12 +# \author Giacomo Parolini + +import ROOT + +import os + +def write_rntuple_to_rfile(fileName): + # Create a new ROOT file for writing + with ROOT.Experimental.RFile.Recreate(fileName) as file: + # Define the RNTuple schema + model = ROOT.RNTupleModel.Create() + model.MakeField["int"]("intField") + # Create an RNTupleWriter to append to the RFile + with ROOT.RNTupleWriter.Append(model, "ntuple", file) as writer: + entry = writer.CreateEntry() + + # Fill the RNTuple with data + for i in range(10): + entry["intField"] = i + writer.Fill(entry) + + # `writer` will write the data to file upon exiting the with statement.... + # ...then `file` will write itself to disk in turn when exiting the outer with statement. + + +def read_rntuple_from_rfile(fileName): + # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + with ROOT.Experimental.RFile.Open(fileName) as file: + # Get back the RNTuple anchor from the file + rntuple = file.Get("ntuple") + + # Create an RNTupleReader from the anchor + with ROOT.RNTupleReader.Open(rntuple) as reader: + entry = reader.CreateEntry() + for i in reader.GetEntryRange(): + reader.LoadEntry(i, entry) + print(f"value[{i}] = {entry['intField']}") + + +fileName = "rfile_rntuple_py.root" +try: + write_rntuple_to_rfile(fileName) + read_rntuple_from_rfile(fileName) + os.remove(fileName) +except FileNotFoundError: + pass diff --git a/tutorials/io/experimental/rfile/rfile003_ttree.C b/tutorials/io/experimental/rfile/rfile003_ttree.C new file mode 100644 index 0000000000000..bbf7e42cf3105 --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile003_ttree.C @@ -0,0 +1,50 @@ +/// \file +/// \ingroup ROOT7 tutorial_io +/// Demonstrate how to read a TTree with RFile. +/// +/// Note that, due to how TTree handles lifetime and its relationship with TDirectory, writing TTree through RFile +/// is currently unsupported. +/// +/// \macro_code +/// \macro_output +/// +/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +/// is welcome! +/// +/// \date 2026-10-12 +/// \author Giacomo Parolini +#include +#include +#include +#include +#include + +void read_tree_from_rfile(const char *fileName) +{ + // Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + // IMPORTANT! This file must remain open while reading data from the tree. + auto file = ROOT::Experimental::RFile::Open(fileName); + + // Get back the TTree from the file + auto tree = file->Get("tree"); + R__ASSERT(tree); // we just wrote the tree so we expect to find it in the file + int intVal; + tree->SetBranchAddress("intBranch", &intVal); + + for (auto i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + std::cout << "value[" << i << "] = " << intVal << "\n"; + } +} + +void rfile003_ttree() +{ + const char *const fileName = "rfile_ttree.root"; + + // Note that TTree cannot be written to RFile, so we use RDataFrame to write it instead. + ROOT::RDataFrame(10).Define("intBranch", "static_cast(rdfentry_)").Snapshot("tree", fileName); + + read_tree_from_rfile(fileName); + + gSystem->Unlink(fileName); +} diff --git a/tutorials/io/experimental/rfile/rfile003_ttree.py b/tutorials/io/experimental/rfile/rfile003_ttree.py new file mode 100644 index 0000000000000..c42ec19b78132 --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile003_ttree.py @@ -0,0 +1,44 @@ +# \file +# \ingroup ROOT7 tutorial_io +# Demonstrate how to read a TTree with RFile. +# +# Note that, due to how TTree handles lifetime and its relationship with TDirectory, writing TTree through RFile +# is currently unsupported. +# +# \macro_code +# \macro_output +# +# \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +# is welcome! +# +# \date 2026-10-12 +# \author Giacomo Parolini + +import array +import os + +import ROOT + + +def read_ttree_from_rfile(fileName): + # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + # IMPORTANT! This file must remain open while reading data from the tree. + with ROOT.Experimental.RFile.Open(fileName) as file: + # Get back the TTree from the file + tree = file.Get("tree") + floatVal = array.array("f", [0]) + tree.SetBranchAddress("floatBranch", floatVal) + + for i in range(tree.GetEntries()): + tree.GetEntry(i) + print(f"value[{i}] = {floatVal[0]}") + +fileName = "rfile_ttree_py.root" +try: + # Note that TTree cannot be written to RFile, so we use RDataFrame to write it instead. + ROOT.RDataFrame(10).Define("floatBranch", "static_cast(rdfentry_)").Snapshot("tree", fileName) + + read_ttree_from_rfile(fileName) + os.remove(fileName) +except FileNotFoundError: + pass diff --git a/tutorials/io/index.md b/tutorials/io/index.md index d19171709deab..1e91aa74ea1b3 100644 --- a/tutorials/io/index.md +++ b/tutorials/io/index.md @@ -52,6 +52,10 @@ RFile is a simpler alternative to TFile that makes operations and object ownersh |------|-----------------| | rfile001_basics.C | Basic reading and writing from/to RFile (C++) | | rfile001_basics.py | Basic reading and writing from/to RFile (Python) | +| rfile002_rntuple.C | Basic reading and writing of RNTuple from/to RFile (C++) | +| rfile002_rntuple.py | Basic reading and writing of RNTuple from/to RFile (Python) | +| rfile003_ttree.C | Basic reading of TTree from RFile (C++) | +| rfile003_ttree.py | Basic reading of TTree from RFile (Python) | @} \defgroup tutorial_ntuple RNTuple tutorials From b6d1a76a599f817cd73e20cbff04e07a2671b25f Mon Sep 17 00:00:00 2001 From: silverweed Date: Wed, 14 Oct 2026 09:57:09 +0200 Subject: [PATCH 4/4] [rfile] ruff/clang format rfile tutorials --- .../io/experimental/rfile/rfile001_basics.py | 3 +- .../io/experimental/rfile/rfile002_rntuple.py | 55 ++++++++++--------- .../io/experimental/rfile/rfile003_ttree.C | 2 +- .../io/experimental/rfile/rfile003_ttree.py | 25 +++++---- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/tutorials/io/experimental/rfile/rfile001_basics.py b/tutorials/io/experimental/rfile/rfile001_basics.py index 14318157e5b92..779ce3a51f5ac 100644 --- a/tutorials/io/experimental/rfile/rfile001_basics.py +++ b/tutorials/io/experimental/rfile/rfile001_basics.py @@ -15,6 +15,7 @@ import os + def write_hist_to_rfile(fileName): # Create a histogram to write to the file hist = ROOT.TH1D("hist", "hist", 10, 0, 100) @@ -30,7 +31,7 @@ def write_hist_to_rfile(fileName): # When the `with` statement is exited the file will write itself to disk and close itself. # To manually write the file without closing it, one can use `file.Flush()`. - + def read_hist_from_rfile(fileName): # Open an existing ROOT file for reading (will raise an exception if `fileName` cannot be read). diff --git a/tutorials/io/experimental/rfile/rfile002_rntuple.py b/tutorials/io/experimental/rfile/rfile002_rntuple.py index 743f89ef297a5..f41df2827fc53 100644 --- a/tutorials/io/experimental/rfile/rfile002_rntuple.py +++ b/tutorials/io/experimental/rfile/rfile002_rntuple.py @@ -15,37 +15,38 @@ import os + def write_rntuple_to_rfile(fileName): - # Create a new ROOT file for writing - with ROOT.Experimental.RFile.Recreate(fileName) as file: - # Define the RNTuple schema - model = ROOT.RNTupleModel.Create() - model.MakeField["int"]("intField") - # Create an RNTupleWriter to append to the RFile - with ROOT.RNTupleWriter.Append(model, "ntuple", file) as writer: - entry = writer.CreateEntry() - - # Fill the RNTuple with data - for i in range(10): - entry["intField"] = i - writer.Fill(entry) - - # `writer` will write the data to file upon exiting the with statement.... - # ...then `file` will write itself to disk in turn when exiting the outer with statement. + # Create a new ROOT file for writing + with ROOT.Experimental.RFile.Recreate(fileName) as file: + # Define the RNTuple schema + model = ROOT.RNTupleModel.Create() + model.MakeField["int"]("intField") + # Create an RNTupleWriter to append to the RFile + with ROOT.RNTupleWriter.Append(model, "ntuple", file) as writer: + entry = writer.CreateEntry() + + # Fill the RNTuple with data + for i in range(10): + entry["intField"] = i + writer.Fill(entry) + + # `writer` will write the data to file upon exiting the with statement.... + # ...then `file` will write itself to disk in turn when exiting the outer with statement. def read_rntuple_from_rfile(fileName): - # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). - with ROOT.Experimental.RFile.Open(fileName) as file: - # Get back the RNTuple anchor from the file - rntuple = file.Get("ntuple") - - # Create an RNTupleReader from the anchor - with ROOT.RNTupleReader.Open(rntuple) as reader: - entry = reader.CreateEntry() - for i in reader.GetEntryRange(): - reader.LoadEntry(i, entry) - print(f"value[{i}] = {entry['intField']}") + # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + with ROOT.Experimental.RFile.Open(fileName) as file: + # Get back the RNTuple anchor from the file + rntuple = file.Get("ntuple") + + # Create an RNTupleReader from the anchor + with ROOT.RNTupleReader.Open(rntuple) as reader: + entry = reader.CreateEntry() + for i in reader.GetEntryRange(): + reader.LoadEntry(i, entry) + print(f"value[{i}] = {entry['intField']}") fileName = "rfile_rntuple_py.root" diff --git a/tutorials/io/experimental/rfile/rfile003_ttree.C b/tutorials/io/experimental/rfile/rfile003_ttree.C index bbf7e42cf3105..d8b4c37816979 100644 --- a/tutorials/io/experimental/rfile/rfile003_ttree.C +++ b/tutorials/io/experimental/rfile/rfile003_ttree.C @@ -1,7 +1,7 @@ /// \file /// \ingroup ROOT7 tutorial_io /// Demonstrate how to read a TTree with RFile. -/// +/// /// Note that, due to how TTree handles lifetime and its relationship with TDirectory, writing TTree through RFile /// is currently unsupported. /// diff --git a/tutorials/io/experimental/rfile/rfile003_ttree.py b/tutorials/io/experimental/rfile/rfile003_ttree.py index c42ec19b78132..e5dde167c773c 100644 --- a/tutorials/io/experimental/rfile/rfile003_ttree.py +++ b/tutorials/io/experimental/rfile/rfile003_ttree.py @@ -1,7 +1,7 @@ # \file # \ingroup ROOT7 tutorial_io # Demonstrate how to read a TTree with RFile. -# +# # Note that, due to how TTree handles lifetime and its relationship with TDirectory, writing TTree through RFile # is currently unsupported. # @@ -21,17 +21,18 @@ def read_ttree_from_rfile(fileName): - # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). - # IMPORTANT! This file must remain open while reading data from the tree. - with ROOT.Experimental.RFile.Open(fileName) as file: - # Get back the TTree from the file - tree = file.Get("tree") - floatVal = array.array("f", [0]) - tree.SetBranchAddress("floatBranch", floatVal) - - for i in range(tree.GetEntries()): - tree.GetEntry(i) - print(f"value[{i}] = {floatVal[0]}") + # Open an existing ROOT file for reading (will throw an exception if `fileName` cannot be read). + # IMPORTANT! This file must remain open while reading data from the tree. + with ROOT.Experimental.RFile.Open(fileName) as file: + # Get back the TTree from the file + tree = file.Get("tree") + floatVal = array.array("f", [0]) + tree.SetBranchAddress("floatBranch", floatVal) + + for i in range(tree.GetEntries()): + tree.GetEntry(i) + print(f"value[{i}] = {floatVal[0]}") + fileName = "rfile_ttree_py.root" try: