Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleModel.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleModel.hxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-04
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#ifndef ROOT7_RNTupleModel
17#define ROOT7_RNTupleModel
18
19#include <ROOT/REntry.hxx>
20#include <ROOT/RError.hxx>
21#include <ROOT/RField.hxx>
22#include <ROOT/RNTupleUtil.hxx>
23#include <string_view>
24
25#include <cstdint>
26#include <functional>
27#include <memory>
28#include <string>
29#include <unordered_map>
30#include <unordered_set>
31#include <utility>
32
33namespace ROOT {
34namespace Experimental {
35
36class RNTupleCollectionWriter;
37class RNTupleModel;
38class RNTupleWriter;
39
40namespace Internal {
41class RPageSinkBuf;
42
43// clang-format off
44/**
45\class ROOT::Experimental::Internal::RNTupleModelChangeset
46\ingroup NTuple
47\brief The incremental changes to a `RNTupleModel`
48
49Represents a set of alterations to a `RNTupleModel` that happened after the model is used to initialize a `RPageSink`
50instance. This object can be used to communicate metadata updates to a `RPageSink`.
51You will not normally use this directly; see `RNTupleModel::RUpdater` instead.
52*/
53// clang-format on
56 /// Points to the fields in fModel that were added as part of an updater transaction
57 std::vector<RFieldBase *> fAddedFields;
58 /// Points to the projected fields in fModel that were added as part of an updater transaction
59 std::vector<RFieldBase *> fAddedProjectedFields;
60
62 bool IsEmpty() const { return fAddedFields.empty() && fAddedProjectedFields.empty(); }
63};
64
65} // namespace Internal
66
67// clang-format off
68/**
69\class ROOT::Experimental::RNTupleModel
70\ingroup NTuple
71\brief The RNTupleModel encapulates the schema of an ntuple.
72
73The ntuple model comprises a collection of hierarchically organized fields. From a model, "entries"
74can be extracted. For convenience, the model provides a default entry unless it is created as a "bare model".
75Models have a unique model identifier that faciliates checking whether entries are compatible with it
76(i.e.: have been extracted from that model).
77
78A model is subject to a state transition during its lifetime: it starts in a building state, in which fields can be
79added and modified. Once the schema is finalized, the model gets frozen. Only frozen models can create entries.
80*/
81// clang-format on
83public:
84 /// A wrapper over a field name and an optional description; used in `AddField()` and `RUpdater::AddField()`
87 NameWithDescription_t(const std::string &name) : fName(name) {}
88 NameWithDescription_t(std::string_view name) : fName(name) {}
89 NameWithDescription_t(std::string_view name, std::string_view descr) : fName(name), fDescription(descr) {}
90
91 std::string_view fName;
92 std::string_view fDescription = "";
93 };
94
95 /// Projected fields are fields whose columns are reused from existing fields. Projected fields are not attached
96 /// to the models zero field. Only the real source fields are written to, projected fields are stored as meta-data
97 /// (header) information only. Only top-level projected fields are supported because otherwise the layout of types
98 /// could be altered in unexpected ways.
99 /// All projected fields and the source fields used to back them are kept in this class.
101 public:
102 /// The map keys are the projected target fields, the map values are the backing source fields
103 /// Note that sub fields are treated individually and indepently of their parent field
104 using FieldMap_t = std::unordered_map<const RFieldBase *, const RFieldBase *>;
105
106 private:
107 explicit RProjectedFields(std::unique_ptr<RFieldZero> fieldZero) : fFieldZero(std::move(fieldZero)) {}
108 /// The projected fields are attached to this zero field
109 std::unique_ptr<RFieldZero> fFieldZero;
110 /// Maps the source fields from fModel to the target projected fields attached to fFieldZero
112 /// The model this set of projected fields belongs to
114
115 /// Asserts that the passed field is a valid target of the source field provided in the field map.
116 /// Checks the field without looking into sub fields.
118
119 public:
120 explicit RProjectedFields(const RNTupleModel *model) : fFieldZero(std::make_unique<RFieldZero>()), fModel(model)
121 {
122 }
127 ~RProjectedFields() = default;
128
129 /// The new model needs to be a clone of fModel
130 std::unique_ptr<RProjectedFields> Clone(const RNTupleModel *newModel) const;
131
132 RFieldZero *GetFieldZero() const { return fFieldZero.get(); }
133 const RFieldBase *GetSourceField(const RFieldBase *target) const;
134 /// Adds a new projected field. The field map needs to provide valid source fields of fModel for 'field'
135 /// and each of its sub fields.
136 RResult<void> Add(std::unique_ptr<RFieldBase> field, const FieldMap_t &fieldMap);
137 bool IsEmpty() const { return fFieldZero->begin() == fFieldZero->end(); }
138 };
139
140 /// A model is usually immutable after passing it to an `RNTupleWriter`. However, for the rare
141 /// cases that require changing the model after the fact, `RUpdater` provides limited support for
142 /// incremental updates, e.g. addition of new fields.
143 ///
144 /// See `RNTupleWriter::CreateModelUpdater()` for an example.
145 class RUpdater {
146 private:
149 std::uint64_t fNewModelId = 0; ///< The model ID after committing
150
151 public:
152 explicit RUpdater(RNTupleWriter &writer);
154 /// Begin a new set of alterations to the underlying model. As a side effect, all `REntry` instances related to
155 /// the model are invalidated.
156 void BeginUpdate();
157 /// Commit changes since the last call to `BeginUpdate()`. All the invalidated `REntry`s remain invalid.
158 /// `CreateEntry()` or `CreateBareEntry()` can be used to create an `REntry` that matching the new model.
159 /// Upon completion, `BeginUpdate()` can be called again to begin a new set of changes.
160 void CommitUpdate();
161
162 template <typename T, typename... ArgsT>
163 std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
164 {
165 auto objPtr = fOpenChangeset.fModel.MakeField<T>(fieldNameDesc, std::forward<ArgsT>(args)...);
166 auto fieldZero = fOpenChangeset.fModel.fFieldZero.get();
167 auto it = std::find_if(fieldZero->begin(), fieldZero->end(),
168 [&](const auto &f) { return f.GetFieldName() == fieldNameDesc.fName; });
169 R__ASSERT(it != fieldZero->end());
170 fOpenChangeset.fAddedFields.emplace_back(&(*it));
171 return objPtr;
172 }
173
174 void AddField(std::unique_ptr<RFieldBase> field);
175
177 AddProjectedField(std::unique_ptr<RFieldBase> field, std::function<std::string(const std::string &)> mapping);
178 };
179
180private:
181 /// Hierarchy of fields consisting of simple types and collections (sub trees)
182 std::unique_ptr<RFieldZero> fFieldZero;
183 /// Contains field values corresponding to the created top-level fields
184 std::unique_ptr<REntry> fDefaultEntry;
185 /// Keeps track of which field names are taken, including projected field names.
186 std::unordered_set<std::string> fFieldNames;
187 /// Free text set by the user
188 std::string fDescription;
189 /// The set of projected top-level fields
190 std::unique_ptr<RProjectedFields> fProjectedFields;
191 /// Every model has a unique ID to distinguish it from other models. Entries are linked to models via the ID.
192 /// Cloned models get a new model ID.
193 std::uint64_t fModelId = 0;
194 /// Changed by Freeze() / Unfreeze() and by the RUpdater.
195 bool fIsFrozen = false;
196
197 /// Checks that user-provided field names are valid in the context
198 /// of this NTuple model. Throws an RException for invalid names.
199 void EnsureValidFieldName(std::string_view fieldName);
200
201 /// Throws an RException if fFrozen is true
202 void EnsureNotFrozen() const;
203
204 /// Throws an RException if fDefaultEntry is nullptr
205 void EnsureNotBare() const;
206
207 /// The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the model.
208 RFieldBase *FindField(std::string_view fieldName) const;
209
210 RNTupleModel(std::unique_ptr<RFieldZero> fieldZero);
211
212public:
213 RNTupleModel(const RNTupleModel&) = delete;
215 ~RNTupleModel() = default;
216
217 std::unique_ptr<RNTupleModel> Clone() const;
218 static std::unique_ptr<RNTupleModel> Create();
219 static std::unique_ptr<RNTupleModel> Create(std::unique_ptr<RFieldZero> fieldZero);
220 /// A bare model has no default entry
221 static std::unique_ptr<RNTupleModel> CreateBare();
222 static std::unique_ptr<RNTupleModel> CreateBare(std::unique_ptr<RFieldZero> fieldZero);
223
224 /// Creates a new field given a `name` or `{name, description}` pair and a
225 /// corresponding value that is managed by a shared pointer.
226 ///
227 /// **Example: create some fields and fill an %RNTuple**
228 /// ~~~ {.cpp}
229 /// #include <ROOT/RNTupleModel.hxx>
230 /// #include <ROOT/RNTupleWriter.hxx>
231 /// using ROOT::Experimental::RNTupleModel;
232 /// using ROOT::Experimental::RNTupleWriter;
233 ///
234 /// #include <vector>
235 ///
236 /// auto model = RNTupleModel::Create();
237 /// auto pt = model->MakeField<float>("pt");
238 /// auto vec = model->MakeField<std::vector<int>>("vec");
239 ///
240 /// // The RNTuple is written to disk when the RNTupleWriter goes out of scope
241 /// {
242 /// auto writer = RNTupleWriter::Recreate(std::move(model), "myNTuple", "myFile.root");
243 /// for (int i = 0; i < 100; i++) {
244 /// *pt = static_cast<float>(i);
245 /// *vec = {i, i+1, i+2};
246 /// writer->Fill();
247 /// }
248 /// }
249 /// ~~~
250 ///
251 /// **Example: create a field with an initial value**
252 /// ~~~ {.cpp}
253 /// #include <ROOT/RNTupleModel.hxx>
254 /// using ROOT::Experimental::RNTupleModel;
255 ///
256 /// auto model = RNTupleModel::Create();
257 /// // pt's initial value is 42.0
258 /// auto pt = model->MakeField<float>("pt", 42.0);
259 /// ~~~
260 /// **Example: create a field with a description**
261 /// ~~~ {.cpp}
262 /// #include <ROOT/RNTupleModel.hxx>
263 /// using ROOT::Experimental::RNTupleModel;
264 ///
265 /// auto model = RNTupleModel::Create();
266 /// auto hadronFlavour = model->MakeField<float>({
267 /// "hadronFlavour", "flavour from hadron ghost clustering"
268 /// });
269 /// ~~~
270 template <typename T, typename... ArgsT>
271 std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
272 {
274 EnsureValidFieldName(fieldNameDesc.fName);
275 auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
276 field->SetDescription(fieldNameDesc.fDescription);
277 std::shared_ptr<T> ptr;
278 if (fDefaultEntry)
279 ptr = fDefaultEntry->AddValue<T>(*field, std::forward<ArgsT>(args)...);
280 fFieldNames.insert(field->GetFieldName());
281 fFieldZero->Attach(std::move(field));
282 return ptr;
283 }
284
285 /// Adds a field whose type is not known at compile time. Thus there is no shared pointer returned.
286 ///
287 /// Throws an exception if the field is null.
288 void AddField(std::unique_ptr<RFieldBase> field);
289
290 /// Adds a top-level field based on existing fields. The mapping function is called with the qualified field names
291 /// of the provided field and the subfields. It should return the qualified field names used as a mapping source.
292 /// Projected fields can only be used for models used to write data.
294 AddProjectedField(std::unique_ptr<RFieldBase> field, std::function<std::string(const std::string &)> mapping);
296
297 void Freeze();
298 void Unfreeze();
299 bool IsFrozen() const { return fIsFrozen; }
300 std::uint64_t GetModelId() const { return fModelId; }
301
302 /// Ingests a model for a sub collection and attaches it to the current model
303 ///
304 /// Throws an exception if collectionModel is null.
305 std::shared_ptr<RNTupleCollectionWriter>
306 MakeCollection(std::string_view fieldName, std::unique_ptr<RNTupleModel> collectionModel);
307
308 std::unique_ptr<REntry> CreateEntry() const;
309 /// In a bare entry, all values point to nullptr. The resulting entry shall use BindValue() in order
310 /// set memory addresses to be serialized / deserialized
311 std::unique_ptr<REntry> CreateBareEntry() const;
312 /// Creates a token to be used in REntry methods to address a top-level field
313 REntry::RFieldToken GetToken(std::string_view fieldName) const;
314 /// Calls the given field's CreateBulk() method. Throws an exception if no field with the given name exists.
315 RFieldBase::RBulk CreateBulk(std::string_view fieldName) const;
316
318 const REntry &GetDefaultEntry() const;
319
320 /// Non-const access to the root field is used to commit clusters during writing
321 /// and to set the on-disk field IDs when connecting a model to a page source or sink.
323 const RFieldZero &GetFieldZero() const { return *fFieldZero; }
324 const RFieldBase &GetField(std::string_view fieldName) const;
325
326 std::string GetDescription() const { return fDescription; }
327 void SetDescription(std::string_view description);
328};
329
330} // namespace Experimental
331} // namespace ROOT
332
333#endif
#define f(i)
Definition RSha256.hxx:104
#define R__ASSERT(e)
Definition TError.h:118
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
char name[80]
Definition TGX11.cxx:110
The field token identifies a top-level field in this entry.
Definition REntry.hxx:54
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:45
Similar to RValue but manages an array of consecutive values.
Definition RField.hxx:231
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:95
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:717
Projected fields are fields whose columns are reused from existing fields.
FieldMap_t fFieldMap
Maps the source fields from fModel to the target projected fields attached to fFieldZero.
RProjectedFields & operator=(RProjectedFields &&)=default
RProjectedFields(std::unique_ptr< RFieldZero > fieldZero)
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.
const RNTupleModel * fModel
The model this set of projected fields belongs to.
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 ...
std::unique_ptr< RFieldZero > fFieldZero
The projected fields are attached to this zero field.
RProjectedFields(const RProjectedFields &)=delete
const RFieldBase * GetSourceField(const RFieldBase *target) const
RProjectedFields & operator=(const RProjectedFields &)=delete
RResult< void > Add(std::unique_ptr< RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
A model is usually immutable after passing it to an RNTupleWriter.
Internal::RNTupleModelChangeset fOpenChangeset
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
void BeginUpdate()
Begin a new set of alterations to the underlying model.
std::uint64_t fNewModelId
The model ID after committing.
std::shared_ptr< T > MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
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.
std::string fDescription
Free text set by the user.
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.
std::uint64_t GetModelId() const
RNTupleModel(const RNTupleModel &)=delete
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::shared_ptr< T > MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
Creates a new field given a name or {name, description} pair and a corresponding value that is manage...
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()
std::unique_ptr< RProjectedFields > fProjectedFields
The set of projected top-level fields.
const RFieldZero & GetFieldZero() const
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...
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
const RProjectedFields & GetProjectedFields() const
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...
RNTupleModel & operator=(const RNTupleModel &)=delete
bool fIsFrozen
Changed by Freeze() / Unfreeze() and by the RUpdater.
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.
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
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
The incremental changes to a RNTupleModel
std::vector< RFieldBase * > fAddedProjectedFields
Points to the projected fields in fModel that were added as part of an updater transaction.
std::vector< RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
A wrapper over a field name and an optional description; used in AddField() and RUpdater::AddField()
NameWithDescription_t(std::string_view name, std::string_view descr)