ATLAS Offline Software
Loading...
Searching...
No Matches
RAuxStore.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3// Local include(s).
5
6#include "ROOTTypes.h"
7#include "isRegisteredType.h"
8#include "lookupVectorType.h"
13
14// Athena include(s).
18
19// ROOT include(s).
20#include <TClass.h>
21#include <TROOT.h>
22
23// System include(s).
24#include <cassert>
25#include <memory>
26#include <string>
27
28namespace {
29
39bool isContainerField(const ROOT::RFieldDescriptor& fieldDesc,
40 SG::auxid_t auxid) {
41
42 // For unknown types it doesn't matter if the field describes a
43 // container or a single element.
45 return true;
46 }
47
48 // If it's a primitive field, then it's not a container.
49 if (xAOD::Utils::isPrimitiveType(fieldDesc.GetTypeName())) {
50 return false;
51 }
52
53 // For non-primitive types, get the dictionary of the type.
54 TClass* cl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
55
56 // If there is no class associated with the field then it should be
57 // a field describing a standalone object. (As it should be a
58 // "primitive" field in this case.)
59 if (!cl) {
60 ::Warning("::isPContainerField",
61 XAOD_MESSAGE("Couldn't get a dictionary for type \"%s\""),
62 fieldDesc.GetTypeName().c_str());
63 return false;
64 }
65
66 // If there is a class, ask for the type_info of its type:
67 const std::type_info* root_ti = cl->GetTypeInfo();
68 if (!root_ti) {
69 // This may be an emulated class. One known case is when the type name
70 // is saved as "basic_string<char>" rather than "string" by Athena I/O.
71 // (It's not fully understood why this happens for dynamic branches...)
72 // So, let's see if we can get a functional TClass by massaging the
73 // type name a bit.
74 ::TString typeName(cl->GetName());
75 typeName.ReplaceAll("basic_string<char>", "string");
76 ::TClass* newCl = ::TClass::GetClass(typeName);
77 if (newCl) {
78 root_ti = newCl->GetTypeInfo();
79 }
80 }
81 if (!root_ti) {
82 ::Error("::isContainerField",
83 XAOD_MESSAGE("Couldn't get an std::type_info object out of "
84 "type \"%s\""),
85 cl->GetName());
86 return false;
87 }
88
89 // Ask for the auxiliary type infos:
90 const std::type_info* aux_obj_ti =
92 if (!aux_obj_ti) {
93 ::Error("::isContainerField",
94 XAOD_MESSAGE("Couldn't get std::type_info object for "
95 "auxiliary id: %i"),
96 static_cast<int>(auxid));
97 return false;
98 }
99 const std::type_info* aux_vec_ti =
101 if (!aux_vec_ti) {
102 ::Error("::isContainerField",
103 XAOD_MESSAGE("Couldn't get std::type_info object for "
104 "auxiliary id: %i"),
105 static_cast<int>(auxid));
106 return false;
107 }
108
109 // Check which one the ROOT type info agrees with:
110 if (*root_ti == *aux_obj_ti) {
111 // This branch describes a single object:
112 return false;
113 } else if (*root_ti == *aux_vec_ti) {
114 // This branch describes a container of objects:
115 return true;
116 }
117
118 // For enum and vector<enum> types (PFO...) the type given by
119 // the aux type registry is vector<int>. We have to take it into account
120 // here...
121 if (cl->GetCollectionProxy() && (*aux_vec_ti == typeid(std::vector<int>))) {
122 return true;
123 }
124
125 TClass* cl2 = xAOD::details::lookupVectorType(*cl);
126 if (cl2) {
127 if (*cl2->GetTypeInfo() == *aux_vec_ti) {
128 return true;
129 }
130 }
131
132 // If we got this far, the branch may have undergone schema evolution. If
133 // it's one that ROOT can deal with itself, then we should still be able
134 // to read the branch with this code.
135 //
136 // Note that even after looking at the ROOT source code, I'm still not
137 // 100% sure whether we would need to delete the objects returned by
138 // TClass::GetConversionStreamerInfo(...) in this code. :-( But based on
139 // general experience with the ROOT code, I'm going to say no...
140 TClass* aux_vec_cl =
141 TClass::GetClass(xAOD::Utils::getTypeName(*aux_vec_ti).c_str());
142 if (aux_vec_cl &&
143 aux_vec_cl->GetConversionStreamerInfo(cl, cl->GetClassVersion())) {
144 return true;
145 }
146 TClass* aux_obj_cl =
147 TClass::GetClass(xAOD::Utils::getTypeName(*aux_obj_ti).c_str());
148 if (aux_obj_cl &&
149 aux_obj_cl->GetConversionStreamerInfo(cl, cl->GetClassVersion())) {
150 return false;
151 }
152
153 // If neither, then something went wrong...
154 ::Error("::isContainerField",
155 XAOD_MESSAGE("Couldn't determine if field describes a single "
156 "object or a container"));
157 ::Error("::isContainerField", XAOD_MESSAGE("ROOT type : %s"),
158 xAOD::Utils::getTypeName(*root_ti).c_str());
159 ::Error("::isContainerField", XAOD_MESSAGE("Object type: %s"),
160 xAOD::Utils::getTypeName(*aux_obj_ti).c_str());
161 ::Error("::isContainerField", XAOD_MESSAGE("Vector type: %s"),
162 xAOD::Utils::getTypeName(*aux_vec_ti).c_str());
163 return kFALSE;
164}
165
167class RFieldHandle {
168
169 public:
171 RFieldHandle(ROOT::RNTupleView<void> field, SG::auxid_t auxid,
172 std::string_view prefix, void* object, const std::type_info* ti)
173 : m_field(std::move(field)),
174 m_auxid(auxid),
175 m_prefix(prefix),
176 m_object(object),
177 m_typeInfo(ti) {}
178
180 StatusCode getEntry(::Long64_t entry) {
181
182 // Check if anything needs to be done:
183 if ((m_entry == entry) && (!m_needsRead)) {
184 return StatusCode::SUCCESS;
185 }
186
187 try {
188 // Load the entry
189 m_field(entry);
190 } catch (const ROOT::RException& e) {
191 ::Error("::RFieldInfo::getEntry",
192 "Failed to load entry %lld for field %s.%s: %s", entry,
193 m_prefix.data(),
194 SG::AuxTypeRegistry::instance().getName(m_auxid).c_str(),
195 e.what());
196 return StatusCode::FAILURE;
197 }
198
199 // Remember what entry was loaded for this field.
200 m_entry = entry;
201 m_needsRead = false;
202 return StatusCode::SUCCESS;
203 }
204
211 void* objectPtr() { return m_object; }
212
214 const std::type_info* typeInfo() const { return m_typeInfo; }
215
217 void reset() { m_needsRead = true; }
218
219 private:
221 ROOT::RNTupleView<void> m_field;
223 SG::auxid_t m_auxid;
225 std::string_view m_prefix;
227 void* m_object = nullptr;
229 const std::type_info* m_typeInfo = nullptr;
231 ::Long64_t m_entry = -1;
233 bool m_needsRead = true;
234
235}; // class RFieldInfo
236
237bool fieldExists(std::string_view fieldName,
238 ROOT::RNTupleReader& ntupleReader) {
239 // If it cannot find a field id it will give the maximum value of
240 // unsigned long
241 return (ntupleReader.GetDescriptor().FindFieldId(fieldName) !=
242 std::numeric_limits<unsigned long>::max());
243}
244
245} // namespace
246
247namespace xAOD {
248
250
258 StatusCode scanInputTuple() {
259
260 // Check if an input ntuple is even available.
261 if (!m_inTuple) {
262 // It's not an error if it isn't.
263 return StatusCode::SUCCESS;
264 }
265
266 // Check if the input was already scanned.
267 if (m_inputScanned) {
268 return StatusCode::SUCCESS;
269 }
270
271 // Iterate over all fields of the input ntuple.
272 for (const ROOT::RFieldBase* field :
273#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 35, 0)
274 m_inTuple->GetModel().GetConstFieldZero().GetConstSubfields()
275#else
276 m_inTuple->GetModel().GetConstFieldZero().GetSubFields()
277#endif // ROOT_VERSION_CODE >= ROOT_VERSION(6, 35, 0)
278 ) {
279
280 // Get the name of the current field.
281 const std::string fieldName = field->GetQualifiedFieldName();
282
283 // Look for static fields.
284 if (m_data.m_topStore && (fieldName == m_data.m_prefix)) {
285
286 // Loop over the sub-fields of this field.
287 for (const ROOT::RFieldBase* subField :
288#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 35, 0)
289 field->GetConstSubfields()
290#else
291 field->GetSubFields()
292#endif // ROOT_VERSION_CODE >= ROOT_VERSION(6, 35, 0)
293 ) {
294
295 // Get the type of this sub-field.
296 const std::string& typeName = subField->GetTypeName();
297
298 // Skip this entry if it refers to a base class.
299 if (typeName.starts_with("xAOD::") ||
300 typeName.starts_with("SG::") ||
301 typeName.starts_with("DMTest::") ||
302 typeName.starts_with("ILockable")) {
303 continue;
304 }
305
306 // Set up this field.
307 RETURN_CHECK("xAOD::RAuxStore::impl::scanInputNtuple",
308 setupAuxField(*subField, subField->GetFieldName()));
309 }
310 // Don't check the rest of the loop's body:
311 continue;
312 }
313
314 // if fieldname doesnt start with the value of m_dynPrefix, skip
315 if (fieldName.starts_with(m_data.m_dynPrefix) == false) {
316 continue;
317 }
318 if (fieldName == m_data.m_dynPrefix) {
319 ::Error("xAOD::RAuxStore::impl::scanInputNtuple",
320 "Dynamic field with empty name found on container: %s",
321 m_data.m_prefix.c_str());
322 continue;
323 }
324 // The auxiliary property name:
325 std::string_view auxName = fieldName;
326 std::string::size_type spos = auxName.find(':');
327 if (spos != std::string::npos) {
328 auxName = auxName.substr(spos + 1);
329 }
330 // Leave the rest up to the function that is shared with the
331 // dynamic fields:
332 RETURN_CHECK("xAOD::RAuxStore::scanInputNtuple",
333 setupAuxField(*field, auxName));
334 }
335
336 // the input was successfully scanned:
337 m_inputScanned = true;
338 return StatusCode::SUCCESS;
339 }
340
350 const std::type_info* auxFieldType(const ROOT::RFieldBase& field,
351 std::string* expectedClassName = nullptr) {
352
353 // Get the type name of the field. Not worrying about automatic schema
354 // evolution for now.
355 const std::string typeName = field.GetTypeName();
356 ::TClass* expectedClass = ::TClass::GetClass(typeName.c_str());
357 if (expectedClassName) {
358 if (expectedClass) {
359 *expectedClassName = expectedClass->GetName();
360 } else {
361 *expectedClassName = typeName;
362 }
363 }
364
365 // If this is a primitive variable, and we're still not sure whether this
366 // is a store for an object or a container, the answer is given...
367 if ((Utils::isPrimitiveType(typeName)) &&
368 (m_data.m_structMode == EStructMode::kUndefinedStore)) {
369 m_data.m_structMode = EStructMode::kObjectStore;
370 }
371
372 // Get the type_info of the branch.
373 const std::type_info* ti = nullptr;
374 if (m_data.m_structMode == EStructMode::kObjectStore) {
375 if (expectedClass) {
376 ti = expectedClass->GetTypeInfo();
377 } else {
378 ti = &(Utils::getTypeInfo(typeName));
379 }
380 } else {
381 if (!expectedClass) {
382 ::Warning("xAOD::RAuxStore::impl::auxFieldType",
383 "Couldn't get the type of field \"%s\"",
384 field.GetFieldName().c_str());
385 } else {
386 ::TVirtualCollectionProxy* prox = expectedClass->GetCollectionProxy();
387
388 if (!prox) {
389 TClass* cl2 = details::lookupVectorType(*expectedClass);
390 if (cl2) {
391 prox = cl2->GetCollectionProxy();
392 }
393 }
394
395 if (!prox) {
396 ::Warning("xAOD::RAuxStore::impl::auxFieldType",
397 "Couldn't get the type of field \"%s\"",
398 field.GetFieldName().c_str());
399 } else {
400 if (prox->GetValueClass()) {
401 ti = prox->GetValueClass()->GetTypeInfo();
402 } else {
403 ti = &(Utils::getTypeInfo(prox->GetType()));
404 }
405 }
406 }
407 }
408
409 return ti;
410 }
411
424 StatusCode setupAuxField(const ROOT::RFieldBase& field,
425 std::string_view auxName) {
426
427 // Get the (on disk) type of the field.
428 std::string expectedClassName;
429 const std::type_info* ti = auxFieldType(field, &expectedClassName);
430 if (ti == nullptr) {
431 // If we didn't find a type_info for the field, give up now...
432 return StatusCode::SUCCESS;
433 }
434
435 // Get the registry:
437
438 // Check if the registry already knows this variable name. If yes, let's
439 // use the type known by the registry. To be able to deal with simple
440 // schema evolution in dynamic fields.
441 const std::string auxNameStr{auxName};//Get rid of this if everything is migrated to string_view
442 if (const SG::auxid_t regAuxid = registry.findAuxID(auxNameStr);
443 regAuxid != SG::null_auxid) {
444 m_data.m_auxIDs.insert(regAuxid);
445 return StatusCode::SUCCESS;
446 }
447
449 SG::auxid_t linkedAuxId = SG::null_auxid;
450
451 if (SG::AuxTypeRegistry::isLinkedName(auxNameStr)) {
453 } else if (SG::AuxTypeRegistry::classNameHasLink(expectedClassName)) {
454 const std::string linkedAttr =
456 const std::string linkedFieldName =
457 SG::AuxTypeRegistry::linkedName(field.GetFieldName());
458 const std::type_info* linkedTi = nullptr;
459 if (::fieldExists(linkedFieldName, *m_inTuple)) {
460 linkedTi =
461 auxFieldType(m_inTuple->GetModel().GetConstField(linkedFieldName));
462 }
463 if (linkedTi) {
464 linkedAuxId = registry.getAuxID(
465 *linkedTi, linkedAttr, "",
467 }
468 if (linkedAuxId == SG::null_auxid) {
469 ::Error("xAOD::RAuxStore::setupAuxField",
470 "Could not find linked variable for %s type %s", auxName.data(),
471 expectedClassName.c_str());
472 }
473 }
474
475 // Check for an auxiliary ID for this field:
476 SG::auxid_t auxid =
477 registry.getAuxID(*ti, auxNameStr, "", flags, linkedAuxId);
478
479 // First try to find a compiled factory for the vector type:
480 if (auxid == SG::null_auxid) {
481
482 // Construct the name of the factory's class:
483 // But be careful --- if we don't exactly match the name
484 // in TClassTable, then we may trigger autoparsing. Besides the
485 // resource usage that implies, that can lead to crashes in dbg
486 // builds due to cling bugs.
487 std::string typeName = Utils::getTypeName(*ti);
488 if (typeName.starts_with("std::vector<"))
489 typeName.erase(0, 5);
490 std::string factoryClassName =
491 "SG::AuxTypeVectorFactory<" + typeName + ",allocator<" + typeName;
492 if (factoryClassName[factoryClassName.size() - 1] == '>') {
493 factoryClassName += ' ';
494 }
495 factoryClassName += "> >";
496
497 // Look for the dictionary of this type:
498 ::TClass* factoryClass = TClass::GetClass(factoryClassName.c_str());
499 if (factoryClass && factoryClass->IsLoaded()) {
500 ::TClass* baseClass = ::TClass::GetClass("SG::IAuxTypeVectorFactory");
501 if (baseClass && baseClass->IsLoaded()) {
502 const Int_t offset = factoryClass->GetBaseClassOffset(baseClass);
503 if (offset >= 0) {
504 void* factoryVoidPointer = factoryClass->New();
505 if (factoryVoidPointer) {
506 unsigned long tmp =
507 reinterpret_cast<unsigned long>(factoryVoidPointer) + offset;
509 reinterpret_cast<SG::IAuxTypeVectorFactory*>(tmp);
510 registry.addFactory(
511 *ti, *factory->tiAlloc(),
512 std::unique_ptr<SG::IAuxTypeVectorFactory>(factory));
513 auxid = registry.getAuxID(*ti, auxNameStr, "", flags,
514 linkedAuxId);
515 }
516 }
517 }
518 }
519 }
520
521 // If that didn't succeed, let's assign a generic factory to this type:
522 if (auxid == SG::null_auxid && linkedAuxId == SG::null_auxid) {
523 // Construct the name of the vector type:
524 std::string vectorClassName = "std::vector<" + Utils::getTypeName(*ti);
525 if (vectorClassName[vectorClassName.size() - 1] == '>') {
526 vectorClassName += ' ';
527 }
528 vectorClassName += '>';
529
530 // Get the dictionary for the type:
531 ::TClass* vectorClass = ::TClass::GetClass(vectorClassName.c_str());
532 if (vectorClass && vectorClass->IsLoaded()) {
533 auto factory = std::make_unique<TAuxVectorFactory>(vectorClass);
534 if (factory->tiAlloc()) {
535 const std::type_info* tiAlloc = factory->tiAlloc();
536 registry.addFactory(*ti, *tiAlloc, std::move(factory));
537 } else {
538 std::string tiAllocName = factory->tiAllocName();
539 registry.addFactory(*ti, tiAllocName, std::move(factory));
540 }
541 auxid = registry.getAuxID(*ti, auxNameStr, "",
543 } else {
544 ::Warning("xAOD::RAuxStore::setupAuxField",
545 "Couldn't find dictionary for type: %s",
546 vectorClassName.c_str());
547 }
548 }
549
550 // Check if we succeeded:
551 if (auxid == SG::null_auxid) {
552 if (linkedAuxId != SG::null_auxid) {
553 ::Error("xAOD::RAuxStore::setupAuxField",
554 XAOD_MESSAGE("Dynamic ROOT vector factory not implemented for "
555 "linked types; field \"%s\""),
556 field.GetFieldName().c_str());
557 } else {
558 ::Error("xAOD::RAuxStore::setupAuxField",
559 XAOD_MESSAGE("Couldn't assign auxiliary ID to field \"%s\""),
560 field.GetFieldName().c_str());
561 }
562 return StatusCode::FAILURE;
563 }
564
565 // Remember the auxiliary ID:
566 m_data.m_auxIDs.insert(auxid);
567 return StatusCode::SUCCESS;
568 }
569
572
574 ROOT::RNTupleReader* m_inTuple = nullptr;
576 ROOT::RNTupleWriter* m_outTuple = nullptr;
577
579 bool m_inputScanned = false;
580
582 ::Long64_t m_entry;
583
585 std::vector<std::unique_ptr<RFieldHandle> > m_fields;
587 std::vector<bool> m_fieldsWritten;
589 std::vector<bool> m_missingFields;
590
593
594}; // struct RAuxStore::impl
595
596RAuxStore::RAuxStore(std::string_view prefix, bool topStore, EStructMode mode)
597 : details::AuxStoreBase(topStore, mode),
598 m_impl{std::make_unique<impl>(m_data)} {
599
601}
602
603RAuxStore::~RAuxStore() = default;
604
605void RAuxStore::setPrefix(std::string_view prefix) {
606
607 m_data.m_prefix = prefix;
608 m_data.m_dynPrefix = Utils::dynFieldPrefix(m_data.m_prefix);
609 reset();
610}
611
619StatusCode RAuxStore::readFrom(ROOT::RNTupleReader& reader) {
620
621 assert(m_impl);
622
623 // Make sure that everything will be re-read after this:
624 reset();
625
626 // We will need to check again which branches are available:
627 m_impl->m_missingFields.clear();
628
629 // Remember the tree:
630 m_impl->m_inTuple = &reader;
631
632 // Catalogue all the branches:
633 RETURN_CHECK("xAOD::RAuxStore::readFrom", m_impl->scanInputTuple());
634
635 // Return gracefully.
636 return StatusCode::SUCCESS;
637}
638
645StatusCode RAuxStore::writeTo(ROOT::RNTupleWriter& writer) {
646
647 assert(m_impl);
648
649 // Look for any auxiliary fields that have not been connected to yet.
650 RETURN_CHECK("xAOD::RAuxStore::writeTo", m_impl->scanInputTuple());
651
652 // Put the object into "output writing" mode.
653 m_impl->m_outTuple = &writer;
654
655 // Create all the variables that we already know about. Notice that the
656 // code makes a copy of the auxid set on purpose. Because the underlying
657 // AuxSelection object gets modified while doing the for loop.
658 const SG::auxid_set_t selAuxIDs = getSelectedAuxIDs();
659 for (SG::auxid_t id : selAuxIDs) {
660 RETURN_CHECK("xAOD::RAuxStore::writeTo", setupOutputData(id));
661 }
662
663 // Return gracefully.
664 return StatusCode::SUCCESS;
665}
666
667StatusCode RAuxStore::getEntry(std::int64_t entry, int getall) {
668
669 assert(m_impl);
670
671 // Guard against multi-threaded execution:
672 guard_t guard(m_impl->m_mutex);
673
674 m_impl->m_entry = entry;
675
676 // Reset the transient store. TEvent::fill() calls this function with
677 // getall==99. When that is happening, we need to keep the transient
678 // store still around. Since the user may want to interact with the
679 // object after it was written out. (And since TEvent::fill() asks for
680 // the transient decorations after calling getEntry(...).)
681 if (m_data.m_transientStore && (getall != 99)) {
682 // Remove the transient auxiliary IDs from the internal list:
683 m_data.m_auxIDs -= m_data.m_transientStore->getAuxIDs();
684 m_data.m_decorIDs -= m_data.m_transientStore->getDecorIDs();
685 // Delete the object:
686 m_data.m_transientStore.reset();
687 }
688
689 // Now remove the IDs of the decorations that are getting persistified:
690 if (getall != 99) {
691 for (SG::auxid_t auxid = 0; auxid < m_data.m_isDecoration.size(); ++auxid) {
692 if (!m_data.m_isDecoration[auxid]) {
693 continue;
694 }
695 m_data.m_auxIDs.erase(auxid);
696 m_data.m_decorIDs.erase(auxid);
697 }
698 }
699
700 // If we don't need everything loaded, return now:
701 if (!getall) {
702 return StatusCode::SUCCESS;
703 }
704
705 // Get all the variables at once:
706 for (auto& field : m_impl->m_fields) {
707 if (field) {
708 RETURN_CHECK("xAOD::RAuxStore::getEntry", field->getEntry(entry));
709 }
710 }
711
712 // Return gracefully.
713 return StatusCode::SUCCESS;
714}
715
716StatusCode RAuxStore::commitTo(ROOT::REntry& entry) {
717
718 assert(m_impl);
719
720 // Loop through all of the output variables.
721 for (SG::auxid_t id : getSelectedAuxIDs()) {
722 // Now connect the output entry to the variable.
723 const std::string fieldName =
724 std::format("{}{}", m_data.m_dynPrefix,
725 SG::AuxTypeRegistry::instance().getName(id));
726 void* fieldPtr = const_cast<void*>(getIOData(id));
727 if (fieldPtr) {
728 entry.BindRawPtr(fieldName, fieldPtr);
729 } else {
730 entry.EmplaceNewValue(fieldName);
731 }
732 }
733
734 // Return gracefully.
735 return StatusCode::SUCCESS;
736}
737
739
740 assert(m_impl);
741
742 for (auto& field : m_impl->m_fields) {
743 if (field) {
744 field->reset();
745 }
746 }
747 m_impl->m_inputScanned = false;
748}
749
751
752 assert(m_impl);
753 return ((m_impl->m_fields.size() > auxid) && m_impl->m_fields[auxid]);
754}
755
757
758 // Guard against multi-threaded execution:
759 guard_t guard(m_impl->m_mutex);
760
761 assert(m_impl);
762 assert(m_impl->m_fields.size() > auxid);
763 assert(m_impl->m_fields[auxid]);
764 RETURN_CHECK("xAOD::RAuxStore::getEntryFor",
765 m_impl->m_fields[auxid]->getEntry(m_impl->m_entry));
766 return StatusCode::SUCCESS;
767}
768
770
771 assert(m_impl);
772 return (m_impl->m_outTuple != nullptr);
773}
774
783
784 // Return right away if we already know that the field is missing.
785 if ((auxid < m_impl->m_missingFields.size()) &&
786 m_impl->m_missingFields[auxid]) {
787 return StatusCode::RECOVERABLE;
788 }
789
790 // We may call this function without an input being used. That's not an
791 // error either.
792 if (m_impl->m_inTuple == nullptr) {
793 return StatusCode::RECOVERABLE;
794 }
795
796 // Make sure the internal storage is large enough:
797 if (m_data.m_vecs.size() <= auxid) {
798 m_data.m_vecs.resize(auxid + 1);
799 }
800 if (m_impl->m_fields.size() <= auxid) {
801 m_impl->m_fields.resize(auxid + 1);
802 }
803
804 // Check if we need to do anything. Remember, output-only variables don't
805 // have an associated RFieldHandle. To tell the caller that no input is
806 // actually available for the variable (only an output), use a different
807 // return value.
808 if (m_data.m_vecs[auxid]) {
809 return (m_impl->m_fields[auxid] ? StatusCode::SUCCESS
810 : StatusCode::RECOVERABLE);
811 }
812
813 // Convenience access to the registry.
815
816 // Get the property name:
817 const std::string statFieldName =
818 std::format("{}{}", m_data.m_prefix, r.getName(auxid));
819 const std::string dynFieldName =
820 std::format("{}{}", m_data.m_dynPrefix, r.getName(auxid));
821
822 // Check if the field exists:
823 std::string fieldName = statFieldName;
824 ROOT::DescriptorId_t fieldId;
825 if ((fieldId = m_impl->m_inTuple->GetDescriptor().FindFieldId(
826 statFieldName)) == std::numeric_limits<unsigned long>::max()) {
827 if ((fieldId = m_impl->m_inTuple->GetDescriptor().FindFieldId(
828 dynFieldName)) == std::numeric_limits<unsigned long>::max()) {
829 // Remember that the field is missing.
830 if (m_impl->m_missingFields.size() <= auxid) {
831 m_impl->m_missingFields.resize(auxid + 1);
832 }
833 m_impl->m_missingFields[auxid] = true;
834 // The field doesn't exist, but this is not an error per se.
835 // The user may just be calling isAvailable(...) on the variable.
836 return StatusCode::RECOVERABLE;
837 }
838 // We have a dynamic field:
839 fieldName = std::move(dynFieldName);
840 }
841
842 // Get the object describing this field.
843 const ROOT::RFieldDescriptor& fieldDesc =
844 m_impl->m_inTuple->GetDescriptor().GetFieldDescriptor(fieldId);
845
846 // Check if it's a "primitive field":
847 const bool primitiveField = Utils::isPrimitiveType(fieldDesc.GetTypeName());
848 // Check if it's a "container field":
849 const bool containerField =
850 (primitiveField ? false : isContainerField(fieldDesc, auxid));
851
852 // Set the structure mode if it has not been defined externally:
853 if (m_data.m_structMode == EStructMode::kUndefinedStore) {
854 m_data.m_structMode = (containerField ? EStructMode::kContainerStore
856 }
857
858 // Check that the branch type makes sense:
859 if ((containerField &&
860 (m_data.m_structMode != EStructMode::kContainerStore) &&
861 !r.isLinked(auxid)) ||
862 ((!containerField) &&
863 (m_data.m_structMode != EStructMode::kObjectStore))) {
864 ::Error("xAOD::RAuxStore::setupInputData",
865 XAOD_MESSAGE("Field type and requested structure mode "
866 "differ for field: %s"),
867 fieldName.c_str());
868 return StatusCode::FAILURE;
869 }
870
871 // Get the property type:
872 const std::type_info* fieldType = nullptr;
873 if (details::isRegisteredType(auxid)) {
874 // Get the type from the auxiliary type registry:
875 fieldType = (containerField ? r.getVecType(auxid) : r.getType(auxid));
876 } else {
877 // Get the type from the input field itself:
878 TClass* clDummy = ::TClass::GetClass(fieldDesc.GetTypeName().c_str());
879 fieldType = (clDummy ? clDummy->GetTypeInfo()
880 : &(Utils::getTypeInfo(fieldDesc.GetTypeName())));
881 }
882 if (!fieldType) {
883 ::Error("xAOD::RAuxStore::setupInputData",
884 XAOD_MESSAGE("Can't read/copy variable %s (%s)"), fieldName.c_str(),
885 fieldDesc.GetTypeName().c_str());
886 return StatusCode::RECOVERABLE;
887 }
888 const TString fieldTypeName = Utils::getTypeName(*fieldType).c_str();
889
890 // Check if we have the needed dictionary for an object field:
891 ::TClass* fieldClass = nullptr;
892 if (!primitiveField) {
893 // Get the property's class:
894 fieldClass = ::TClass::GetClass(*fieldType, true, true);
895 if (!fieldClass) {
896 fieldClass = ::TClass::GetClass(fieldTypeName);
897 }
898 if (!fieldClass) {
899 ::Error("xAOD::RAuxStore::setupInputData",
900 XAOD_MESSAGE("No dictionary available for class \"%s\""),
901 fieldTypeName.Data());
902 return StatusCode::FAILURE;
903 }
904 }
905
906 // Create the smart object holding this vector:
907 if (details::isRegisteredType(auxid)) {
908 m_data.m_vecs[auxid] = r.makeVector(auxid, (size_t)0, (size_t)0);
909 if (!containerField) {
910 m_data.m_vecs[auxid]->resize(1);
911 }
912 if (fieldClass &&
913 strncmp(fieldClass->GetName(), "SG::PackedContainer<", 20) == 0) {
914 std::unique_ptr<SG::IAuxTypeVector> packed =
915 m_data.m_vecs[auxid]->toPacked();
916 std::swap(m_data.m_vecs[auxid], packed);
917 }
918 } else {
919 ::Error("xAOD::RAuxStore::setupInputData",
920 XAOD_MESSAGE("Couldn't create in-memory vector for "
921 "variable %s (%i)"),
922 fieldName.c_str(), static_cast<int>(auxid));
923 return StatusCode::FAILURE;
924 }
925
926 // Access/create the field, and create a field handle object.
927 void* objectPtr = (containerField ? m_data.m_vecs[auxid]->toVector()
928 : m_data.m_vecs[auxid]->toPtr());
929 m_impl->m_fields[auxid] = std::make_unique<RFieldHandle>(
930 m_impl->m_inTuple->GetView<void>(fieldName.c_str(), objectPtr), auxid,
931 m_data.m_prefix, objectPtr, fieldType);
932
933 // Get the current entry.
934 RETURN_CHECK("xAOD::RAuxStore::setupInputData",
935 m_impl->m_fields[auxid]->getEntry(m_impl->m_entry));
936
937 // Remember which variable got created:
938 m_data.m_auxIDs.insert(auxid);
939
940 // Check if we just replaced a generic object:
941 if (details::isRegisteredType(auxid)) {
942 // The name of the variable we just created:
943 const std::string auxname = r.getName(auxid);
944 // Check if there's another variable with this name already:
945 for (SG::auxid_t i = 0; i < m_data.m_vecs.size(); ++i) {
946 // Check if we have this aux ID:
947 if (!m_data.m_vecs[i]) {
948 continue;
949 }
950 // Ingore the object that we *just* created:
951 if (i == auxid) {
952 continue;
953 }
954 // The name of the variable:
955 const std::string testname = r.getName(i);
956 // Check if it has the same name:
957 if (testname != auxname) {
958 continue;
959 }
960 // Check that the other one is a non-registered type:
962 ::Error("xAOD::RAuxStore::setupInputData",
963 XAOD_MESSAGE("Internal logic error!"));
964 continue;
965 }
966 // Okay, we do need to remove this object:
967 m_data.m_vecs[i].reset();
968 m_impl->m_fields[i].reset();
969 m_data.m_auxIDs.erase(i);
970 }
971 }
972
973 SG::auxid_t linked_auxid = r.linkedVariable(auxid);
974 if (linked_auxid != SG::null_auxid) {
975 return setupInputData(linked_auxid);
976 }
977
978 // Return gracefully:
979 return StatusCode::SUCCESS;
980}
981
990
991 assert(m_impl);
992
993 // Check whether we need to do anything.
994 if (!m_impl->m_outTuple) {
995 return StatusCode::SUCCESS;
996 }
997
998 // Check if the variable needs to be written out.
999 if (!isAuxIDSelected(auxid)) {
1000 return StatusCode::SUCCESS;
1001 }
1002
1003 // Make sure that containers are large enough:
1004 if (m_data.m_vecs.size() <= auxid) {
1005 m_data.m_vecs.resize(auxid + 1);
1006 }
1007 if (m_impl->m_fieldsWritten.size() <= auxid) {
1008 m_impl->m_fieldsWritten.resize(auxid + 1);
1009 }
1010
1011 // Check if this auxiliary variable is already in the output:
1012 if (m_impl->m_fieldsWritten[auxid]) {
1013 return StatusCode::SUCCESS;
1014 }
1015
1016 // After this point, we either succeed with setting up the writing of this
1017 // variable, or the code fails completely. So let's set this flag already,
1018 // as unfortunately we can recursively end up here using the code below.
1019 m_impl->m_fieldsWritten[auxid] = true;
1020
1021 // The registry:
1023
1024 // Check if the variable was put into the transient store as a
1025 // decoration, and now needs to be put into the output file:
1026 if ((!m_data.m_vecs[auxid]) && m_data.m_transientStore &&
1027 (m_data.m_transientStore->getAuxIDs().test(auxid))) {
1028
1029 // Get the variable from the transient store:
1030 const void* pptr = m_data.m_transientStore->getData(auxid);
1031 if (!pptr) {
1032 ::Fatal("xAOD::RAuxStore::setupOutputData",
1033 XAOD_MESSAGE("Internal logic error detected"));
1034 return StatusCode::FAILURE;
1035 }
1036
1037 // Create the new object:
1038 m_data.m_vecs[auxid] = reg.makeVector(auxid, m_data.m_size, m_data.m_size);
1039 void* ptr = m_data.m_vecs[auxid]->toPtr();
1040 if (!ptr) {
1041 ::Error("xAOD::RAuxStore::setupOutputData",
1042 XAOD_MESSAGE("Couldn't create decoration in memory "
1043 "for writing"));
1044 return StatusCode::FAILURE;
1045 }
1046
1047 // Get the type of this variable:
1048 const std::type_info* type = reg.getType(auxid);
1049 if (!type) {
1050 ::Error("xAOD::RAuxStore::setupOutputData",
1051 XAOD_MESSAGE("Couldn't get the type of transient "
1052 "variable %i"),
1053 static_cast<int>(auxid));
1054 return StatusCode::FAILURE;
1055 }
1056 // Now get the factory for this variable:
1057 const SG::IAuxTypeVectorFactory* factory = reg.getFactory(auxid);
1058 if (!factory) {
1059 ::Error("xAOD::RAuxStore::setupOutputData",
1060 XAOD_MESSAGE("No factory found for transient variable "
1061 "%i"),
1062 static_cast<int>(auxid));
1063 return StatusCode::FAILURE;
1064 }
1065
1066 // Mark it as a decoration already, otherwise the copy may fail.
1067 if (m_data.m_isDecoration.size() <= auxid) {
1068 m_data.m_isDecoration.resize(auxid + 1);
1069 }
1070 m_data.m_isDecoration[auxid] = true;
1071
1072 // Finally, do the copy, and remove the variable from the transient store.
1073 factory->copy(auxid, SG::AuxVectorInterface(*this), 0,
1074 SG::AuxVectorInterface(*m_data.m_transientStore), 0,
1075 m_data.m_size);
1076 }
1077
1078 // Check if we know about this variable to be on the input,
1079 // but haven't connected to it yet:
1080 if ((m_data.m_auxIDs.test(auxid)) && (!m_data.m_vecs[auxid]) &&
1081 (!m_impl->m_fields[auxid])) {
1082 RETURN_CHECK("xAOD::RAuxStore::setupOutputData", setupInputData(auxid));
1083 }
1084
1085 // Check that we know the store's type:
1086 if ((m_data.m_structMode != EStructMode::kContainerStore) &&
1087 (m_data.m_structMode != EStructMode::kObjectStore)) {
1088 ::Error("xAOD::RAuxStore::setupOutputData",
1089 XAOD_MESSAGE("Structure mode unknown for variable %s"),
1090 reg.getName(auxid).c_str());
1091 return StatusCode::FAILURE;
1092 }
1093
1094 // Check if the variable exists already in memory:
1095 if (!m_data.m_vecs[auxid]) {
1096 m_data.m_vecs[auxid] = reg.makeVector(auxid, (size_t)0, (size_t)0);
1097 if (m_data.m_structMode == EStructMode::kObjectStore) {
1098 m_data.m_vecs[auxid]->resize(1);
1099 }
1100 }
1101
1102 // Figure out the type and name of the output field.
1103 const std::string fieldName =
1104 std::format("{}{}", m_data.m_dynPrefix, reg.getName(auxid));
1105 const std::string typeName = SG::normalizedTypeinfoName(
1106 *(m_data.m_structMode == EStructMode::kContainerStore
1107 ? reg.getVecType(auxid)
1108 : reg.getType(auxid)));
1109
1110 // Update the output ntuple's model.
1111 {
1112 auto field = ROOT::RFieldBase::Create(fieldName, typeName).Unwrap();
1113 auto updater = m_impl->m_outTuple->CreateModelUpdater();
1114 updater->BeginUpdate();
1115 updater->AddField(std::move(field));
1116 updater->CommitUpdate();
1117 }
1118
1119 // Remember that we now handle this variable.
1120 m_data.m_auxIDs.insert(auxid);
1121
1122 // We were successful:
1123 return StatusCode::SUCCESS;
1124}
1125
1126const void* RAuxStore::getInputObject(SG::auxid_t auxid) const {
1127
1128 assert(m_impl);
1129 assert(m_impl->m_fields.size() > auxid);
1130 assert(m_impl->m_fields[auxid]);
1131 return m_impl->m_fields[auxid]->objectPtr();
1132}
1133
1134const std::type_info* RAuxStore::getInputType(SG::auxid_t auxid) const {
1135
1136 assert(m_impl);
1137 assert(m_impl->m_fields.size() > auxid);
1138 assert(m_impl->m_fields[auxid]);
1139 return m_impl->m_fields[auxid]->typeInfo();
1140}
1141
1142} // namespace xAOD
An auxiliary data store that holds data internally.
Handle mappings between names and auxid_t.
Make an AuxVectorData object from either a raw vector or an aux store.
#define XAOD_MESSAGE(MESSAGE)
Simple macro for printing error/verbose messages.
#define RETURN_CHECK(CONTEXT, EXP)
Helper macro for checking return codes in a compact form in the code.
Definition ReturnCheck.h:26
Handle mappings between names and auxid_t.
const std::type_info * getType(SG::auxid_t auxid) const
Return the type of an aux data item.
SG::auxid_t getAuxID(const std::string &name, const std::string &clsname="", const Flags flags=Flags::None, const SG::auxid_t linkedVariable=SG::null_auxid)
Look up a name -> auxid_t mapping.
SG::auxid_t findAuxID(const std::string &name, const std::string &clsname="") const
Look up a name -> auxid_t mapping.
static bool isLinkedName(const std::string &name)
Test if a variable name corresponds to a linked variable.
std::string getName(SG::auxid_t auxid) const
Return the name of an aux data item.
static std::string linkedName(const std::string &name)
Given a variable name, return the name of the corresponding linked variable.
const std::type_info * getVecType(SG::auxid_t auxid) const
Return the type of the STL vector used to hold an aux data item.
static bool classNameHasLink(const std::string &className)
Test to see if a class name corresponds to a class with a linked variable.
static AuxTypeRegistry & instance()
Return the singleton registry instance.
const IAuxTypeVectorFactory * addFactory(const std::type_info &ti, const std::type_info &ti_alloc, std::unique_ptr< const IAuxTypeVectorFactory > factory)
Add a new type -> factory mapping.
Make an AuxVectorData object from either a raw array or an aux store.
Interface for factory objects that create vectors.
virtual const std::type_info * tiAlloc() const =0
Return the type_info of the vector allocator.
virtual void copy(SG::auxid_t auxid, AuxVectorData &dst, size_t dst_index, const AuxVectorData &src, size_t src_index, size_t n) const =0
Copy elements between vectors.
A set of aux data identifiers.
Definition AuxTypes.h:47
RAuxStore(std::string_view prefix="", bool topStore=true, EStructMode mode=EStructMode::kUndefinedStore)
Constructor.
StatusCode readFrom(ROOT::RNTupleReader &reader)
Connect the object to an input RNTuple.
virtual const std::type_info * getInputType(SG::auxid_t auxid) const override
Get the type of an input object, for getIOType()
StatusCode writeTo(ROOT::RNTupleWriter &writer)
Add the variables of the store to an output RNTuple.
StatusCode getEntry(std::int64_t entry, int getall=0)
Get entry from the input RNTuple.
virtual StatusCode setupInputData(SG::auxid_t auxid) override
Connect a variable to the input.
virtual StatusCode getEntryFor(SG::auxid_t auxid) override
Load a single variable from the input.
std::unique_ptr< impl > m_impl
Pointer to the internal object.
Definition RAuxStore.h:89
virtual const void * getInputObject(SG::auxid_t auxid) const override
Get a pointer to an input object, as it is in memory, for getIOData()
virtual bool hasEntryFor(SG::auxid_t auxid) const override
Check if a given variable is available from the input.
virtual void setPrefix(std::string_view prefix) override
Set the object name prefix.
StatusCode commitTo(ROOT::REntry &entry)
Commit a new entry to the output RNTuple.
virtual StatusCode setupOutputData(SG::auxid_t auxid) override
Connect a variable to the output.
virtual bool hasOutput() const override
Check if an output is being written by the object.
virtual ~RAuxStore()
Destructor.
virtual void reset() override
Tell the object that all branches will need to be re-read.
virtual const void * getIOData(SG::auxid_t auxid) const override
Get a pointer to the data being stored for one aux data item.
const std::string & prefix() const
Get the currently configured object name prefix.
bool isAuxIDSelected(SG::auxid_t auxid) const
Check if an auxiliary variable is selected for ouput writing.
virtual SG::auxid_set_t getSelectedAuxIDs() const override
Get the IDs of the selected aux variables.
AthContainers_detail::mutex mutex_t
Mutex type for multithread synchronization.
EStructMode
"Structural" modes of the object
@ kUndefinedStore
The structure mode is not defined.
@ kObjectStore
The object describes a single object.
@ kContainerStore
The object describes an entire container.
AuxStoreBase(bool topStore=true, EStructMode mode=EStructMode::kUndefinedStore)
Constructor.
Members m_data
Member variables of the base class.
AthContainers_detail::lock_guard< mutex_t > guard_t
Guard type for multithreaded synchronisation.
int r
Definition globals.cxx:22
constexpr std::enable_if_t< is_bitmask_v< E >, E & > reset(E &lhs, E rhs)
Convenience function to clear bits in a class enum bitmask.
Definition bitmask.h:251
::StatusCode StatusCode
StatusCode definition for legacy code.
AuxVarFlags
Additional flags to qualify an auxiliary variable.
Definition AuxTypes.h:58
@ SkipNameCheck
Definition AuxTypes.h:81
@ Linked
Mark that this variable is linked to another one.
Definition AuxTypes.h:77
std::string normalizedTypeinfoName(const std::type_info &info)
Convert a type_info to a normalized string representation (matching the names used in the root dictio...
static const auxid_t null_auxid
To signal no aux data item.
Definition AuxTypes.h:30
size_t auxid_t
Identifier for a particular aux data item.
Definition AuxTypes.h:27
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
STL namespace.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
const std::type_info & getTypeInfo(EDataType type)
This function is used when reading a primitive branch from an input file without the user explicitly ...
bool isPrimitiveType(std::string_view typeName)
Check if the type name describes a primitive type.
std::string getTypeName(const std::type_info &ti)
This function is necessary in order to create type names that ROOT can understand.
std::string dynFieldPrefix(const std::string &key)
This function is used to figure out what to name dynamic auxiliary field coming from a container call...
TClass * lookupVectorType(TClass &cl)
Internal function used by xAOD::TAuxStore and xAOD::RAuxStore.
bool isRegisteredType(SG::auxid_t auxid)
Check if the auxiliary variable has a registered type.
ICaloAffectedTool is abstract interface for tools checking if 4 mom is in calo affected region.
bool m_inputScanned
"Scan status" of the input RNTuple
std::vector< bool > m_fieldsWritten
"Write status" of the different variables
::Long64_t m_entry
The entry to load from the ntuple.
std::vector< bool > m_missingFields
Mark fields we've found to be missing.
Members & m_data
Variables coming from AuxStoreBase.
ROOT::RNTupleWriter * m_outTuple
The ntuple being written to.
std::vector< std::unique_ptr< RFieldHandle > > m_fields
Fields containing the various auxiliary variables.
StatusCode scanInputTuple()
Scans the input ntuple for auxiliary data fields and sets up the necessary structures to access them.
ROOT::RNTupleReader * m_inTuple
The ntuple being read from.
const std::type_info * auxFieldType(const ROOT::RFieldBase &field, std::string *expectedClassName=nullptr)
This function retrieves the type information for a given auxiliary field.
StatusCode setupAuxField(const ROOT::RFieldBase &field, std::string_view auxName)
This function sets up an auxiliary field by determining its type and attempting to register it with t...
mutex_t m_mutex
Mutex object used for multithreaded synchronisation.
Struct collecting all member variables of this base class.