Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-20 08:33:04

0001 /*
0002  * Copyright (c) 2014-2024 Key4hep-Project.
0003  *
0004  * This file is part of Key4hep.
0005  * See https://key4hep.github.io/key4hep-doc/ for further info.
0006  *
0007  * Licensed under the Apache License, Version 2.0 (the "License");
0008  * you may not use this file except in compliance with the License.
0009  * You may obtain a copy of the License at
0010  *
0011  *     http://www.apache.org/licenses/LICENSE-2.0
0012  *
0013  * Unless required by applicable law or agreed to in writing, software
0014  * distributed under the License is distributed on an "AS IS" BASIS,
0015  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016  * See the License for the specific language governing permissions and
0017  * limitations under the License.
0018  */
0019 #ifndef FWCORE_FUNCTIONALUTILS_H
0020 #define FWCORE_FUNCTIONALUTILS_H
0021 
0022 #include "Gaudi/Functional/details.h"
0023 #include "GaudiKernel/AnyDataWrapper.h"
0024 #include "GaudiKernel/DataObjID.h"
0025 #include "GaudiKernel/DataObjectHandle.h"
0026 #include "GaudiKernel/EventContext.h"
0027 #include "GaudiKernel/IDataProviderSvc.h"
0028 #include "GaudiKernel/ThreadLocalContext.h"
0029 #include <GaudiKernel/GaudiException.h>
0030 
0031 #include "podio/CollectionBase.h"
0032 
0033 #include "k4FWCore/DataWrapper.h"
0034 
0035 // #include "GaudiKernel/CommonMessaging.h"
0036 
0037 #include <fmt/format.h>
0038 
0039 #include <memory>
0040 #include <tuple>
0041 #include <type_traits>
0042 
0043 namespace k4FWCore {
0044 
0045 static const std::string frameLocation = "/_Frame";
0046 
0047 namespace details {
0048 
0049   // It doesn't need to be a template but this allows parameter pack expansion
0050   template <typename T>
0051   struct EventStoreType {
0052     using type = std::unique_ptr<podio::CollectionBase>;
0053   };
0054   using EventStoreType_t = typename EventStoreType<void>::type;
0055 
0056   // This is used when there is an arbitrary number of collections as input/output
0057   template <typename T, typename P>
0058     requires(!std::is_same_v<P, podio::CollectionBase*>)
0059   const auto& maybeTransformToEDM4hep(const P& arg) {
0060     return arg;
0061   }
0062 
0063   // This is used by the FilterPredicate
0064   template <typename T, typename P>
0065     requires std::same_as<P, podio::CollectionBase*>
0066   const auto& maybeTransformToEDM4hep(P&& arg) {
0067     return static_cast<const T&>(*arg);
0068   }
0069 
0070   // This is used by the EfficiencyFilter
0071   template <typename T, typename P>
0072     requires std::same_as<P, podio::CollectionBase* const>
0073   const auto& maybeTransformToEDM4hep(P& arg) {
0074     return static_cast<const podio::CollectionBase&>(*arg);
0075   }
0076 
0077   // This is used in all the remaining cases
0078   template <typename T, typename P>
0079     requires(std::is_base_of_v<podio::CollectionBase, P> && !std::same_as<podio::CollectionBase, P>)
0080   const auto& maybeTransformToEDM4hep(P* arg) {
0081     return *arg;
0082   }
0083 
0084   template <typename T, bool addPtr = std::is_base_of_v<podio::CollectionBase, T>>
0085   using addPtrIfColl = std::conditional_t<addPtr, std::add_pointer_t<T>, T>;
0086 
0087   // Check if the type is a vector like type, where vector is the special
0088   // type to have an arbitrary number of collections as input or output:
0089   // std::vector<Coll> where Coll is the collection type for output
0090   // and const std::vector<const Coll*>& for input
0091   template <typename T>
0092   struct isVectorLike : std::false_type {};
0093 
0094   template <typename Value>
0095     requires std::is_base_of_v<podio::CollectionBase, std::remove_cv_t<Value>> ||
0096              std::is_same_v<podio::CollectionBase*, std::remove_cv_t<Value>>
0097   struct isVectorLike<std::vector<Value*>> : std::true_type {};
0098 
0099   template <typename Value>
0100     requires std::is_base_of_v<podio::CollectionBase, std::remove_cv_t<Value>>
0101   struct isVectorLike<std::vector<Value>> : std::true_type {};
0102 
0103   template <class T>
0104   inline constexpr bool isVectorLike_v = isVectorLike<T>::value;
0105 
0106   template <typename T>
0107   auto convertToUniquePtr(T&& arg) {
0108     // This is the case for CollectionMerger.cpp, where a raw pointer is
0109     // returned from the algorithm
0110     if constexpr (std::same_as<T, podio::CollectionBase*>) {
0111       return std::unique_ptr<podio::CollectionBase>(std::forward<T>(arg));
0112     } else {
0113       // Most common case, when an algorithm returns a collection and
0114       // we want to store a unique_ptr
0115       return std::make_unique<T>(std::forward<T>(arg));
0116     }
0117   }
0118 
0119   template <typename... In>
0120   struct filter_evtcontext {
0121     static_assert(!std::disjunction_v<std::is_same<EventContext, In>...>,
0122                   "EventContext can only appear as first argument");
0123 
0124     using type = std::tuple<In...>;
0125     static constexpr std::size_t size = std::tuple_size_v<type>;
0126 
0127     template <typename Algorithm, typename Handles>
0128     static auto apply(const Algorithm& algo, Handles& handles) {
0129       return std::apply(
0130           [&](const auto&... handle) { return algo(get(handle, algo, Gaudi::Hive::currentContext())...); }, handles);
0131     }
0132     template <typename Algorithm, typename Handles>
0133     static auto apply(const Algorithm& algo, const EventContext&, Handles& handles) {
0134       auto inputTuple = std::tuple<addPtrIfColl<In>...>();
0135 
0136       // Build the input tuple by picking up either std::vector with an arbitrary
0137       // number of collections or single collections
0138       readVectorInputs<0, In...>(handles, &algo, inputTuple);
0139 
0140       return std::apply([&](const auto&... input) { return algo(maybeTransformToEDM4hep<decltype(input)>(input)...); },
0141                         inputTuple);
0142     }
0143   };
0144 
0145   template <typename... In>
0146   struct filter_evtcontext<EventContext, In...> {
0147     static_assert(!std::disjunction_v<std::is_same<EventContext, In>...>,
0148                   "EventContext can only appear as first argument");
0149 
0150     using type = std::tuple<In...>;
0151     static constexpr std::size_t size = std::tuple_size_v<type>;
0152 
0153     template <typename Algorithm, typename Handles>
0154     static auto apply(const Algorithm& algo, const EventContext& ctx, Handles& handles) {
0155       auto inputTuple = std::tuple<addPtrIfColl<In>...>();
0156 
0157       // Build the input tuple by picking up either std::vector with an arbitrary
0158       // number of collections or single collections
0159       readVectorInputs<0, In...>(handles, &algo, inputTuple);
0160 
0161       return std::apply(
0162           [&](const auto&... input) { return algo(ctx, maybeTransformToEDM4hep<decltype(input)>(input)...); },
0163           inputTuple);
0164     }
0165   };
0166 
0167   template <typename... In>
0168   using filter_evtcontext_t = typename filter_evtcontext<In...>::type;
0169 
0170   // This is a helper class to create the input and output types because a double parameter
0171   // pack expansion is needed. Once to filter out the event context, and the other one to
0172   // create the tuple of vectors of handles
0173   template <template <typename> class Handle, typename Tuple>
0174   struct tuple_of_handle_vec {
0175     using type = std::tuple<>;
0176   };
0177 
0178   template <template <typename> class Handle, typename... Ts>
0179   struct tuple_of_handle_vec<Handle, std::tuple<Ts...>> {
0180     using type = std::tuple<std::vector<Handle<typename EventStoreType<Ts>::type>>...>;
0181   };
0182 
0183   template <template <typename> class Handle, typename... Ts>
0184   using tuple_of_handle_vec_t = typename tuple_of_handle_vec<Handle, Ts...>::type;
0185 
0186   template <size_t Index, typename... In, typename... Handles, typename InputTuple>
0187   void readVectorInputs(const std::tuple<Handles...>& handles, auto thisClass, InputTuple& inputTuple) {
0188     if constexpr (Index < sizeof...(Handles)) {
0189       using TupleType = std::tuple_element_t<Index, std::tuple<In...>>;
0190       if constexpr (isVectorLike_v<TupleType>) {
0191         // Bare EDM4hep type, without pointers or const
0192         using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<typename TupleType::value_type>>;
0193         auto inputVector = std::vector<const EDM4hepType*>();
0194         inputVector.reserve(std::get<Index>(handles).size());
0195         for (const auto& handle : std::get<Index>(handles)) {
0196           podio::CollectionBase* collection = handle.get()->get();
0197           auto* typedCollection = dynamic_cast<const EDM4hepType*>(collection);
0198           if (typedCollection) {
0199             inputVector.push_back(typedCollection);
0200           } else {
0201             throw GaudiException(
0202                 fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
0203                             handle.objKey(), typeid(EDM4hepType).name(),
0204                             collection ? collection->getTypeName() : "[undetermined]"),
0205                 thisClass->name(), StatusCode::FAILURE);
0206           }
0207         }
0208         std::get<Index>(inputTuple) = std::move(inputVector);
0209       } else {
0210         // Bare EDM4hep type, without pointers or const
0211         using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<TupleType>>;
0212         try {
0213           podio::CollectionBase* collection = std::get<Index>(handles)[0].get()->get();
0214           auto* typedCollection = dynamic_cast<EDM4hepType*>(collection);
0215           if (typedCollection) {
0216             std::get<Index>(inputTuple) = typedCollection;
0217           } else {
0218             throw GaudiException(
0219                 fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
0220                             std::get<Index>(handles)[0].objKey(), typeid(EDM4hepType).name(),
0221                             collection ? collection->getTypeName() : "[undetermined]"),
0222                 thisClass->name(), StatusCode::FAILURE);
0223           }
0224         } catch (GaudiException& e) {
0225           // When the type of the collection is different from the one requested, this can happen because
0226           // 1. a mistake was made in the input types of a functional algorithm
0227           // 2. the data was produced using the old DataHandle, which is never going to be in the input type
0228           if (e.message().find("different from") != std::string::npos) {
0229             thisClass->debug() << "Trying to cast the collection " << std::get<Index>(handles)[0].objKey()
0230                                << " to the requested type didn't work " << endmsg;
0231             DataObject* dataObject;
0232             IDataProviderSvc* eventDataSvc = thisClass->evtSvc();
0233             eventDataSvc->retrieveObject("/Event/" + std::get<Index>(handles)[0].objKey(), dataObject).ignore();
0234             // This is how Gaudi::Algorithms saves collections through the DataHandle
0235             const auto* wrapper = dynamic_cast<const DataWrapper<EDM4hepType>*>(dataObject);
0236             // This is how the Marlin wrapper saves collections when converting from LCIO to EDM4hep
0237             const auto* marlinWrapper = dynamic_cast<const DataWrapper<podio::CollectionBase>*>(dataObject);
0238             if (!wrapper && !marlinWrapper) {
0239               throw GaudiException(fmt::format("Failed to cast collection {} to the required type {}",
0240                                                std::get<Index>(handles)[0].objKey(), typeid(EDM4hepType).name()),
0241                                    thisClass->name(), StatusCode::FAILURE);
0242             }
0243             if (wrapper) {
0244               std::get<Index>(inputTuple) = const_cast<EDM4hepType*>(wrapper->getData());
0245             } else {
0246               std::get<Index>(inputTuple) =
0247                   dynamic_cast<EDM4hepType*>(const_cast<podio::CollectionBase*>(marlinWrapper->getData()));
0248             }
0249           } else {
0250             throw e;
0251           }
0252         }
0253       }
0254 
0255       // Recursive call for the next index
0256       readVectorInputs<Index + 1, In...>(handles, thisClass, inputTuple);
0257     }
0258   }
0259 
0260   template <size_t Index, typename... Out, typename... Handles>
0261   void putVectorOutputs(std::tuple<Handles...>&& handles, const auto& outputs, auto thisClass) {
0262     if constexpr (Index < sizeof...(Handles)) {
0263       auto& outputHandles = std::get<Index>(handles); // Can not be const to allow std::move(value) below
0264       if constexpr (isVectorLike_v<std::tuple_element_t<Index, std::tuple<Out...>>>) {
0265         const auto& outputVector = std::get<Index>(outputs);
0266         if (outputHandles.size() != outputVector.size()) {
0267           throw GaudiException(fmt::format("Size of the output vector {} with type {} does not match the expected size "
0268                                            "from the steering file {}",
0269                                            outputHandles.size(), typeid(outputHandles).name(), outputVector.size()),
0270                                thisClass->name(), StatusCode::FAILURE);
0271         }
0272         size_t index = 0;
0273         for (auto& value : outputHandles) {
0274           Gaudi::Functional::details::put(outputVector[index], convertToUniquePtr(std::move(value)));
0275           ++index;
0276         }
0277       } else {
0278         Gaudi::Functional::details::put(std::get<Index>(outputs)[0], convertToUniquePtr(std::move(outputHandles)));
0279       }
0280 
0281       // Recursive call for the next index
0282       putVectorOutputs<Index + 1, Out...>(std::move(handles), outputs, thisClass);
0283     }
0284   }
0285 
0286   inline std::vector<DataObjID> to_DataObjID(const std::vector<std::string>& inputStrings) {
0287     std::vector<DataObjID> outputIds;
0288     outputIds.reserve(inputStrings.size());
0289     std::transform(inputStrings.begin(), inputStrings.end(), std::back_inserter(outputIds),
0290                    [](const std::string& str) { return DataObjID{str}; });
0291     return outputIds;
0292   }
0293 
0294   inline std::vector<DataObjID> to_DataObjID(const std::string& inputString) { return {DataObjID{inputString}}; }
0295 
0296   // Functional handles
0297   // This is currently used so that the FilterPredicate can be used together with the
0298   // consumer/producer/transformer
0299   template <typename T>
0300   class FunctionalDataObjectReadHandle : public ::details::ReadHandle<T> {
0301     template <typename... Args, std::size_t... Is>
0302     FunctionalDataObjectReadHandle(std::tuple<Args...>&& args, std::index_sequence<Is...>)
0303         : FunctionalDataObjectReadHandle(std::get<Is>(std::move(args))...) {}
0304 
0305   public:
0306     /// Autodeclaring constructor with property name, mode, key and documentation.
0307     /// @note the use std::enable_if is required to avoid ambiguities
0308     template <typename OWNER, typename K, typename = std::enable_if_t<std::is_base_of_v<IProperty, OWNER>>>
0309     FunctionalDataObjectReadHandle(OWNER* owner, std::string propertyName, K key = {}, std::string doc = "")
0310         : ::details::ReadHandle<T>(std::move(key), Gaudi::DataHandle::Reader, owner) {
0311       auto p = owner->declareProperty(std::move(propertyName), *this, std::move(doc));
0312       p->template setOwnerType<OWNER>();
0313     }
0314 
0315     template <typename... Args>
0316     FunctionalDataObjectReadHandle(std::tuple<Args...>&& args)
0317         : FunctionalDataObjectReadHandle(std::move(args), std::index_sequence_for<Args...>{}) {}
0318 
0319     const T& get() const;
0320   };
0321 
0322   template <typename T>
0323   const T& FunctionalDataObjectReadHandle<T>::get() const {
0324     const auto dataObj = this->fetch();
0325     if (!dataObj) {
0326       throw GaudiException(fmt::format("Cannot retrieve '{}' from transient store [{}]", this->objKey(),
0327                                        this->m_owner ? this->owner()->name() : "no owner"),
0328                            "FunctionalDataObjectReadHandle", StatusCode::FAILURE);
0329     }
0330     const auto ptr = dynamic_cast<AnyDataWrapper<std::unique_ptr<podio::CollectionBase>>*>(dataObj);
0331     return maybeTransformToEDM4hep<T>(ptr->getData().get());
0332   }
0333 
0334   struct BaseClass_t {
0335     template <typename T>
0336     using InputHandle = FunctionalDataObjectReadHandle<T>;
0337     // template <typename T> using OutputHandle = DataObjectWriteHandle<T>;
0338 
0339     using BaseClass = Gaudi::Algorithm;
0340   };
0341 
0342   template <typename InputHandle, typename KeyValue>
0343   Gaudi::Property<DataObjID> makeInputPropSingle(const auto& inp, auto& classInputs, auto* thisClass) {
0344     if (inp.index() == 0) {
0345       const auto& input = std::get<KeyValue>(inp);
0346       return {Gaudi::Property<DataObjID>(
0347           thisClass, input.first, to_DataObjID(input.second)[0],
0348           [thisClass, &classInputs](Gaudi::Details::PropertyBase& p) {
0349             std::vector<InputHandle> handles;
0350             auto handle = InputHandle(static_cast<Gaudi::Property<DataObjID>&>(p).value(), thisClass);
0351             handles.push_back(std::move(handle));
0352             classInputs = std::move(handles);
0353           },
0354           Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0355     } else {
0356       return {};
0357     }
0358   }
0359   template <typename InputHandle, typename KeyValues>
0360   Gaudi::Property<std::vector<DataObjID>> makeInputPropVector(const auto& inp, auto& classInputs, auto* thisClass) {
0361     if (inp.index() == 1) {
0362       const auto& input = std::get<KeyValues>(inp);
0363       return {Gaudi::Property<std::vector<DataObjID>>(
0364           thisClass, input.first, to_DataObjID(input.second),
0365           [thisClass, &classInputs](Gaudi::Details::PropertyBase& p) {
0366             const auto& tmpprop = static_cast<Gaudi::Property<std::vector<DataObjID>>&>(p);
0367             const auto& tmpval = tmpprop.value();
0368             std::vector<InputHandle> handles;
0369             handles.reserve(tmpval.size());
0370             for (const auto& value : tmpval) {
0371               auto handle = InputHandle(value, thisClass);
0372               handles.push_back(std::move(handle));
0373             }
0374             classInputs = std::move(handles);
0375           },
0376           Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0377     } else {
0378       return {};
0379     }
0380   }
0381 
0382   template <typename OutputHandle, typename KeyValue>
0383   Gaudi::Property<DataObjID> makeOutputPropSingle(const auto& out, auto& classOutputs, auto* thisClass) {
0384     if (out.index() == 0) {
0385       const auto& output = std::get<KeyValue>(out);
0386       return {Gaudi::Property<DataObjID>(
0387           thisClass, output.first, to_DataObjID(output.second)[0],
0388           [thisClass, &classOutputs](Gaudi::Details::PropertyBase& p) {
0389             std::vector<OutputHandle> handles;
0390             auto handle = OutputHandle(static_cast<Gaudi::Property<DataObjID>&>(p).value(), thisClass);
0391             handles.push_back(std::move(handle));
0392             classOutputs = std::move(handles);
0393           },
0394           Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0395     } else {
0396       return {};
0397     }
0398   }
0399   template <typename OutputHandle, typename KeyValues>
0400   Gaudi::Property<std::vector<DataObjID>> makeOutputPropVector(const auto& out, auto& classOutputs, auto* thisClass) {
0401     if (out.index() == 1) {
0402       const auto& output = std::get<KeyValues>(out);
0403       return {Gaudi::Property<std::vector<DataObjID>>(
0404           thisClass, output.first, to_DataObjID(output.second),
0405           [thisClass, &classOutputs](Gaudi::Details::PropertyBase& p) {
0406             std::vector<OutputHandle> handles;
0407             const auto& tmpprop = static_cast<Gaudi::Property<std::vector<DataObjID>>&>(p);
0408             const auto& tmpval = tmpprop.value();
0409             for (const auto& value : tmpval) {
0410               if (value.key().empty()) {
0411                 continue;
0412               }
0413               auto handle = OutputHandle(value, thisClass);
0414               handles.push_back(std::move(handle));
0415             }
0416             classOutputs = std::move(handles);
0417           },
0418           Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0419     } else {
0420       return {};
0421     }
0422   }
0423 
0424 } // namespace details
0425 } // namespace k4FWCore
0426 
0427 #endif // CORE_FUNCTIONALUTILS_H