diff --git a/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h b/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h index cccd4f0..ae271a4 100644 --- a/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h +++ b/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h @@ -33,6 +33,7 @@ namespace edm4hep { #endif #include #include +#include #include "podio/Frame.h" @@ -59,6 +60,7 @@ namespace edm4hep { #include #include +#include // Preprocessor symbol that can be used in downstream code to switch on the // namespace for the conversion @@ -88,6 +90,42 @@ namespace EDM4hep2LCIOConv { ObjectMapT particleIDs {}; }; + /// The minimal necessary information to do late conversions of ParticleIDs, + /// which is necessary to have consistent algorithmType information in the + /// conversion + struct ParticleIDConvData { + std::string name; + const edm4hep::ParticleIDCollection* coll; + std::optional metadata; + }; + + /// Sort the ParticleIDs according to their algorithmType. + /// + /// This sorting allows for a fully consistent roundtrip conversion under the + /// following conditions: + /// - All ParticleIDs are converted (where all means all that belong to a + /// given ReconstructedParticle collection) + /// - All of these conversions had the necessary metadata available + /// + /// In case these conditions are not met, the assigned algorithmTypes might + /// differ between LCIO and EDM4hep, but the metadata that is set will be + /// consistent for usage. + void sortParticleIDs(std::vector& pidCollections); + + /// Attach the meta information for one ParticleID collection to the passed + /// LCIO Event + /// + /// This returns the algorithmID according to the PIDHandler that is used to + /// attach the information to the LCIO reconstructed particle collection. If + /// there is no fitting reconstructed particle collection to attach this to + /// but the meta data in the ParticleIDConvData is valid, the algoType of that + /// will be returned. If there is no reconstructed particle collection or the + /// meta data is invalid an empty optional is returned. + std::optional attachParticleIDMetaData( + IMPL::LCEventImpl* lcEvent, + const podio::Frame& edmEvent, + const ParticleIDConvData& pidCollMetaInfo); + /** * Convert EDM4hep Tracks to LCIO. Simultaneously populate the mapping from * EDM4hep to LCIO objects for relation resolving in a second step. diff --git a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp index 0f91015..acad944 100644 --- a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp +++ b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp @@ -42,6 +42,38 @@ namespace EDM4hep2LCIOConv { return {collectionName.substr(pidPos + 5), collectionName.substr(0, pidPos)}; } + void sortParticleIDs(std::vector& pidCollections) + { + std::sort(pidCollections.begin(), pidCollections.end(), [](const auto& pid1, const auto& pid2) { + static const auto defaultPidMeta = edm4hep::utils::ParticleIDMeta {"", std::numeric_limits::max(), {}}; + return pid1.metadata.value_or(defaultPidMeta).algoType < pid2.metadata.value_or(defaultPidMeta).algoType; + }); + } + + std::optional attachParticleIDMetaData( + IMPL::LCEventImpl* lcEvent, + const podio::Frame& edmEvent, + const ParticleIDConvData& pidCollMetaInfo) + { + const auto& [name, coll, pidMetaInfo] = pidCollMetaInfo; + const auto recoName = edmEvent.getName((*coll)[0].getParticle().id().collectionID); + // If we can't get the reconstructed particle collection name there is not + // much we can do + if (!recoName.has_value()) { + return std::nullopt; + } + // If we can't get meta data information there is not much we can do either + if (!pidMetaInfo.has_value()) { + return std::nullopt; + } + if (pidMetaInfo.has_value() && !recoName.has_value()) { + return pidMetaInfo->algoType; + } + + UTIL::PIDHandler pidHandler(lcEvent->getCollection(recoName.value())); + return pidHandler.addAlgorithm(pidMetaInfo->algoName, pidMetaInfo->paramNames); + } + std::unique_ptr convertEvent(const podio::Frame& edmEvent, const podio::Frame& metadata) { auto lcioEvent = std::make_unique(); @@ -53,9 +85,7 @@ namespace EDM4hep2LCIOConv { // properly. Here we store the name, the collection as well as potentially // available meta information that we obtain when we first come across a // collection - std::vector< - std::tuple>> - pidCollections {}; + std::vector pidCollections {}; const auto& collections = edmEvent.getAvailableCollections(); for (const auto& name : collections) { @@ -133,41 +163,12 @@ namespace EDM4hep2LCIOConv { } } - // Sort the particle id collections according to their algorithmType from - // EDM4hep. This way we will get the same algorithmTypes in LCIO **iff** all - // of the ParticleIDs are converted **and** all of them have meta - // information available - std::sort(pidCollections.begin(), pidCollections.end(), [](const auto& pid1, const auto& pid2) { - static const auto defaultPidMeta = edm4hep::utils::ParticleIDMeta {"", std::numeric_limits::max(), {}}; - return std::get<2>(pid1).value_or(defaultPidMeta).algoType < std::get<2>(pid2).value_or(defaultPidMeta).algoType; - }); - - for (const auto& [name, coll, pidMetaInfo] : pidCollections) { - if (!pidMetaInfo.has_value()) { - std::cerr << "Cannot find meta information for ParticleID collection " << name << std::endl; - } - const auto recoName = edmEvent.getName((*coll)[0].getParticle().id().collectionID); - if (!recoName.has_value()) { - std::cerr << "Cannot find the ReconstructedParticle collection to which ParticleIDs from \'" << name - << "' point to" << std::endl; - } - - // Use a somewhat easy to detect default value. - int32_t algoId = -1; - // In case we have a reconstructed particle collection to which we can - // attach meta information and we have that meta information, we use the - // value that is assigned by the PIDHandler from LCIO - if (pidMetaInfo.has_value() && recoName.has_value()) { - UTIL::PIDHandler pidHandler(lcioEvent->getCollection(recoName.value())); - algoId = pidHandler.addAlgorithm(pidMetaInfo->algoName, pidMetaInfo->paramNames); - } - // If we do not have a reconstructed particle collection but some meta - // information from the edm4hep side, we at least preserve that - else if (pidMetaInfo.has_value()) { - algoId = pidMetaInfo->algoType; - } - - convertParticleIDs(coll, objectMappings.particleIDs, algoId); + sortParticleIDs(pidCollections); + for (const auto& pidCollMeta : pidCollections) { + // Use -1 as a somewhat easy to identify value of missing reco collections + // or pid metadata + const auto algoId = attachParticleIDMetaData(lcioEvent.get(), edmEvent, pidCollMeta).value_or(-1); + convertParticleIDs(pidCollMeta.coll, objectMappings.particleIDs, algoId); } resolveRelations(objectMappings);