Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleProcessor.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleProcessor.hxx
2/// \author Florine de Geus <florine.de.geus@cern.ch>
3/// \date 2024-03-26
4/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
5/// is welcome!
6
7/*************************************************************************
8 * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers. *
9 * All rights reserved. *
10 * *
11 * For the licensing terms see $ROOTSYS/LICENSE. *
12 * For the list of contributors see $ROOTSYS/README/CREDITS. *
13 *************************************************************************/
14
15#ifndef ROOT_RNTupleProcessor
16#define ROOT_RNTupleProcessor
17
18#include <ROOT/REntry.hxx>
19#include <ROOT/RError.hxx>
22#include <ROOT/RNTupleModel.hxx>
23#include <ROOT/RNTupleTypes.hxx>
25#include <ROOT/RPageStorage.hxx>
26
27#include <memory>
28#include <string>
29#include <string_view>
30#include <vector>
31
32namespace ROOT {
33namespace Experimental {
34
35namespace Internal {
36struct RNTupleProcessorEntryLoader;
37} // namespace Internal
38
39// clang-format off
40/**
41\class ROOT::Experimental::RNTupleOpenSpec
42\ingroup NTuple
43\brief Specification of the name and location of an RNTuple, used for creating a new RNTupleProcessor.
44
45An RNTupleOpenSpec can be created by providing either a string with a path to the ROOT file or a pointer to the
46TDirectory (or any of its subclasses) that contains the RNTuple.
47
48Note that the RNTupleOpenSpec is *write-only*, to prevent usability issues with Python.
49*/
50// clang-format on
52 friend class RNTupleProcessor;
55
56private:
57 std::string fNTupleName;
58 std::variant<std::string, TDirectory *> fStorage;
59
60public:
61 RNTupleOpenSpec(std::string_view n, TDirectory *s) : fNTupleName(n), fStorage(s) {}
62 RNTupleOpenSpec(std::string_view n, const std::string &s) : fNTupleName(n), fStorage(s) {}
63
64 std::unique_ptr<ROOT::Internal::RPageSource> CreatePageSource() const;
65};
66
68private:
69 /// By default, the processor name is the name of the underlying RNTuple for RNTupleSingleProcessor, the name of the
70 /// first processor for RNTupleChainProcessor, or the name of the primary RNTuple for RNTupleJoinProcessor.
71 std::string fProcessorName = "";
72
73public:
74 const std::string &GetProcessorName() const { return fProcessorName; }
75
76 void SetProcessorName(std::string_view name) { fProcessorName = name; }
77};
78
79// clang-format off
80/**
81\class ROOT::Experimental::RNTupleProcessorOptionalPtr<T>
82\ingroup NTuple
83\brief The RNTupleProcessorOptionalPtr provides access to values from fields present in an RNTupleProcessor, with support
84and checks for missing values.
85*/
86// clang-format on
87template <typename T>
89 friend class RNTupleProcessor;
90
91private:
94
100
101 /////////////////////////////////////////////////////////////////////////////
102 /// \brief Get a non-owning pointer to the field value managed by the processor's entry.
103 ///
104 /// \return A `T*` if the field is valid in the current entry, or a `nullptr` otherwise.
105 T *GetRawPtr() const { return GetPtr().get(); }
106
107 /////////////////////////////////////////////////////////////////////////////
108 /// \brief Bind the value to `valuePtr`.
109 ///
110 /// \param[in] valuePtr Pointer to bind the value to.
111 ///
112 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
113 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
114 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
115 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
116 /// its data through this interface, to ensure that only valid data can be read.
118
119public:
120 /////////////////////////////////////////////////////////////////////////////
121 /// \brief Check if the pointer currently holds a valid value.
123
124 /////////////////////////////////////////////////////////////////////////////
125 /// \brief Get a shared pointer to the field value managed by the processor's entry.
126 ///
127 /// \return A `std::shared_ptr<T>` if the field is valid in the current entry, or a `nullptr` otherwise.
128 std::shared_ptr<T> GetPtr() const
129 {
132 return value.template GetPtr<T>();
133 }
134
135 return nullptr;
136 }
137
138 /////////////////////////////////////////////////////////////////////////////
139 /// \brief Bind the value to `valuePtr`.
140 ///
141 /// \param[in] valuePtr Pointer to bind the value to.
142 ///
143 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
144 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
145 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
146 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
147 /// its data through this interface, to ensure that only valid data can be read.
148 void Bind(std::shared_ptr<T> valuePtr) { fProcessorEntry->Bind(fFieldIndex, std::move(valuePtr)); }
149
150 /////////////////////////////////////////////////////////////////////////////
151 /// \brief Get a reference to the field value managed by the processor's entry.
152 ///
153 /// Throws an exception if the field is invalid in the processor's current entry.
154 const T &operator*() const
155 {
156 if (auto ptr = GetPtr())
157 return *ptr;
158 else
159 throw RException(R__FAIL("cannot read \"" + fProcessorEntry->FindFieldName(fFieldIndex) +
160 "\" because it has no value for the current entry"));
161 }
162
163 /////////////////////////////////////////////////////////////////////////////
164 /// \brief Access the field value managed by the processor's entry.
165 ///
166 /// Throws an exception if the field is invalid in the processor's current entry.
167 const T *operator->() const
168 {
169 if (auto ptr = GetPtr())
170 return ptr.get();
171 else
172 throw RException(R__FAIL("cannot read \"" + fProcessorEntry->FindFieldName(fFieldIndex) +
173 "\" because it has no value for the current entry"));
174 }
175};
176
177// clang-format off
178/**
179\class ROOT::Experimental::RNTupleProcessorOptionalPtr<void>
180\ingroup NTuple
181\brief Specialization of RNTupleProcessorOptionalPtr<T> for `void`-type pointers.
182*/
183// clang-format on
184template <>
186 friend class RNTupleProcessor;
187
188private:
191
197
198 /////////////////////////////////////////////////////////////////////////////
199 /// \brief Get a non-owning pointer to the field value managed by the processor's entry.
200 ///
201 /// \return A `void*` if the field is valid in the current entry, or a `nullptr` otherwise.
202 void *GetRawPtr() const { return GetPtr().get(); }
203
204 /////////////////////////////////////////////////////////////////////////////
205 /// \brief Bind the value to `valuePtr`.
206 ///
207 /// \param[in] valuePtr Pointer to bind the value to.
208 ///
209 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
210 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
211 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
212 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
213 /// its data through this interface, to ensure that only valid data can be read.
215
216public:
217 /////////////////////////////////////////////////////////////////////////////
218 /// \brief Check if the pointer currently holds a valid value.
220
221 /////////////////////////////////////////////////////////////////////////////
222 /// \brief Get the pointer to the field value managed by the processor's entry.
223 ///
224 /// \return A `std::shared_ptr<void>` if the field is valid in the current entry, or a `nullptr` otherwise.
225 std::shared_ptr<void> GetPtr() const
226 {
229 return value.template GetPtr<void>();
230 }
231
232 return nullptr;
233 }
234
235 /////////////////////////////////////////////////////////////////////////////
236 /// \brief Bind the value to `valuePtr`.
237 ///
238 /// \param[in] valuePtr Pointer to bind the value to.
239 ///
240 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
241 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
242 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
243 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
244 /// its data through this interface, to ensure that only valid data can be read.
245 void Bind(std::shared_ptr<void> valuePtr) { fProcessorEntry->Bind(fFieldIndex, std::move(valuePtr)); }
246};
247
248// clang-format off
249/**
250\class ROOT::Experimental::RNTupleProcessor
251\ingroup NTuple
252\brief Interface for iterating over entries of vertically ("chained") and/or horizontally ("joined") combined RNTuples.
253
254Example usage (see ntpl012_processor_chain.C and ntpl015_processor_join.C for bigger examples):
255
256~~~{.cpp}
257#include <ROOT/RNTupleProcessor.hxx>
258using ROOT::Experimental::RNTupleProcessor;
259using ROOT::Experimental::RNTupleOpenSpec;
260
261std::vector<RNTupleOpenSpec> ntuples = {{"ntuple1", "ntuple1.root"}, {"ntuple2", "ntuple2.root"}};
262auto processor = RNTupleProcessor::CreateChain(ntuples);
263
264auto pt = processor->RequestField<float>("pt");
265
266for (const auto idx : *processor) {
267 std::cout << "event = " << idx << ", pt = " << *pt << std::endl;
268}
269~~~
270
271An RNTupleProcessor is created either:
2721. By providing one or more RNTupleOpenSpecs, each of which contains the name and storage location of a single RNTuple;
2732. By providing a previously created RNTupleProcessor.
274
275The RNTupleProcessor provides an iterator which gives access to the index of the current *global* entry of the
276processor, i.e. taking into account previously processed RNTuples.
277
278Because the schemas of each RNTuple that are part of an RNTupleProcessor may not necessarily be identical, or because
279it can occur that entries are only partially complete in a join-based processor, field values may be marked as
280"invalid", at which point their data should not be read. This is handled by the RNTupleProcessorOptionalPtr
281that is returned by RequestField().
282*/
283// clang-format on
289
290protected:
292
293 std::shared_ptr<Internal::RNTupleProcessorEntry> fEntry = nullptr;
294 std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> fFieldIdxs;
295
296 /// Total number of entries. Only to be used internally by the processor, not meant to be exposed in the public
297 /// interface.
299
300 ROOT::NTupleSize_t fNEntriesProcessed = 0; //< Total number of entries processed so far
301
302 /////////////////////////////////////////////////////////////////////////////
303 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
304 virtual void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry) = 0;
305
306 /////////////////////////////////////////////////////////////////////////////
307 /// \brief Check if the processor already has been initialized.
308 bool IsInitialized() const { return fEntry != nullptr; }
309
310 /////////////////////////////////////////////////////////////////////////////
311 /// \brief Connect fields to the page source of the processor's underlying RNTuple(s).
312 ///
313 /// \param[in] fieldIdxs Indices of the fields to connect.
314 /// \param[in] provenance Provenance of the processor.
315 /// \param[in] updateFields Whether the fields in the entry need to be updated, because the current underlying
316 /// RNTuple source changed.
317 virtual void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
319
320 /////////////////////////////////////////////////////////////////////////////
321 /// \brief Load the entry identified by the provided entry number.
322 ///
323 /// \param[in] entryNumber Entry number to load
324 ///
325 /// \return `entryNumber` if the entry was successfully loaded, `kInvalidNTupleIndex` otherwise.
327
328 /////////////////////////////////////////////////////////////////////////////
329 /// \brief Get the total number of entries in this processor
331
332 /////////////////////////////////////////////////////////////////////////////
333 /// \brief Check if a field exists on-disk and can be read by the processor.
334 ///
335 /// \param[in] fieldName Name of the field to check.
336 virtual bool CanReadFieldFromDisk(std::string_view fieldName) = 0;
337
338 /////////////////////////////////////////////////////////////////////////////
339 /// \brief Add a field to the entry.
340 ///
341 ///
342 /// \param[in] fieldName Name of the field to add.
343 /// \param[in] typeName Type of the field to add.
344 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
345 /// created.
346 /// \param[in] provenance Provenance of the processor.
347 ///
348 /// \return The index of the newly added field in the entry.
349 ///
350 /// In case the field was already present in the entry, the index of the existing field is returned.
352 AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr,
354
355 /////////////////////////////////////////////////////////////////////////////
356 /// \brief Add the entry mappings for this processor to the provided join table.
357 ///
358 /// \param[in] joinTable the join table to map the entries to.
359 /// \param[in] entryOffset In case the entry mapping is added from a chain, the offset of the entry indexes to use
360 /// with respect to the processor's position in the chain.
362
363 /////////////////////////////////////////////////////////////////////////////
364 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
365 ///
366 /// \param[in,out] output Output stream to print to.
367 virtual void PrintStructureImpl(std::ostream &output) const = 0;
368
369 /////////////////////////////////////////////////////////////////////////////
370 /// \brief Create a new base RNTupleProcessor.
371 ///
372 /// \param[in] processorName Name of the processor. By default, this is the name of the underlying RNTuple for
373 /// RNTupleSingleProcessor, the name of the first processor for RNTupleChainProcessor, or the name of the primary
374 /// RNTuple for RNTupleJoinProcessor.
376
377public:
382 virtual ~RNTupleProcessor() = default;
383
384 /////////////////////////////////////////////////////////////////////////////
385 /// \brief Get the options used for this processor.
386 const RNTupleProcessorOptions &GetOptions() const { return fOptions; }
387
388 /////////////////////////////////////////////////////////////////////////////
389 /// \brief Get the total number of entries processed so far.
391
392 /////////////////////////////////////////////////////////////////////////////
393 /// \brief Request access to a field for reading during processing.
394 ///
395 /// \tparam T Type of the requested field.
396 ///
397 /// \param[in] fieldName Name of the requested field.
398 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
399 /// created.
400 ///
401 /// \return An RNTupleProcessorOptionalPtr of type `T`, which provides access to the field's value.
402 ///
403 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
404 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
405 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
406 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
407 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
408 template <typename T>
410 {
412 std::string typeName{};
413 if constexpr (!std::is_void_v<T>) {
414 typeName = ROOT::Internal::GetRenormalizedTypeName(typeid(T));
415 }
418 }
419
420 /////////////////////////////////////////////////////////////////////////////
421 /// \brief Request access to a field for reading during processing.
422 ///
423 /// \param[in] fieldName Name of the requested field.
424 /// \param[in] typeName Type of the requested field.
425 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
426 /// created.
427 ///
428 /// \return An void-type RNTupleProcessorOptionalPtr, which provides access to the field's value.
429 ///
430 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
431 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
432 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
433 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
434 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
436 RequestField(const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr)
437 {
441 }
442
443 /////////////////////////////////////////////////////////////////////////////
444 /// \brief Print a graphical representation of the processor composition.
445 ///
446 /// \param[in,out] output Stream to print to (default is stdout).
447 ///
448 /// ### Example:
449 /// The structure of a processor representing a join between a single primary RNTuple and a chain of two auxiliary
450 /// RNTuples will be printed as follows:
451 /// ~~~
452 /// +-----------------------------+ +-----------------------------+
453 /// | ntuple | | ntuple_aux |
454 /// | ntuple.root | | ntuple_aux1.root |
455 /// +-----------------------------+ +-----------------------------+
456 /// +-----------------------------+
457 /// | ntuple_aux |
458 /// | ntuple_aux2.root |
459 /// +-----------------------------+
460 /// ~~~
461 void PrintStructure(std::ostream &output = std::cout) { PrintStructureImpl(output); }
462
463 // clang-format off
464 /**
465 \class ROOT::Experimental::RNTupleProcessor::RIterator
466 \ingroup NTuple
467 \brief Iterator over the entries of an RNTuple, or vertical concatenation thereof.
468 */
469 // clang-format on
470 class RIterator {
471 private:
474
475 public:
476 using iterator_category = std::input_iterator_tag;
479 using difference_type = std::ptrdiff_t;
482
485 {
486 if (!fProcessor.fEntry) {
488 }
489 // This constructor is called with kInvalidNTupleIndex for RNTupleProcessor::end(). In that case, we already
490 // know there is nothing to load.
493 /*updateFields=*/false);
495 }
496 }
497
503
505 {
506 auto obj = *this;
507 ++(*this);
508 return obj;
509 }
510
512
513 friend bool operator!=(const iterator &lh, const iterator &rh)
514 {
515 return lh.fCurrentEntryNumber != rh.fCurrentEntryNumber;
516 }
517 friend bool operator==(const iterator &lh, const iterator &rh)
518 {
519 return lh.fCurrentEntryNumber == rh.fCurrentEntryNumber;
520 }
521 };
522
523 RIterator begin() { return RIterator(*this, 0); }
525
526 /////////////////////////////////////////////////////////////////////////////
527 /// \brief Create an RNTupleProcessor for a single RNTuple.
528 ///
529 /// \param[in] ntuple The name and storage location of the RNTuple to process.
530 /// \param[in] opts Options for the processor.
531 ///
532 /// \return A pointer to the newly created RNTupleProcessor.
533 static std::unique_ptr<RNTupleProcessor>
535
536 /////////////////////////////////////////////////////////////////////////////
537 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of RNTuples.
538 ///
539 /// \param[in] ntuples A list specifying the names and locations of the RNTuples to process.
540 /// \param[in] opts Options for the processor.
541 ///
542 /// \return A pointer to the newly created RNTupleProcessor.
543 static std::unique_ptr<RNTupleProcessor>
544 CreateChain(std::vector<RNTupleOpenSpec> ntuples, const RNTupleProcessorOptions &opts = RNTupleProcessorOptions());
545
546 /////////////////////////////////////////////////////////////////////////////
547 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of other RNTupleProcessors.
548 ///
549 /// \param[in] innerProcessors A list with the processors to chain.
550 /// \param[in] opts Options for the processor.
551 ///
552 /// \return A pointer to the newly created RNTupleProcessor.
553 static std::unique_ptr<RNTupleProcessor>
554 CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
556
557 /////////////////////////////////////////////////////////////////////////////
558 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
559 ///
560 /// \param[in] primaryNTuple The name and location of the primary RNTuple. Its entries are processed in sequential
561 /// order.
562 /// \param[in] auxNTuple The name and location of the RNTuple to join the primary RNTuple with. The order in which
563 /// its entries are processed is determined by the primary RNTuple and doesn't necessarily have to be sequential.
564 /// \param[in] joinFields The names of the fields on which to join, in case the specified RNTuples are unaligned.
565 /// The join is made based on the combined join field values, and therefore each field has to be present in each
566 /// specified RNTuple. If an empty list is provided, it is assumed that the specified ntuple are fully aligned.
567 /// \param[in] opts Options for the processor.
568 ///
569 /// \return A pointer to the newly created RNTupleProcessor.
570 static std::unique_ptr<RNTupleProcessor> CreateJoin(RNTupleOpenSpec primaryNTuple, RNTupleOpenSpec auxNTuple,
571 const std::vector<std::string> &joinFields,
573
574 /////////////////////////////////////////////////////////////////////////////
575 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
576 ///
577 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
578 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
579 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
580 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
581 /// The join is made based on the combined join field values, and therefore each field has to be present in each
582 /// specified processors. If an empty list is provided, it is assumed that the specified processors are fully
583 /// aligned.
584 /// \param[in] opts Options for the processor.
585 ///
586 /// \return A pointer to the newly created RNTupleProcessor.
587 static std::unique_ptr<RNTupleProcessor> CreateJoin(std::unique_ptr<RNTupleProcessor> primaryProcessor,
588 std::unique_ptr<RNTupleProcessor> auxProcessor,
589 const std::vector<std::string> &joinFields,
591};
592
593// clang-format off
594/**
595\class ROOT::Experimental::RNTupleSingleProcessor
596\ingroup NTuple
597\brief Processor specialization for processing a single RNTuple.
598*/
599// clang-format on
601 friend class RNTupleProcessor;
602
603private:
605 std::unique_ptr<ROOT::Internal::RPageSource> fPageSource;
606
607 /////////////////////////////////////////////////////////////////////////////
608 /// \brief Create a new field and connect it to the processor's page source.
609 ///
610 /// \param[in] qualifiedFieldName Name of the field to add, prefixed with its parent fields, if applicable.
611 /// \param[in] typeName Type of the field to add.
612 ///
613 /// \return The newly created field.
614 /// \throws ROOT::RException In case the requested field cannot be found on disk.
615 std::unique_ptr<ROOT::RFieldBase>
616 CreateAndConnectField(const std::string &qualifiedFieldName, const std::string &typeName);
617
618 /////////////////////////////////////////////////////////////////////////////
619 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
620 ///
621 /// At this point, the page source for the underlying RNTuple of the processor will be created and opened.
622 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
623
624 /////////////////////////////////////////////////////////////////////////////
625 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
626 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
628 bool updateFields = false) final;
629
630 /////////////////////////////////////////////////////////////////////////////
631 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
632 /// processor).
633 ///
634 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
636
637 /////////////////////////////////////////////////////////////////////////////
638 /// \brief Get the total number of entries in this processor.
645
646 /////////////////////////////////////////////////////////////////////////////
647 /// \brief Check if a field exists on-disk and can be read by the processor.
648 ///
649 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
650 bool CanReadFieldFromDisk(std::string_view fieldName) final;
651
652 /////////////////////////////////////////////////////////////////////////////
653 /// \brief Add a field to the entry.
654 ///
655 /// \sa RNTupleProcessor::AddFieldToEntry()
657 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
659
660 /////////////////////////////////////////////////////////////////////////////
661 /// \brief Add the entry mappings for this processor to the provided join table.
662 ///
663 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
664 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
665
666 /////////////////////////////////////////////////////////////////////////////
667 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
668 ///
669 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
670 void PrintStructureImpl(std::ostream &output) const final;
671
672 /////////////////////////////////////////////////////////////////////////////
673 /// \brief Construct a new RNTupleProcessor for processing a single RNTuple.
674 ///
675 /// \param[in] ntuple The source specification (name and storage location) for the RNTuple to process.
676 /// \param[in] opts Options for the processor.
678
679public:
685 {
686 // The entry's fields need to be deleted before fPageSource.
687 if (fEntry)
688 fEntry->Clear();
689 };
690};
691
692// clang-format off
693/**
694\class ROOT::Experimental::RNTupleChainProcessor
695\ingroup NTuple
696\brief Processor specialization for vertically combined (*chained*) RNTupleProcessors.
697*/
698// clang-format on
700 friend class RNTupleProcessor;
701
702private:
703 std::vector<std::unique_ptr<RNTupleProcessor>> fInnerProcessors;
704 std::vector<ROOT::NTupleSize_t> fInnerNEntries;
705
706 ROOT::NTupleSize_t fLastLoadedEntry = 0; //< Last (global) entry number that was loaded
707 std::size_t fCurrentProcessorNumber = 0; //< Number of the currently open inner processor
708
710
711 /////////////////////////////////////////////////////////////////////////////
712 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
713 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
714
715 /////////////////////////////////////////////////////////////////////////////
716 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
717 ///
718 /// \sa RNTupleProcessor::Connect()
719 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
721 bool updateFields = false) final;
722
723 /////////////////////////////////////////////////////////////////////////////
724 /// \brief Update the entry to reflect any missing fields in the current inner processor.
725 void ConnectInnerProcessor(std::size_t processorNumber);
726
727 /////////////////////////////////////////////////////////////////////////////
728 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
729 /// processor).
730 ///
731 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
733
734 /////////////////////////////////////////////////////////////////////////////
735 /// \brief Get the total number of entries in this processor.
736 ///
737 /// \note This requires opening all underlying RNTuples being processed in the chain, and could become costly!
739
740 /////////////////////////////////////////////////////////////////////////////
741 /// \brief Check if a field exists on-disk and can be read by the processor.
742 ///
743 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
744 bool CanReadFieldFromDisk(std::string_view fieldName) final
745 {
746 return fInnerProcessors[fCurrentProcessorNumber]->CanReadFieldFromDisk(fieldName);
747 }
748
749 /////////////////////////////////////////////////////////////////////////////
750 /// \brief Add a field to the entry.
751 ///
752 /// \sa RNTupleProcessor::AddFieldToEntry()
754 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
756
757 /////////////////////////////////////////////////////////////////////////////
758 /// \brief Add the entry mappings for this processor to the provided join table.
759 ///
760 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
761 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
762
763 /////////////////////////////////////////////////////////////////////////////
764 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
765 ///
766 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
767 void PrintStructureImpl(std::ostream &output) const final;
768
769 /////////////////////////////////////////////////////////////////////////////
770 /// \brief Construct a new RNTupleChainProcessor.
771 ///
772 /// \param[in] ntuples The source specification (name and storage location) for each RNTuple to process.
773 /// \param[in] opts Options for the processor.
774 ///
775 /// RNTuples are processed in the order in which they are specified.
776 RNTupleChainProcessor(std::vector<std::unique_ptr<RNTupleProcessor>> processors,
778
779public:
785};
786
787// clang-format off
788/**
789\class ROOT::Experimental::RNTupleJoinProcessor
790\ingroup NTuple
791\brief Processor specialization for horizontally combined (*joined*) RNTupleProcessors.
792*/
793// clang-format on
795 friend class RNTupleProcessor;
796
797private:
798 std::unique_ptr<RNTupleProcessor> fPrimaryProcessor;
799 std::unique_ptr<RNTupleProcessor> fAuxiliaryProcessor;
800
801 std::vector<std::string> fJoinFieldNames;
802 std::set<Internal::RNTupleProcessorEntry::FieldIndex_t> fJoinFieldIdxs;
803
804 std::unique_ptr<Internal::RNTupleJoinTable> fJoinTable;
805 bool fJoinTableIsBuilt = false;
806
807 std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> fAuxiliaryFieldIdxs;
808
809 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
810 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
811
812 /////////////////////////////////////////////////////////////////////////////
813 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
814 ///
815 /// \sa RNTupleProcessor::Connect()
816 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
818 bool updateFields = false) final;
819
820 /////////////////////////////////////////////////////////////////////////////
821 /// \brief Load the entry identified by the provided entry number of the primary processor.
822 ///
823 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
825
826 /////////////////////////////////////////////////////////////////////////////
827 /// \brief Get the total number of entries in this processor.
829
830 /////////////////////////////////////////////////////////////////////////////
831 /// \brief Set the validity for all fields in the auxiliary processor at once.
832 void SetAuxiliaryFieldValidity(bool validity);
833
834 /////////////////////////////////////////////////////////////////////////////
835 /// \brief Check if a field exists on-disk and can be read by the processor.
836 ///
837 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
838 bool CanReadFieldFromDisk(std::string_view fieldName) final
839 {
840 if (!fPrimaryProcessor->CanReadFieldFromDisk(fieldName)) {
841 if (fieldName.find(fAuxiliaryProcessor->fOptions.GetProcessorName()) == 0)
842 fieldName = fieldName.substr(fAuxiliaryProcessor->fOptions.GetProcessorName().size() + 1);
843 return fAuxiliaryProcessor->CanReadFieldFromDisk(fieldName);
844 }
845
846 return true;
847 }
848
849 /////////////////////////////////////////////////////////////////////////////
850 /// \brief Add a field to the entry.
851 ///
852 /// \sa RNTupleProcessor::AddFieldToEntry()
854 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
856
857 /////////////////////////////////////////////////////////////////////////////
858 /// \brief Add the entry mappings for this processor to the provided join table.
859 ///
860 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
861 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
862
863 /////////////////////////////////////////////////////////////////////////////
864 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
865 ///
866 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
867 void PrintStructureImpl(std::ostream &output) const final;
868
869 /////////////////////////////////////////////////////////////////////////////
870 /// \brief Construct a new RNTupleJoinProcessor.
871 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
872 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
873 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
874 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
875 /// The join is made based on the combined join field values, and therefore each field has to be present in each
876 /// specified processor. If an empty list is provided, it is assumed that the processors are fully aligned.
877 /// \param[in] opts Options for the processor.
879 std::unique_ptr<RNTupleProcessor> auxProcessor, const std::vector<std::string> &joinFields,
881
882public:
888};
889
890} // namespace Experimental
891} // namespace ROOT
892
893#endif // ROOT_RNTupleProcessor
#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:322
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
char name[80]
Definition TGX11.cxx:142
Builds a join table on one or several fields of an RNTuple so it can be joined onto other RNTuples.
Collection of values in an RNTupleProcessor, analogous to REntry, with checks and support for missing...
void Bind(FieldIndex_t fieldIdx, std::shared_ptr< void > valuePtr)
Bind a new value pointer to a field in the entry.
void BindRawPtr(FieldIndex_t fieldIdx, void *valuePtr)
Bind a new value pointer to a field in the entry.
const ROOT::RFieldBase::RValue & GetValue(FieldIndex_t fieldIdx) const
bool IsValidField(FieldIndex_t fieldIdx) const
Check whether a field is valid for reading.
const std::string & FindFieldName(FieldIndex_t fieldIdx) const
Find the name of a field from its field index.
Processor specialization for vertically combined (chained) RNTupleProcessors.
bool CanReadFieldFromDisk(std::string_view fieldName) final
Check if a field exists on-disk and can be read by the processor.
void PrintStructureImpl(std::ostream &output) const final
Processor-specific implementation for printing its structure, called by PrintStructure().
void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0) final
Add the entry mappings for this processor to the provided join table.
void ConnectInnerProcessor(std::size_t processorNumber)
Update the entry to reflect any missing fields in the current inner processor.
Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance()) final
Add a field to the entry.
Internal::RNTupleProcessorProvenance fProvenance
ROOT::NTupleSize_t GetNEntries() final
Get the total number of entries in this processor.
void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry=nullptr) final
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
std::vector< ROOT::NTupleSize_t > fInnerNEntries
void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance(), bool updateFields=false) final
Connect the provided fields indices in the entry to their on-disk fields.
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) RNTupleProcessors.
std::set< Internal::RNTupleProcessorEntry::FieldIndex_t > fJoinFieldIdxs
std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > fAuxiliaryFieldIdxs
std::unique_ptr< RNTupleProcessor > fPrimaryProcessor
std::unique_ptr< Internal::RNTupleJoinTable > fJoinTable
std::unique_ptr< RNTupleProcessor > fAuxiliaryProcessor
Specification of the name and location of an RNTuple, used for creating a new RNTupleProcessor.
RNTupleOpenSpec(std::string_view n, const std::string &s)
std::variant< std::string, TDirectory * > fStorage
RNTupleOpenSpec(std::string_view n, TDirectory *s)
std::unique_ptr< ROOT::Internal::RPageSource > CreatePageSource() const
RNTupleProcessorOptionalPtr(Internal::RNTupleProcessorEntry *processorEntry, Internal::RNTupleProcessorEntry::FieldIndex_t fieldIdx)
void BindRawPtr(void *valuePtr)
Bind the value to valuePtr.
void * GetRawPtr() const
Get a non-owning pointer to the field value managed by the processor's entry.
Internal::RNTupleProcessorEntry::FieldIndex_t fFieldIndex
std::shared_ptr< void > GetPtr() const
Get the pointer to the field value managed by the processor's entry.
bool HasValue() const
Check if the pointer currently holds a valid value.
void Bind(std::shared_ptr< void > valuePtr)
Bind the value to valuePtr.
std::shared_ptr< T > GetPtr() const
Get a shared pointer to the field value managed by the processor's entry.
void Bind(std::shared_ptr< T > valuePtr)
Bind the value to valuePtr.
const T & operator*() const
Get a reference to the field value managed by the processor's entry.
Internal::RNTupleProcessorEntry::FieldIndex_t fFieldIndex
const T * operator->() const
Access the field value managed by the processor's entry.
void BindRawPtr(T *valuePtr)
Bind the value to valuePtr.
bool HasValue() const
Check if the pointer currently holds a valid value.
T * GetRawPtr() const
Get a non-owning pointer to the field value managed by the processor's entry.
Internal::RNTupleProcessorEntry * fProcessorEntry
RNTupleProcessorOptionalPtr(Internal::RNTupleProcessorEntry *processorEntry, Internal::RNTupleProcessorEntry::FieldIndex_t fieldIdx)
std::string fProcessorName
By default, the processor name is the name of the underlying RNTuple for RNTupleSingleProcessor,...
Identifies how a processor is composed.
Iterator over the entries of an RNTuple, or vertical concatenation thereof.
friend bool operator==(const iterator &lh, const iterator &rh)
friend bool operator!=(const iterator &lh, const iterator &rh)
RIterator(RNTupleProcessor &processor, ROOT::NTupleSize_t entryNumber)
Interface for iterating over entries of vertically ("chained") and/or horizontally ("joined") combine...
virtual bool CanReadFieldFromDisk(std::string_view fieldName)=0
Check if a field exists on-disk and can be read by the processor.
static std::unique_ptr< RNTupleProcessor > CreateChain(std::vector< RNTupleOpenSpec > ntuples, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a chain (i.e., a vertical combination) of RNTuples.
RNTupleProcessorOptionalPtr< T > RequestField(const std::string &fieldName, void *valuePtr=nullptr)
Request access to a field for reading during processing.
virtual ROOT::NTupleSize_t GetNEntries()=0
Get the total number of entries in this processor.
ROOT::NTupleSize_t fNEntries
Total number of entries.
RNTupleProcessorOptionalPtr< void > RequestField(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr)
Request access to a field for reading during processing.
friend struct ROOT::Experimental::Internal::RNTupleProcessorEntryLoader
static std::unique_ptr< RNTupleProcessor > CreateJoin(RNTupleOpenSpec primaryNTuple, RNTupleOpenSpec auxNTuple, const std::vector< std::string > &joinFields, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a join (i.e., a horizontal combination) of RNTuples.
const RNTupleProcessorOptions & GetOptions() const
Get the options used for this processor.
RNTupleProcessor(RNTupleProcessor &&)=delete
std::shared_ptr< Internal::RNTupleProcessorEntry > fEntry
virtual void PrintStructureImpl(std::ostream &output) const =0
Processor-specific implementation for printing its structure, called by PrintStructure().
virtual ROOT::NTupleSize_t LoadEntry(ROOT::NTupleSize_t entryNumber)=0
Load the entry identified by the provided entry number.
virtual void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance, bool updateFields)=0
Connect fields to the page source of the processor's underlying RNTuple(s).
std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > fFieldIdxs
virtual void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry)=0
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
bool IsInitialized() const
Check if the processor already has been initialized.
virtual void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0)=0
Add the entry mappings for this processor to the provided join table.
virtual Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr, const Internal::RNTupleProcessorProvenance &provenance)=0
Add a field to the entry.
void PrintStructure(std::ostream &output=std::cout)
Print a graphical representation of the processor composition.
ROOT::NTupleSize_t GetNEntriesProcessed() const
Get the total number of entries processed so far.
RNTupleProcessor(const RNTupleProcessorOptions &options)
Create a new base RNTupleProcessor.
RNTupleProcessor(const RNTupleProcessor &)=delete
RNTupleProcessor & operator=(RNTupleProcessor &&)=delete
static std::unique_ptr< RNTupleProcessor > Create(RNTupleOpenSpec ntuple, const RNTupleProcessorOptions &opts=RNTupleProcessorOptions())
Create an RNTupleProcessor for a single RNTuple.
RNTupleProcessor & operator=(const RNTupleProcessor &)=delete
Processor specialization for processing a single RNTuple.
void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset=0) final
Add the entry mappings for this processor to the provided join table.
void Connect(const std::unordered_set< Internal::RNTupleProcessorEntry::FieldIndex_t > &fieldIdxs, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance(), bool updateFields=false) final
Connect the provided fields indices in the entry to their on-disk fields.
void Initialize(std::shared_ptr< Internal::RNTupleProcessorEntry > entry=nullptr) final
Initialize the processor by creating an (initially empty) fEntry, or setting an existing one.
void PrintStructureImpl(std::ostream &output) const final
Processor-specific implementation for printing its structure, called by PrintStructure().
std::unique_ptr< ROOT::Internal::RPageSource > fPageSource
bool CanReadFieldFromDisk(std::string_view fieldName) final
Check if a field exists on-disk and can be read by the processor.
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...
Internal::RNTupleProcessorEntry::FieldIndex_t AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr=nullptr, const Internal::RNTupleProcessorProvenance &provenance=Internal::RNTupleProcessorProvenance()) final
Add a field to the entry.
ROOT::NTupleSize_t GetNEntries() final
Get the total number of entries in this processor.
std::unique_ptr< ROOT::RFieldBase > CreateAndConnectField(const std::string &qualifiedFieldName, const std::string &typeName)
Create a new field and connect it to the processor's page source.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Describe directory structure in memory.
Definition TDirectory.h:45
const Int_t n
Definition legend1.C:16
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.