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 RCollectionNTupleWriter;
37class RNTupleModel;
38class RNTupleWriter;
39
40namespace Detail {
41
42// clang-format off
43/**
44\class ROOT::Experimental::Detail::RNTupleModelChangeset
45\ingroup NTuple
46\brief The incremental changes to a `RNTupleModel`
47
48Represents a set of alterations to a `RNTupleModel` that happened after the model is used to initialize a `RPageSink`
49instance. This object can be used to communicate metadata updates to a `RPageSink`.
50You will not normally use this directly; see `RNTupleModel::RUpdater` instead.
51*/
52// clang-format on
55 /// Points to the fields in fModel that were added as part of an updater transaction
56 std::vector<RFieldBase *> fAddedFields;
57 /// Points to the projected fields in fModel that were added as part of an updater transaction
58 std::vector<RFieldBase *> fAddedProjectedFields;
59
61 bool IsEmpty() const { return fAddedFields.empty() && fAddedProjectedFields.empty(); }
62};
63
64} // namespace Detail
65
66// clang-format off
67/**
68\class ROOT::Experimental::RNTupleModel
69\ingroup NTuple
70\brief The RNTupleModel encapulates the schema of an ntuple.
71
72The ntuple model comprises a collection of hierarchically organized fields. From a model, "entries"
73can be extracted. For convenience, the model provides a default entry. Models have a unique model identifier
74that faciliates checking whether entries are compatible with it (i.e.: have been extracted from that model).
75*/
76// clang-format on
78public:
79 /// A wrapper over a field name and an optional description; used in `AddField()` and `RUpdater::AddField()`
82 NameWithDescription_t(std::string_view name) : fName(name) {}
83 NameWithDescription_t(std::string_view name, std::string_view descr) : fName(name), fDescription(descr) {}
84
85 std::string_view fName;
86 std::string_view fDescription = "";
87 };
88
89 /// Projected fields are fields whose columns are reused from existing fields. Projected fields are not attached
90 /// to the models zero field. Only the real source fields are written to, projected fields are stored as meta-data
91 /// (header) information only. Only top-level projected fields are supported because otherwise the layout of types
92 /// could be altered in unexpected ways.
93 /// All projected fields and the source fields used to back them are kept in this class.
95 public:
96 /// The map keys are the projected target fields, the map values are the backing source fields
97 /// Note that sub fields are treated individually and indepently of their parent field
98 using FieldMap_t = std::unordered_map<const Detail::RFieldBase *, const Detail::RFieldBase *>;
99
100 private:
101 explicit RProjectedFields(std::unique_ptr<RFieldZero> fieldZero) : fFieldZero(std::move(fieldZero)) {}
102 /// The projected fields are attached to this zero field
103 std::unique_ptr<RFieldZero> fFieldZero;
104 /// Maps the source fields from fModel to the target projected fields attached to fFieldZero
106 /// The model this set of projected fields belongs to
108
109 /// Asserts that the passed field is a valid target of the source field provided in the field map.
110 /// Checks the field without looking into sub fields.
112
113 public:
114 explicit RProjectedFields(const RNTupleModel *model) : fFieldZero(std::make_unique<RFieldZero>()), fModel(model)
115 {
116 }
121 ~RProjectedFields() = default;
122
123 /// The new model needs to be a clone of fModel
124 std::unique_ptr<RProjectedFields> Clone(const RNTupleModel *newModel) const;
125
126 RFieldZero *GetFieldZero() const { return fFieldZero.get(); }
128 /// Adds a new projected field. The field map needs to provide valid source fields of fModel for 'field'
129 /// and each of its sub fields.
130 RResult<void> Add(std::unique_ptr<Detail::RFieldBase> field, const FieldMap_t &fieldMap);
131 bool IsEmpty() const { return fFieldZero->begin() == fFieldZero->end(); }
132 };
133
134 /// A model is usually immutable after passing it to an `RNTupleWriter`. However, for the rare
135 /// cases that require changing the model after the fact, `RUpdater` provides limited support for
136 /// incremental updates, e.g. addition of new fields.
137 ///
138 /// See `RNTupleWriter::CreateModelUpdater()` for an example.
139 class RUpdater {
140 private:
143
144 public:
145 explicit RUpdater(RNTupleWriter &writer);
147 /// Begin a new set of alterations to the underlying model. As a side effect, all `REntry` instances related to
148 /// the model are invalidated.
149 void BeginUpdate();
150 /// Commit changes since the last call to `BeginUpdate()`. All the invalidated `REntry`s remain invalid.
151 /// `CreateEntry()` or `CreateBareEntry()` can be used to create an `REntry` that matching the new model.
152 /// Upon completion, `BeginUpdate()` can be called again to begin a new set of changes.
153 void CommitUpdate();
154
155 void AddField(std::unique_ptr<Detail::RFieldBase> field);
156 template <typename T>
157 void AddField(const NameWithDescription_t &fieldNameDesc, T *fromWhere)
158 {
159 fOpenChangeset.fModel.AddField<T>(fieldNameDesc, fromWhere);
160 auto fieldZero = fOpenChangeset.fModel.GetFieldZero();
161 auto it = std::find_if(fieldZero->begin(), fieldZero->end(),
162 [&](const auto &f) { return f.GetName() == fieldNameDesc.fName; });
163 R__ASSERT(it != fieldZero->end());
164 fOpenChangeset.fAddedFields.emplace_back(&(*it));
165 }
166
167 RResult<void> AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
168 std::function<std::string(const std::string &)> mapping);
169 };
170
171private:
172 /// Hierarchy of fields consisting of simple types and collections (sub trees)
173 std::unique_ptr<RFieldZero> fFieldZero;
174 /// Contains field values corresponding to the created top-level fields
175 std::unique_ptr<REntry> fDefaultEntry;
176 /// Keeps track of which field names are taken, including projected field names.
177 std::unordered_set<std::string> fFieldNames;
178 /// Free text set by the user
179 std::string fDescription;
180 /// The set of projected top-level fields
181 std::unique_ptr<RProjectedFields> fProjectedFields;
182 /// Upon freezing, every model has a unique ID to distingusish it from other models. Cloning preserves the ID.
183 /// Entries are linked to models via the ID.
184 std::uint64_t fModelId = 0;
185
186 /// Checks that user-provided field names are valid in the context
187 /// of this NTuple model. Throws an RException for invalid names.
188 void EnsureValidFieldName(std::string_view fieldName);
189
190 /// Throws an RException if fFrozen is true
191 void EnsureNotFrozen() const;
192
193 /// Throws an RException if fDefaultEntry is nullptr
194 void EnsureNotBare() const;
195
196 RNTupleModel();
197
198public:
199 RNTupleModel(const RNTupleModel&) = delete;
201 ~RNTupleModel() = default;
202
203 std::unique_ptr<RNTupleModel> Clone() const;
204 static std::unique_ptr<RNTupleModel> Create();
205 /// A bare model has no default entry
206 static std::unique_ptr<RNTupleModel> CreateBare();
207
208 /// Creates a new field given a `name` or `{name, description}` pair and a
209 /// corresponding tree value that is managed by a shared pointer.
210 ///
211 /// **Example: create some fields and fill an %RNTuple**
212 /// ~~~ {.cpp}
213 /// #include <ROOT/RNTuple.hxx>
214 /// using ROOT::Experimental::RNTupleModel;
215 /// using ROOT::Experimental::RNTupleWriter;
216 ///
217 /// #include <vector>
218 ///
219 /// auto model = RNTupleModel::Create();
220 /// auto pt = model->MakeField<float>("pt");
221 /// auto vec = model->MakeField<std::vector<int>>("vec");
222 ///
223 /// // The RNTuple is written to disk when the RNTupleWriter goes out of scope
224 /// {
225 /// auto ntuple = RNTupleWriter::Recreate(std::move(model), "myNTuple", "myFile.root");
226 /// for (int i = 0; i < 100; i++) {
227 /// *pt = static_cast<float>(i);
228 /// *vec = {i, i+1, i+2};
229 /// ntuple->Fill();
230 /// }
231 /// }
232 /// ~~~
233 ///
234 /// **Example: create a field with an initial value**
235 /// ~~~ {.cpp}
236 /// #include <ROOT/RNTuple.hxx>
237 /// using ROOT::Experimental::RNTupleModel;
238 ///
239 /// auto model = RNTupleModel::Create();
240 /// // pt's initial value is 42.0
241 /// auto pt = model->MakeField<float>("pt", 42.0);
242 /// ~~~
243 /// **Example: create a field with a description**
244 /// ~~~ {.cpp}
245 /// #include <ROOT/RNTuple.hxx>
246 /// using ROOT::Experimental::RNTupleModel;
247 ///
248 /// auto model = RNTupleModel::Create();
249 /// auto hadronFlavour = model->MakeField<float>({
250 /// "hadronFlavour", "flavour from hadron ghost clustering"
251 /// });
252 /// ~~~
253 template <typename T, typename... ArgsT>
254 std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc,
255 ArgsT&&... args)
256 {
258 EnsureValidFieldName(fieldNameDesc.fName);
259 auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
260 field->SetDescription(fieldNameDesc.fDescription);
261 std::shared_ptr<T> ptr;
262 if (fDefaultEntry)
263 ptr = fDefaultEntry->AddValue<T>(field.get(), std::forward<ArgsT>(args)...);
264 fFieldZero->Attach(std::move(field));
265 return ptr;
266 }
267
268 /// Adds a field whose type is not known at compile time. Thus there is no shared pointer returned.
269 ///
270 /// Throws an exception if the field is null.
271 void AddField(std::unique_ptr<Detail::RFieldBase> field);
272
273 /// Throws an exception if fromWhere is null.
274 template <typename T>
275 void AddField(const NameWithDescription_t &fieldNameDesc, T* fromWhere) {
278 if (!fromWhere)
279 throw RException(R__FAIL("null field fromWhere"));
280 EnsureValidFieldName(fieldNameDesc.fName);
281
282 auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
283 field->SetDescription(fieldNameDesc.fDescription);
284 fDefaultEntry->AddValue(field->BindValue(fromWhere));
285 fFieldZero->Attach(std::move(field));
286 }
287
288 /// Adds a top-level field based on existing fields. The mapping function is called with the qualified field names
289 /// of the provided field and the subfields. It should return the qualified field names used as a mapping source.
290 /// Projected fields can only be used for models used to write data.
291 RResult<void> AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
292 std::function<std::string(const std::string &)> mapping);
293
294 template <typename T>
295 T *Get(std::string_view fieldName) const
296 {
298 return fDefaultEntry->Get<T>(fieldName);
299 }
300
302
303 void Freeze();
304 void Unfreeze();
305 bool IsFrozen() const { return fModelId != 0; }
306 std::uint64_t GetModelId() const { return fModelId; }
307
308 /// Ingests a model for a sub collection and attaches it to the current model
309 ///
310 /// Throws an exception if collectionModel is null.
311 std::shared_ptr<RCollectionNTupleWriter> MakeCollection(
312 std::string_view fieldName,
313 std::unique_ptr<RNTupleModel> collectionModel);
314
315 std::unique_ptr<REntry> CreateEntry() const;
316 /// In a bare entry, all values point to nullptr. The resulting entry shall use CaptureValueUnsafe() in order
317 /// set memory addresses to be serialized / deserialized
318 std::unique_ptr<REntry> CreateBareEntry() const;
319 REntry *GetDefaultEntry() const;
320
321 RFieldZero *GetFieldZero() const { return fFieldZero.get(); }
322 const Detail::RFieldBase *GetField(std::string_view fieldName) const;
323
324 std::string GetDescription() const { return fDescription; }
325 void SetDescription(std::string_view description);
326};
327
328} // namespace Experimental
329} // namespace ROOT
330
331#endif
#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
#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
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:85
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:657
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.
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 ...
RProjectedFields & operator=(RProjectedFields &&)=default
RProjectedFields(std::unique_ptr< RFieldZero > fieldZero)
const RNTupleModel * fModel
The model this set of projected fields belongs to.
RResult< void > Add(std::unique_ptr< Detail::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
std::unique_ptr< RFieldZero > fFieldZero
The projected fields are attached to this zero field.
RProjectedFields(const RProjectedFields &)=delete
RProjectedFields & operator=(const RProjectedFields &)=delete
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.
const Detail::RFieldBase * GetSourceField(const Detail::RFieldBase *target) const
A model is usually immutable after passing it to an RNTupleWriter.
void AddField(const NameWithDescription_t &fieldNameDesc, T *fromWhere)
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
Detail::RNTupleModelChangeset fOpenChangeset
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 AddField(const NameWithDescription_t &fieldNameDesc, T *fromWhere)
Throws an exception if fromWhere is null.
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
Upon freezing, every model has a unique ID to distingusish it from other models.
std::uint64_t GetModelId() const
RNTupleModel(const RNTupleModel &)=delete
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 tree value that is m...
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()
std::unique_ptr< RProjectedFields > fProjectedFields
The set of projected top-level fields.
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
RFieldZero * GetFieldZero() const
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
T * Get(std::string_view fieldName) const
const RProjectedFields & GetProjectedFields() const
RNTupleModel & operator=(const RNTupleModel &)=delete
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:359
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
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
The incremental changes to a RNTupleModel
std::vector< RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
std::vector< RFieldBase * > fAddedProjectedFields
Points to the projected 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)