Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleImporter.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleImporter.hxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2022-11-22
4/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
5/// is welcome!
6
7/*************************************************************************
8 * Copyright (C) 1995-2022, Rene Brun and Fons Rademakers. *
9 * All rights reserved. *
10 * *
11 * For the licensing terms see $ROOTSYS/LICENSE. *
12 * For the list of contributors see $ROOTSYS/README/CREDITS. *
13 *************************************************************************/
14
15#ifndef ROOT7_RNTuplerImporter
16#define ROOT7_RNTuplerImporter
17
18#include <ROOT/REntry.hxx>
19#include <ROOT/RError.hxx>
20#include <ROOT/RField.hxx>
21#include <ROOT/RNTupleModel.hxx>
24#include <string_view>
25
26#include <TFile.h>
27#include <TTree.h>
28
29#include <cstdlib>
30#include <functional>
31#include <map>
32#include <memory>
33#include <vector>
34
35class TLeaf;
36
37namespace ROOT {
38namespace Experimental {
39
40// clang-format off
41/**
42\class ROOT::Experimental::RNTupleImporter
43\ingroup NTuple
44\brief Converts a TTree into an RNTuple
45
46Example usage (see the ntpl008_import.C tutorial for a full example):
47
48~~~ {.cpp}
49#include <ROOT/RNTupleImporter.hxx>
50using ROOT::Experimental::RNTupleImporter;
51
52auto importer = RNTupleImporter::Create("data.root", "TreeName", "output.root");
53// As required: importer->SetNTupleName(), importer->SetWriteOptions(), ...
54importer->Import();
55~~~
56
57The output file is created if it does not exist, otherwise the ntuple is added to the existing file.
58Directories in the output file are created as necessary, allowing ntuples to be stored in a nested structure (e.g. DirName/TreeName).
59Note that input file and output file can be identical if the ntuple is stored under a different name than the tree
60(use `SetNTupleName()`).
61
62By default, the RNTuple is compressed with zstd, independent of the input compression. The compression settings
63(and other output parameters) can be changed by `SetWriteOptions()`. For example, to compress the imported RNTuple
64using lz4 (with compression level 4) instead:
65
66~~~ {.cpp}
67auto writeOptions = importer->GetWriteOptions();
68writeOptions.SetCompression(404);
69importer->SetWriteOptions(writeOptions);
70~~~
71
72Most RNTuple fields have a type identical to the corresponding TTree input branch. Exceptions are
73 - C string branches are translated to `std::string` fields
74 - C style arrays are translated to `std::array<...>` fields
75 - Leaf lists are translated to untyped records
76 - Leaf count arrays are translated to anonymous collections with generic names (`_collection0`, `_collection1`, etc.).
77 In order to keep field names and branch names aligned, RNTuple projects the members of these collections and
78 its collection counter to the input branch names. For instance, the following input leafs:
79~~~
80Int_t njets
81float jet_pt[njets]
82float jet_eta[njets]
83~~~
84 will be converted to the following RNTuple schema:
85~~~
86 _collection0 (untyped collection)
87 |- float jet_pt
88 |- float jet_eta
89 std::size_t (RNTupleCardinality) njets (projected from _collection0 without subfields)
90 ROOT::RVec<float> jet_pt (projected from _collection0.jet_pt)
91 ROOT::RVec<float> jet_eta (projected from _collection0.jet_eta)
92~~~
93 These projections are meta-data only operations and don't involve duplicating the data.
94
95Current limitations of the importer:
96 - No support for trees containing TClonesArray collections
97 - Due to RNTuple currently storing data fully split, "don't split" markers are ignored
98 - Some types are not available in RNTuple. Please refer to the
99 [RNTuple specification](https://github.com/root-project/root/blob/master/tree/ntuple/doc/BinaryFormatSpecification.md)
100 for an overview of all types currently supported.
101*/
102// clang-format on
104public:
105 /// Used to make adjustments to the fields of the output model.
106 using FieldModifier_t = std::function<void(ROOT::RFieldBase &)>;
107
108 /// Summary printed after the RNTuple writer commits (footer, streamer info, last cluster).
110 std::uint64_t fCompressedPayloadBytes = 0; ///< Sealed column page blobs (RPageSinkFile.szWritePayload)
111 std::uint64_t fUncompressedPageBytes = 0; ///< Logical page bytes before compression (RPageSinkFile.szZip)
112 std::uint64_t fEntries = 0;
113 std::uint64_t fFileBytesOnDisk = 0; ///< Destination TFile size after commit (matches ls -lh)
114 };
115
116 /// Used to report every ~100 MB of compressed page payload, and at the end about the status of the import.
118 public:
119 virtual ~RProgressCallback() = default;
120 void operator()(std::uint64_t nbytesWritten, std::uint64_t neventsWritten)
121 {
123 }
124 virtual void Call(std::uint64_t nbytesWritten, std::uint64_t neventsWritten) = 0;
125 virtual void Finish(const RImportReport &report) = 0;
126 };
127
128private:
130 RImportBranch() = default;
135 std::string fBranchName; ///< Top-level branch name from the input TTree
136 std::unique_ptr<unsigned char[]> fBranchBuffer; ///< The destination of SetBranchAddress() for `fBranchName`
137 };
138
140 RImportField() = default;
141 ~RImportField() = default;
146
147 /// The field is kept during schema preparation and transferred to the fModel before the writing starts
149 std::unique_ptr<ROOT::RFieldBase::RValue> fValue; ///< Set if a value is generated, only for transformed fields
150 void *fFieldBuffer = nullptr; ///< Usually points to the corresponding RImportBranch::fBranchBuffer but not always
151 };
152
153 /// Base class to perform data transformations from TTree branches to RNTuple fields if necessary
155 std::size_t fImportBranchIdx = 0;
156 std::size_t fImportFieldIdx = 0;
157
162 virtual ~RImportTransformation() = default;
164 };
165
166 /// When the schema is set up and the import started, it needs to be reset before the next Import() call
167 /// can start. This RAII guard ensures that ResetSchema is called.
178
179 /// Leaf count arrays require special treatment. They are translated into untyped collections of untyped records.
180 /// This class does the bookkeeping of the sub-schema for these collections.
187 std::string fFieldName; ///< name of the untyped collection, e.g. `_collection0`, `_collection1`, etc.
188 /// Stores count leaf GetMaximum() to create large enough buffers for the array leafs.
189 /// Uses Int_t because that is the return type if TLeaf::GetMaximum().
191 /// The number of elements for the collection for a particular event. Used as a destination for SetBranchAddress()
192 /// of the count leaf
193 std::unique_ptr<Int_t> fCountVal;
194 /// The leafs of the array as we encounter them traversing the TTree schema.
195 /// Eventually, the fields are moved as leaves to an untyped collection of untyped records that in turn
196 /// is attached to the RNTuple model.
197 std::vector<std::unique_ptr<ROOT::RFieldBase>> fLeafFields;
198 std::vector<size_t> fLeafBranchIndexes; ///< Points to the correspondings leaf branches in fImportBranches
200 nullptr; ///< Points to the item field of the untyped collection field in the model.
201 std::vector<unsigned char> fFieldBuffer; ///< The collection field memory representation. Bound to the entry.
202 /// Cached after Freeze() so Import() does not reallocate GetConstSubfields() on every entry.
203 std::size_t fSizeOfRecord = 0;
204 struct RPackedLeaf {
205 std::size_t fOffset = 0;
206 std::size_t fValueSize = 0;
207 std::size_t fImportBranchIdx = 0;
208 };
209 std::vector<RPackedLeaf> fPackedLeaves;
210 };
211
212 /// Transform a NULL terminated C string branch into an `std::string` field
218
219 RNTupleImporter() = default;
220
221 std::unique_ptr<TFile> fSourceFile;
223
224 std::string fDestFileName;
225 std::string fNTupleName;
226 std::unique_ptr<TFile> fDestFile;
228
229 /// Whether or not dot characters in branch names should be converted to underscores. If this option is not set and a
230 /// branch with a '.' is encountered, the importer will throw an exception.
232
233 /// The maximum number of entries to import. When this value is -1 (default), import all entries.
234 std::int64_t fMaxEntries = -1;
235
236 /// No standard output, conversely if set to false, schema information and progress is printed.
237 bool fIsQuiet = false;
239 std::unique_ptr<RProgressCallback> fProgressCallback;
241
242 std::unique_ptr<ROOT::RNTupleModel> fModel;
243 std::unique_ptr<ROOT::REntry> fEntry;
244 std::vector<RImportBranch> fImportBranches;
245 std::vector<RImportField> fImportFields;
246 /// Maps the count leaf to the information about the corresponding untyped collection
247 std::map<std::string, RImportLeafCountCollection> fLeafCountCollections;
248 /// The list of transformations to be performed for every entry
249 std::vector<std::unique_ptr<RImportTransformation>> fImportTransformations;
250
252
253 void ResetSchema();
254 /// Sets up the connection from TTree branches to RNTuple fields, including initialization of the memory
255 /// buffers used for reading and writing.
257 void ReportSchema();
258
259public:
264 ~RNTupleImporter() = default;
265
266 /// Opens the input file for reading and the output file for writing (update).
267 static std::unique_ptr<RNTupleImporter>
268 Create(std::string_view sourceFileName, std::string_view treeName, std::string_view destFileName);
269
270 /// Directly uses the provided tree and opens the output file for writing (update).
271 static std::unique_ptr<RNTupleImporter> Create(TTree *sourceTree, std::string_view destFileName);
272
274 void SetWriteOptions(const ROOT::RNTupleWriteOptions &options) { fWriteOptions = options; }
275 void SetNTupleName(const std::string &name) { fNTupleName = name; }
276 void SetMaxEntries(std::uint64_t maxEntries) { fMaxEntries = maxEntries; };
277
278 /// Whereas branch names may contain dots, RNTuple field names may not. By setting this option, dot characters are
279 /// automatically converted into underscores to prevent the importer from throwing an exception.
281
282 /// Whether or not information and progress is printed to stdout.
283 void SetIsQuiet(bool value) { fIsQuiet = value; }
284
285 /// Metrics from the most recent Import() call (always filled, even when quiet).
287
288 /// Add custom method to adjust column representations. Will be called for every field of the frozen model
289 /// before it is attached to the page sink
291
292 /// Import works in two steps:
293 /// 1. PrepareSchema() calls SetBranchAddress() on all the TTree branches and creates the corresponding RNTuple
294 /// fields and the model
295 /// 2. An event loop reads every entry from the TTree, applies transformations where necessary, and writes the
296 /// output entry to the RNTuple.
297 void Import();
298}; // class RNTupleImporter
299
300} // namespace Experimental
301} // namespace ROOT
302
303#endif
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t modifier
char name[80]
Definition TGX11.cxx:142
Used to report every ~100 MB of compressed page payload, and at the end about the status of the impor...
virtual void Finish(const RImportReport &report)=0
void operator()(std::uint64_t nbytesWritten, std::uint64_t neventsWritten)
virtual void Call(std::uint64_t nbytesWritten, std::uint64_t neventsWritten)=0
Converts a TTree into an RNTuple.
std::function< void(ROOT::RFieldBase &)> FieldModifier_t
Used to make adjustments to the fields of the output model.
bool fConvertDotsInBranchNames
Whether or not dot characters in branch names should be converted to underscores.
std::unique_ptr< ROOT::REntry > fEntry
std::int64_t fMaxEntries
The maximum number of entries to import. When this value is -1 (default), import all entries.
std::map< std::string, RImportLeafCountCollection > fLeafCountCollections
Maps the count leaf to the information about the corresponding untyped collection.
RNTupleImporter & operator=(const RNTupleImporter &other)=delete
std::unique_ptr< ROOT::RNTupleModel > fModel
std::vector< RImportBranch > fImportBranches
ROOT::RResult< void > InitDestination(std::string_view destFileName)
void SetNTupleName(const std::string &name)
RNTupleImporter(const RNTupleImporter &other)=delete
void SetConvertDotsInBranchNames(bool value)
Whereas branch names may contain dots, RNTuple field names may not.
RNTupleImporter & operator=(RNTupleImporter &&other)=delete
void SetWriteOptions(const ROOT::RNTupleWriteOptions &options)
static std::unique_ptr< RNTupleImporter > Create(std::string_view sourceFileName, std::string_view treeName, std::string_view destFileName)
Opens the input file for reading and the output file for writing (update).
std::unique_ptr< RProgressCallback > fProgressCallback
RNTupleImporter(RNTupleImporter &&other)=delete
void Import()
Import works in two steps:
RImportReport GetLastImportReport() const
Metrics from the most recent Import() call (always filled, even when quiet).
ROOT::RNTupleWriteOptions fWriteOptions
void SetFieldModifier(const FieldModifier_t &modifier)
Add custom method to adjust column representations.
bool fIsQuiet
No standard output, conversely if set to false, schema information and progress is printed.
std::vector< RImportField > fImportFields
void SetIsQuiet(bool value)
Whether or not information and progress is printed to stdout.
void SetMaxEntries(std::uint64_t maxEntries)
ROOT::RNTupleWriteOptions GetWriteOptions() const
std::vector< std::unique_ptr< RImportTransformation > > fImportTransformations
The list of transformations to be performed for every entry.
ROOT::RResult< void > PrepareSchema()
Sets up the connection from TTree branches to RNTuple fields, including initialization of the memory ...
A field translates read and write calls from/to underlying columns to/from tree values.
Common user-tunable settings for storing RNTuples.
The field for an untyped record.
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:222
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
A TTree represents a columnar dataset.
Definition TTree.h:89
Transform a NULL terminated C string branch into an std::string field.
RResult< void > Transform(const RImportBranch &branch, RImportField &field) final
std::string fBranchName
Top-level branch name from the input TTree.
RImportBranch(const RImportBranch &other)=delete
RImportBranch & operator=(RImportBranch &&other)=default
RImportBranch & operator=(const RImportBranch &other)=delete
std::unique_ptr< unsigned char[]> fBranchBuffer
The destination of SetBranchAddress() for fBranchName
RImportBranch(RImportBranch &&other)=default
void * fFieldBuffer
Usually points to the corresponding RImportBranch::fBranchBuffer but not always.
std::unique_ptr< ROOT::RFieldBase::RValue > fValue
Set if a value is generated, only for transformed fields.
ROOT::RFieldBase * fField
The field is kept during schema preparation and transferred to the fModel before the writing starts.
RImportField(RImportField &&other)=default
RImportField & operator=(const RImportField &other)=delete
RImportField & operator=(RImportField &&other)=default
RImportField(const RImportField &other)=delete
When the schema is set up and the import started, it needs to be reset before the next Import() call ...
RImportGuard & operator=(const RImportGuard &)=delete
RImportGuard & operator=(RImportGuard &&)=delete
std::string fFieldName
name of the untyped collection, e.g.
Int_t fMaxLength
Stores count leaf GetMaximum() to create large enough buffers for the array leafs.
std::vector< unsigned char > fFieldBuffer
The collection field memory representation.
ROOT::RRecordField * fRecordField
Points to the item field of the untyped collection field in the model.
RImportLeafCountCollection & operator=(const RImportLeafCountCollection &other)=delete
std::vector< std::unique_ptr< ROOT::RFieldBase > > fLeafFields
The leafs of the array as we encounter them traversing the TTree schema.
RImportLeafCountCollection(RImportLeafCountCollection &&other)=default
RImportLeafCountCollection(const RImportLeafCountCollection &other)=delete
std::size_t fSizeOfRecord
Cached after Freeze() so Import() does not reallocate GetConstSubfields() on every entry.
RImportLeafCountCollection & operator=(RImportLeafCountCollection &&other)=default
std::vector< size_t > fLeafBranchIndexes
Points to the correspondings leaf branches in fImportBranches.
std::unique_ptr< Int_t > fCountVal
The number of elements for the collection for a particular event.
Summary printed after the RNTuple writer commits (footer, streamer info, last cluster).
std::uint64_t fCompressedPayloadBytes
Sealed column page blobs (RPageSinkFile.szWritePayload)
std::uint64_t fUncompressedPageBytes
Logical page bytes before compression (RPageSinkFile.szZip)
std::uint64_t fFileBytesOnDisk
Destination TFile size after commit (matches ls -lh)
Base class to perform data transformations from TTree branches to RNTuple fields if necessary.
virtual RResult< void > Transform(const RImportBranch &branch, RImportField &field)=0
RImportTransformation(std::size_t branchIdx, std::size_t fieldIdx)