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>
18#include <ROOT/RNTupleModel.hxx>
19#include <ROOT/RNTuple.hxx>
20#include <ROOT/StringUtils.hxx>
21
22#include <atomic>
23#include <cstdlib>
24#include <memory>
25#include <utility>
26
29 const FieldMap_t &fieldMap)
30{
31 auto source = fieldMap.at(target);
32 const bool hasCompatibleStructure =
33 (source->GetStructure() == target->GetStructure()) ||
34 ((source->GetStructure() == ENTupleStructure::kCollection) && dynamic_cast<const RCardinalityField *>(target));
35 if (!hasCompatibleStructure)
36 return R__FAIL("field mapping structural mismatch: " + source->GetName() + " --> " + target->GetName());
37 if (source->GetStructure() == ENTupleStructure::kLeaf) {
38 if (target->GetType() != source->GetType())
39 return R__FAIL("field mapping type mismatch: " + source->GetName() + " --> " + target->GetName());
40 }
41
42 // We support projections only across records and collections. In the following, we check that the projected
43 // field is on the same path of collection fields in the field tree than the source field.
44
45 // Finds the first non-record parent field of the input field
46 auto fnBreakPoint = [](const Detail::RFieldBase *f) -> const Detail::RFieldBase * {
47 auto parent = f->GetParent();
48 while (parent) {
49 if (parent->GetStructure() != ENTupleStructure::kRecord)
50 return parent;
51 parent = parent->GetParent();
52 }
53 // We reached the zero field
54 return nullptr;
55 };
56
57 // If source or target has a variant or reference as a parent, error out
58 auto *sourceBreakPoint = fnBreakPoint(source);
59 if (sourceBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
60 return R__FAIL("unsupported field mapping (source structure)");
61 auto *targetBreakPoint = fnBreakPoint(target);
62 if (targetBreakPoint && sourceBreakPoint->GetStructure() != ENTupleStructure::kCollection)
63 return R__FAIL("unsupported field mapping (target structure)");
64
65 if (!sourceBreakPoint && !targetBreakPoint) {
66 // Source and target have no collections as parent
68 }
69 if (sourceBreakPoint && targetBreakPoint) {
70 if (sourceBreakPoint == targetBreakPoint) {
71 // Source and target are children of the same collection
73 }
74 if (auto it = fieldMap.find(targetBreakPoint); it != fieldMap.end() && it->second == sourceBreakPoint) {
75 // The parent collection of parent is mapped to the parent collection of the source
77 }
78 // Source and target are children of different collections
79 return R__FAIL("field mapping structure mismatch: " + source->GetName() + " --> " + target->GetName());
80 }
81
82 // Either source or target have no collection as a parent, but the other one has; that doesn't fit
83 return R__FAIL("field mapping structure mismatch: " + source->GetName() + " --> " + target->GetName());
84}
85
87ROOT::Experimental::RNTupleModel::RProjectedFields::Add(std::unique_ptr<Detail::RFieldBase> field,
88 const FieldMap_t &fieldMap)
89{
90 auto result = EnsureValidMapping(field.get(), fieldMap);
91 if (!result)
93 for (const auto &f : *field) {
94 result = EnsureValidMapping(&f, fieldMap);
95 if (!result)
97 }
98
99 fFieldMap.insert(fieldMap.begin(), fieldMap.end());
100 fFieldZero->Attach(std::move(field));
101 return RResult<void>::Success();
102}
103
106{
107 if (auto it = fFieldMap.find(target); it != fFieldMap.end())
108 return it->second;
109 return nullptr;
110}
111
112std::unique_ptr<ROOT::Experimental::RNTupleModel::RProjectedFields>
114{
115 auto cloneFieldZero = std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(fFieldZero->Clone("").release()));
116 auto clone = std::unique_ptr<RProjectedFields>(new RProjectedFields(std::move(cloneFieldZero)));
117 clone->fModel = newModel;
118 // TODO(jblomer): improve quadratic search to re-wire the field mappings given the new model and the cloned
119 // projected fields. Not too critical as we generally expect a limited number of projected fields
120 for (const auto &[k, v] : fFieldMap) {
121 for (const auto &f : *clone->GetFieldZero()) {
122 if (f.GetQualifiedFieldName() == k->GetQualifiedFieldName()) {
123 clone->fFieldMap[&f] = clone->fModel->GetField(v->GetQualifiedFieldName());
124 break;
125 }
126 }
127 }
128 return clone;
129}
130
132 : fWriter(writer), fOpenChangeset(*fWriter.fModel)
133{
134}
135
137{
138 fOpenChangeset.fModel.Unfreeze();
139}
140
142{
143 fOpenChangeset.fModel.Freeze();
144 if (fOpenChangeset.IsEmpty())
145 return;
146 Detail::RNTupleModelChangeset toCommit{fOpenChangeset.fModel};
147 std::swap(fOpenChangeset.fAddedFields, toCommit.fAddedFields);
148 std::swap(fOpenChangeset.fAddedProjectedFields, toCommit.fAddedProjectedFields);
149 fWriter.fSink->UpdateSchema(toCommit, fWriter.fNEntries);
150}
151
152void ROOT::Experimental::RNTupleModel::RUpdater::AddField(std::unique_ptr<Detail::RFieldBase> field)
153{
154 auto fieldp = field.get();
155 fOpenChangeset.fModel.AddField(std::move(field));
156 fOpenChangeset.fAddedFields.emplace_back(fieldp);
157}
158
161 std::function<std::string(const std::string &)> mapping)
162{
163 auto fieldp = field.get();
164 auto result = fOpenChangeset.fModel.AddProjectedField(std::move(field), mapping);
165 if (result)
166 fOpenChangeset.fAddedProjectedFields.emplace_back(fieldp);
168}
169
171{
173 if (!nameValid) {
174 nameValid.Throw();
175 }
176 auto fieldNameStr = std::string(fieldName);
177 if (fFieldNames.insert(fieldNameStr).second == false) {
178 throw RException(R__FAIL("field name '" + fieldNameStr + "' already exists in NTuple model"));
179 }
180}
181
183{
184 if (IsFrozen())
185 throw RException(R__FAIL("invalid attempt to modify frozen model"));
186}
187
189{
190 if (!fDefaultEntry)
191 throw RException(R__FAIL("invalid attempt to use default entry of bare model"));
192}
193
195 : fFieldZero(std::make_unique<RFieldZero>())
196{}
197
198std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::CreateBare()
199{
200 auto model = std::unique_ptr<RNTupleModel>(new RNTupleModel());
201 model->fProjectedFields = std::make_unique<RProjectedFields>(model.get());
202 return model;
203}
204
205std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Create()
206{
207 auto model = CreateBare();
208 model->fDefaultEntry = std::unique_ptr<REntry>(new REntry());
209 return model;
210}
211
212std::unique_ptr<ROOT::Experimental::RNTupleModel> ROOT::Experimental::RNTupleModel::Clone() const
213{
214 auto cloneModel = std::unique_ptr<RNTupleModel>(new RNTupleModel());
215 auto cloneFieldZero = fFieldZero->Clone("");
216 cloneModel->fModelId = fModelId;
217 cloneModel->fFieldZero = std::unique_ptr<RFieldZero>(static_cast<RFieldZero *>(cloneFieldZero.release()));
218 cloneModel->fFieldNames = fFieldNames;
219 cloneModel->fDescription = fDescription;
220 cloneModel->fProjectedFields = fProjectedFields->Clone(cloneModel.get());
221 if (fDefaultEntry) {
222 cloneModel->fDefaultEntry = std::unique_ptr<REntry>(new REntry(fModelId));
223 for (const auto &f : cloneModel->fFieldZero->GetSubFields()) {
224 cloneModel->fDefaultEntry->AddValue(f->GenerateValue());
225 }
226 }
227 return cloneModel;
228}
229
230
231void ROOT::Experimental::RNTupleModel::AddField(std::unique_ptr<Detail::RFieldBase> field)
232{
233 EnsureNotFrozen();
234 if (!field)
235 throw RException(R__FAIL("null field"));
236 EnsureValidFieldName(field->GetName());
237
238 if (fDefaultEntry)
239 fDefaultEntry->AddValue(field->GenerateValue());
240 fFieldZero->Attach(std::move(field));
241}
242
244ROOT::Experimental::RNTupleModel::AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
245 std::function<std::string(const std::string &)> mapping)
246{
247 EnsureNotFrozen();
248 if (!field)
249 return R__FAIL("null field");
250 auto fieldName = field->GetName();
251
253 auto sourceField = GetField(mapping(fieldName));
254 if (!sourceField)
255 return R__FAIL("no such field: " + mapping(fieldName));
256 fieldMap[field.get()] = sourceField;
257 for (const auto &subField : *field) {
258 sourceField = GetField(mapping(subField.GetQualifiedFieldName()));
259 if (!sourceField)
260 return R__FAIL("no such field: " + mapping(fieldName));
261 fieldMap[&subField] = sourceField;
262 }
263
264 EnsureValidFieldName(fieldName);
265 auto result = fProjectedFields->Add(std::move(field), fieldMap);
266 if (!result) {
267 fFieldNames.erase(fieldName);
268 return R__FORWARD_ERROR(result);
269 }
270 return RResult<void>::Success();
271}
272
273std::shared_ptr<ROOT::Experimental::RCollectionNTupleWriter> ROOT::Experimental::RNTupleModel::MakeCollection(
274 std::string_view fieldName, std::unique_ptr<RNTupleModel> collectionModel)
275{
276 EnsureNotFrozen();
277 EnsureValidFieldName(fieldName);
278 if (!collectionModel) {
279 throw RException(R__FAIL("null collectionModel"));
280 }
281 auto collectionNTuple = std::make_shared<RCollectionNTupleWriter>(std::move(collectionModel->fDefaultEntry));
282 auto field = std::make_unique<RCollectionField>(fieldName, collectionNTuple, std::move(collectionModel));
283 if (fDefaultEntry)
284 fDefaultEntry->AddValue(field->BindValue(collectionNTuple->GetOffsetPtr()));
285 fFieldZero->Attach(std::move(field));
286 return collectionNTuple;
287}
288
290ROOT::Experimental::RNTupleModel::GetField(std::string_view fieldName) const
291{
292 if (fieldName.empty())
293 return nullptr;
294
295 auto *field = static_cast<ROOT::Experimental::Detail::RFieldBase *>(fFieldZero.get());
296 for (auto subfieldName : ROOT::Split(fieldName, ".")) {
297 const auto subfields = field->GetSubFields();
298 auto it =
299 std::find_if(subfields.begin(), subfields.end(), [&](const auto *f) { return f->GetName() == subfieldName; });
300 if (it != subfields.end()) {
301 field = *it;
302 } else {
303 field = nullptr;
304 break;
305 }
306 }
307
308 return field;
309}
310
312{
313 if (!IsFrozen())
314 throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
315 EnsureNotBare();
316 return fDefaultEntry.get();
317}
318
319std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateEntry() const
320{
321 if (!IsFrozen())
322 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
323
324 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
325 for (const auto &f : fFieldZero->GetSubFields()) {
326 entry->AddValue(f->GenerateValue());
327 }
328 return entry;
329}
330
331std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateBareEntry() const
332{
333 if (!IsFrozen())
334 throw RException(R__FAIL("invalid attempt to create entry of unfrozen model"));
335
336 auto entry = std::unique_ptr<REntry>(new REntry(fModelId));
337 for (const auto &f : fFieldZero->GetSubFields()) {
338 entry->AddValue(f->BindValue(nullptr));
339 }
340 return entry;
341}
342
344{
345 if (!IsFrozen())
346 throw RException(R__FAIL("invalid attempt to unfreeze an unfrozen model"));
347 fModelId = 0;
348}
349
351{
352 if (IsFrozen())
353 return;
354
355 static std::atomic<std::uint64_t> gLastModelId = 0;
356 fModelId = ++gLastModelId;
357 if (fDefaultEntry)
358 fDefaultEntry->fModelId = fModelId;
359}
360
361void ROOT::Experimental::RNTupleModel::SetDescription(std::string_view description)
362{
363 EnsureNotFrozen();
364 fDescription = std::string(description);
365}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:307
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:305
#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:303
#define f(i)
Definition RSha256.hxx:104
TObject * clone(const char *newname) const override
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
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:83
static RResult< void > EnsureValidFieldName(std::string_view fieldName)
Check whether a given string is a valid field name.
Definition RField.cxx:494
std::vector< RFieldBase * > GetSubFields() const
Definition RField.cxx:598
void Throw()
Throws an RException with fError.
Definition RError.cxx:69
An artificial field that transforms an RNTuple column that contains the offset of collections into co...
Definition RField.hxx:1422
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:42
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:655
Projected fields are fields whose columns are reused from existing fields.
std::unordered_map< const Detail::RFieldBase *, const Detail::RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
RResult< void > Add(std::unique_ptr< Detail::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
RResult< void > EnsureValidMapping(const Detail::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::unique_ptr< RProjectedFields > Clone(const RNTupleModel *newModel) const
The new model needs to be a clone of fModel.
const Detail::RFieldBase * GetSourceField(const Detail::RFieldBase *target) const
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
void BeginUpdate()
Begin a new set of alterations to the underlying model.
void AddField(std::unique_ptr< Detail::RFieldBase > field)
RResult< void > AddProjectedField(std::unique_ptr< Detail::RFieldBase > field, std::function< std::string(const std::string &)> mapping)
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.
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::unique_ptr< REntry > CreateBareEntry() const
In a bare entry, all values point to nullptr.
std::unique_ptr< REntry > CreateEntry() const
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.
std::shared_ptr< RCollectionNTupleWriter > MakeCollection(std::string_view fieldName, std::unique_ptr< RNTupleModel > collectionModel)
Ingests a model for a sub collection and attaches it to the current model.
RResult< void > AddProjectedField(std::unique_ptr< Detail::RFieldBase > field, std::function< std::string(const std::string &)> mapping)
Adds a top-level field based on existing fields.
const Detail::RFieldBase * GetField(std::string_view fieldName) const
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
void AddField(std::unique_ptr< Detail::RFieldBase > field)
Adds a field whose type is not known at compile time.
std::unique_ptr< RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees)
An RNTuple that gets filled with entries (data) and writes them to storage.
Definition RNTuple.hxx:358
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:207
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