Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation and a convenience overload to the ParticleID utilities #395

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/PIDHandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ over them in parallel.

In case you just want look at one `ParticleID` collection, but need to look at
some properties of the `ReconstructedParticle`, simply use the existing relation
to do that.
to do that and loop over the `ParticleID` collection instead.


## ParticleIDMeta basics
Expand Down
22 changes: 22 additions & 0 deletions test/utils/test_PIDHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,28 @@ TEST_CASE("PIDHandler w/ addMetaInfo", "[pid_utils]") {
REQUIRE(handler.getParamIndex(42, "p2").value_or(-1) == 1);
}

TEST_CASE("PIDHandler add multiple meta info objects", "[pid_utils]") {
using namespace edm4hep::utils;
auto handler = PIDHandler();

const auto pidInfo1 = ParticleIDMeta{"fancyAlgo", {"param1", "param2"}};
const auto pidInfo2 = ParticleIDMeta{"fancyAlgo2", {"p1", "p2"}};
const auto pidInfo3 = ParticleIDMeta{"fancyAlgo3", {"p1", "p2"}};

// Can add all of them at once or do it in steps
handler.addMetaInfos(pidInfo1);
handler.addMetaInfos(pidInfo2, pidInfo3);

REQUIRE(handler.getParamIndex(pidInfo1.algoType(), "param2").value() == 1);
REQUIRE(handler.getParamIndex(pidInfo2.algoType(), "p1").value() == 0);
REQUIRE(handler.getParamIndex(pidInfo3.algoType(), "p2").value() == 1);
REQUIRE(handler.getAlgoType("fancyAlgo").value() == pidInfo1.algoType());
REQUIRE(handler.getAlgoType("fancyAlgo3").value() == pidInfo3.algoType());

const auto duplicate = ParticleIDMeta{"fancyAlgo", {}};
REQUIRE_THROWS_AS(handler.addMetaInfos(duplicate), std::runtime_error);
}

