Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleProcessor.cxx
Go to the documentation of this file.
1/// \file RNTupleProcessor.cxx
2/// \ingroup NTuple ROOT7
3/// \author Florine de Geus <florine.de.geus@cern.ch>
4/// \date 2024-03-26
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
17
18#include <ROOT/RFieldBase.hxx>
19
20namespace {
22void EnsureUniqueNTupleNames(const std::vector<RNTupleOpenSpec> &ntuples)
23{
24 std::unordered_set<std::string> uniqueNTupleNames;
25 for (const auto &ntuple : ntuples) {
26 auto res = uniqueNTupleNames.emplace(ntuple.fNTupleName);
27 if (!res.second) {
28 throw ROOT::RException(R__FAIL("horizontal joining of RNTuples with the same name is not allowed"));
29 }
30 }
31}
32} // anonymous namespace
33
34std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
35ROOT::Experimental::RNTupleProcessor::Create(const RNTupleOpenSpec &ntuple, std::unique_ptr<RNTupleModel> model)
36{
37 return Create(ntuple, ntuple.fNTupleName, std::move(model));
38}
39
40std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
42 std::unique_ptr<RNTupleModel> model)
43{
44 return std::unique_ptr<RNTupleSingleProcessor>(new RNTupleSingleProcessor(ntuple, processorName, std::move(model)));
45}
46
47std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
49 std::unique_ptr<RNTupleModel> model)
50{
51 if (ntuples.empty())
52 throw RException(R__FAIL("at least one RNTuple must be provided"));
53
54 return CreateChain(ntuples, ntuples[0].fNTupleName, std::move(model));
55}
56
57std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
59 std::string_view processorName, std::unique_ptr<RNTupleModel> model)
60{
61 if (ntuples.empty())
62 throw RException(R__FAIL("at least one RNTuple must be provided"));
63
64 std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors;
65 innerProcessors.reserve(ntuples.size());
66
67 // If no model is provided, infer it from the first ntuple.
68 if (!model) {
69 auto firstPageSource = Internal::RPageSource::Create(ntuples[0].fNTupleName, ntuples[0].fStorage);
70 firstPageSource->Attach();
71 model = firstPageSource->GetSharedDescriptorGuard()->CreateModel();
72 }
73
74 for (const auto &ntuple : ntuples) {
75 innerProcessors.emplace_back(Create(ntuple, model->Clone()));
76 }
77
78 return CreateChain(std::move(innerProcessors), processorName, std::move(model));
79}
80
81std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
82ROOT::Experimental::RNTupleProcessor::CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
83 std::unique_ptr<RNTupleModel> model)
84{
85 if (innerProcessors.empty())
86 throw RException(R__FAIL("at least one inner processor must be provided"));
87
88 auto processorName = innerProcessors[0]->GetProcessorName();
89 return CreateChain(std::move(innerProcessors), processorName, std::move(model));
90}
91
92std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
93ROOT::Experimental::RNTupleProcessor::CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
94 std::string_view processorName, std::unique_ptr<RNTupleModel> model)
95{
96 if (innerProcessors.empty())
97 throw RException(R__FAIL("at least one inner processor must be provided"));
98
99 // If no model is provided, infer it from the first inner processor.
100 if (!model) {
101 model = innerProcessors[0]->GetModel().Clone();
102 }
103
104 return std::unique_ptr<RNTupleChainProcessor>(
105 new RNTupleChainProcessor(std::move(innerProcessors), processorName, std::move(model)));
106}
107
108std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
110 const std::vector<std::string> &joinFields,
111 std::vector<std::unique_ptr<RNTupleModel>> models)
112{
113 if (ntuples.empty())
114 throw RException(R__FAIL("at least one RNTuple must be provided"));
115 return CreateJoin(ntuples, joinFields, ntuples[0].fNTupleName, std::move(models));
116}
117
118std::unique_ptr<ROOT::Experimental::RNTupleProcessor>
120 const std::vector<std::string> &joinFields,
121 std::string_view processorName,
122 std::vector<std::unique_ptr<RNTupleModel>> models)
123{
124 if (ntuples.size() < 1)
125 throw RException(R__FAIL("at least one RNTuple must be provided"));
126
127 if (models.size() > 0 && models.size() != ntuples.size()) {
128 throw RException(R__FAIL("number of provided models must match number of specified ntuples"));
129 }
130
131 if (joinFields.size() > 4) {
132 throw RException(R__FAIL("a maximum of four join fields is allowed"));
133 }
134
135 if (std::set(joinFields.begin(), joinFields.end()).size() < joinFields.size()) {
136 throw RException(R__FAIL("join fields must be unique"));
137 }
138
139 // TODO(fdegeus) allow for the provision of aliases for ntuples with the same name, removing the constraint of
140 // uniquely-named ntuples.
142
143 std::unique_ptr<RNTupleJoinProcessor> processor;
144 if (models.size() > 0) {
145 processor = std::unique_ptr<RNTupleJoinProcessor>(
146 new RNTupleJoinProcessor(ntuples[0], processorName, std::move(models[0])));
147 } else {
148 processor = std::unique_ptr<RNTupleJoinProcessor>(new RNTupleJoinProcessor(ntuples[0], processorName));
149 }
150
151 for (unsigned i = 1; i < ntuples.size(); ++i) {
152 if (models.size() > 0)
153 processor->AddAuxiliary(ntuples[i], joinFields, std::move(models[i]));
154 else
155 processor->AddAuxiliary(ntuples[i], joinFields);
156 }
157
158 processor->SetJoinFieldTokens(joinFields);
159 processor->ConnectFields();
160
161 return processor;
162}
163
165 REntry &entry)
166{
167 auto desc = pageSource.GetSharedDescriptorGuard();
168
169 const auto fieldId = desc->FindFieldId(fieldContext.GetProtoField().GetFieldName());
171 throw RException(
172 R__FAIL("field \"" + fieldContext.GetProtoField().GetFieldName() + "\" not found in current RNTuple"));
173 }
174
175 fieldContext.SetConcreteField();
176 fieldContext.fConcreteField->SetOnDiskId(fieldId);
178
179 auto valuePtr = entry.GetPtr<void>(fieldContext.fToken);
180 auto value = fieldContext.fConcreteField->BindValue(valuePtr);
181 entry.UpdateValue(fieldContext.fToken, value);
182}
183
184//------------------------------------------------------------------------------
185
187 std::string_view processorName,
188 std::unique_ptr<RNTupleModel> model)
189 : RNTupleProcessor(processorName, std::move(model)), fNTupleSpec(ntuple)
190{
191 if (!fModel) {
193 fPageSource->Attach();
194 fModel = fPageSource->GetSharedDescriptorGuard()->CreateModel();
195 }
196
197 fModel->Freeze();
198 fEntry = fModel->CreateEntry();
199
200 for (const auto &value : *fEntry) {
201 auto &field = value.GetField();
202 auto token = fEntry->GetToken(field.GetFieldName());
203
204 // If the model has a default entry, use the value pointers from the entry in the entry managed by the
205 // processor. This way, the pointers returned by RNTupleModel::MakeField can be used in the processor loop to
206 // access the corresponding field values.
207 if (!fModel->IsBare()) {
208 auto valuePtr = fModel->GetDefaultEntry().GetPtr<void>(token);
209 fEntry->BindValue(token, valuePtr);
210 }
211
212 auto fieldContext = RFieldContext(field.Clone(field.GetFieldName()), token);
213 fFieldContexts.try_emplace(field.GetFieldName(), std::move(fieldContext));
214 }
215}
216
218{
219 Connect();
220
221 if (entryNumber >= fNEntries)
222 return kInvalidNTupleIndex;
223
224 fEntry->Read(entryNumber);
225
226 fNEntriesProcessed++;
227 fCurrentEntryNumber = entryNumber;
228 return entryNumber;
229}
230
232{
233 for (const auto &value : *fEntry) {
234 auto &field = value.GetField();
235 auto valuePtr = entry.GetPtr<void>(field.GetQualifiedFieldName());
236
237 fEntry->BindValue(field.GetQualifiedFieldName(), valuePtr);
238 }
239}
240
242{
243 // The processor has already been connected.
244 if (fNEntries != kInvalidNTupleIndex)
245 return;
246
247 if (!fPageSource)
248 fPageSource = Internal::RPageSource::Create(fNTupleSpec.fNTupleName, fNTupleSpec.fStorage);
249 fPageSource->Attach();
250 fNEntries = fPageSource->GetNEntries();
251
252 for (auto &[_, fieldContext] : fFieldContexts) {
253 ConnectField(fieldContext, *fPageSource, *fEntry);
254 }
255}
256
257//------------------------------------------------------------------------------
258
260 std::vector<std::unique_ptr<RNTupleProcessor>> processors, std::string_view processorName,
261 std::unique_ptr<RNTupleModel> model)
262 : RNTupleProcessor(processorName, std::move(model)), fInnerProcessors(std::move(processors))
263{
265
266 fModel->Freeze();
267 fEntry = fModel->CreateEntry();
268
269 for (const auto &value : *fEntry) {
270 auto &field = value.GetField();
271 auto token = fEntry->GetToken(field.GetQualifiedFieldName());
272
273 // If the model has a default entry, use the value pointers from the entry in the entry managed by the
274 // processor. This way, the pointers returned by RNTupleModel::MakeField can be used in the processor loop to
275 // access the corresponding field values.
276 if (!fModel->IsBare()) {
277 auto valuePtr = fModel->GetDefaultEntry().GetPtr<void>(token);
278 fEntry->BindValue(token, valuePtr);
279 }
280 }
281
282 for (auto &innerProc : fInnerProcessors) {
283 innerProc->SetEntryPointers(*fEntry);
284 }
285}
286
288{
289 if (fNEntries == kInvalidNTupleIndex) {
290 fNEntries = 0;
291
292 for (unsigned i = 0; i < fInnerProcessors.size(); ++i) {
293 if (fInnerNEntries[i] == kInvalidNTupleIndex) {
294 fInnerNEntries[i] = fInnerProcessors[i]->GetNEntries();
295 }
296
297 fNEntries += fInnerNEntries[i];
298 }
299 }
300
301 return fNEntries;
302}
303
305{
306 for (const auto &value : *fEntry) {
307 auto &field = value.GetField();
308 auto valuePtr = entry.GetPtr<void>(field.GetQualifiedFieldName());
309
310 fEntry->BindValue(field.GetQualifiedFieldName(), valuePtr);
311 }
312
313 for (auto &innerProc : fInnerProcessors) {
314 innerProc->SetEntryPointers(*fEntry);
315 }
316}
317
319{
321 size_t currProcessor = 0;
322
323 // As long as the entry fails to load from the current processor, we decrement the local entry number with the number
324 // of entries in this processor and try with the next processor until we find the correct local entry number.
325 while (fInnerProcessors[currProcessor]->LoadEntry(localEntryNumber) == kInvalidNTupleIndex) {
326 if (fInnerNEntries[currProcessor] == kInvalidNTupleIndex) {
327 fInnerNEntries[currProcessor] = fInnerProcessors[currProcessor]->GetNEntries();
328 }
329
330 localEntryNumber -= fInnerNEntries[currProcessor];
331
332 // The provided global entry number is larger than the number of available entries.
333 if (++currProcessor >= fInnerProcessors.size())
334 return kInvalidNTupleIndex;
335 }
336
337 if (currProcessor != fCurrentProcessorNumber)
338 fCurrentProcessorNumber = currProcessor;
339
340 fNEntriesProcessed++;
341 fCurrentEntryNumber = entryNumber;
342 return entryNumber;
343}
344
345//------------------------------------------------------------------------------
346
348 std::string_view processorName,
349 std::unique_ptr<RNTupleModel> model)
351{
352 fNTuples.emplace_back(mainNTuple);
354 fPageSource->Attach();
355
356 if (fPageSource->GetNEntries() == 0) {
357 throw RException(R__FAIL("provided RNTuple is empty"));
358 }
359
360 fNEntries = fPageSource->GetNEntries();
361
362 if (!model)
363 model = fPageSource->GetSharedDescriptorGuard()->CreateModel();
364
365 fModel = model->Clone();
366 fModel->Freeze();
367 fEntry = fModel->CreateEntry();
368
369 for (const auto &value : *fEntry) {
370 auto &field = value.GetField();
371 const auto &fieldName = field.GetQualifiedFieldName();
372
373 // If the model has a default entry, use the value pointers from the default entry of the model that was passed to
374 // this constructor. This way, the pointers returned by RNTupleModel::MakeField can be used in the processor loop
375 // to access the corresponding field values.
376 if (!fModel->IsBare()) {
377 auto valuePtr = model->GetDefaultEntry().GetPtr<void>(fieldName);
378 fEntry->BindValue(fieldName, valuePtr);
379 }
380
381 const auto &[fieldContext, _] =
382 fFieldContexts.try_emplace(fieldName, field.Clone(fieldName), fEntry->GetToken(fieldName));
384 }
385}
386
388 const std::vector<std::string> &joinFields,
389 std::unique_ptr<RNTupleModel> model)
390{
391 assert(fNEntriesProcessed == 0 && "cannot add auxiliary ntuples after processing has started");
392
393 fNTuples.emplace_back(auxNTuple);
394
395 auto pageSource = Internal::RPageSource::Create(auxNTuple.fNTupleName, auxNTuple.fStorage);
396 pageSource->Attach();
397
398 if (pageSource->GetNEntries() == 0) {
399 throw RException(R__FAIL("provided RNTuple is empty"));
400 }
401
402 if (!model)
403 model = pageSource->GetSharedDescriptorGuard()->CreateModel();
404
405 model->Freeze();
406 auto entry = model->CreateBareEntry();
407
408 // Append the auxiliary fields to the join model
409 fModel->Unfreeze();
410
411 // The fields of the auxiliary ntuple are contained in an anonymous record field and subsequently registered as
412 // subfields to the join model. This way they can be accessed through the processor as `auxNTupleName.fieldName`,
413 // which is necessary in case there are duplicate field names between the main ntuple and (any of the) auxiliary
414 // ntuples.
415 std::vector<std::unique_ptr<RFieldBase>> auxFields;
416 auxFields.reserve(entry->fValues.size());
417 for (const auto &val : *entry) {
418 auto &field = val.GetField();
419
420 auxFields.emplace_back(field.Clone(field.GetQualifiedFieldName()));
421 }
422 std::unique_ptr<RFieldBase> auxParentField =
423 std::make_unique<RRecordField>(auxNTuple.fNTupleName, std::move(auxFields));
424
425 if (!auxParentField) {
426 throw RException(R__FAIL("could not create auxiliary RNTuple parent field"));
427 }
428
429 const auto &subFields = auxParentField->GetSubFields();
430 fModel->AddField(std::move(auxParentField));
431 for (const auto &field : subFields) {
432 fModel->RegisterSubfield(field->GetQualifiedFieldName());
433 }
434
435 fModel->Freeze();
436 // After modifying the join model, we need to create a new entry since the old one is invalidated. However, we do
437 // want to carry over the value pointers, so the pointers returned by `MakeField` during the creation of the original
438 // model by the user can be used in the processor loop.
439 auto newEntry = fModel->CreateEntry();
440
441 for (const auto &value : *newEntry) {
442 const auto &field = value.GetField();
443
444 // Skip if the field is the untyped record that holds the fields of auxiliary ntuples.
445 const auto fnIsNTuple = [&field](RNTupleOpenSpec n) { return n.fNTupleName == field.GetFieldName(); };
446 if (std::find_if(fNTuples.cbegin(), fNTuples.cend(), fnIsNTuple) != fNTuples.end()) {
447 continue;
448 }
449
450 auto fieldContext = fFieldContexts.find(field.GetQualifiedFieldName());
451 // If the field belongs to the auxiliary ntuple currently being added, apart from assigning its entry value the
452 // correct pointer, we also have to create a field context for it.
453 if (fieldContext == fFieldContexts.end()) {
454 // If the model has a default entry, use the value pointers from the entry in the entry managed by the
455 // processor. This way, the pointers returned by RNTupleModel::MakeField can be used in the processor loop to
456 // access the corresponding field values.
457 if (!model->IsBare()) {
458 auto valuePtr = model->GetDefaultEntry().GetPtr<void>(field.GetFieldName());
459 newEntry->BindValue(field.GetQualifiedFieldName(), valuePtr);
460 }
461
462 auto token = newEntry->GetToken(field.GetQualifiedFieldName());
463 fFieldContexts.try_emplace(field.GetQualifiedFieldName(), field.Clone(field.GetFieldName()), token,
464 fNTuples.size() - 1);
465 } else {
466 auto valuePtr = fEntry->GetPtr<void>(fieldContext->second.fToken);
467 auto newToken = newEntry->GetToken(field.GetQualifiedFieldName());
468 newEntry->BindValue(newToken, valuePtr);
469 fieldContext->second.fToken = std::move(newToken);
470 }
471 }
472
473 fEntry.swap(newEntry);
474
475 // If no join fields have been specified, an aligned join is assumed and an join table won't be created.
476 if (joinFields.size() > 0)
477 fJoinTables.emplace_back(Internal::RNTupleJoinTable::Create(joinFields));
478
479 fAuxiliaryPageSources.emplace_back(std::move(pageSource));
480}
481
483{
484 for (auto &[_, fieldContext] : fFieldContexts) {
486 fieldContext.IsAuxiliary() ? *fAuxiliaryPageSources.at(fieldContext.fNTupleIdx - 1) : *fPageSource;
487 ConnectField(fieldContext, pageSource, *fEntry);
488 }
489}
490
492{
493 for (const auto &[_, fieldContext] : fFieldContexts) {
494 auto fieldName = fieldContext.GetProtoField().GetQualifiedFieldName();
495 if (fieldContext.IsAuxiliary()) {
496 fieldName = fNTuples[fieldContext.fNTupleIdx].fNTupleName + "." + fieldName;
497 }
498 auto valuePtr = entry.GetPtr<void>(fieldName);
499 fEntry->BindValue(fieldName, valuePtr);
500 }
501}
502
504{
505 if (entryNumber >= fPageSource->GetNEntries())
507
508 // Read the values of the primary ntuple. If no join table is used (i.e., the join is aligned), also read the values
509 // of auxiliary ntuples.
510 for (const auto &[_, fieldContext] : fFieldContexts) {
511 if (!fieldContext.IsAuxiliary() || !HasJoinTable()) {
512 auto &value = fEntry->GetValue(fieldContext.fToken);
513 value.Read(entryNumber);
514 }
515 }
516
517 fCurrentEntryNumber = entryNumber;
518 fNEntriesProcessed++;
519
520 // If no join table is used (i.e., the join is aligned), there's nothing left to do.
521 if (!HasJoinTable())
522 return entryNumber;
523
524 // Collect the values of the join fields for this entry.
525 std::vector<void *> valPtrs;
526 valPtrs.reserve(fJoinFieldTokens.size());
527 for (const auto &token : fJoinFieldTokens) {
528 auto ptr = fEntry->GetPtr<void>(token);
529 valPtrs.push_back(ptr.get());
530 }
531
532 // Find the entry index corresponding to the join field values for each auxiliary ntuple.
533 std::vector<ROOT::NTupleSize_t> auxEntryIdxs;
534 auxEntryIdxs.reserve(fJoinTables.size());
535 for (unsigned i = 0; i < fJoinTables.size(); ++i) {
536 auto &joinTable = fJoinTables[i];
537 if (!joinTable->IsBuilt())
538 joinTable->Build(*fAuxiliaryPageSources[i]);
539
540 auto entryIdxs = joinTable->GetEntryIndexes(valPtrs);
541
542 if (entryIdxs.empty())
544 else
545 auxEntryIdxs.push_back(entryIdxs[0]);
546 }
547
548 // For each auxiliary field, load its value according to the entry number we just found of the ntuple it belongs to.
549 for (const auto &[_, fieldContext] : fFieldContexts) {
550 if (!fieldContext.IsAuxiliary())
551 continue;
552
553 auto &value = fEntry->GetValue(fieldContext.fToken);
554 if (auxEntryIdxs[fieldContext.fNTupleIdx - 1] == ROOT::kInvalidNTupleIndex) {
555 // No matching entry exists, so we reset the field's value to a default value.
556 // TODO(fdegeus): further consolidate how non-existing join matches should be handled. N.B.: in case
557 // ConstructValue is not used anymore in the future, remove friend in RFieldBase.
558 fieldContext.fProtoField->ConstructValue(value.GetPtr<void>().get());
559 } else {
560 value.Read(auxEntryIdxs[fieldContext.fNTupleIdx - 1]);
561 }
562 }
563
564 return entryNumber;
565}
#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:299
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
#define _(A, B)
Definition cfortran.h:108
static std::unique_ptr< RNTupleJoinTable > Create(const std::vector< std::string > &fieldNames)
Create an RNTupleJoinTable from an existing RNTuple.
Abstract interface to read data from an ntuple.
static std::unique_ptr< RPageSource > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Guess the concrete derived page source from the file name (location)
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:51
Processor specialization for vertically combined (chained) RNTupleProcessors.
ROOT::NTupleSize_t GetNEntries() final
Get the total number of entries in this processor.
std::vector< ROOT::NTupleSize_t > fInnerNEntries
ROOT::NTupleSize_t LoadEntry(ROOT::NTupleSize_t entryNumber) final
Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in th...
std::vector< std::unique_ptr< RNTupleProcessor > > fInnerProcessors
Processor specialization for horizontally combined (joined) RNTuples.
void AddAuxiliary(const RNTupleOpenSpec &auxNTuple, const std::vector< std::string > &joinFields, std::unique_ptr< RNTupleModel > model=nullptr)
Add an auxiliary RNTuple to the processor.
ROOT::NTupleSize_t LoadEntry(ROOT::NTupleSize_t entryNumber) final
Load the entry identified by the provided entry number of the primary RNTuple.
void ConnectFields()
Connect all fields, once the primary and all auxiliary RNTuples have been added.
Manager for a field as part of the RNTupleProcessor.
Interface for iterating over entries of RNTuples and vertically concatenated RNTuples (chains).
ROOT::NTupleSize_t fNEntries
Total number of entries.
static std::unique_ptr< RNTupleProcessor > Create(const RNTupleOpenSpec &ntuple, std::unique_ptr< RNTupleModel > model=nullptr)
Create an RNTupleProcessor for a single RNTuple.
static std::unique_ptr< RNTupleProcessor > CreateChain(const std::vector< RNTupleOpenSpec > &ntuples, std::unique_ptr< RNTupleModel > model=nullptr)
Create an RNTupleProcessor for a chain (i.e., a vertical combination) of RNTuples.
std::unordered_map< std::string, RFieldContext > fFieldContexts
Maps the (qualified) field name to its corresponding field context.
static std::unique_ptr< RNTupleProcessor > CreateJoin(const std::vector< RNTupleOpenSpec > &ntuples, const std::vector< std::string > &joinFields, std::vector< std::unique_ptr< RNTupleModel > > models={})
Create an RNTupleProcessor for a join (i.e., a horizontal combination) of RNTuples.
std::unique_ptr< RNTupleModel > fModel
void ConnectField(RFieldContext &fieldContext, Internal::RPageSource &pageSource, REntry &entry)
Create and connect a concrete field to the current page source, based on its proto field.
std::unique_ptr< Internal::RPageSource > fPageSource
std::vector< RNTupleOpenSpec > fNTuples
Processor specialization for processing a single RNTuple.
void Connect()
Connect the page source of the underlying RNTuple.
ROOT::NTupleSize_t LoadEntry(ROOT::NTupleSize_t entryNumber) final
Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in th...
void SetEntryPointers(const REntry &entry) final
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
const_iterator begin() const
const_iterator end() const
const Int_t n
Definition legend1.C:16
void CallConnectPageSourceOnField(RFieldBase &, RPageSource &)
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Used to specify the underlying RNTuples in RNTupleProcessor.