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) 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/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 99% rename from tutorials/io/experimental/rfile001_basics.py rename to tutorials/io/experimental/rfile/rfile001_basics.py index 14318157e5b92..779ce3a51f5ac 100644 --- a/tutorials/io/experimental/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.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..f41df2827fc53 --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile002_rntuple.py @@ -0,0 +1,58 @@ +# \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..d8b4c37816979 --- /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..e5dde167c773c --- /dev/null +++ b/tutorials/io/experimental/rfile/rfile003_ttree.py @@ -0,0 +1,45 @@ +# \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