Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleModel.cxx
Go to the documentation of this file.
1/// \file RNTupleModel.cxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-15
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-2019, 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
16#include <ROOT/RError.hxx>
17#include <ROOT/RField.hxx>
19#include <ROOT/RNTupleModel.hxx>
21#include <ROOT/StringUtils.hxx>
22
23#include <atomic>
24#include <cstdlib>
25#include <memory>
26#include <utility>
27
28namespace {
29std::uint64_t GetNewModelId()
30{
31 static std::atomic<std::uint64_t> gLastModelId = 0;
32 return ++gLastModelId;
33}
34} // anonymous namespace
35
36std::unique_ptr<ROOT::Experimental::RNTupleModel>
38 std::string_view rightFieldPrefix)
39{
40 if (!left.IsFrozen() || !right.IsFrozen())
41 throw RException(R__FAIL("invalid attempt to merge unfrozen models"));
42
43 auto newModel = left.Clone();
44 newModel->Unfreeze();
45
46 if (!rightFieldPrefix.empty()) {
47 newModel->MakeCollection(std::string(rightFieldPrefix), right.Clone());
48 } else {
49 for (const auto &f : right.GetFieldZero().GetSubFields()) {
50 newModel->AddField(f->Clone(f->GetFieldName()));
51 }
52 }
53
54 newModel->Freeze();
55 return newModel;
56}
57
58//------------------------------------------------------------------------------
59
62 const FieldMap_t &fieldMap)
63{
64 auto source = fieldMap.at(target);
65 const bool hasCompatibleStructure =
66 (source->GetStructure() == target->GetStructure()) ||
67 ((source->GetStructure() == ENTupleStructure::kCollection) && dynamic_cast<const RCardinalityField *>(target));
68 if (!hasCompatibleStructure)
69 return R__FAIL("field mapping structural mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
70 if ((source->GetStructure() == ENTupleStructure::kLeaf) || (source->GetStructure() == ENTupleStructure::kUnsplit)) {
71 if (target->GetTypeName() != source->GetTypeName())
72 return R__FAIL("field mapping type mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
73 }
74
75 // We support projections only across records and collections. In the following, we check that the projected
76 // field is on the same path of collection fields in the field tree than the source field.
77
78 // Finds the first non-record parent field of the input field
79 auto fnBreakPoint = [](const RFieldBase *f) -> const RFieldBase * {
80 auto parent = f->GetParent();
81 while (parent) {
82 if (parent->GetStructure() != ENTupleStructure::kRecord)
83 return parent;
84 parent = parent->GetParent();
85 }
86 // We reached the zero field
87 return nullptr;
88 };
89
90 // If source or target has a variant or reference as a parent, error out
91 auto *sourceBreakPoint = fnBreakPoint(source);
92 if (sourceBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
93 return R__FAIL("unsupported field mapping (source structure)");
94 auto *targetBreakPoint = fnBreakPoint(target);
95 if (targetBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
96 return R__FAIL("unsupported field mapping (target structure)");
97
98 if (!sourceBreakPoint && !targetBreakPoint) {
99 // Source and target have no collections as parent
100 return RResult<void>::Success();
101 }
102 if (sourceBreakPoint && targetBreakPoint) {
103 if (sourceBreakPoint == targetBreakPoint) {
104 // Source and target are children of the same collection
105 return RResult<void>::Success();
106 }
107 if (auto it = fieldMap.find(targetBreakPoint); it != fieldMap.end() && it->second == sourceBreakPoint) {
108 // The parent collection of parent is mapped to the parent collection of the source
109 return RResult<void>::Success();
110 }
111 // Source and target are children of different collections
112 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
113 }
114
115 // Either source or target have no collection as a parent, but the other one has; that doesn't fit
116 return R__FAIL("field mapping structure mismatch: " + source->GetFieldName() + " --> " + target->GetFieldName());
117}
118
120ROOT::Experimental::RNTupleModel::RProjectedFields::Add(std::unique_ptr<RFieldBase> field, const FieldMap_t &fieldMap)
121{
122 auto result = EnsureValidMapping(field.get(), fieldMap);
123 if (!result)
124 return R__FORWARD_ERROR(result);
125 for (const auto &f : *field) {
126 result = EnsureValidMapping(&f, fieldMap);
127 if (!result)
128 return R__FORWARD_ERROR(result);
129 }
130
131 fFieldMap.insert(fieldMap.begin(), fieldMap.end());
132 fFieldZero->Attach(std::move(field));
133 return RResult<void>::Success();
134}
135
138{
139 if (auto it = fFieldMap.find(target); it != fFieldMap.end())
140 return it->second;
141 return nullptr;
142}
143
144std::unique_ptr<ROOT::Experimental::RNTupleModel::RProjectedFields>
146{
147 auto cloneFieldZero = std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(fFieldZero->Clone("").release()));
148 auto clone = std::unique_ptr<RProjectedFields>(new RProjectedFields(std::move(cloneFieldZero)));
149 clone->fModel = newModel;
150 // TODO(jblomer): improve quadratic search to re-wire the field mappings given the new model and the cloned
151 // projected fields. Not too critical as we generally expect a limited number of projected fields
152 for (const auto &[k, v] : fFieldMap) {
153 for (const auto &f : *clone->GetFieldZero()) {
154 if (f.GetQualifiedFieldName() == k->GetQualifiedFieldName()) {
155 clone->fFieldMap[&f] = clone->fModel->FindField(v->GetQualifiedFieldName());
156 break;
157 }
158 }
159 }
160 return clone;
161}
162
164 : fWriter(writer), fOpenChangeset(fWriter.GetUpdatableModel())
165{
166}
167
169{
170 fOpenChangeset.fModel.Unfreeze();
171 // We set the model ID to zero until CommitUpdate(). That prevents calls to RNTupleWriter::Fill() in the middle
172 // of updates
173 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
174}
175
177{
178 fOpenChangeset.fModel.Freeze();
179 std::swap(fOpenChangeset.fModel.fModelId, fNewModelId);
180 if (fOpenChangeset.IsEmpty())
181 return;
182 Internal::RNTupleModelChangeset toCommit{fOpenChangeset.fModel};
183 std::swap(fOpenChangeset.fAddedFields, toCommit.fAddedFields);
184 std::swap(fOpenChangeset.fAddedProjectedFields, toCommit.fAddedProjectedFields);
185 fWriter.GetSink().UpdateSchema(toCommit, fWriter.GetNEntries());
186}
187
188void ROOT::Experimental::RNTupleModel::RUpdater::AddField(std::unique_ptr<RFieldBase> field)
189{
190 auto fieldp = field.get();
191 fOpenChangeset.fModel.AddField(std::move(field));
192 fOpenChangeset.fAddedFields.emplace_back(fieldp);
193}
194
197 std::function<std::string(const std::string &)> mapping)
198{
199 auto fieldp = field.get();
200 auto result = fOpenChangeset.fModel.AddProjectedField(std::move(field), mapping);
201 if (result)
202 fOpenChangeset.fAddedProjectedFields.emplace_back(fieldp);
204}
205
207{
208 RResult<void> nameValid = RFieldBase::EnsureValidFieldName(fieldName);
209 if (!nameValid) {
210 nameValid.Throw();
211 }
212 auto fieldNameStr = std::string(fieldName);
213 if (fFieldNames.count(fieldNameStr) > 0)
214 throw RException(R__FAIL("field name '" + fieldNameStr + "' already exists in NTuple model"));
215}
216
218{
219 if (IsFrozen())
220 throw RException(R__FAIL("invalid attempt to modify frozen model"));
221}
222
224{
225 if (!fDefaultEntry)
226 throw RException(R__FAIL("invalid attempt to use default entry of bare model"));
227}
228
229ROOT::Experimental::RNTupleModel::RNTupleModel(std::unique_ptr<RFieldZero> fieldZero)
230 : fFieldZero(std::move(fieldZero)), fModelId(GetNewModelId())
231{}
232
233std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::CreateBare()
234{
235 return CreateBare(std::make_unique<RFieldZero>());
236}
237
238std::unique_ptr<ROOT::Experimental::RNTupleModel>
239ROOT::Experimental::RNTupleModel::CreateBare(std::unique_ptr<RFieldZero> fieldZero)
240{
241 auto model = std::unique_ptr<RNTupleModel>(new RNTupleModel(std::move(fieldZero)));
242 model->fProjectedFields = std::make_unique<RProjectedFields>(model.get());
243 return model;
244}
245
246std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Create()
247{
248 return Create(std::make_unique<RFieldZero>());
249}
250
251std::unique_ptr<ROOT::Experimental::RNTupleModel>
252ROOT::Experimental::RNTupleModel::Create(std::unique_ptr<RFieldZero> fieldZero)
253{
254 auto model = CreateBare(std::move(fieldZero));
255 model->fDefaultEntry = std::unique_ptr<REntry>(new REntry(model->fModelId));
256 return model;
257}
258
259std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Clone() const
260{
261 auto cloneModel = std::unique_ptr<RNTupleModel>(
262 new RNTupleModel(std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(fFieldZero->Clone("").release()))));
263 cloneModel->fModelId = GetNewModelId();
264 cloneModel->fIsFrozen = fIsFrozen;
265 cloneModel->fFieldNames = fFieldNames;
266 cloneModel->fDescription = fDescription;
267 cloneModel->fProjectedFields = fProjectedFields->Clone(cloneModel.get());
268 if (fDefaultEntry) {
269 cloneModel->fDefaultEntry = std::unique_ptr<REntry>(new REntry(cloneModel->fModelId));
270 for (const auto &f : cloneModel->fFieldZero->GetSubFields()) {
271 cloneModel->fDefaultEntry->AddValue(f->CreateValue());
272 }
273 }
274 return cloneModel;
275}
276
278{
279 if (fieldName.empty())
280 return nullptr;
281
282 auto *field = static_cast<ROOT::Experimental::RFieldBase *>(fFieldZero.get());
283 for (auto subfieldName : ROOT::Split(fieldName, ".")) {
284 const auto subfields = field->GetSubFields();
285 auto it = std::find_if(subfields.begin(), subfields.end(),
286 [&](const auto *f) { return f->GetFieldName() == subfieldName; });
287 if (it != subfields.end()) {
288 field = *it;
289 } else {
290 field = nullptr;
291 break;
292 }
293 }
294
295 return field;
296}
297
298void ROOT::Experimental::RNTupleModel::AddField(std::unique_ptr<RFieldBase> field)
299{
300 EnsureNotFrozen();
301 if (!field)
302 throw RException(R__FAIL("null field"));
303 EnsureValidFieldName(field->GetFieldName());
304
305 if (fDefaultEntry)
306 fDefaultEntry->AddValue(field->CreateValue());
307 fFieldNames.insert(field->GetFieldName());
308 fFieldZero->Attach(std::move(field));
309}
310
313 std::function<std::string(const std::string &)> mapping)
314{
315 EnsureNotFrozen();
316 if (!field)
317 return R__FAIL("null field");
318 auto fieldName = field->GetFieldName();
319
321 auto sourceField = FindField(mapping(fieldName));
322 if (!sourceField)
323 return R__FAIL("no such field: " + mapping(fieldName));
324 fieldMap[field.get()] = sourceField;
325 for (const auto &subField : *field) {
326 sourceField = FindField(mapping(subField.GetQualifiedFieldName()));
327 if (!sourceField)
328 return R__FAIL("no such field: " + mapping(fieldName));
329 fieldMap[&subField] = sourceField;
330 }
331
332 EnsureValidFieldName(fieldName);
333 auto result = fProjectedFields->Add(std::move(field), fieldMap);
334 if (!result) {
335 return R__FORWARD_ERROR(result);
336 }
337 fFieldNames.insert(fieldName);
338 return RResult<void>::Success();
339}
340
341std::shared_ptr<ROOT::Experimental::RNTupleCollectionWriter>
343 std::unique_ptr<RNTupleModel> collectionModel)
344{
345 EnsureNotFrozen();
346 EnsureValidFieldName(fieldName);
347 if (!collectionModel) {
348 throw RException(R__FAIL("null collectionModel"));
349 }
350
351 auto collectionWriter = std::make_shared<RNTupleCollectionWriter>(std::move(collectionModel->fDefaultEntry));
352
353 auto field = std::make_unique<RCollectionField>(fieldName, collectionWriter, std::move(collectionModel->fFieldZero));
354 field->SetDescription(collectionModel->GetDescription());
355
356 if (fDefaultEntry)
357 fDefaultEntry->AddValue(field->BindValue(std::shared_ptr<void>(collectionWriter->GetOffsetPtr(), [](void *) {})));
358
359 fFieldNames.insert(field->GetFieldName());
360 fFieldZero->Attach(std::move(field));
361 return collectionWriter;
362}
363
365{
366 if (!IsFrozen())
367 throw RException(R__FAIL("invalid attempt to get mutable zero field of unfrozen model"));
368 return *fFieldZero;
369}
370
372{
373 auto f = FindField(fieldName);
374 if (!f)
375 throw RException(R__FAIL("invalid field: " + std::string(fieldName)));
376
377 return *f;
378}
379
381{
382 EnsureNotBare();
383 return *fDefaultEntry;
384}
385
387{
388 if (!IsFrozen())
389 throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
390 EnsureNotBare();
391 return *fDefaultEntry;
392}
393
394std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateEntry() const
395{
396 if (!IsFrozen())
397 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
398
399 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
400 for (const auto &f : fFieldZero->GetSubFields()) {
401 entry->AddValue(f->CreateValue());
402 }
403 return entry;
404}
405
406std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateBareEntry() const
407{
408 if (!IsFrozen())
409 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
410
411 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
412 for (const auto &f : fFieldZero->GetSubFields()) {
413 entry->AddValue(f->BindValue(nullptr));
414 }
415 return entry;
416}
417
419{
420 if (!IsFrozen())
421 throw RException(R__FAIL("invalid attempt to get field token of unfrozen model"));
422
423 const auto &topLevelFields = fFieldZero->GetSubFields();
424 auto it = std::find_if(topLevelFields.begin(), topLevelFields.end(),
425 [&fieldName](const RFieldBase *f) { return f->GetFieldName() == fieldName; });
426
427 if (it == topLevelFields.end()) {
428 throw RException(R__FAIL("invalid field name: " + std::string(fieldName)));
429 }
430 return REntry::RFieldToken(std::distance(topLevelFields.begin(), it), fModelId);
431}
432
434{
435 if (!IsFrozen())
436 throw RException(R__FAIL("invalid attempt to create bulk of unfrozen model"));
437
438 auto f = FindField(fieldName);
439 if (!f)
440 throw RException(R__FAIL("no such field: " + std::string(fieldName)));
441 return f->CreateBulk();
442}
443
445{
446 if (!IsFrozen())
447 return;
448
449 fModelId = GetNewModelId();
450 if (fDefaultEntry)
451 fDefaultEntry->fModelId = fModelId;
452 fIsFrozen = false;
453}
454
456{
457 fIsFrozen = true;
458}
459
460void ROOT::Experimental::RNTupleModel::SetDescription(std::string_view description)
461{
462 EnsureNotFrozen();
463 fDescription = std::string(description);
464}
465
467{
468 std::size_t bytes = 0;
469
470 // First estimate the write pages per column. Do not bother with computing the number of elements first, just take
471 // the value as set in the options.
472 std::size_t pageBufferPerColumn = options.GetApproxUnzippedPageSize();
473 if (options.GetUseTailPageOptimization()) {
474 // For tail page optimization, RColumn::ConnectPageSink allocates two pages that are larger by 50% to accomodate
475 // merging a small tail page.
476 pageBufferPerColumn *= 3;
477 }
478
479 std::size_t nColumns = 0;
480 for (auto &&field : *fFieldZero) {
481 nColumns += field.GetColumnRepresentative().size();
482 }
483 bytes += nColumns * pageBufferPerColumn;
484
485 // If using buffered writing with RPageSinkBuf, we keep at least the compressed pages in memory.
486 if (options.GetUseBufferedWrite()) {
487 // Use the target cluster size as an estimate for all compressed pages combined.
489 int compression = options.GetCompression();
490 if (compression != 0 && options.GetUseImplicitMT() == RNTupleWriteOptions::EImplicitMT::kDefault) {
491 // With IMT, compression happens asynchronously which means that the uncompressed pages also stay around. Use a
492 // compression factor of 2x as a very rough estimate.
493 bytes += 2 * options.GetApproxZippedClusterSize();
494 }
495 }
496
497 return bytes;
498}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:294
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:292
#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:290
#define f(i)
Definition RSha256.hxx:104
TObject * clone(const char *newname) const override
Definition RooChi2Var.h:9
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
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 WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t bytes
An artificial field that transforms an RNTuple column that contains the offset of collections into co...
Definition RField.hxx:1814
The field token identifies a top-level field in this entry.
Definition REntry.hxx:55
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:46
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Similar to RValue but manages an array of consecutive values.
Definition RField.hxx:235
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:99
const RFieldBase * GetParent() const
Definition RField.hxx:685
std::vector< RFieldBase * > GetSubFields()
Definition RField.cxx:948
static RResult< void > EnsureValidFieldName(std::string_view fieldName)
Check whether a given string is a valid field name.
Definition RField.cxx:846
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:729
Projected fields are fields whose columns are reused from existing fields.
RResult< void > EnsureValidMapping(const RFieldBase *target, const FieldMap_t &fieldMap)
Asserts that the passed field is a valid target of the source field provided in the field map.
std::unordered_map< const RFieldBase *, const RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
const RFieldBase * GetSourceField(const RFieldBase *target) const
RResult< void > Add(std::unique_ptr< RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
std::unique_ptr< RProjectedFields > Clone(const RNTupleModel *newModel) const
The new model needs to be a clone of fModel.
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
void BeginUpdate()
Begin a new set of alterations to the underlying model.
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, std::function< std::string(const std::string &)> mapping)
void AddField(std::unique_ptr< RFieldBase > field)
The RNTupleModel encapulates the schema of an ntuple.
std::unordered_set< std::string > fFieldNames
Keeps track of which field names are taken, including projected field names.
void EnsureValidFieldName(std::string_view fieldName)
Checks that user-provided field names are valid in the context of this NTuple model.
std::uint64_t fModelId
Every model has a unique ID to distinguish it from other models.
REntry::RFieldToken GetToken(std::string_view fieldName) const
Creates a token to be used in REntry methods to address a top-level field.
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, std::function< std::string(const std::string &)> mapping)
Adds a top-level field based on existing fields.
void EnsureNotBare() const
Throws an RException if fDefaultEntry is nullptr.
std::unique_ptr< RNTupleModel > Clone() const
void EnsureNotFrozen() const
Throws an RException if fFrozen is true.
std::size_t EstimateWriteMemoryUsage(const RNTupleWriteOptions &options=RNTupleWriteOptions()) const
Estimate the memory usage for this model during writing.
std::shared_ptr< RNTupleCollectionWriter > MakeCollection(std::string_view fieldName, std::unique_ptr< RNTupleModel > collectionModel)
Ingests a model for a sub collection and attaches it to the current model.
const RFieldBase & GetField(std::string_view fieldName) const
std::unique_ptr< REntry > CreateBareEntry() const
In a bare entry, all values point to nullptr.
std::unique_ptr< REntry > CreateEntry() const
RFieldBase::RBulk CreateBulk(std::string_view fieldName) const
Calls the given field's CreateBulk() method. Throws an exception if no field with the given name exis...
static std::unique_ptr< RNTupleModel > Create()
void SetDescription(std::string_view description)
std::unique_ptr< REntry > fDefaultEntry
Contains field values corresponding to the created top-level fields.
RFieldBase * FindField(std::string_view fieldName) const
The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the...
RNTupleModel(std::unique_ptr< RFieldZero > fieldZero)
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
void AddField(std::unique_ptr< RFieldBase > field)
Adds a field whose type is not known at compile time.
RFieldZero & GetFieldZero()
Non-const access to the root field is used to commit clusters during writing and to set the on-disk f...
std::unique_ptr< RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees)
Common user-tunable settings for storing ntuples.
An RNTuple that gets filled with entries (data) and writes them to storage.
void Throw()
Throws an RException with fError.
Definition RError.cxx:67
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:194
std::unique_ptr< RNTupleModel > MergeModels(const RNTupleModel &left, const RNTupleModel &right, std::string_view rightFieldPrefix="")
Merge two RNTuple models.
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
The incremental changes to a RNTupleModel