Skip to content

Commit

Permalink
Move particle id metadata handling into reusable functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadlener committed Apr 15, 2024
1 parent 6f84362 commit 789635c
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 38 deletions.
38 changes: 38 additions & 0 deletions k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace edm4hep {
#endif
#include <edm4hep/TrackerHitPlaneCollection.h>
#include <edm4hep/VertexCollection.h>
#include <edm4hep/utils/ParticleIDUtils.h>

#include "podio/Frame.h"

Expand All @@ -59,6 +60,7 @@ namespace edm4hep {
#include <lcio.h>

#include <memory>
#include <optional>

// Preprocessor symbol that can be used in downstream code to switch on the
// namespace for the conversion
Expand Down Expand Up @@ -88,6 +90,42 @@ namespace EDM4hep2LCIOConv {
ObjectMapT<lcio::ParticleIDImpl*, edm4hep::ParticleID> 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<edm4hep::utils::ParticleIDMeta> 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<ParticleIDConvData>& 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<int32_t> 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.
Expand Down
77 changes: 39 additions & 38 deletions k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,38 @@ namespace EDM4hep2LCIOConv {
return {collectionName.substr(pidPos + 5), collectionName.substr(0, pidPos)};
}

void sortParticleIDs(std::vector<ParticleIDConvData>& pidCollections)
{
std::sort(pidCollections.begin(), pidCollections.end(), [](const auto& pid1, const auto& pid2) {
static const auto defaultPidMeta = edm4hep::utils::ParticleIDMeta {"", std::numeric_limits<int>::max(), {}};
return pid1.metadata.value_or(defaultPidMeta).algoType < pid2.metadata.value_or(defaultPidMeta).algoType;
});
}

std::optional<int32_t> 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<lcio::LCEventImpl> convertEvent(const podio::Frame& edmEvent, const podio::Frame& metadata)
{
auto lcioEvent = std::make_unique<lcio::LCEventImpl>();
Expand All @@ -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<std::string, const edm4hep::ParticleIDCollection*, std::optional<edm4hep::utils::ParticleIDMeta>>>
pidCollections {};
std::vector<ParticleIDConvData> pidCollections {};

const auto& collections = edmEvent.getAvailableCollections();
for (const auto& name : collections) {
Expand Down Expand Up @@ -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<int>::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);
Expand Down

0 comments on commit 789635c

Please sign in to comment.