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
101public:
102 /////////////////////////////////////////////////////////////////////////////
103 /// \brief Check if the pointer currently holds a valid value.
105
106 /////////////////////////////////////////////////////////////////////////////
107 /// \brief Get a shared pointer to the field value managed by the processor's entry.
108 ///
109 /// \return A `std::shared_ptr<T>` if the field is valid in the current entry, or a `nullptr` otherwise.
110 std::shared_ptr<T> GetPtr() const
111 {
114 return value.template GetPtr<T>();
115 }
116
117 return nullptr;
118 }
119
120 /////////////////////////////////////////////////////////////////////////////
121 /// \brief Get a non-owning pointer to the field value managed by the processor's entry.
122 ///
123 /// \return A `T*` if the field is valid in the current entry, or a `nullptr` otherwise.
124 T *GetRawPtr() const { return GetPtr().get(); }
125
126 /////////////////////////////////////////////////////////////////////////////
127 /// \brief Bind the value to `valuePtr`.
128 ///
129 /// \param[in] valuePtr Pointer to bind the value to.
130 ///
131 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
132 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
133 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
134 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
135 /// its data through this interface, to ensure that only valid data can be read.
137
138 /////////////////////////////////////////////////////////////////////////////
139 /// \brief Get a reference to the field value managed by the processor's entry.
140 ///
141 /// Throws an exception if the field is invalid in the processor's current entry.
142 const T &operator*() const
143 {
144 if (auto ptr = GetPtr())
145 return *ptr;
146 else
147 throw RException(R__FAIL("cannot read \"" + fProcessorEntry->FindFieldName(fFieldIndex) +
148 "\" because it has no value for the current entry"));
149 }
150
151 /////////////////////////////////////////////////////////////////////////////
152 /// \brief Access the field value managed by the processor's entry.
153 ///
154 /// Throws an exception if the field is invalid in the processor's current entry.
155 const T *operator->() const
156 {
157 if (auto ptr = GetPtr())
158 return ptr.get();
159 else
160 throw RException(R__FAIL("cannot read \"" + fProcessorEntry->FindFieldName(fFieldIndex) +
161 "\" because it has no value for the current entry"));
162 }
163};
164
165// clang-format off
166/**
167\class ROOT::Experimental::RNTupleProcessorOptionalPtr<void>
168\ingroup NTuple
169\brief Specialization of RNTupleProcessorOptionalPtr<T> for `void`-type pointers.
170*/
171// clang-format on
172template <>
174 friend class RNTupleProcessor;
175
176private:
179
185
186public:
187 /////////////////////////////////////////////////////////////////////////////
188 /// \brief Check if the pointer currently holds a valid value.
190
191 /////////////////////////////////////////////////////////////////////////////
192 /// \brief Get the pointer to the field value managed by the processor's entry.
193 ///
194 /// \return A `std::shared_ptr<void>` if the field is valid in the current entry, or a `nullptr` otherwise.
195 std::shared_ptr<void> GetPtr() const
196 {
199 return value.template GetPtr<void>();
200 }
201
202 return nullptr;
203 }
204
205 /////////////////////////////////////////////////////////////////////////////
206 /// \brief Get a non-owning pointer to the field value managed by the processor's entry.
207 ///
208 /// \return A `void*` if the field is valid in the current entry, or a `nullptr` otherwise.
209 void *GetRawPtr() const { return GetPtr().get(); }
210
211 /////////////////////////////////////////////////////////////////////////////
212 /// \brief Bind the value to `valuePtr`.
213 ///
214 /// \param[in] valuePtr Pointer to bind the value to.
215 ///
216 /// \warning Use this function with care! Values may not always be valid for every entry during processing, for
217 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
218 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
219 /// invalid data. After binding a pointer to an `RNTupleProcessorOptionalPtr`, we *strongly* recommend only accessing
220 /// its data through this interface, to ensure that only valid data can be read.
222};
223
224// clang-format off
225/**
226\class ROOT::Experimental::RNTupleProcessor
227\ingroup NTuple
228\brief Interface for iterating over entries of vertically ("chained") and/or horizontally ("joined") combined RNTuples.
229
230Example usage (see ntpl012_processor_chain.C and ntpl015_processor_join.C for bigger examples):
231
232~~~{.cpp}
233#include <ROOT/RNTupleProcessor.hxx>
234using ROOT::Experimental::RNTupleProcessor;
235using ROOT::Experimental::RNTupleOpenSpec;
236
237std::vector<RNTupleOpenSpec> ntuples = {{"ntuple1", "ntuple1.root"}, {"ntuple2", "ntuple2.root"}};
238auto processor = RNTupleProcessor::CreateChain(ntuples);
239
240auto pt = processor->RequestField<float>("pt");
241
242for (const auto idx : *processor) {
243 std::cout << "event = " << idx << ", pt = " << *pt << std::endl;
244}
245~~~
246
247An RNTupleProcessor is created either:
2481. By providing one or more RNTupleOpenSpecs, each of which contains the name and storage location of a single RNTuple;
2492. By providing a previously created RNTupleProcessor.
250
251The RNTupleProcessor provides an iterator which gives access to the index of the current *global* entry of the
252processor, i.e. taking into account previously processed RNTuples.
253
254Because the schemas of each RNTuple that are part of an RNTupleProcessor may not necessarily be identical, or because
255it can occur that entries are only partially complete in a join-based processor, field values may be marked as
256"invalid", at which point their data should not be read. This is handled by the RNTupleProcessorOptionalPtr
257that is returned by RequestField().
258*/
259// clang-format on
265
266protected:
268
269 std::shared_ptr<Internal::RNTupleProcessorEntry> fEntry = nullptr;
270 std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> fFieldIdxs;
271
272 /// Total number of entries. Only to be used internally by the processor, not meant to be exposed in the public
273 /// interface.
275
276 ROOT::NTupleSize_t fNEntriesProcessed = 0; //< Total number of entries processed so far
277 ROOT::NTupleSize_t fCurrentEntryNumber = 0; //< Current processor entry number
278 std::size_t fCurrentProcessorNumber = 0; //< Number of the currently open inner processor
279
280 /////////////////////////////////////////////////////////////////////////////
281 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
282 virtual void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry) = 0;
283
284 /////////////////////////////////////////////////////////////////////////////
285 /// \brief Check if the processor already has been initialized.
286 bool IsInitialized() const { return fEntry != nullptr; }
287
288 /////////////////////////////////////////////////////////////////////////////
289 /// \brief Connect fields to the page source of the processor's underlying RNTuple(s).
290 ///
291 /// \param[in] fieldIdxs Indices of the fields to connect.
292 /// \param[in] provenance Provenance of the processor.
293 /// \param[in] updateFields Whether the fields in the entry need to be updated, because the current underlying
294 /// RNTuple source changed.
295 virtual void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
297
298 /////////////////////////////////////////////////////////////////////////////
299 /// \brief Load the entry identified by the provided entry number.
300 ///
301 /// \param[in] entryNumber Entry number to load
302 ///
303 /// \return `entryNumber` if the entry was successfully loaded, `kInvalidNTupleIndex` otherwise.
305
306 /////////////////////////////////////////////////////////////////////////////
307 /// \brief Get the total number of entries in this processor
309
310 /////////////////////////////////////////////////////////////////////////////
311 /// \brief Check if a field exists on-disk and can be read by the processor.
312 ///
313 /// \param[in] fieldName Name of the field to check.
314 virtual bool CanReadFieldFromDisk(std::string_view fieldName) = 0;
315
316 /////////////////////////////////////////////////////////////////////////////
317 /// \brief Add a field to the entry.
318 ///
319 ///
320 /// \param[in] fieldName Name of the field to add.
321 /// \param[in] typeName Type of the field to add.
322 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
323 /// created.
324 /// \param[in] provenance Provenance of the processor.
325 ///
326 /// \return The index of the newly added field in the entry.
327 ///
328 /// In case the field was already present in the entry, the index of the existing field is returned.
330 AddFieldToEntry(const std::string &fieldName, const std::string &typeName, void *valuePtr,
332
333 /////////////////////////////////////////////////////////////////////////////
334 /// \brief Add the entry mappings for this processor to the provided join table.
335 ///
336 /// \param[in] joinTable the join table to map the entries to.
337 /// \param[in] entryOffset In case the entry mapping is added from a chain, the offset of the entry indexes to use
338 /// with respect to the processor's position in the chain.
340
341 /////////////////////////////////////////////////////////////////////////////
342 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
343 ///
344 /// \param[in,out] output Output stream to print to.
345 virtual void PrintStructureImpl(std::ostream &output) const = 0;
346
347 /////////////////////////////////////////////////////////////////////////////
348 /// \brief Create a new base RNTupleProcessor.
349 ///
350 /// \param[in] processorName Name of the processor. By default, this is the name of the underlying RNTuple for
351 /// RNTupleSingleProcessor, the name of the first processor for RNTupleChainProcessor, or the name of the primary
352 /// RNTuple for RNTupleJoinProcessor.
354
355public:
360 virtual ~RNTupleProcessor() = default;
361
362 /////////////////////////////////////////////////////////////////////////////
363 /// \brief Get the options used for this processor.
364 const RNTupleProcessorOptions &GetOptions() const { return fOptions; }
365
366 /////////////////////////////////////////////////////////////////////////////
367 /// \brief Get the total number of entries processed so far.
369
370 /////////////////////////////////////////////////////////////////////////////
371 /// \brief Get the entry number that is currently being processed.
373
374 /////////////////////////////////////////////////////////////////////////////
375 /// \brief Get the number of the inner processor currently being read.
376 ///
377 /// This method is only relevant for the RNTupleChainProcessor. For the other processors, 0 is always returned.
379
380 /////////////////////////////////////////////////////////////////////////////
381 /// \brief Request access to a field for reading during processing.
382 ///
383 /// \tparam T Type of the requested field.
384 ///
385 /// \param[in] fieldName Name of the requested field.
386 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
387 /// created.
388 ///
389 /// \return An RNTupleProcessorOptionalPtr of type `T`, which provides access to the field's value.
390 ///
391 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
392 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
393 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
394 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
395 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
396 template <typename T>
398 {
400 std::string typeName{};
401 if constexpr (!std::is_void_v<T>) {
402 typeName = ROOT::Internal::GetRenormalizedTypeName(typeid(T));
403 }
406 }
407
408 /////////////////////////////////////////////////////////////////////////////
409 /// \brief Request access to a field for reading during processing.
410 ///
411 /// \param[in] fieldName Name of the requested field.
412 /// \param[in] typeName Type of the requested field.
413 /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be
414 /// created.
415 ///
416 /// \return An void-type RNTupleProcessorOptionalPtr, which provides access to the field's value.
417 ///
418 /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for
419 /// example when a field is not present in one of the chained processors or when during a join operation, no matching
420 /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading
421 /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through
422 /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read.
424 RequestField(const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr)
425 {
429 }
430
431 /////////////////////////////////////////////////////////////////////////////
432 /// \brief Print a graphical representation of the processor composition.
433 ///
434 /// \param[in,out] output Stream to print to (default is stdout).
435 ///
436 /// ### Example:
437 /// The structure of a processor representing a join between a single primary RNTuple and a chain of two auxiliary
438 /// RNTuples will be printed as follows:
439 /// ~~~
440 /// +-----------------------------+ +-----------------------------+
441 /// | ntuple | | ntuple_aux |
442 /// | ntuple.root | | ntuple_aux1.root |
443 /// +-----------------------------+ +-----------------------------+
444 /// +-----------------------------+
445 /// | ntuple_aux |
446 /// | ntuple_aux2.root |
447 /// +-----------------------------+
448 /// ~~~
449 void PrintStructure(std::ostream &output = std::cout) { PrintStructureImpl(output); }
450
451 // clang-format off
452 /**
453 \class ROOT::Experimental::RNTupleProcessor::RIterator
454 \ingroup NTuple
455 \brief Iterator over the entries of an RNTuple, or vertical concatenation thereof.
456 */
457 // clang-format on
458 class RIterator {
459 private:
462
463 public:
464 using iterator_category = std::input_iterator_tag;
467 using difference_type = std::ptrdiff_t;
470
473 {
474 if (!fProcessor.fEntry) {
476 }
477 // This constructor is called with kInvalidNTupleIndex for RNTupleProcessor::end(). In that case, we already
478 // know there is nothing to load.
481 /*updateFields=*/false);
483 }
484 }
485
491
493 {
494 auto obj = *this;
495 ++(*this);
496 return obj;
497 }
498
500
501 friend bool operator!=(const iterator &lh, const iterator &rh)
502 {
503 return lh.fCurrentEntryNumber != rh.fCurrentEntryNumber;
504 }
505 friend bool operator==(const iterator &lh, const iterator &rh)
506 {
507 return lh.fCurrentEntryNumber == rh.fCurrentEntryNumber;
508 }
509 };
510
511 RIterator begin() { return RIterator(*this, 0); }
513
514 /////////////////////////////////////////////////////////////////////////////
515 /// \brief Create an RNTupleProcessor for a single RNTuple.
516 ///
517 /// \param[in] ntuple The name and storage location of the RNTuple to process.
518 /// \param[in] opts Options for the processor.
519 ///
520 /// \return A pointer to the newly created RNTupleProcessor.
521 static std::unique_ptr<RNTupleProcessor>
523
524 /////////////////////////////////////////////////////////////////////////////
525 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of RNTuples.
526 ///
527 /// \param[in] ntuples A list specifying the names and locations of the RNTuples to process.
528 /// \param[in] opts Options for the processor.
529 ///
530 /// \return A pointer to the newly created RNTupleProcessor.
531 static std::unique_ptr<RNTupleProcessor>
532 CreateChain(std::vector<RNTupleOpenSpec> ntuples, const RNTupleProcessorOptions &opts = RNTupleProcessorOptions());
533
534 /////////////////////////////////////////////////////////////////////////////
535 /// \brief Create an RNTupleProcessor for a *chain* (i.e., a vertical combination) of other RNTupleProcessors.
536 ///
537 /// \param[in] innerProcessors A list with the processors to chain.
538 /// \param[in] opts Options for the processor.
539 ///
540 /// \return A pointer to the newly created RNTupleProcessor.
541 static std::unique_ptr<RNTupleProcessor>
542 CreateChain(std::vector<std::unique_ptr<RNTupleProcessor>> innerProcessors,
544
545 /////////////////////////////////////////////////////////////////////////////
546 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
547 ///
548 /// \param[in] primaryNTuple The name and location of the primary RNTuple. Its entries are processed in sequential
549 /// order.
550 /// \param[in] auxNTuple The name and location of the RNTuple to join the primary RNTuple with. The order in which
551 /// its entries are processed is determined by the primary RNTuple and doesn't necessarily have to be sequential.
552 /// \param[in] joinFields The names of the fields on which to join, in case the specified RNTuples are unaligned.
553 /// The join is made based on the combined join field values, and therefore each field has to be present in each
554 /// specified RNTuple. If an empty list is provided, it is assumed that the specified ntuple are fully aligned.
555 /// \param[in] opts Options for the processor.
556 ///
557 /// \return A pointer to the newly created RNTupleProcessor.
558 static std::unique_ptr<RNTupleProcessor> CreateJoin(RNTupleOpenSpec primaryNTuple, RNTupleOpenSpec auxNTuple,
559 const std::vector<std::string> &joinFields,
561
562 /////////////////////////////////////////////////////////////////////////////
563 /// \brief Create an RNTupleProcessor for a *join* (i.e., a horizontal combination) of RNTuples.
564 ///
565 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
566 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
567 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
568 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
569 /// The join is made based on the combined join field values, and therefore each field has to be present in each
570 /// specified processors. If an empty list is provided, it is assumed that the specified processors are fully
571 /// aligned.
572 /// \param[in] opts Options for the processor.
573 ///
574 /// \return A pointer to the newly created RNTupleProcessor.
575 static std::unique_ptr<RNTupleProcessor> CreateJoin(std::unique_ptr<RNTupleProcessor> primaryProcessor,
576 std::unique_ptr<RNTupleProcessor> auxProcessor,
577 const std::vector<std::string> &joinFields,
579};
580
581// clang-format off
582/**
583\class ROOT::Experimental::RNTupleSingleProcessor
584\ingroup NTuple
585\brief Processor specialization for processing a single RNTuple.
586*/
587// clang-format on
589 friend class RNTupleProcessor;
590
591private:
593 std::unique_ptr<ROOT::Internal::RPageSource> fPageSource;
594
595 /////////////////////////////////////////////////////////////////////////////
596 /// \brief Create a new field and connect it to the processor's page source.
597 ///
598 /// \param[in] qualifiedFieldName Name of the field to add, prefixed with its parent fields, if applicable.
599 /// \param[in] typeName Type of the field to add.
600 ///
601 /// \return The newly created field.
602 /// \throws ROOT::RException In case the requested field cannot be found on disk.
603 std::unique_ptr<ROOT::RFieldBase>
604 CreateAndConnectField(const std::string &qualifiedFieldName, const std::string &typeName);
605
606 /////////////////////////////////////////////////////////////////////////////
607 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
608 ///
609 /// At this point, the page source for the underlying RNTuple of the processor will be created and opened.
610 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
611
612 /////////////////////////////////////////////////////////////////////////////
613 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
614 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
616 bool updateFields = false) final;
617
618 /////////////////////////////////////////////////////////////////////////////
619 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
620 /// processor).
621 ///
622 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
624
625 /////////////////////////////////////////////////////////////////////////////
626 /// \brief Get the total number of entries in this processor.
633
634 /////////////////////////////////////////////////////////////////////////////
635 /// \brief Check if a field exists on-disk and can be read by the processor.
636 ///
637 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
638 bool CanReadFieldFromDisk(std::string_view fieldName) final;
639
640 /////////////////////////////////////////////////////////////////////////////
641 /// \brief Add a field to the entry.
642 ///
643 /// \sa RNTupleProcessor::AddFieldToEntry()
645 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
647
648 /////////////////////////////////////////////////////////////////////////////
649 /// \brief Add the entry mappings for this processor to the provided join table.
650 ///
651 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
652 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
653
654 /////////////////////////////////////////////////////////////////////////////
655 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
656 ///
657 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
658 void PrintStructureImpl(std::ostream &output) const final;
659
660 /////////////////////////////////////////////////////////////////////////////
661 /// \brief Construct a new RNTupleProcessor for processing a single RNTuple.
662 ///
663 /// \param[in] ntuple The source specification (name and storage location) for the RNTuple to process.
664 /// \param[in] opts Options for the processor.
666
667public:
673 {
674 // The entry's fields need to be deleted before fPageSource.
675 if (fEntry)
676 fEntry->Clear();
677 };
678};
679
680// clang-format off
681/**
682\class ROOT::Experimental::RNTupleChainProcessor
683\ingroup NTuple
684\brief Processor specialization for vertically combined (*chained*) RNTupleProcessors.
685*/
686// clang-format on
688 friend class RNTupleProcessor;
689
690private:
691 std::vector<std::unique_ptr<RNTupleProcessor>> fInnerProcessors;
692 std::vector<ROOT::NTupleSize_t> fInnerNEntries;
693
695
696 /////////////////////////////////////////////////////////////////////////////
697 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
698 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
699
700 /////////////////////////////////////////////////////////////////////////////
701 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
702 ///
703 /// \sa RNTupleProcessor::Connect()
704 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
706 bool updateFields = false) final;
707
708 /////////////////////////////////////////////////////////////////////////////
709 /// \brief Update the entry to reflect any missing fields in the current inner processor.
710 void ConnectInnerProcessor(std::size_t processorNumber);
711
712 /////////////////////////////////////////////////////////////////////////////
713 /// \brief Load the entry identified by the provided (global) entry number (i.e., considering all RNTuples in this
714 /// processor).
715 ///
716 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
718
719 /////////////////////////////////////////////////////////////////////////////
720 /// \brief Get the total number of entries in this processor.
721 ///
722 /// \note This requires opening all underlying RNTuples being processed in the chain, and could become costly!
724
725 /////////////////////////////////////////////////////////////////////////////
726 /// \brief Check if a field exists on-disk and can be read by the processor.
727 ///
728 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
729 bool CanReadFieldFromDisk(std::string_view fieldName) final
730 {
731 return fInnerProcessors[fCurrentProcessorNumber]->CanReadFieldFromDisk(fieldName);
732 }
733
734 /////////////////////////////////////////////////////////////////////////////
735 /// \brief Add a field to the entry.
736 ///
737 /// \sa RNTupleProcessor::AddFieldToEntry()
739 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
741
742 /////////////////////////////////////////////////////////////////////////////
743 /// \brief Add the entry mappings for this processor to the provided join table.
744 ///
745 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
746 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
747
748 /////////////////////////////////////////////////////////////////////////////
749 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
750 ///
751 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
752 void PrintStructureImpl(std::ostream &output) const final;
753
754 /////////////////////////////////////////////////////////////////////////////
755 /// \brief Construct a new RNTupleChainProcessor.
756 ///
757 /// \param[in] ntuples The source specification (name and storage location) for each RNTuple to process.
758 /// \param[in] opts Options for the processor.
759 ///
760 /// RNTuples are processed in the order in which they are specified.
761 RNTupleChainProcessor(std::vector<std::unique_ptr<RNTupleProcessor>> processors,
763
764public:
770};
771
772// clang-format off
773/**
774\class ROOT::Experimental::RNTupleJoinProcessor
775\ingroup NTuple
776\brief Processor specialization for horizontally combined (*joined*) RNTupleProcessors.
777*/
778// clang-format on
780 friend class RNTupleProcessor;
781
782private:
783 std::unique_ptr<RNTupleProcessor> fPrimaryProcessor;
784 std::unique_ptr<RNTupleProcessor> fAuxiliaryProcessor;
785
786 std::vector<std::string> fJoinFieldNames;
787 std::set<Internal::RNTupleProcessorEntry::FieldIndex_t> fJoinFieldIdxs;
788
789 std::unique_ptr<Internal::RNTupleJoinTable> fJoinTable;
790 bool fJoinTableIsBuilt = false;
791
792 std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> fAuxiliaryFieldIdxs;
793
794 /// \brief Initialize the processor by creating an (initially empty) `fEntry`, or setting an existing one.
795 void Initialize(std::shared_ptr<Internal::RNTupleProcessorEntry> entry = nullptr) final;
796
797 /////////////////////////////////////////////////////////////////////////////
798 /// \brief Connect the provided fields indices in the entry to their on-disk fields.
799 ///
800 /// \sa RNTupleProcessor::Connect()
801 void Connect(const std::unordered_set<Internal::RNTupleProcessorEntry::FieldIndex_t> &fieldIdxs,
803 bool updateFields = false) final;
804
805 /////////////////////////////////////////////////////////////////////////////
806 /// \brief Load the entry identified by the provided entry number of the primary processor.
807 ///
808 /// \sa ROOT::Experimental::RNTupleProcessor::LoadEntry
810
811 /////////////////////////////////////////////////////////////////////////////
812 /// \brief Get the total number of entries in this processor.
814
815 /////////////////////////////////////////////////////////////////////////////
816 /// \brief Set the validity for all fields in the auxiliary processor at once.
817 void SetAuxiliaryFieldValidity(bool validity);
818
819 /////////////////////////////////////////////////////////////////////////////
820 /// \brief Check if a field exists on-disk and can be read by the processor.
821 ///
822 /// \sa RNTupleProcessor::CanReadFieldFromDisk()
823 bool CanReadFieldFromDisk(std::string_view fieldName) final
824 {
825 if (!fPrimaryProcessor->CanReadFieldFromDisk(fieldName)) {
826 if (fieldName.find(fAuxiliaryProcessor->fOptions.GetProcessorName()) == 0)
827 fieldName = fieldName.substr(fAuxiliaryProcessor->fOptions.GetProcessorName().size() + 1);
828 return fAuxiliaryProcessor->CanReadFieldFromDisk(fieldName);
829 }
830
831 return true;
832 }
833
834 /////////////////////////////////////////////////////////////////////////////
835 /// \brief Add a field to the entry.
836 ///
837 /// \sa RNTupleProcessor::AddFieldToEntry()
839 const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr,
841
842 /////////////////////////////////////////////////////////////////////////////
843 /// \brief Add the entry mappings for this processor to the provided join table.
844 ///
845 /// \sa ROOT::Experimental::RNTupleProcessor::AddEntriesToJoinTable
846 void AddEntriesToJoinTable(Internal::RNTupleJoinTable &joinTable, ROOT::NTupleSize_t entryOffset = 0) final;
847
848 /////////////////////////////////////////////////////////////////////////////
849 /// \brief Processor-specific implementation for printing its structure, called by PrintStructure().
850 ///
851 /// \sa ROOT::Experimental::RNTupleProcessor::PrintStructureImpl
852 void PrintStructureImpl(std::ostream &output) const final;
853
854 /////////////////////////////////////////////////////////////////////////////
855 /// \brief Construct a new RNTupleJoinProcessor.
856 /// \param[in] primaryProcessor The primary processor. Its entries are processed in sequential order.
857 /// \param[in] auxProcessor The processor to join the primary processor with. The order in which its entries are
858 /// processed is determined by the primary processor and doesn't necessarily have to be sequential.
859 /// \param[in] joinFields The names of the fields on which to join, in case the specified processors are unaligned.
860 /// The join is made based on the combined join field values, and therefore each field has to be present in each
861 /// specified processor. If an empty list is provided, it is assumed that the processors are fully aligned.
862 /// \param[in] opts Options for the processor.
864 std::unique_ptr<RNTupleProcessor> auxProcessor, const std::vector<std::string> &joinFields,
866
867public:
873};
874
875} // namespace Experimental
876} // namespace ROOT
877
878#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 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.
std::shared_ptr< T > GetPtr() const
Get a shared pointer to the field value managed by the processor's entry.
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.