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 ROOT::NTupleSize_t fCurrentEntryNumber = 0; //< Current processor entry number
302 std::size_t fCurrentProcessorNumber = 0; //< Number of the currently open inner processor
303
304 /////////////////////////////////////////////////////////////////////////////
305 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
306 virtual void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry) = 0;
307
308 /////////////////////////////////////////////////////////////////////////////
309 /// \brief Check if the processor already has been initialized.
310 bool IsInitialized() const { return fEntry != nullptr; }
311
312 /////////////////////////////////////////////////////////////////////////////
313 /// \brief Connect fields to the page source of the processor's underlying RNTuple(s).
314 ///
315 /// \param[in] fieldIdxs Indices of the fields to connect.
316 /// \param[in] provenance Provenance of the processor.
317 /// \param[in] updateFields Whether the fields in the entry need to be updated, because the current underlying
318 /// RNTuple source changed.
319 virtual void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
321
322 /////////////////////////////////////////////////////////////////////////////
323 /// \brief Load the entry identified by the provided entry number.
324 ///
325 /// \param[in] entryNumber Entry number to load
326 ///
327 /// \return `entryNumber` if the entry was successfully loaded, `kInvalidNTupleIndex` otherwise.
329
330 /////////////////////////////////////////////////////////////////////////////
331 /// \brief Get the total number of entries in this processor
333
334 /////////////////////////////////////////////////////////////////////////////
335 /// \brief Check if a field exists on-disk and can be read by the processor.
336 ///
337 /// \param[in] fieldName Name of the field to check.
338 virtual bool CanReadFieldFromDisk(std::string_view fieldName) = 0;
339
340 /////////////////////////////////////////////////////////////////////////////
341 /// \brief Add a field to the entry.
342 ///
343 ///
344 /// \param[in] fieldName Name of the field to add.
345 /// \param[in] typeName Type of the field to add.
346 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
347 /// created.
348 /// \param[in] provenance Provenance of the processor.
349 ///
350 /// \return The index of the newly added field in the entry.
351 ///
352 /// In case the field was already present in the entry, the index of the existing field is returned.
354 AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr,
356
357 /////////////////////////////////////////////////////////////////////////////
358 /// \brief Add the entry mappings for this processor to the provided join table.
359 ///
360 /// \param[in] joinTable the join table to map the entries to.
361 /// \param[in] entryOffset In case the entry mapping is added from a chain, the offset of the entry indexes to use
362 /// with respect to the processor's position in the chain.
364
365 /////////////////////////////////////////////////////////////////////////////
366 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
367 ///
368 /// \param[in,out] output Output stream to print to.
369 virtual void PrintStructureImpl(std::ostream &output) const = 0;
370
371 /////////////////////////////////////////////////////////////////////////////
372 /// \brief Create a new base RNTupleProcessor.
373 ///
374 /// \param[in] processorName Name of the processor. By default, this is the name of the underlying RNTuple for
375 /// RNTupleSingleProcessor, the name of the first processor for RNTupleChainProcessor, or the name of the primary
376 /// RNTuple for RNTupleJoinProcessor.
378
379public:
384 virtual ~RNTupleProcessor() = default;
385
386 /////////////////////////////////////////////////////////////////////////////
387 /// \brief Get the options used for this processor.
388 const RNTupleProcessorOptions &GetOptions() const { return fOptions; }
389
390 /////////////////////////////////////////////////////////////////////////////
391 /// \brief Get the total number of entries processed so far.
393
394 /////////////////////////////////////////////////////////////////////////////
395 /// \brief Get the entry number that is currently being processed.
397
398 /////////////////////////////////////////////////////////////////////////////
399 /// \brief Get the number of the inner processor currently being read.
400 ///
401 /// This method is only relevant for the RNTupleChainProcessor. For the other processors, 0 is always returned.
403
404 /////////////////////////////////////////////////////////////////////////////
405 /// \brief Request access to a field for reading during processing.
406 ///
407 /// \tparam T Type of the requested field.
408 ///
409 /// \param[in] fieldName Name of the requested field.
410 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
411 /// created.
412 ///
413 /// \return An RNTupleProcessorOptionalPtr of type `T`, which provides access to the field's value.
414 ///
415 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
416 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
417 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
418 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
419 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
420 template <typename T>
422 {
424 std::string typeName{};
425 if constexpr (!std::is_void_v<T>) {
426 typeName = ROOT::Internal::GetRenormalizedTypeName(typeid(T));
427 }
430 }
431
432 /////////////////////////////////////////////////////////////////////////////
433 /// \brief Request access to a field for reading during processing.
434 ///
435 /// \param[in] fieldName Name of the requested field.
436 /// \param[in] typeName Type of the requested field.
437 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
438 /// created.
439 ///
440 /// \return An void-type RNTupleProcessorOptionalPtr, which provides access to the field's value.
441 ///
442 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
443 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
444 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
445 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
446 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
448 RequestField(const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr)
449 {
453 }
454
455 /////////////////////////////////////////////////////////////////////////////
456 /// \brief Print a graphical representation of the processor composition.
457 ///
458 /// \param[in,out] output Stream to print to (default is stdout).
459 ///
460 /// ### Example:
461 /// The structure of a processor representing a join between a single primary RNTuple and a chain of two auxiliary
462 /// RNTuples will be printed as follows:
463 /// ~~~
464 /// +-----------------------------+ +-----------------------------+
465 /// | ntuple | | ntuple_aux |
466 /// | ntuple.root | | ntuple_aux1.root |
467 /// +-----------------------------+ +-----------------------------+
468 /// +-----------------------------+
469 /// | ntuple_aux |
470 /// | ntuple_aux2.root |
471 /// +-----------------------------+
472 /// ~~~
473 void PrintStructure(std::ostream &output = std::cout) { PrintStructureImpl(output); }
474
475 // clang-format off
476 /**
477 \class ROOT::Experimental::RNTupleProcessor::RIterator
478 \ingroup NTuple
479 \brief Iterator over the entries of an RNTuple, or vertical concatenation thereof.
480 */
481 // clang-format on
482 class RIterator {
483 private:
486
487 public:
488 using iterator_category = std::input_iterator_tag;
491 using difference_type = std::ptrdiff_t;
494
497 {
498 if (!fProcessor.fEntry) {
500 }
501 // This constructor is called with kInvalidNTupleIndex for RNTupleProcessor::end(). In that case, we already
502 // know there is nothing to load.
505 /*updateFields=*/false);
507 }
508 }
509
515
517 {
518 auto obj = *this;
519 ++(*this);
520 return obj;
521 }
522
524
525 friend bool operator!=(const iterator &lh, const iterator &rh)
526 {
527 return lh.fCurrentEntryNumber != rh.fCurrentEntryNumber;
528 }
529 friend bool operator==(const iterator &lh, const iterator &rh)
530 {
531 return lh.fCurrentEntryNumber == rh.fCurrentEntryNumber;
532 }
533 };
534
535 RIterator begin() { return RIterator(*this, 0); }
537
538 /////////////////////////////////////////////////////////////////////////////
539 /// \brief Create an RNTupleProcessor for a single RNTuple.
540 ///
541 /// \param[in] ntuple The name and storage location of the RNTuple to process.
542 /// \param[in] opts Options for the processor.
543 ///
544 /// \return A pointer to the newly created RNTupleProcessor.
545 static std::unique_ptr<RNTupleProcessor>
547
548 /////////////////////////////////////////////////////////////////////////////
549 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of RNTuples.
550 ///
551 /// \param[in] ntuples A list specifying the names and locations of the RNTuples to process.
552 /// \param[in] opts Options for the processor.
553 ///
554 /// \return A pointer to the newly created RNTupleProcessor.
555 static std::unique_ptr<RNTupleProcessor>
556 CreateChain(std::vector<RNTupleOpenSpec> ntuples, const RNTupleProcessorOptions &opts = RNTupleProcessorOptions());
557
558 /////////////////////////////////////////////////////////////////////////////
559 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of other RNTupleProcessors.
560 ///
561 /// \param[in] innerProcessors A list with the processors to chain.
562 /// \param[in] opts Options for the processor.
563 ///
564 /// \return A pointer to the newly created RNTupleProcessor.
565 static std::unique_ptr<RNTupleProcessor>
566 CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
568
569 /////////////////////////////////////////////////////////////////////////////
570 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
571 ///
572 /// \param[in] primaryNTuple The name and location of the primary RNTuple. Its entries are processed in sequential
573 /// order.
574 /// \param[in] auxNTuple The name and location of the RNTuple to join the primary RNTuple with. The order in which
575 /// its entries are processed is determined by the primary RNTuple and doesn't necessarily have to be sequential.
576 /// \param[in] joinFields The names of the fields on which to join, in case the specified RNTuples are unaligned.
577 /// The join is made based on the combined join field values, and therefore each field has to be present in each
578 /// specified RNTuple. If an empty list is provided, it is assumed that the specified ntuple are fully aligned.
579 /// \param[in] opts Options for the processor.
580 ///
581 /// \return A pointer to the newly created RNTupleProcessor.
582 static std::unique_ptr<RNTupleProcessor> CreateJoin(RNTupleOpenSpec primaryNTuple, RNTupleOpenSpec auxNTuple,
583 const std::vector<std::string> &joinFields,
585
586 /////////////////////////////////////////////////////////////////////////////
587 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
588 ///
589 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
590 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
591 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
592 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
593 /// The join is made based on the combined join field values, and therefore each field has to be present in each
594 /// specified processors. If an empty list is provided, it is assumed that the specified processors are fully
595 /// aligned.
596 /// \param[in] opts Options for the processor.
597 ///
598 /// \return A pointer to the newly created RNTupleProcessor.
599 static std::unique_ptr<RNTupleProcessor> CreateJoin(std::unique_ptr<RNTupleProcessor> primaryProcessor,
600 std::unique_ptr<RNTupleProcessor> auxProcessor,
601 const std::vector<std::string> &joinFields,
603};
604
605// clang-format off
606/**
607\class ROOT::Experimental::RNTupleSingleProcessor
608\ingroup NTuple
609\brief Processor specialization for processing a single RNTuple.
610*/
611// clang-format on
613 friend class RNTupleProcessor;
614
615private:
617 std::unique_ptr<ROOT::Internal::RPageSource> fPageSource;
618
619 /////////////////////////////////////////////////////////////////////////////
620 /// \brief Create a new field and connect it to the processor's page source.
621 ///
622 /// \param[in] qualifiedFieldName Name of the field to add, prefixed with its parent fields, if applicable.
623 /// \param[in] typeName Type of the field to add.
624 ///
625 /// \return The newly created field.
626 /// \throws ROOT::RException In case the requested field cannot be found on disk.
627 std::unique_ptr<ROOT::RFieldBase>
628 CreateAndConnectField(const std::string &qualifiedFieldName, const std::string &typeName);
629
630 /////////////////////////////////////////////////////////////////////////////
631 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
632 ///
633 /// At this point, the page source for the underlying RNTuple of the processor will be created and opened.
634 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
635
636 /////////////////////////////////////////////////////////////////////////////
637 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
638 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
640 bool updateFields = false) final;
641
642 /////////////////////////////////////////////////////////////////////////////
643 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
644 /// processor).
645 ///
646 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
648
649 /////////////////////////////////////////////////////////////////////////////
650 /// \brief Get the total number of entries in this processor.
657
658 /////////////////////////////////////////////////////////////////////////////
659 /// \brief Check if a field exists on-disk and can be read by the processor.
660 ///
661 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
662 bool CanReadFieldFromDisk(std::string_view fieldName) final;
663
664 /////////////////////////////////////////////////////////////////////////////
665 /// \brief Add a field to the entry.
666 ///
667 /// \sa RNTupleProcessor::AddFieldToEntry()
669 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
671
672 /////////////////////////////////////////////////////////////////////////////
673 /// \brief Add the entry mappings for this processor to the provided join table.
674 ///
675 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
676 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
677
678 /////////////////////////////////////////////////////////////////////////////
679 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
680 ///
681 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
682 void PrintStructureImpl(std::ostream &output) const final;
683
684 /////////////////////////////////////////////////////////////////////////////
685 /// \brief Construct a new RNTupleProcessor for processing a single RNTuple.
686 ///
687 /// \param[in] ntuple The source specification (name and storage location) for the RNTuple to process.
688 /// \param[in] opts Options for the processor.
690
691public:
697 {
698 // The entry's fields need to be deleted before fPageSource.
699 if (fEntry)
700 fEntry->Clear();
701 };
702};
703
704// clang-format off
705/**
706\class ROOT::Experimental::RNTupleChainProcessor
707\ingroup NTuple
708\brief Processor specialization for vertically combined (*chained*) RNTupleProcessors.
709*/
710// clang-format on
712 friend class RNTupleProcessor;
713
714private:
715 std::vector<std::unique_ptr<RNTupleProcessor>> fInnerProcessors;
716 std::vector<ROOT::NTupleSize_t> fInnerNEntries;
717
719
720 /////////////////////////////////////////////////////////////////////////////
721 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
722 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
723
724 /////////////////////////////////////////////////////////////////////////////
725 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
726 ///
727 /// \sa RNTupleProcessor::Connect()
728 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
730 bool updateFields = false) final;
731
732 /////////////////////////////////////////////////////////////////////////////
733 /// \brief Update the entry to reflect any missing fields in the current inner processor.
734 void ConnectInnerProcessor(std::size_t processorNumber);
735
736 /////////////////////////////////////////////////////////////////////////////
737 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
738 /// processor).
739 ///
740 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
742
743 /////////////////////////////////////////////////////////////////////////////
744 /// \brief Get the total number of entries in this processor.
745 ///
746 /// \note This requires opening all underlying RNTuples being processed in the chain, and could become costly!
748
749 /////////////////////////////////////////////////////////////////////////////
750 /// \brief Check if a field exists on-disk and can be read by the processor.
751 ///
752 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
753 bool CanReadFieldFromDisk(std::string_view fieldName) final
754 {
755 return fInnerProcessors[fCurrentProcessorNumber]->CanReadFieldFromDisk(fieldName);
756 }
757
758 /////////////////////////////////////////////////////////////////////////////
759 /// \brief Add a field to the entry.
760 ///
761 /// \sa RNTupleProcessor::AddFieldToEntry()
763 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
765
766 /////////////////////////////////////////////////////////////////////////////
767 /// \brief Add the entry mappings for this processor to the provided join table.
768 ///
769 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
770 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
771
772 /////////////////////////////////////////////////////////////////////////////
773 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
774 ///
775 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
776 void PrintStructureImpl(std::ostream &output) const final;
777
778 /////////////////////////////////////////////////////////////////////////////
779 /// \brief Construct a new RNTupleChainProcessor.
780 ///
781 /// \param[in] ntuples The source specification (name and storage location) for each RNTuple to process.
782 /// \param[in] opts Options for the processor.
783 ///
784 /// RNTuples are processed in the order in which they are specified.
785 RNTupleChainProcessor(std::vector<std::unique_ptr<RNTupleProcessor>> processors,
787
788public:
794};
795
796// clang-format off
797/**
798\class ROOT::Experimental::RNTupleJoinProcessor
799\ingroup NTuple
800\brief Processor specialization for horizontally combined (*joined*) RNTupleProcessors.
801*/
802// clang-format on
804 friend class RNTupleProcessor;
805
806private:
807 std::unique_ptr<RNTupleProcessor> fPrimaryProcessor;
808 std::unique_ptr<RNTupleProcessor> fAuxiliaryProcessor;
809
810 std::vector<std::string> fJoinFieldNames;
811 std::set<Internal::RNTupleProcessorEntry::FieldIndex_t> fJoinFieldIdxs;
812
813 std::unique_ptr<Internal::RNTupleJoinTable> fJoinTable;
814 bool fJoinTableIsBuilt = false;
815
816 std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> fAuxiliaryFieldIdxs;
817
818 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
819 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
820
821 /////////////////////////////////////////////////////////////////////////////
822 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
823 ///
824 /// \sa RNTupleProcessor::Connect()
825 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
827 bool updateFields = false) final;
828
829 /////////////////////////////////////////////////////////////////////////////
830 /// \brief Load the entry identified by the provided entry number of the primary processor.
831 ///
832 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
834
835 /////////////////////////////////////////////////////////////////////////////
836 /// \brief Get the total number of entries in this processor.
838
839 /////////////////////////////////////////////////////////////////////////////
840 /// \brief Set the validity for all fields in the auxiliary processor at once.
841 void SetAuxiliaryFieldValidity(bool validity);
842
843 /////////////////////////////////////////////////////////////////////////////
844 /// \brief Check if a field exists on-disk and can be read by the processor.
845 ///
846 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
847 bool CanReadFieldFromDisk(std::string_view fieldName) final
848 {
849 if (!fPrimaryProcessor->CanReadFieldFromDisk(fieldName)) {
850 if (fieldName.find(fAuxiliaryProcessor->fOptions.GetProcessorName()) == 0)
851 fieldName = fieldName.substr(fAuxiliaryProcessor->fOptions.GetProcessorName().size() + 1);
852 return fAuxiliaryProcessor->CanReadFieldFromDisk(fieldName);
853 }
854
855 return true;
856 }
857
858 /////////////////////////////////////////////////////////////////////////////
859 /// \brief Add a field to the entry.
860 ///
861 /// \sa RNTupleProcessor::AddFieldToEntry()
863 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
865
866 /////////////////////////////////////////////////////////////////////////////
867 /// \brief Add the entry mappings for this processor to the provided join table.
868 ///
869 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
870 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
871
872 /////////////////////////////////////////////////////////////////////////////
873 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
874 ///
875 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
876 void PrintStructureImpl(std::ostream &output) const final;
877
878 /////////////////////////////////////////////////////////////////////////////
879 /// \brief Construct a new RNTupleJoinProcessor.
880 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
881 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
882 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
883 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
884 /// The join is made based on the combined join field values, and therefore each field has to be present in each
885 /// specified processor. If an empty list is provided, it is assumed that the processors are fully aligned.
886 /// \param[in] opts Options for the processor.
888 std::unique_ptr<RNTupleProcessor> auxProcessor, const std::vector<std::string> &joinFields,
890
891public:
897};
898
899} // namespace Experimental
900} // namespace ROOT
901
902#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.
ROOT::NTupleSize_t GetCurrentEntryNumber() const
Get the entry number that is currently being processed.
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.
std::size_t GetCurrentProcessorNumber() const
Get the number of the inner processor currently being read.
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.