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