Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleImporter.cxx
Go to the documentation of this file.
1/// \file RNTupleImporter.cxx
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#include <ROOT/RError.hxx>
16#include <ROOT/RField.hxx>
18#include <ROOT/RNTupleTypes.hxx>
21#include <ROOT/RPageSinkBuf.hxx>
22#include <ROOT/RPageStorage.hxx>
24#include <string_view>
25
26#include <TBranch.h>
27#include <TChain.h>
28#include <TClass.h>
29#include <TDataType.h>
30#include <TLeaf.h>
31#include <TLeafC.h>
32#include <TLeafElement.h>
33#include <TLeafObject.h>
34
35#include <cassert>
36#include <cstdint>
37#include <cstring>
38#include <iomanip>
39#include <iostream>
40#include <sstream>
41#include <utility>
42
43namespace {
44
45std::string FormatBinarySize(std::uint64_t nbytes)
46{
47 const char *suffix = "B";
48 double v = static_cast<double>(nbytes);
49 if (v >= 1024.0 * 1024.0 * 1024.0) {
50 v /= 1024.0 * 1024.0 * 1024.0;
51 suffix = "GiB";
52 } else if (v >= 1024.0 * 1024.0) {
53 v /= 1024.0 * 1024.0;
54 suffix = "MiB";
55 } else if (v >= 1024.0) {
56 v /= 1024.0;
57 suffix = "KiB";
58 }
59 std::ostringstream os;
60 os.setf(std::ios::fixed);
61 os.precision(v >= 100.0 ? 0 : (v >= 10.0 ? 1 : 2));
62 os << v << ' ' << suffix;
63 return os.str();
64}
65
66class RDefaultProgressCallback : public ROOT::Experimental::RNTupleImporter::RProgressCallback {
67private:
68 static constexpr std::uint64_t gUpdateFrequencyBytes = 100 * 1000 * 1000; // report every 100 MB payload
69 std::uint64_t fNbytesNext = gUpdateFrequencyBytes;
70
71public:
72 ~RDefaultProgressCallback() override {}
73 void Call(std::uint64_t nbytesWritten, std::uint64_t neventsWritten) final
74 {
75 // Report if more than 100 MB of compressed page payload were written since the last status update.
76 if (nbytesWritten < fNbytesNext)
77 return;
78 std::cout << "Wrote " << nbytesWritten / 1000 / 1000 << "MB payload, " << neventsWritten << " entries\n";
79 fNbytesNext += gUpdateFrequencyBytes;
80 if (nbytesWritten > fNbytesNext) {
81 // If we already passed the next threshold, increase by a sensible amount.
82 fNbytesNext = nbytesWritten + gUpdateFrequencyBytes;
83 }
84 }
85
87 {
88 std::cout << "Done: " << report.fEntries << " entries\n";
89 std::cout << " compressed page payload: " << FormatBinarySize(report.fCompressedPayloadBytes) << '\n';
90 std::cout << " on disk: " << FormatBinarySize(report.fFileBytesOnDisk) << '\n';
91 if (report.fFileBytesOnDisk > report.fCompressedPayloadBytes) {
92 const auto overhead = report.fFileBytesOnDisk - report.fCompressedPayloadBytes;
93 std::cout << " file overhead (approx): " << FormatBinarySize(overhead)
94 << " (page lists, footer, streamer info, ROOT keys)\n";
95 }
96 if (report.fUncompressedPageBytes > 0 && report.fCompressedPayloadBytes > 0) {
97 const double ratio =
98 static_cast<double>(report.fCompressedPayloadBytes) / static_cast<double>(report.fUncompressedPageBytes);
99 std::cout << " page compression ratio: " << std::fixed << std::setprecision(3) << ratio
100 << " (payload / uncompressed pages)\n";
101 }
102 }
103};
104
105} // anonymous namespace
106
109{
110 *reinterpret_cast<std::string *>(field.fFieldBuffer) = reinterpret_cast<const char *>(branch.fBranchBuffer.get());
111 return RResult<void>::Success();
112}
113
114std::unique_ptr<ROOT::Experimental::RNTupleImporter>
116 std::string_view destFileName)
117{
118 auto importer = std::unique_ptr<RNTupleImporter>(new RNTupleImporter());
119 importer->fNTupleName = treeName;
120 importer->fSourceFile = std::unique_ptr<TFile>(TFile::Open(std::string(sourceFileName).c_str()));
121 if (!importer->fSourceFile || importer->fSourceFile->IsZombie()) {
122 throw RException(R__FAIL("cannot open source file " + std::string(sourceFileName)));
123 }
124
125 importer->fSourceTree = importer->fSourceFile->Get<TTree>(std::string(treeName).c_str());
126 if (!importer->fSourceTree) {
127 throw RException(R__FAIL("cannot read TTree " + std::string(treeName) + " from " + std::string(sourceFileName)));
128 }
129
130 // If we have IMT enabled, its best use is for parallel page compression
131 importer->fSourceTree->SetImplicitMT(false);
132 auto result = importer->InitDestination(destFileName);
133
134 if (!result)
136
137 return importer;
138}
139
140std::unique_ptr<ROOT::Experimental::RNTupleImporter>
142{
143 auto importer = std::unique_ptr<RNTupleImporter>(new RNTupleImporter());
144
145 if (sourceTree->IsA() == TChain::Class() && std::strcmp(sourceTree->GetName(), "") == 0) {
146 if (sourceTree->LoadTree(0) != 0)
147 throw RException(R__FAIL("failure retrieving first tree from provided chain"));
148 importer->fNTupleName = sourceTree->GetTree()->GetName();
149 } else {
150 importer->fNTupleName = sourceTree->GetName();
151 }
152
153 importer->fSourceTree = sourceTree;
154
155 // If we have IMT enabled, its best use is for parallel page compression
156 importer->fSourceTree->SetImplicitMT(false);
157 auto result = importer->InitDestination(destFileName);
158
159 if (!result)
161
162 return importer;
163}
164
166{
168 fDestFile = std::unique_ptr<TFile>(TFile::Open(fDestFileName.c_str(), "UPDATE"));
169 if (!fDestFile || fDestFile->IsZombie()) {
170 return R__FAIL("cannot open dest file " + std::string(fDestFileName));
171 }
172
173 return RResult<void>::Success();
174}
175
177{
178 for (const auto &f : fImportFields) {
179 std::cout << "Importing '" << f.fField->GetFieldName() << "' [" << f.fField->GetTypeName() << "]\n";
180 }
182 std::cout << "Importing (projected) '" << f->GetFieldName() << "' [" << f->GetTypeName() << "]\n";
183 }
184}
185
195
197{
198 ResetSchema();
199
200 // Browse through all branches and their leaves, create corresponding fields and prepare the memory buffers for
201 // reading and writing. Usually, reading and writing share the same memory buffer, i.e. the object is read from TTree
202 // and written as-is to the RNTuple. There are exceptions, e.g. for leaf count arrays and C strings.
204 assert(b);
205 const auto firstLeaf = static_cast<TLeaf *>(b->GetListOfLeaves()->First());
207
208 const bool isLeafList = b->GetNleaves() > 1;
209 const bool isCountLeaf = firstLeaf->IsRange(); // A leaf storing the number of elements of a leaf count array
210 const bool isClass = (firstLeaf->IsA() == TLeafElement::Class()); // STL or user-defined class
211 if (isLeafList && isClass)
212 return R__FAIL("unsupported: classes in leaf list, branch " + std::string(b->GetName()));
213 if (isLeafList && isCountLeaf)
214 return R__FAIL("unsupported: count leaf arrays in leaf list, branch " + std::string(b->GetName()));
215
216 // Only plain leafs with type identifies 'C' are C strings. Otherwise, they are char arrays.
217 // We use GetLeafCounter instead of GetLeafCount and GetLenStatic because the latter don't distinguish between
218 // char arrays and C strings.
220 const bool isCString = !isLeafList && (firstLeaf->IsA() == TLeafC::Class()) &&
221 (!firstLeaf->GetLeafCounter(firstLeafCountval)) && (firstLeafCountval == 1);
222
223 if (isCountLeaf) {
224 // This is a count leaf. We expect that this is not part of a leaf list. We also expect that the
225 // leaf count comes before any array leaves that use it.
226 // Count leaf branches do not end up as (physical) fields but they trigger the creation of an untyped
227 // collection, together the collection mode.
229 c.fMaxLength = firstLeaf->GetMaximum();
230 c.fCountVal = std::make_unique<Int_t>(); // count leafs are integers
231 // Casting to void * makes it work for both Int_t and UInt_t
232 fSourceTree->SetBranchAddress(b->GetName(), static_cast<void *>(c.fCountVal.get()));
233 fLeafCountCollections.emplace(firstLeaf->GetName(), std::move(c));
234 continue;
235 }
236
237 std::size_t branchBufferSize = 0; // Size of the memory location into which TTree reads the events' branch data
238 // For leaf lists, every leaf translates into a sub field of an untyped RNTuple record
239 std::vector<std::unique_ptr<ROOT::RFieldBase>> recordItems;
240 // For leaf count arrays, we expect to find a single leaf; we don't add a field right away but only
241 // later through a projection
242 bool isLeafCountArray = false;
243 for (auto l : TRangeDynCast<TLeaf>(b->GetListOfLeaves())) {
244 if (l->IsA() == TLeafObject::Class()) {
245 return R__FAIL("unsupported: TObject branches, branch: " + std::string(b->GetName()));
246 }
247
248 // We don't use GetLeafCounter() because it relies on the correct format of the leaf title.
249 // There are files in the public where the title is broken (empty).
250 Int_t countval = l->GetLenStatic();
251 auto *countleaf = l->GetLeafCount();
252 const bool isFixedSizeArray = !isCString && (countleaf == nullptr) && (countval > 1);
253 isLeafCountArray = (countleaf != nullptr);
254
255 // The base case for branches with fundamental, single numerical types.
256 // For other types of branches, different field names or types are necessary,
257 // which is determined below.
258 std::string fieldName = b->GetName();
259 std::string fieldType = l->GetTypeName();
260
261 if (isLeafList)
262 fieldName = l->GetName();
263
264 if (isCString)
265 fieldType = "std::string";
266
267 if (isClass)
268 fieldType = b->GetClassName();
269
271 fieldType = "std::array<" + fieldType + "," + std::to_string(countval) + ">";
272
274 // Replace any occurrence of a dot ('.') with an underscore.
275 std::replace(fieldName.begin(), fieldName.end(), '.', '_');
276 }
277
280 if (!fieldOrError)
282 auto field = fieldOrError.Unwrap();
283 if (isCString) {
284 branchBufferSize = l->GetMaximum();
285 f.fValue = std::make_unique<ROOT::RFieldBase::RValue>(field->CreateValue());
286 f.fFieldBuffer = f.fValue->GetPtr<void>().get();
287 fImportTransformations.emplace_back(
288 std::make_unique<RCStringTransformation>(fImportBranches.size(), fImportFields.size()));
289 } else {
290 if (isClass) {
291 // For classes, the branch buffer contains a pointer to object, which gets instantiated by TTree upon
292 // calling SetBranchAddress()
293 branchBufferSize = sizeof(void *) * countval;
294 } else if (isLeafCountArray) {
295 branchBufferSize = fLeafCountCollections[countleaf->GetName()].fMaxLength * field->GetValueSize();
296 } else {
297 branchBufferSize = l->GetOffset() + field->GetValueSize();
298 }
299 }
300 f.fField = field.get();
301
302 if (isLeafList) {
303 recordItems.emplace_back(std::move(field));
304 } else if (isLeafCountArray) {
305 const std::string countleafName = countleaf->GetName();
306 fLeafCountCollections[countleafName].fLeafFields.emplace_back(std::move(field));
307 fLeafCountCollections[countleafName].fLeafBranchIndexes.emplace_back(fImportBranches.size());
308 R__ASSERT(b->GetListOfLeaves()->GetEntries() == 1);
309 break;
310 } else {
311 fModel->AddField(std::move(field));
312 fImportFields.emplace_back(std::move(f));
313 }
314 }
315 if (!recordItems.empty()) {
316 auto recordField = std::make_unique<ROOT::RRecordField>(b->GetName(), std::move(recordItems));
318 f.fField = recordField.get();
319 fImportFields.emplace_back(std::move(f));
320 fModel->AddField(std::move(recordField));
321 }
322
324 ib.fBranchName = b->GetName();
325 ib.fBranchBuffer = std::make_unique<unsigned char[]>(branchBufferSize);
326 if (isClass) {
327 auto klass = TClass::GetClass(b->GetClassName());
328 if (!klass) {
329 return R__FAIL("unable to load class " + std::string(b->GetClassName()) + " for branch " +
330 std::string(b->GetName()));
331 }
332 auto ptrBuf = reinterpret_cast<void **>(ib.fBranchBuffer.get());
333 fSourceTree->SetBranchAddress(b->GetName(), ptrBuf, klass, EDataType::kOther_t, true /* isptr*/);
334 } else {
335 fSourceTree->SetBranchAddress(b->GetName(), reinterpret_cast<void *>(ib.fBranchBuffer.get()));
336 }
337
338 // If the TTree branch type and the RNTuple field type match, use the branch read buffer as RNTuple write buffer
339 if (!isLeafCountArray && !fImportFields.back().fFieldBuffer) {
340 fImportFields.back().fFieldBuffer =
341 isClass ? *reinterpret_cast<void **>(ib.fBranchBuffer.get()) : ib.fBranchBuffer.get();
342 }
343
344 fImportBranches.emplace_back(std::move(ib));
345 }
346
347 int iLeafCountCollection = 0;
348 for (auto &p : fLeafCountCollections) {
349 // We want to capture this variable, which is not possible with a
350 // structured binding in C++17. Explicitly defining a variable works.
351 auto countLeafName = p.first;
352 auto &c = p.second;
353
354 c.fFieldName = "_collection" + std::to_string(iLeafCountCollection);
355 auto recordField = std::make_unique<ROOT::RRecordField>("_0", std::move(c.fLeafFields));
356 auto collectionField = ROOT::RVectorField::CreateUntyped(c.fFieldName, std::move(recordField));
357 c.fRecordField = static_cast<RRecordField *>(collectionField->GetMutableSubfields()[0]);
358 fModel->AddField(std::move(collectionField));
359
360 // Add projected fields for all leaf count arrays
361 for (const auto leaf : c.fRecordField->GetConstSubfields()) {
362 const auto name = leaf->GetFieldName();
363 auto projectedField =
364 ROOT::RFieldBase::Create(name, "ROOT::VecOps::RVec<" + leaf->GetTypeName() + ">").Unwrap();
365 fModel->AddProjectedField(std::move(projectedField), [&name, &c](const std::string &fieldName) {
366 if (fieldName == name)
367 return c.fFieldName;
368 else
369 return c.fFieldName + "._0." + name;
370 });
371 }
372
374 // Replace any occurrenceof a dot ('.') in the count leaf name with an underscore.
375 std::replace(countLeafName.begin(), countLeafName.end(), '.', '_');
376 }
377
378 // Add projected fields for count leaf
379 auto projectedField = ROOT::RFieldBase::Create(countLeafName, "ROOT::RNTupleCardinality<std::uint32_t>").Unwrap();
380 fModel->AddProjectedField(std::move(projectedField), [&c](const std::string &) { return c.fFieldName; });
381
383 }
384
385 if (fFieldModifier) {
386 for (auto &field : fModel->GetMutableFieldZero()) {
388 }
389 }
390
391 fModel->Freeze();
392
393 fEntry = fModel->CreateBareEntry();
394 for (const auto &f : fImportFields) {
395 fEntry->BindRawPtr(f.fField->GetFieldName(), f.fFieldBuffer);
396 }
397 for (auto &[_, c] : fLeafCountCollections) {
398 fEntry->BindRawPtr<void>(c.fFieldName, &c.fFieldBuffer);
399 c.fSizeOfRecord = c.fRecordField->GetValueSize();
400 const auto subfields = c.fRecordField->GetConstSubfields();
401 const auto &offsets = c.fRecordField->GetOffsets();
402 R__ASSERT(subfields.size() == c.fLeafBranchIndexes.size());
403 assert(c.fPackedLeaves.empty());
404 c.fPackedLeaves.reserve(subfields.size());
405 for (std::size_t l = 0; l < subfields.size(); ++l) {
407 packed.fOffset = offsets[l];
408 packed.fValueSize = subfields[l]->GetValueSize();
409 packed.fImportBranchIdx = c.fLeafBranchIndexes[l];
410 c.fPackedLeaves.push_back(packed);
411 }
412 c.fFieldBuffer.reserve(static_cast<std::size_t>(c.fMaxLength) * c.fSizeOfRecord);
413 }
414
415 if (!fIsQuiet)
416 ReportSchema();
417
418 return RResult<void>::Success();
419}
420
422{
424 // Extract the ntuple name and directory name
425 auto lastSlash = fNTupleName.find_last_of('/');
426 std::string ntupleName = fNTupleName;
427 if (lastSlash != std::string::npos) {
428 // Create the directory in the output file if it does not exist
429 auto dirName = fNTupleName.substr(0, lastSlash);
430 ntupleName = fNTupleName.substr(lastSlash + 1);
431 targetDir = fDestFile->mkdir(dirName.c_str(), "", true);
432 if (!targetDir) {
433 throw RException(R__FAIL("Failed to create directory " + dirName + " in file " + fDestFileName));
434 }
435 }
436
437 if (targetDir->FindKey(ntupleName.c_str()) != nullptr) {
438 throw RException(R__FAIL("Key '" + fNTupleName + "' already exists in file " + fDestFileName));
439 }
440
442
443 std::unique_ptr<ROOT::Internal::RPageSink> sink =
444 std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, *targetDir, fWriteOptions);
445 sink->GetMetrics().Enable();
446 auto ctrZippedBytes = sink->GetMetrics().GetCounter("RPageSinkFile.szWritePayload");
447 auto ctrUnzipBytes = sink->GetMetrics().GetCounter("RPageSinkFile.szZip");
448
450 sink = std::make_unique<ROOT::Internal::RPageSinkBuf>(std::move(sink));
451 }
452
453 fProgressCallback = fIsQuiet ? nullptr : std::make_unique<RDefaultProgressCallback>();
454
456
457 if (fMaxEntries >= 0 && fMaxEntries < nEntries) {
459 }
460
461 std::uint64_t payloadBytes = 0;
462 std::uint64_t unzipBytes = 0;
463 {
464 auto ntplWriter = ROOT::Internal::CreateRNTupleWriter(std::move(fModel), std::move(sink));
465 // The guard needs to be destructed before the writer goes out of scope
467
468 for (decltype(nEntries) i = 0; i < nEntries; ++i) {
470
471 for (auto &[_, c] : fLeafCountCollections) {
472 const auto nItems = static_cast<std::size_t>(*c.fCountVal);
473 const auto sizeOfRecord = c.fSizeOfRecord;
474 c.fFieldBuffer.resize(sizeOfRecord * nItems);
475
476 for (const auto &leaf : c.fPackedLeaves) {
477 const auto *src = fImportBranches[leaf.fImportBranchIdx].fBranchBuffer.get();
478 auto *dst = c.fFieldBuffer.data() + leaf.fOffset;
479 for (std::size_t j = 0; j < nItems; ++j) {
480 memcpy(dst + j * sizeOfRecord, src + j * leaf.fValueSize, leaf.fValueSize);
481 }
482 }
483 }
484
485 for (auto &t : fImportTransformations) {
486 auto result = t->Transform(fImportBranches[t->fImportBranchIdx], fImportFields[t->fImportFieldIdx]);
487 if (!result)
489 }
490
491 ntplWriter->Fill(*fEntry);
492
494 fProgressCallback->Call(ctrZippedBytes->GetValueAsInt(), i);
495 }
496
497 payloadBytes = ctrZippedBytes->GetValueAsInt();
498 if (ctrUnzipBytes)
499 unzipBytes = ctrUnzipBytes->GetValueAsInt();
500 } // ntplWriter commits footer / streamer info / last cluster here
501
502 fDestFile->Flush();
504 report.fCompressedPayloadBytes = payloadBytes;
505 report.fUncompressedPageBytes = unzipBytes;
506 report.fEntries = nEntries;
507 report.fFileBytesOnDisk = static_cast<std::uint64_t>(fDestFile->GetSize());
510 fProgressCallback->Finish(report);
511}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:326
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
double * dst
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kOther_t
Definition TDataType.h:32
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:130
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
char name[80]
Definition TGX11.cxx:142
#define _(A, B)
Definition cfortran.h:108
Used to report every ~100 MB of compressed page payload, and at the end about the status of the impor...
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.
std::unique_ptr< ROOT::RNTupleModel > fModel
std::vector< RImportBranch > fImportBranches
ROOT::RResult< void > InitDestination(std::string_view destFileName)
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
void Import()
Import works in two steps:
ROOT::RNTupleWriteOptions fWriteOptions
bool fIsQuiet
No standard output, conversely if set to false, schema information and progress is printed.
std::vector< RImportField > fImportFields
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 ...
ROOT::RFieldZero & GetFieldZero()
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
std::vector< const RFieldBase * > GetConstSubfields() const
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
const_iterator begin() const
const_iterator end() const
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
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
static TClass * Class()
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2999
Describe directory structure in memory.
Definition TDirectory.h:45
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:3802
static TClass * Class()
static TClass * Class()
static TClass * Class()
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
TClass * IsA() const override
Definition TLine.h:79
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:461
A TTree represents a columnar dataset.
Definition TTree.h:89
virtual Int_t SetBranchAddress(const char *bname, void *add, TBranch **ptr, TClass *realClass, EDataType datatype, bool isptr, bool suppressMissingBranchError)
Definition TTree.cxx:8818
virtual Int_t GetEntry(Long64_t entry, Int_t getall=0)
Read all branches of entry and return total number of bytes read.
Definition TTree.cxx:5745
virtual Long64_t GetEntries() const
Definition TTree.h:518
virtual TObjArray * GetListOfBranches()
Definition TTree.h:583
std::unique_ptr< RNTupleWriter > CreateRNTupleWriter(std::unique_ptr< ROOT::RNTupleModel > model, std::unique_ptr< Internal::RPageSink > sink)
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
double ratio(double numerator, double denominator)
Definition MathFuncs.h:106
RResult< void > Transform(const RImportBranch &branch, RImportField &field) final
ROOT::RFieldBase * fField
The field is kept during schema preparation and transferred to the fModel before the writing starts.
When the schema is set up and the import started, it needs to be reset before the next Import() call ...
Int_t fMaxLength
Stores count leaf GetMaximum() to create large enough buffers for the array leafs.
Summary printed after the RNTuple writer commits (footer, streamer info, last cluster).
TLine l
Definition textangle.C:4