TEST_CASE("PIDHandler from Frame w/ metadata", "[pid_utils]") {
using namespace edm4hep;
const auto& [event, metadata] = createEventAndMetadata();
Expand Down
208 changes: 202 additions & 6 deletions utils/include/edm4hep/utils/ParticleIDUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,45 @@
namespace edm4hep::utils {

/// A simple struct bundling relevant metadata for a ParticleID collection
///
/// This is the recommended class to use for storing ParticleID related
/// metadata. Most importantly it contains the name of an algorithm as well as
/// the names of the parameters (if any). Additionally, it can also
/// automatically determine an algorithm type (to be used in
/// edm4hep::PartilceID::setAlgorithmType) given a name. This is a 32 bit hash
/// of the algorithm name.
///
/// @note Given that this structure and the actual contents of the corresponding
/// ParticleIDCollection are disjoint it is your responsibility to set the
/// information accordingly. We offer some utility functionality to ensure
/// consistent setting, but the tools can in principle also be used without
/// that.
struct ParticleIDMeta {
ParticleIDMeta(const std::string& algName, int32_t algType, const std::vector<std::string>& parNames);
ParticleIDMeta(const std::string& algName, const std::vector<std::string>& parNames);
/// Constructor for I/O purposes or with a pre-defined algorithm type
///
/// @note We generally recommend not using pre-defined algorithm types, but
/// rather to use the automatially determined ones from the
///
/// @param algName The name of the PID algorithm
/// @param algType The (encoded) algorithm type (stored in the ParticleID
/// algorithmType) field
/// @param parNames The (optional) parameter names for this PID algorithms.
/// There should be a parameter name for each parameter that
/// is stored in the ParticleID parameters field
ParticleIDMeta(const std::string& algName, int32_t algType, const std::vector<std::string>& parNames = {});
/// Main constructor for creating a ParticleIDMeta object
///
/// @param algName The name of the PID algorithm
/// @param parNames The (optional) parameter names for this PID algorithms.
/// There should be a parameter name for each parameter that
/// is stored in the ParticleID parameters field
ParticleIDMeta(const std::string& algName, const std::vector<std::string>& parNames = {});

~ParticleIDMeta() = default;
/// Default constructor
///
/// @note It is not possible to change the algorithm type when this
/// constructor is used
ParticleIDMeta() = default;
ParticleIDMeta(const ParticleIDMeta&) = default;
ParticleIDMeta& operator=(const ParticleIDMeta&) = default;
Expand All @@ -28,18 +62,47 @@ struct ParticleIDMeta {
std::string algoName{}; ///< The name of the algorithm
std::vector<std::string> paramNames{}; ///< The names of the parameters

/// Get the encoded algorithm type for the PID algorithm that is described by
/// this meta object
///
/// @returns the algorithm type (usually a 32 bit hash of the algorithm name)
int32_t algoType() const {
return m_algoType;
}

private:
int32_t m_algoType{0}; ///< The (user defined) algorithm type
int32_t m_algoType{0}; ///< The (potentially user defined) algorithm type
};

/// Get the index of the parameter in the passed ParticleID meta info
/// Get the index of a parameter for a given PID algorithm
///
/// @param pidMetaInfo A metadata object describig a ParticleID algorithm
/// @param param The name of the parameter
///
/// @returns The index of the parameter which can be used to index into the
/// ParticleID::getParameters() values or an empty optional in case the
/// parameter name could not be found in the metadata that were passed.
std::optional<int> getParamIndex(const ParticleIDMeta& pidMetaInfo, const std::string& param);

/// Utility class to invert the ParticleID to ReconstructedParticle relation
///
/// This is the main utility class to use when trying to obtain ParticleID
/// objects related to ReconstructedParticles. Basic functionality (i.e.
/// retrieving the related ParticleIDs) can be done without any additional
/// metadata. The full functionality is enabled by also passing in metadata (in
/// the form of ParticleIDMeta) or alternatively adding it after the initial
/// construction.
///
/// This uses an internal map that is updated whenever a new
/// ParticleIDCollection is added. Hence, lookup of related ParticleID objects
/// for a given ReconstructedParticle avoids looping over all collections.
///
/// @note there are no checks in place that ensure that the metadata that has
/// been added to a given PIDHandler actually correspond to any of the stored
/// ParticleIDs. It is in fact even possible to use the PIDHandler without any
/// PartileID objects to simply query meta information about ParticleID
/// algorithms.
///
/// See [this page](@ref md_doc_2_p_i_d_handler) for example usage and more information.
class PIDHandler {

Expand All @@ -62,6 +125,22 @@ class PIDHandler {
PIDHandler& operator=(PIDHandler&&) = default;

/// Construct a PIDHandler from an arbitrary number of ParticleIDCollections
///
/// This constructor does not retrieve any metadata and on its own will only
/// enable the basic functionality. For the full functionality add the
/// necessary metadata in a follow up call to @ref addMetaInfos or @ref
/// addMetaInfo
///
/// @tparam PIDColls A variadic template that we use to enable passing
/// arbitrary numbers of collections
///
/// @param coll A ParticleIDCollection for constructing a PIDHandler
/// @param pidColls An arbitrary number of additional ParticleIDCollections
/// that will also be added to the internal map
///
/// @returns A PIDHandler that is able to handle relations between
/// ReconstructedParticles and all ParticleID objects that are part
/// of the passed collection
template <typename... PIDColls>
static PIDHandler from(const ParticleIDCollection& coll, const PIDColls&... pidColls) {
static_assert((std::is_same_v<PIDColls, edm4hep::ParticleIDCollection> && ...),
Expand All @@ -73,36 +152,136 @@ class PIDHandler {
}

/// Create a PIDHandler from a Frame potentially also populating some metadata information.
///
/// Create a PIDHandler using all ParticleIDCollections that can be found. If
/// metadata is passed automatically also load all related metadata (if
/// available).
///
/// @note This constructor does not guarantee that **all**
/// ParticleIDCollections will have valid metadata loaded. Only if such
/// metadata is actually present and found will it be ingested, otherwise only
/// the ParticleID objecs will be added to the internal map.
///
/// @param event The event Frame from which all ParticleIDCollections will be
/// retrieved
/// @param metadata An (optional) metadata Frame from wich ParticleIDMeta will
/// be automatically retrieved for all ParticleIDCollections
/// for which it is actually available.
///
/// @returns A PIDHandler that is able to handle relations between
/// ReconstructedParticles and all ParticleID objects that were found
/// in the event Frame. If metadata is found on top of that, also
/// that has been ingested and can now be queried through this handler.
static PIDHandler from(const podio::Frame& event, const podio::Frame& metadata = {});

/// Add the information from one ParticleIDCollection to the handler
///
/// @note This will only add the collection to enable basic functionality. In
/// order to enable the full functionality it is also necessary to add the
/// necessary ParticleIDMeta information that corresponds to the added
/// collection.
///
/// This is the main function for adding new collections. All other methods
/// that add ParticleID collections will eventually call this in one way or
/// another.
///
/// @param coll A ParticleIDCollection that should be added to the internal map
void addColl(const edm4hep::ParticleIDCollection& coll);

/// Add the information from one ParticleIDCollection to the handler together
/// with its meta data
///
/// @note This method does no check in any form whether the passed collection
/// and metadata are actually related in any form.
///
/// @param coll A ParticleIDCollection that should be added to the internal
/// map
/// @param pidInfo The metadata describing the algorithm that has been used to
/// determine the ParticleIDs of the coll
void addColl(const edm4hep::ParticleIDCollection& coll, const edm4hep::utils::ParticleIDMeta& pidInfo);

/// Add meta information for a collection
/// Add (arbitrary) metadata describing a PID algorithm.
///
/// This is the main function for ingesting metadata. All other methods that
/// add ParticleID algorithm metadata will eventually call this in one way or
/// another.
///
/// @param pidInfo The metadata describing an algorithm that has been used to
/// determine ParticleID objects
void addMetaInfo(const edm4hep::utils::ParticleIDMeta& pidInfo);

/// Add several (arbitrary) meta informations simultaneously
///
/// @tparam PIDMetas A variadic template that we use to enable calling this
/// with an arbitrary number of ParticleIDMeta objects
///
/// @param pidMetas An aribtrary number of metadata objects describing
/// algorithms that have been used to determine ParticleID
/// objects
template <typename... PIDMetas>
void addMetaInfos(const PIDMetas&... pidMetas) {
static_assert((std::is_same_v<PIDMetas, edm4hep::utils::ParticleIDMeta> && ...),
"Only ParticleIDMeta can be used to add metadata to a PIDHandler");
(addMetaInfo(pidMetas), ...);
}

/// Retrieve all ParticleIDs that are related to the passed
/// ReconstructedParticle
///
/// @param reco The ReconstructedParticle for which ParticleIDs should be
/// looked up
///
/// @returns All ParticleID objects (that this PIDHandler knows about) that
/// point to the passed ReconstructedParticle
std::vector<edm4hep::ParticleID> getPIDs(const edm4hep::ReconstructedParticle& reco) const;

/// Retrieve the ParticleID for a given algorithm type
/// Retrieve the ParticleID for a given PID algorithm
///
/// @param reco The ReconstructedParticle for which a ParticleID should be
/// looked up
/// @param algoType The (encoded) algorithm type that corresponds to the
/// desired algorithm. See also @ref getAlgoType
///
/// @returns The ParticleID object for this PID algorithm that points to the
/// passed ReconstrucedParticle if found or an empty optional
/// otherwise
std::optional<edm4hep::ParticleID> getPID(const edm4hep::ReconstructedParticle& reco, int algoType) const;

/// Retrieve the index in the parameters for a given parameter name and
/// algoType
///
/// @param algoType The (encoded) algorithm type that corresponds to the
/// desired algorithm. See also @ref getAlgoType
/// @param paramName The name of the parameter
///
/// @returns The index of the parameter which can be used to index into the
/// ParticleID::getParameters() values or an empty optional in case the
/// parameter name could not be found for the passed algorithm type
///
/// See also @ref edm4hep::utils::getParamIndex
std::optional<int> getParamIndex(int32_t algoType, const std::string& paramName) const;

/// Retrieve the algoType for a given algorithm name
///
/// @param algoName
///
/// @returns The (encoded) algorithm type for the desired algorithm if known
/// to the PIDHandler otherwise an empty optional.
std::optional<int32_t> getAlgoType(const std::string& algoName) const;

/// Set the metadata information for the passed collection in the metadata Frame.
///
/// This also sets the algorithmType of all elements in the collection to the
/// one that is found in the meta information.
///
/// @param metadata The metadata Frame into which the information should be
/// stored
/// @param pidcoll A ParticleIDCollection for which the corresponding
/// algorithm type will be set (consistent with what is found
/// in the @p pidMetaInfo)
/// @param pidMetaInfo The metadata oject that corresponds to the ParticleID
/// algorithm that has been used for creating the @p
/// pidcoll
static void setAlgoInfo(podio::Frame& metadata, edm4hep::ParticleIDCollection& pidcoll, const std::string& collname,
const edm4hep::utils::ParticleIDMeta& pidMetaInfo);

Expand All @@ -111,10 +290,27 @@ class PIDHandler {
/// @note It is user responsibility to ensure that the meta information that
/// is passed here and the one that is present in the collection with the
/// given name is consistent
///
/// @param metadata The metadata Frame into which the information should be
/// stored
/// @param collname The name of the (ParticleID) collection for which this
/// meta information should be stored
/// @param pidMetaInfo The metadata oject that corresponds to the ParticleID
/// algorithm that has been used for creating the
/// ParticleID objects stored in the collection with the @p
/// collname
static void setAlgoInfo(podio::Frame& metadata, const std::string& collname,
const edm4hep::utils::ParticleIDMeta& pidMetaInfo);

/// Get the ParticleID meta information for a given collection name from the metadata Frame.
///
/// @param metadata The metadata frame in which to search for ParticleID
/// related metadata
/// @param collName The (ParticleID) collection name for which to obtain metadata
///
/// @returns The metadata related to the PID algorithm that has been used to
/// create the @p collName collection if available, otherwise an
/// empty optional.
static std::optional<edm4hep::utils::ParticleIDMeta> getAlgoInfo(const podio::Frame& metadata,
const std::string& collName);
};
Expand Down
Loading