From f3efa0ad302e56b8aadade47077913e7bfe004ee Mon Sep 17 00:00:00 2001 From: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:33:19 +0200 Subject: [PATCH 01/13] Do not try to set the read-only cmake property ALIAS_GLOBAL (#292) --- edm4hep/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/edm4hep/CMakeLists.txt b/edm4hep/CMakeLists.txt index 16b6c10d2..075eba1f3 100644 --- a/edm4hep/CMakeLists.txt +++ b/edm4hep/CMakeLists.txt @@ -5,7 +5,6 @@ PODIO_GENERATE_DATAMODEL(edm4hep ../edm4hep.yaml headers sources IO_BACKEND_HAND PODIO_ADD_DATAMODEL_CORE_LIB(edm4hep "${headers}" "${sources}") add_library(EDM4HEP::edm4hep ALIAS edm4hep) -set_target_properties(edm4hep PROPERTIES ALIAS_GLOBAL true) file(GLOB_RECURSE top_headers ${PROJECT_SOURCE_DIR}/include/*.h) target_sources(edm4hep PUBLIC FILE_SET headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR}/include FILES ${top_headers}) @@ -17,7 +16,6 @@ endif() PODIO_ADD_ROOT_IO_DICT(edm4hepDict edm4hep "${headers}" src/selection.xml) add_library(EDM4HEP::edm4hepDict ALIAS edm4hepDict ) -set_target_properties(edm4hep PROPERTIES ALIAS_GLOBAL true) list(APPEND EDM4HEP_INSTALL_LIBS edm4hep edm4hepDict) From e27064f91cb1a8cdcbb840c6ff794dd1096f464a Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 4 Apr 2024 14:49:53 +0200 Subject: [PATCH 02/13] Use non-deprecated versions of SIO Reader/Writer (#293) --- test/read_events_sio.cc | 10 +++++++++- test/write_events_sio.cc | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/test/read_events_sio.cc b/test/read_events_sio.cc index 85ce3f8c3..92fe0b5ea 100644 --- a/test/read_events_sio.cc +++ b/test/read_events_sio.cc @@ -1,9 +1,17 @@ #include "read_events.h" +#include "podio/podioVersion.h" +#if PODIO_BUILD_VERSION >= PODIO_VERSION(0, 99, 0) +#include "podio/SIOReader.h" +#else #include "podio/SIOFrameReader.h" +namespace podio { +using SIOReader = podio::SIOFrameReader; +} +#endif int main(int, char**) { - read_events("edm4hep_events.sio"); + read_events("edm4hep_events.sio"); return 0; } diff --git a/test/write_events_sio.cc b/test/write_events_sio.cc index 032e7fa61..15454d5cc 100644 --- a/test/write_events_sio.cc +++ b/test/write_events_sio.cc @@ -1,8 +1,16 @@ #include "write_events.h" +#include "podio/podioVersion.h" +#if PODIO_BUILD_VERSION >= PODIO_VERSION(0, 99, 0) +#include "podio/SIOWriter.h" +#else #include "podio/SIOFrameWriter.h" +namespace podio { +using SIOWriter = podio::SIOFrameWriter; +} +#endif int main(int, char**) { - write("edm4hep_events.sio"); + write("edm4hep_events.sio"); return 0; } From 792cefc3a0f7cb3dfa12aba50a24a6f37089e84e Mon Sep 17 00:00:00 2001 From: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:04:00 +0200 Subject: [PATCH 03/13] Load Constants.h in python (#294) --- python/edm4hep/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/edm4hep/__init__.py b/python/edm4hep/__init__.py index f7e39750b..ab57c658e 100644 --- a/python/edm4hep/__init__.py +++ b/python/edm4hep/__init__.py @@ -18,6 +18,10 @@ res = ROOT.gInterpreter.LoadFile('edm4hep/utils/dataframe.h') if res != 0: raise RuntimeError('Failed to load dataframe.h') + +res = ROOT.gInterpreter.LoadFile('edm4hep/Constants.h') +if res != 0: + raise RuntimeError('Failed to load Constants.h') from ROOT import edm4hep # Make TAB completion work for utils From dd2b2f49a1877a1e32322ccef5ceb12df72ca183 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 19 Apr 2024 08:37:22 +0200 Subject: [PATCH 04/13] Give the utils target a more discernible output name (#295) --- utils/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 232d0ffce..8194ae0a0 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -9,6 +9,7 @@ target_link_libraries(kinematics PUBLIC INTERFACE ROOT::Core) target_compile_features(kinematics INTERFACE cxx_std_17) add_library(utils INTERFACE) +set_target_properties(utils PROPERTIES OUTPUT_NAME "edm4hepUtils") add_library(EDM4HEP::utils ALIAS utils) target_link_libraries(utils INTERFACE kinematics) From 4e3c1084568fdd46f855174aca8f4485eb5c5ab9 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 19 Apr 2024 13:49:51 +0200 Subject: [PATCH 05/13] Improve Doxygen config to only include what is strictly necessary (#289) * Be more explicit about inputs and path stripping * Fix missing inclusion of markdown files --- doc/Doxyfile.in | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 566068727..f3186dc2d 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -144,8 +144,10 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ @PROJECT_BINARY_DIR@ -STRIP_FROM_PATH += @PROJECT_SOURCE_DIR@/edm4hep +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/include \ + @PROJECT_SOURCE_DIR@/utils/include \ + @PROJECT_SOURCE_DIR@/edm4hep \ + @PROJECT_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -154,8 +156,10 @@ STRIP_FROM_PATH += @PROJECT_SOURCE_DIR@/edm4hep # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = @DOXYGEN_INCLUDE_DIRS@ @PROJECT_BINARY_DIR@/include -STRIP_FROM_INC_PATH += @PROJECT_SOURCE_DIR@/edm4hep +STRIP_FROM_INC_PATH = @PROJECT_SOURCE_DIR@/include \ + @PROJECT_SOURCE_DIR@/utils/include \ + @PROJECT_SOURCE_DIR@/edm4hep + # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -745,9 +749,11 @@ WARN_LOGFILE = @PROJECT_BINARY_DIR@/doxygen-warnings.log # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @PROJECT_SOURCE_DIR@ -INPUT += @PROJECT_BINARY_DIR@/include -INPUT += @CMAKE_CURRENT_BINARY_DIR@ +INPUT = @PROJECT_SOURCE_DIR@/edm4hep \ + @PROJECT_SOURCE_DIR@/utils/include \ + @PROJECT_SOURCE_DIR@/include \ + @PROJECT_SOURCE_DIR@/doc \ + @PROJECT_SOURCE_DIR@/README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -805,6 +811,7 @@ EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */podio/* */test/* */tests/* EXCLUDE_PATTERNS += */dict/* */cmake/* */scripts/* +EXCLUDE_PATTERNS += */edm4hep/src/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -815,7 +822,7 @@ EXCLUDE_PATTERNS += */dict/* */cmake/* */scripts/* # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = podio # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include From 162c858c8464daa42e8c0aa16e1657ee6c4069a5 Mon Sep 17 00:00:00 2001 From: jmcarcell Date: Tue, 23 Apr 2024 21:08:43 +0200 Subject: [PATCH 06/13] Update build workflow. Previous commit: Improve Doxygen config to only include what is strictly necessary (#289) --- .github/workflows/key4hep-build.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/key4hep-build.yaml b/.github/workflows/key4hep-build.yaml index 5ba768a48..2acf17468 100644 --- a/.github/workflows/key4hep-build.yaml +++ b/.github/workflows/key4hep-build.yaml @@ -1,3 +1,6 @@ +# Do not edit this file, it will be overwritten! +# The template file can be found in +# https://github.com/key4hep/key4hep-actions/blob/main/workflows/key4hep-build.yaml name: Key4hep build on: @@ -7,7 +10,7 @@ on: pull_request: workflow_dispatch: schedule: - - cron: '17 5 * * *' + - cron: '16 5 * * *' jobs: From 9878b164e97f4b4165825f086c36937717674eec Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 1 May 2024 12:04:13 +0200 Subject: [PATCH 07/13] Reverse the direction of the ParticleID relation(s) (#268) * Remove the ParticlID from the Cluster * Remove the ParticleIDUsed from the ReconstructedParticle * Remove the ParticleIDs from the reco particles * Add a relation from a PID to a reco particle * Add a first version of a PIDHandler * Fix typo Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> * Add basic tests for PIDHandler * Add a bit of metadata handling to PIDHandler * Add testing for PIDHandler metadata handling * Simplify signatures a bit by introducing helper struct * Fix README links once again * Add helper function to get all meta info in one go * Make it possible to add meta information post-hoc * Make retrieving param indices free function * More verbose docstrings * Add short document describing PIDHandler and its usage * Fix grammar and be more specific Co-authored-by: Andre Sailer * fix typos in README Co-authored-by: Mateusz Jakub Fila <37295697+m-fila@users.noreply.github.com> * Fix usage example code * Do not create an unnecessary intermediate vector * Add section about how (not) to use the PIDHandler * Try to not advertise optional misuse --------- Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> Co-authored-by: Andre Sailer Co-authored-by: Mateusz Jakub Fila <37295697+m-fila@users.noreply.github.com> --- README.md | 6 +- doc/PIDHandler.md | 239 ++++++++++++++++++ edm4hep.yaml | 6 +- include/edm4hep/Constants.h | 6 + test/utils/CMakeLists.txt | 2 +- test/utils/test_PIDHandler.cpp | 219 ++++++++++++++++ utils/CMakeLists.txt | 11 +- utils/include/edm4hep/utils/ParticleIDUtils.h | 107 ++++++++ utils/src/ParticleIDUtils.cc | 140 ++++++++++ 9 files changed, 727 insertions(+), 9 deletions(-) create mode 100644 doc/PIDHandler.md create mode 100644 test/utils/test_PIDHandler.cpp create mode 100644 utils/include/edm4hep/utils/ParticleIDUtils.h create mode 100644 utils/src/ParticleIDUtils.cc diff --git a/README.md b/README.md index 4f76e6b62..34d5e2428 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ A generic event data model for future HEP collider experiments. |-|-|-| | [EventHeader](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L172) | [MCParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L184) | [SimTrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L252) | | [CaloHitContribution](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L294) | [SimCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L306) | [RawCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L318) | -| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L327) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L339) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L352) | -| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L373) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L388) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L407) | -| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L420) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L439) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L456) | +| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L327) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L339) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L354) | +| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L375) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L390) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L409) | +| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L422) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L441) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L458) | | [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L564) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L598) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L611) | | [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L622) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L634) | | diff --git a/doc/PIDHandler.md b/doc/PIDHandler.md new file mode 100644 index 000000000..76474ab0c --- /dev/null +++ b/doc/PIDHandler.md @@ -0,0 +1,239 @@ +# PIDHandler introduction and usage + +This page contains some bigger picture introduction for the utilities that are +available to work with `ParticleID`s and its related metadata. It also contains +examples for the most common usage patterns. + +## PIDHandler basics +The `PIDHandler` can be use to work with `ParticleIDCollection`s. The main +features are +- the retrieval meta information for a `ParticleIDCollection`, making it + possible to e.g. retrieve the indices of parameters from parameter names +- the possibility to invert the relation to `ReconstructedParticle`s, which + allows one to, e.g. get all `ParticleID`s that point to a specific + `ReconstructedParticle`. + +### When (not) to use the PIDHandler +NOTE: Depending on how you use the `PIDHandler` it might incur an unnecessary +performance overhead. The main purpose is to use it in cases, where the relation +between `ParticleID`s and `ReconstructedParticle`s is not trivial, e.g. +- When the two collections do **not** run in parallel +- When not all `ReconstructedParticle`s have a `ParticleID` attached +- When you want to have access to several `ParticleID`s for a given + `ReonstructedParticle` and there is no trivial relation between them. + +In case your collections run in parallel it will be much quicker to just loop +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. + + +## ParticleIDMeta basics +`ParticleIDMeta` is a simple struct that bundles all ParticleID meta information +for one collection together. Whenever metadata is involved for ParticleIDs this +will be the thing to use. + +## General considerations + +There is no strictly enforced coupling between the meta information that is set +for a collection and the contents of that collection. In order for everything to +work as expected the following assumptions have to be met +- The collection name that is passed to the `setAlgoInfo` methods to set meta + information has to match the collection name used for putting collections into + events. +- All elements of a `ParticleIDCollection` have the same value set for + `algorithmType`. Additionally, it is usually assumed that this is a unique + value. +- The `ParticleIDMeta::paramNames` is assumed to match the parameters as they + are set in the `ParticleID` elements. + +Additionally there are a few usage considerations to be kept in mind +- The `PIDHandler` can be used without any meta data about the contained + `ParticleID`s. However, in this case it will only be useful for inverting + relations and for getting `ParticleID`s that point to a given + `ReconstructedParticle`. +- There is no guarantee that the meta information that is set in a `PIDHandler` + is consistent with the elements it knows about. Some constructors attempt to + construct a consistent handler, but it is always possible to set meta + information via `setMetaInfo`, which might be completely unrelated to the + actual contents of a handler. + +Given that there is no enforced consistency the interface and the utility +functionality makes heavy use of +[`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) in its +return values / types. This makes it possible to check whether the desired +information was actually present, without having to reserve some special values +to indicate that fact. + +## Usage examples + +**The following examples assume the following includes** (which will not be visible in the examples) + +```cpp +#include +#include + +#include +``` + +**The following examples additionally assume, that there is a `metadata` and an +`event` Frame present.** The `metadata` Frame can be obtained like this from +standard EDM4hep files. + +```cpp +#include +#include + +podio::Frame getMetadataFrame(const std::string& filename) { + podio::ROOTReader reader{}; + reader.openFile(filename); + return podio::Frame(reader.readNextEntry(podio::Category::Metadata)); +} +``` + +Alternatively it can be created as an empty Frame and then just written using +the `podio::Category::Metadata` category name. + +**Finally most of the examples assume that the desired values were found and +simply get the `value` from the returned `std::optional` directly.** This means +that most of the times you will see lines like this + +```cpp +const auto value = pidHandler.getAlgoType("someAlgo").value(); +``` + +This will throw an exception if some information is not available. Check if the +optional has a value when actually using these utilities. + +### Creating a PIDHandler for a complete event + +If you want to work with a `PIDHandler` that has a somewhat global view of all +`ParticleID`s that are present you can simply construct one from an event Frame + +```cpp +const auto pidHandler = edm4hep::utils::PIDHandler::from(event); +``` + +You can also construct a `pidHandler` that populates some meta information +internally by also passing in the `metadata` Frame to this constructor +```cpp +const auto pidHandler = edm4hep::utils::PIDHandler::from(event, metadata); +``` + +### Creating a PIDHandler from an arbitrary number of collections + +If you simply want to use a `PIDHandler` to invert the relations for a given set +of `ParticleIDCollection`s you can do that via + +```cpp +const auto& tofPID = event.get("ToFPID"); +const auto& dNdXPID = event.get("dNdXPID"); +const auto& mvaPID = event.get("mvaPID"); + +const auto pidHandler = edm4hep::utils::PIDHandler::from(tofPID, dNdXPID, mvaPID); +``` + +**This handler will not have any access to metadata!** If you need metadata as +well, you can add that after the fact using the `addMetaInfo` method. + +### Using a PIDHandler to get the ParticleIDs for a reco particle + +If you have constructed a `PIDHandler` you can use it to get all or specific +related `ParticleID` objects of a `ReconstructedParticle` + +```cpp +const auto pidHandler = /* constructed as above somehow */; + +const auto recos = event.get("recos"); +const auto reco = recos[0]; + +// Get all related ParticleIDs +const auto pids = pidHandler.getPIDs(reco); + +// Only get a specific ParticleID +const auto tofAlgoType = pidHandler.getAlgoType("ToFPID").value(); +const auto tofPID = pidHandler.getPID(reco, tofAlgoType).value(); +``` + +### Retrieving meta information for a collection + +If you simply want to get the meta information for a collection (called "ToFPID" +in this example) do + +```cpp +const auto algoInfo = edm4hep::utils::PIDHandler::getAlgoInfo(metadata, "ToFPID").value(); +``` + + +### Using meta information of a collection + +If you have retrieved the meta information you can directly use that to learn +something about the `ParticleID`s it describes + +```cpp +const auto& tofPIDs = event.get("ToFPID"); +const auto algoInfo = edm4hep::utils::PIDHandler::getAlgoInfo(metadata, "ToFPID").value(); +const auto timeIndex = edm4hep::utils::getParamIndex(algoInfo, "time").value(); + +for (const auto tof : tofPIDs) { + const auto time = tof.getParameters(timeIndex); + // ... +} +``` + +If you have a `PIDHandler` with enough meta information set it is also possible +to retrieve the `timeIndex` from that + +```cpp +const auto pidHandler = /* construct somehow see above */ +const auto tofAlgoType = pidHandler.getAlgoType("ToFPID").value(); +const auto timeIndex = pidHandler.getParamIndex("time").value(); + +// ... as above +``` + + +### Setting meta information for a collection + +If you have a `ParticleIDCollection` and want to persist some meta information +for it the following snippet shows you how to do that + +```cpp +// Create ParticleID meta information for an algorithm +const auto tofMeta = edm4hep::utils::ParticleIDMeta{"TimeOfFlight", 42, {"time", "time_error"}}; + +auto tofPIDs = edm4hep::ParticleIDCollection{}; +// ... fill collection + +edm4hep::utils::PIDHandler::setAlgoInfo(metadata, tofPIDs, "ToFPID", tofMeta); +event.put(std::move(tofPIDs), "ToFPID"); +``` + +This will put the necessary metadata into the `metadata` Frame and also take +care of setting the `algorithmType` field of all elements of the `tofPIDs` +collection to `42` (the value it is set to in the `tofMeta` object). + +**Things to note:** +- It is important to use the same names for putting the collection into the + `event` Frame and the one that is passed to `PIDHandler::setAlgoInfo`! + +### Setting meta information using a collection name + +If you want to set the meta information for a `ParticleIDCollection` that you no +longer have mutable access to, this can be done via + +```cpp +// Create ParticleID meta information for an algorithm +const auto tofMeta = edm4hep::utils::ParticleIDMeta{"TimeOfFlight", 42, {"time", "time_error"}}; + +edm4hep::utils::PIDHandler::setAlgoInfo(metadata, "ToFPID", tofMeta); +``` + +**Things to note:** +- In this case it is user responsibility to set the `algoType` of the + `ParticleIDMeta` to the same value as the one that is used for the elements of + the collection. +- It is important to use the same names for putting the collection into the + `event` Frame and the one that is passed to `PIDHandler::setAlgoInfo`! diff --git a/edm4hep.yaml b/edm4hep.yaml index 05d793cfb..20d76371d 100644 --- a/edm4hep.yaml +++ b/edm4hep.yaml @@ -346,6 +346,8 @@ datatypes: - float likelihood // likelihood of this hypothesis - in a user defined normalization VectorMembers: - float parameters // parameters associated with this hypothesis + OneToOneRelations: + - edm4hep::ReconstructedParticle particle // the particle from which this PID has been computed #------ Cluster @@ -367,7 +369,7 @@ datatypes: OneToManyRelations: - edm4hep::Cluster clusters // clusters that have been combined to this cluster - edm4hep::CalorimeterHit hits // hits that have been combined to this cluster - - edm4hep::ParticleID particleIDs // particle IDs (sorted by their likelihood) + #------------- TrackerHit edm4hep::TrackerHit3D: @@ -467,12 +469,10 @@ datatypes: - std::array covMatrix // cvariance matrix of the reconstructed particle 4vector (10 parameters). Stored as lower triangle matrix of the four momentum (px,py,pz,E), i.e. cov(px,px), cov(py,## OneToOneRelations: - edm4hep::Vertex startVertex // start vertex associated to this particle - - edm4hep::ParticleID particleIDUsed // particle Id used for the kinematics of this particle OneToManyRelations: - edm4hep::Cluster clusters // clusters that have been used for this particle - edm4hep::Track tracks // tracks that have been used for this particle - edm4hep::ReconstructedParticle particles // reconstructed particles that have been combined to this particle - - edm4hep::ParticleID particleIDs // particle Ids (not sorted by their likelihood) ExtraCode: declaration: " bool isCompound() const { return particles_size() > 0 ;}\n diff --git a/include/edm4hep/Constants.h b/include/edm4hep/Constants.h index 90e61a450..25abc7d68 100644 --- a/include/edm4hep/Constants.h +++ b/include/edm4hep/Constants.h @@ -24,6 +24,12 @@ static constexpr const char* CellIDEncoding = "CellIDEncoding"; static constexpr const char* EventHeaderName = "EventHeader"; static constexpr const char* EventWeights = "EventWeightNames"; static constexpr const char* shapeParameterNames = "shapeParameterNames"; + +/// The collection parameter name for accessing the names of the parameters for +/// a ParticleID collection +static constexpr const char* pidParameterNames = "ParameterNames"; +static constexpr const char* pidAlgoName = "AlgoName"; +static constexpr const char* pidAlgoType = "AlgoType"; } // namespace edm4hep #endif // EDM4HEP_CONSTANTS_H diff --git a/test/utils/CMakeLists.txt b/test/utils/CMakeLists.txt index ae6eaf952..e588c861f 100644 --- a/test/utils/CMakeLists.txt +++ b/test/utils/CMakeLists.txt @@ -16,7 +16,7 @@ endif() include(Catch) add_executable(unittests_edm4hep - test_kinematics.cpp test_vector_utils.cpp) + test_kinematics.cpp test_vector_utils.cpp test_PIDHandler.cpp) target_link_libraries(unittests_edm4hep edm4hep EDM4HEP::utils Catch2::Catch2 Catch2::Catch2WithMain) option(SKIP_CATCH_DISCOVERY "Skip the Catch2 test discovery" OFF) diff --git a/test/utils/test_PIDHandler.cpp b/test/utils/test_PIDHandler.cpp new file mode 100644 index 000000000..1b018a08e --- /dev/null +++ b/test/utils/test_PIDHandler.cpp @@ -0,0 +1,219 @@ +#include + +#include +#include + +#include + +#include + +#include + +edm4hep::ReconstructedParticleCollection createRecos() { + edm4hep::ReconstructedParticleCollection coll; + + for (size_t i = 1; i < 10; ++i) { + auto reco = coll.create(); + reco.setPDG(i); + } + + return coll; +} + +edm4hep::ParticleIDCollection createParticleIDs(const edm4hep::ReconstructedParticleCollection& recos, + float likelihood) { + edm4hep::ParticleIDCollection coll; + for (const auto reco : recos) { + auto pid = coll.create(); + pid.setLikelihood(likelihood); + pid.setParticle(reco); + + pid.addToParameters(1.23f); + pid.addToParameters(3.14f); + } + + return coll; +} + +/// Create an event with some ParticleID collections and a metadata Frame that +/// holds the corresponding metadata +std::tuple createEventAndMetadata() { + auto retTuple = std::make_tuple(podio::Frame{}, podio::Frame{}); + auto& [event, metadata] = retTuple; + + const auto& recos = event.put(createRecos(), "reco_particles"); + auto pidColl1 = createParticleIDs(recos, 1.0f); + auto pidColl2 = createParticleIDs(recos, 2.0f); + + edm4hep::utils::PIDHandler::setAlgoInfo(metadata, pidColl1, "particleIds_1", + {"pidAlgo_1", 42, {"first_param", "second_param"}}); + edm4hep::utils::PIDHandler::setAlgoInfo(metadata, pidColl2, "particleIds_2", {"algo_2", 123, {"1", "2"}}); + + event.put(std::move(pidColl1), "particleIds_1"); + event.put(std::move(pidColl2), "particleIds_2"); + + return retTuple; +} + +void checkHandlerValidReco(const edm4hep::utils::PIDHandler& handler, const edm4hep::ReconstructedParticle& reco) { + const auto pids = handler.getPIDs(reco); + + REQUIRE(pids.size() == 2); + REQUIRE(pids[0].getParticle() == reco); + REQUIRE(pids[1].getParticle() == reco); + REQUIRE(pids[0].getParameters()[0] == 1.23f); + REQUIRE(pids[0].getParameters()[1] == 3.14f); + + // Cannot guarantee an order if the handler is constructed from a Frame + const auto llh1 = pids[0].getLikelihood(); + const auto llh2 = pids[1].getLikelihood(); + if (llh1 == 1.0f) { + REQUIRE(llh2 == 2.0f); + } else { + REQUIRE(llh2 == 1.0f); + } +} + +TEST_CASE("PIDHandler basics", "[pid_utils]") { + using namespace edm4hep; + + const auto recoColl = createRecos(); + const auto pidColl1 = createParticleIDs(recoColl, 1.0f); + const auto pidColl2 = createParticleIDs(recoColl, 2.0f); + + auto handler = utils::PIDHandler(); + handler.addColl(pidColl1); + handler.addColl(pidColl2); + + SECTION("Valid PID for reco") { + auto reco = recoColl[0]; + checkHandlerValidReco(handler, reco); + } + + SECTION("Unknown reco") { + const auto reco = edm4hep::ReconstructedParticle(); + const auto pids = handler.getPIDs(reco); + + REQUIRE(pids.empty()); + } +} + +TEST_CASE("PIDHandler from variadic list of collections", "[pid_utils]") { + using namespace edm4hep; + + const auto recoColl = createRecos(); + const auto pidColl1 = createParticleIDs(recoColl, 1.0f); + const auto pidColl2 = createParticleIDs(recoColl, 2.0f); + + const auto handler = utils::PIDHandler::from(pidColl1, pidColl2); + + SECTION("Valid PID for reco") { + auto reco = recoColl[3]; + checkHandlerValidReco(handler, reco); + } + + SECTION("Unkown reco") { + const auto reco = edm4hep::ReconstructedParticle(); + const auto pids = handler.getPIDs(reco); + + REQUIRE(pids.empty()); + } +} + +TEST_CASE("PIDHandler w/ addMetaInfo", "[pid_utils]") { + using namespace edm4hep; + auto handler = utils::PIDHandler(); + + const auto recoColl = createRecos(); + auto pidColl1 = createParticleIDs(recoColl, 1.0f); + for (auto pid : pidColl1) { + pid.setAlgorithmType(42); + } + const auto pidInfo1 = utils::ParticleIDMeta{"fancyAlgo", 42, {"p1", "p2"}}; + + handler.addColl(pidColl1, pidInfo1); + + REQUIRE(handler.getAlgoType("fancyAlgo").value_or(0) == 42); + REQUIRE(handler.getParamIndex(42, "p2").value_or(-1) == 1); + REQUIRE(handler.getPID(recoColl[0], 42).value() == pidColl1[0]); + + // Technically, we can even just add meta data without having a corresponding + // ParticleID collection to match + handler.addMetaInfo(utils::ParticleIDMeta{"anotherAlgo", 123, {}}); + REQUIRE(handler.getAlgoType("anotherAlgo").value() == 123); + + // Expected exceptions also get thrown + REQUIRE_THROWS_AS(handler.addMetaInfo(utils::ParticleIDMeta{"anotherAlgo", 321, {"param"}}), std::runtime_error); + // No information about this meta data can be obtained + REQUIRE_FALSE(handler.getParamIndex(321, "param").has_value()); + + REQUIRE_THROWS_AS(handler.addMetaInfo(utils::ParticleIDMeta{"newAlgo", 42, {"PARAM"}}), std::runtime_error); + // Existing meta info is unchanged + REQUIRE_FALSE(handler.getParamIndex(42, "PARAM").has_value()); + REQUIRE(handler.getParamIndex(42, "p2").value_or(-1) == 1); +} + +TEST_CASE("PIDHandler from Frame w/ metadata", "[pid_utils]") { + using namespace edm4hep; + const auto& [event, metadata] = createEventAndMetadata(); + + const auto handler = utils::PIDHandler::from(event, metadata); + + const auto pidAlgo1 = handler.getAlgoType("pidAlgo_1").value(); + const auto pidAlgo2 = handler.getAlgoType("algo_2").value(); + REQUIRE(pidAlgo1 == 42); + REQUIRE(pidAlgo2 == 123); + REQUIRE_FALSE(handler.getAlgoType("non-existant-algo").has_value()); + + // Check that getting a ParticleID object for a reconstructed particle via the + // algorithmType works + const auto& recos = event.get("reco_particles"); + const auto& pidColl1 = event.get("particleIds_1"); + const auto& pidColl2 = event.get("particleIds_2"); + const auto pid1 = handler.getPID(recos[0], pidAlgo1).value(); + REQUIRE(pid1 == pidColl1[0]); + const auto pid2 = handler.getPID(recos[0], pidAlgo2).value(); + REQUIRE(pid2 == pidColl2[0]); + REQUIRE_FALSE(handler.getPID(recos[0], -1).has_value()); // empty optional for non-existant algoType + + // Check that parameter handling works as well + const auto parIndex1 = handler.getParamIndex(pidAlgo1, "first_param").value(); + REQUIRE(parIndex1 == 0); + const auto parIndex2 = handler.getParamIndex(pidAlgo2, "2").value(); + REQUIRE(parIndex2 == 1); + // Valid algo but invalid parameter name + REQUIRE_FALSE(handler.getParamIndex(pidAlgo1, "non-existant-param").has_value()); + // Invalid algorithm, the parameter name is not even checked in this case + REQUIRE_FALSE(handler.getParamIndex(-1, "doesnot matter").has_value()); + + const auto pidInfo = utils::PIDHandler::getAlgoInfo(metadata, "particleIds_1").value(); + REQUIRE(pidInfo.algoName == "pidAlgo_1"); + REQUIRE(pidInfo.algoType == 42); + REQUIRE(pidInfo.paramNames.size() == 2); + REQUIRE(pidInfo.paramNames[0] == "first_param"); + REQUIRE(pidInfo.paramNames[1] == "second_param"); +} + +TEST_CASE("PIDHandler from Frame w/o metadata", "[pid_utils]") { + using namespace edm4hep; + const auto& [event, _] = createEventAndMetadata(); + + const auto handler = utils::PIDHandler::from(event); + // No metadata available info available in this case + REQUIRE_FALSE(handler.getAlgoType("pidAlgo_1").has_value()); + + // But the rest should still work as expected + const auto& recoColl = event.get("reco_particles"); + + SECTION("Valid PID for reco") { + auto reco = recoColl[0]; + checkHandlerValidReco(handler, reco); + } + + SECTION("Unknown reco") { + const auto reco = edm4hep::ReconstructedParticle(); + const auto pids = handler.getPIDs(reco); + + REQUIRE(pids.empty()); + } +} diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 8194ae0a0..821938008 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -8,10 +8,17 @@ target_include_directories(kinematics target_link_libraries(kinematics PUBLIC INTERFACE ROOT::Core) target_compile_features(kinematics INTERFACE cxx_std_17) -add_library(utils INTERFACE) +set(utils_sources + src/ParticleIDUtils.cc +) + +add_library(utils SHARED ${utils_sources}) set_target_properties(utils PROPERTIES OUTPUT_NAME "edm4hepUtils") add_library(EDM4HEP::utils ALIAS utils) -target_link_libraries(utils INTERFACE kinematics) +target_include_directories(utils + PUBLIC $ + $) +target_link_libraries(utils PUBLIC EDM4HEP::edm4hep kinematics) set(sources src/dataframe.cc) diff --git a/utils/include/edm4hep/utils/ParticleIDUtils.h b/utils/include/edm4hep/utils/ParticleIDUtils.h new file mode 100644 index 000000000..0878c3a09 --- /dev/null +++ b/utils/include/edm4hep/utils/ParticleIDUtils.h @@ -0,0 +1,107 @@ +#ifndef EDM4HEP_UTILS_PARTICLEIDUTILS_H +#define EDM4HEP_UTILS_PARTICLEIDUTILS_H + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace edm4hep::utils { + +/// A simple struct bundling relevant metadata for a ParticleID collection +struct ParticleIDMeta { + std::string algoName{}; ///< The name of the algorithm + int32_t algoType{0}; ///< The (user defined) algorithm type + std::vector paramNames{}; ///< The names of the parameters +}; + +/// Get the index of the parameter in the passed ParticleID meta info +std::optional getParamIndex(const ParticleIDMeta& pidMetaInfo, const std::string& param); + +/// Utility class to invert the ParticleID to ReconstructedParticle relation +/// See [this page](@ref md_doc_2_p_i_d_handler) for example usage and more information. +class PIDHandler { + + using RecoPidMapT = std::multimap; + + RecoPidMapT m_recoPidMap{}; ///< The internal map from recos to pids + + std::map m_algoTypes{}; ///< Maps algo names to algo types + + /// Maps algo types to the full particle id meta information + std::map m_algoPidMeta{}; + +public: + PIDHandler() = default; + ~PIDHandler() = default; + PIDHandler(const PIDHandler&) = default; + PIDHandler& operator=(const PIDHandler&) = default; + PIDHandler(PIDHandler&&) = default; + PIDHandler& operator=(PIDHandler&&) = default; + + /// Construct a PIDHandler from an arbitrary number of ParticleIDCollections + template + static PIDHandler from(const ParticleIDCollection& coll, const PIDColls&... pidColls) { + static_assert((std::is_same_v && ...), + "PIDHandler can only be constructed from ParticleIDCollections"); + PIDHandler handler{}; + handler.addColl(coll); + (handler.addColl(pidColls), ...); + return handler; + } + + /// Create a PIDHandler from a Frame potentially also populating some metadata information. + static PIDHandler from(const podio::Frame& event, const podio::Frame& metadata = {}); + + /// Add the information from one ParticleIDCollection to the handler + void addColl(const edm4hep::ParticleIDCollection& coll); + + /// Add the information from one ParticleIDCollection to the handler together + /// with its meta data + void addColl(const edm4hep::ParticleIDCollection& coll, const edm4hep::utils::ParticleIDMeta& pidInfo); + + /// Add meta information for a collection + void addMetaInfo(const edm4hep::utils::ParticleIDMeta& pidInfo); + + /// Retrieve all ParticleIDs that are related to the passed + /// ReconstructedParticle + std::vector getPIDs(const edm4hep::ReconstructedParticle& reco) const; + + /// Retrieve the ParticleID for a given algorithm type + std::optional getPID(const edm4hep::ReconstructedParticle& reco, int algoType) const; + + /// Retrieve the index in the parameters for a given parameter name and + /// algoType + std::optional getParamIndex(int32_t algoType, const std::string& paramName) const; + + /// Retrieve the algoType for a given algorithm name + std::optional 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. + static void setAlgoInfo(podio::Frame& metadata, edm4hep::ParticleIDCollection& pidcoll, const std::string& collname, + const edm4hep::utils::ParticleIDMeta& pidMetaInfo); + + /// Set the metadata information for a given collection name in the metadata Frame. + /// + /// @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 consisent + 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. + static std::optional getAlgoInfo(const podio::Frame& metadata, + const std::string& collName); +}; +} // namespace edm4hep::utils + +#endif // EDM4HEP_UTILS_PARTICLEIDUTILS_H diff --git a/utils/src/ParticleIDUtils.cc b/utils/src/ParticleIDUtils.cc new file mode 100644 index 000000000..43c1c61ab --- /dev/null +++ b/utils/src/ParticleIDUtils.cc @@ -0,0 +1,140 @@ +#include + +#include "edm4hep/Constants.h" + +#include + +#include +#include +#include +#include + +namespace edm4hep::utils { + +std::optional getParamIndex(const ParticleIDMeta& pidMetaInfo, const std::string& param) { + const auto nameIt = std::find(pidMetaInfo.paramNames.begin(), pidMetaInfo.paramNames.end(), param); + if (nameIt != pidMetaInfo.paramNames.end()) { + return std::distance(pidMetaInfo.paramNames.begin(), nameIt); + } + return std::nullopt; +} + +void PIDHandler::addColl(const edm4hep::ParticleIDCollection& coll) { + for (const auto pid : coll) { + m_recoPidMap.emplace(pid.getParticle(), pid); + } +} + +void PIDHandler::addColl(const edm4hep::ParticleIDCollection& coll, const edm4hep::utils::ParticleIDMeta& pidInfo) { + addColl(coll); + addMetaInfo(pidInfo); +} + +void PIDHandler::addMetaInfo(const edm4hep::utils::ParticleIDMeta& pidInfo) { + const auto [algoIt, inserted] = m_algoTypes.emplace(pidInfo.algoName, pidInfo.algoType); + if (!inserted) { + throw std::runtime_error("Cannot have duplicate algorithm names (" + pidInfo.algoName + " already exists)"); + } + + const auto [__, metaInserted] = m_algoPidMeta.emplace(pidInfo.algoType, pidInfo); + if (!metaInserted) { + if (inserted) { + m_algoTypes.erase(algoIt); + } + throw std::runtime_error("Cannot have duplicate algorithm types (" + std::to_string(pidInfo.algoType) + + " already exists)"); + } +} + +PIDHandler PIDHandler::from(const podio::Frame& event, const podio::Frame& metadata) { + PIDHandler handler{}; + for (const auto& name : event.getAvailableCollections()) { + const auto* coll = event.get(name); + if (const auto pidColl = dynamic_cast(coll)) { + handler.addColl(*pidColl); + + auto maybeMetaInfo = PIDHandler::getAlgoInfo(metadata, name); + if (maybeMetaInfo) { + handler.addMetaInfo(std::move(maybeMetaInfo.value())); + } + } + } + + return handler; +} + +std::vector PIDHandler::getPIDs(const edm4hep::ReconstructedParticle& reco) const { + std::vector pids; + const auto& [begin, end] = m_recoPidMap.equal_range(reco); + pids.reserve(std::distance(begin, end)); + + for (auto it = begin; it != end; ++it) { + pids.emplace_back(it->second); + } + + return pids; +} + +std::optional PIDHandler::getPID(const edm4hep::ReconstructedParticle& reco, + int32_t algoType) const { + const auto& [begin, end] = m_recoPidMap.equal_range(reco); + for (auto it = begin; it != end; ++it) { + if (it->second.getAlgorithmType() == algoType) { + return it->second; + } + } + + return std::nullopt; +} + +std::optional PIDHandler::getParamIndex(int32_t algoType, const std::string& paramName) const { + if (const auto it = m_algoPidMeta.find(algoType); it != m_algoPidMeta.end()) { + return edm4hep::utils::getParamIndex(it->second, paramName); + } + + return std::nullopt; +} + +std::optional PIDHandler::getAlgoType(const std::string& algoName) const { + if (const auto it = m_algoTypes.find(algoName); it != m_algoTypes.end()) { + return it->second; + } + + return std::nullopt; +} + +void PIDHandler::setAlgoInfo(podio::Frame& metadata, edm4hep::ParticleIDCollection& pidColl, + const std::string& collName, const edm4hep::utils::ParticleIDMeta& pidMetaInfo) { + for (auto pid : pidColl) { + pid.setAlgorithmType(pidMetaInfo.algoType); + } + + PIDHandler::setAlgoInfo(metadata, collName, pidMetaInfo); +} + +void PIDHandler::setAlgoInfo(podio::Frame& metadata, const std::string& collName, + const edm4hep::utils::ParticleIDMeta& pidMetaInfo) { + metadata.putParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoName), pidMetaInfo.algoName); + metadata.putParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoType), pidMetaInfo.algoType); + metadata.putParameter(podio::collMetadataParamName(collName, edm4hep::pidParameterNames), pidMetaInfo.paramNames); +} + +std::optional PIDHandler::getAlgoInfo(const podio::Frame& metadata, + const std::string& collName) { + ParticleIDMeta pidInfo{}; + + pidInfo.algoName = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoName)); + // Use the algoName as proxy to see whether we could actually get the + // information from the metadata + if (pidInfo.algoName.empty()) { + return std::nullopt; + } + + pidInfo.algoType = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoType)); + pidInfo.paramNames = metadata.getParameter>( + podio::collMetadataParamName(collName, edm4hep::pidParameterNames)); + + return pidInfo; +} + +} // namespace edm4hep::utils From f46cf67f39d5d4b587e08359786d808a1c5c8ea3 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 1 May 2024 12:09:45 +0200 Subject: [PATCH 08/13] Add dedicated covariance matrix components and use them (#287) * Add a 6D cov matrix component for use in TrackState * Add a 3D covariance matrix component for positions * Add a 4D covariance matrix component for four momenta * Add a 2D covariance matrix component and use it * Make CovMatrixNf components behave more array like for compatibility * Use CovMatrix4f instead of array * Fix typos * Remove storage details from cov matrix docstrings * Move common functionality into header and include that in yaml * Make sure things fail at compile time for wrong inputs * Move things into detail and add exceptions for invalid access * Pass arrays by const-ref * Make constructors take arguments by const-ref * Add constructor from initializer lists for cov matrix components * Update README links * Add covariance matrix components to README tables * Remove unnecessary overload to fix ambiguities * Update README links --------- Co-authored-by: hegner --- CMakeLists.txt | 2 + README.md | 31 ++-- edm4hep.yaml | 161 ++++++++++++++++-- edm4hep/CMakeLists.txt | 4 + include/edm4hep/Constants.h | 20 +++ include/edm4hep/detail/CovMatrixCommon.ipp | 90 ++++++++++ test/utils/CMakeLists.txt | 3 +- test/utils/test_covmatrix_utils.cpp | 127 ++++++++++++++ test/utils/test_kinematics.py | 2 +- .../include/edm4hep/utils/cov_matrix_utils.h | 128 ++++++++++++++ 10 files changed, 540 insertions(+), 28 deletions(-) create mode 100644 include/edm4hep/detail/CovMatrixCommon.ipp create mode 100644 test/utils/test_covmatrix_utils.cpp create mode 100644 utils/include/edm4hep/utils/cov_matrix_utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0785c02b4..7fdf3fb31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,8 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/edm4hep/edm4hep/EDM4hepVersion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/edm4hep ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/python/edm4hep/__version__.py.in ${CMAKE_CURRENT_SOURCE_DIR}/python/edm4hep/__version__.py) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/edm4hep + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) #--- add CMake infrastructure -------------------------------------------------- include(cmake/EDM4HEPCreateConfig.cmake) diff --git a/README.md b/README.md index 34d5e2428..e2ef22b4a 100644 --- a/README.md +++ b/README.md @@ -12,36 +12,39 @@ A generic event data model for future HEP collider experiments. | | | | |-|-|-| -| [Vector4f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L9) | [Vector3f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L34) | [Vector3d](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L56) | -| [Vector2i](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L84) | [Vector2f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L104) | [TrackState](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L124) | -| [Quantity](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L147) | [Hypothesis](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L155) | [HitLevelData](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L162) | +| [Vector4f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L9) | [Vector3f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L34) | [Vector3d](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L56) | +| [Vector2i](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L84) | [Vector2f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L104) | [TrackState](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L192) | +| [Quantity](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L221) | [Hypothesis](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L229) | [HitLevelData](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L236) | +| [CovMatrix2f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L123) |[CovMatrix3f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L140) |[CovMatrix4f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L157) | +| [CovMatrix6f](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L174) | | | **Datatypes** | | | | |-|-|-| -| [EventHeader](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L172) | [MCParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L184) | [SimTrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L252) | -| [CaloHitContribution](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L294) | [SimCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L306) | [RawCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L318) | -| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L327) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L339) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L354) | -| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L375) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L390) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L409) | -| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L422) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L441) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L458) | -| [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L564) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L598) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L611) | -| [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L622) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L634) | | + +| [EventHeader](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L246) | [MCParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L258) | [SimTrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L326) | +| [CaloHitContribution](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L368) | [SimCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L380) | [RawCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L392) | +| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L401) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L413) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L426) | +| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L459) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L485) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L515) | +| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L528) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L547) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L575) | +| [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L689) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L723) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L748) | +| [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L759) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L771) | | **Associations** | | | | |-|-|-| -| [MCRecoParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L490) | [MCRecoCaloAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L499) | [MCRecoTrackerAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L508) | -| [MCRecoTrackerHitPlaneAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L517) | [MCRecoCaloParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L526) | [MCRecoClusterParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L535) | -| [MCRecoTrackParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L544) | [RecoParticleVertexAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L553) | | +| [MCRecoParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L615) | [MCRecoCaloAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L624) | [MCRecoTrackerAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L633) | +| [MCRecoTrackerHitPlaneAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L642) | [MCRecoCaloParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L651) | [MCRecoClusterParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L660) | +| [MCRecoTrackParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L669) | [RecoParticleVertexAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L678) | | **Interfaces** | | | | |-|-|-| -| [TrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L648) | | | +| [TrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L785) | | | The tests and examples in the `tests` directory show how to read, write, and use these types in your code. diff --git a/edm4hep.yaml b/edm4hep.yaml index 20d76371d..9f3544c3e 100644 --- a/edm4hep.yaml +++ b/edm4hep.yaml @@ -120,6 +120,74 @@ components: return *( &a + i ) ; }\n " + edm4hep::CovMatrix2f: + Description: "A generic 2 dimensional covariance matrix with values stored in lower triangular form" + Members: + - std::array values // the covariance matrix values + ExtraCode: + includes: "#include " + declaration: " + constexpr CovMatrix2f() = default;\n + template\n + constexpr CovMatrix2f(Vs... v) : values{v...} {}\n + constexpr CovMatrix2f(const std::array& v) : values(v) {}\n + constexpr CovMatrix2f& operator=(std::array& v) { values = v; return *this; }\n + bool operator==(const CovMatrix2f& v) const { return v.values == values; }\n + bool operator!=(const CovMatrix2f& v) const { return v.values != values; }\n + #include + " + + edm4hep::CovMatrix3f: + Description: "A generic 3 dimensional covariance matrix with values stored in lower triangular form" + Members: + - std::array values // the covariance matrix values + ExtraCode: + includes: "#include " + declaration: " + constexpr CovMatrix3f() = default;\n + constexpr CovMatrix3f(const std::array& v) : values(v) {}\n + template\n + constexpr CovMatrix3f(Vs... v) : values{v...} {}\n + constexpr CovMatrix3f& operator=(std::array& v) { values = v; return *this; }\n + bool operator==(const CovMatrix3f& v) const { return v.values == values; }\n + bool operator!=(const CovMatrix3f& v) const { return v.values != values; }\n + #include + " + + edm4hep::CovMatrix4f: + Description: "A generic 4 dimensional covariance matrix with values stored in lower triangular form" + Members: + - std::array values // the covariance matrix values + ExtraCode: + includes: "#include " + declaration: " + constexpr CovMatrix4f() = default;\n + template\n + constexpr CovMatrix4f(Vs... v) : values{v...} {}\n + constexpr CovMatrix4f(const std::array& v) : values(v) {}\n + constexpr CovMatrix4f& operator=(std::array& v) { values = v; return *this; }\n + bool operator==(const CovMatrix4f& v) const { return v.values == values; }\n + bool operator!=(const CovMatrix4f& v) const { return v.values != values; }\n + #include + " + + edm4hep::CovMatrix6f: + Description: "A generic 6 dimensional covariance matrix with values stored in lower triangular form" + Members: + - std::array values // the covariance matrix values + ExtraCode: + includes: "#include " + declaration: " + constexpr CovMatrix6f() = default;\n + template\n + constexpr CovMatrix6f(Vs... v) : values{v...} {}\n + constexpr CovMatrix6f(const std::array& v) : values(v) {}\n + constexpr CovMatrix6f& operator=(std::array& v) { values = v; return *this; }\n + bool operator==(const CovMatrix6f& v) const { return v.values == values; }\n + bool operator!=(const CovMatrix6f& v) const { return v.values != values; }\n + #include \n + " + # Parametrized description of a particle track edm4hep::TrackState: Members: @@ -131,8 +199,9 @@ components: - float tanLambda // lambda is the dip angle of the track in r-z - float time [ns] // time of the track at this trackstate - edm4hep::Vector3f referencePoint [mm] // Reference point of the track parameters, e.g. the origin at the IP, or the position of the first/last hits or the entry point into the calorimeter - - std::array covMatrix // lower triangular covariance matrix of the track parameters. the order of parameters is d0, phi, omega, z0, tan(lambda), time. the array is a row-major flattening of the matrix + - edm4hep::CovMatrix6f covMatrix // covariance matrix of the track parameters. ExtraCode: + includes: "#include " declaration: " static const int AtOther = 0 ; // any location other than the ones defined below\n static const int AtIP = 1 ;\n @@ -141,6 +210,11 @@ components: static const int AtCalorimeter = 4 ;\n static const int AtVertex = 5 ;\n static const int LastLocation = AtVertex ;\n + + /// Get the covariance matrix value for the two passed parameters\n + constexpr float getCovMatrix(edm4hep::TrackParams parI, edm4hep::TrackParams parJ) const { return covMatrix.getValue(parI, parJ); }\n + /// Set the covariance matrix value for the two passed parameters\n + constexpr void setCovMatrix(float value, edm4hep::TrackParams parI, edm4hep::TrackParams parJ) { covMatrix.setValue(value, parI, parJ); } " # quantity with an identifier, a value and an error @@ -359,17 +433,29 @@ datatypes: - float energy [GeV] // energy of the cluster - float energyError [GeV] // error on the energy - edm4hep::Vector3f position [mm] // position of the cluster - - std::array positionError // covariance matrix of the position (6 Parameters) + - edm4hep::CovMatrix3f positionError // covariance matrix of the position - float iTheta // intrinsic direction of cluster at position Theta. Not to be confused with direction cluster is seen from IP - float phi // intrinsic direction of cluster at position - Phi. Not to be confused with direction cluster is seen from IP - - edm4hep::Vector3f directionError [mm**2] // covariance matrix of the direction (3 Parameters) + - edm4hep::Vector3f directionError [mm**2] // covariance matrix of the direction VectorMembers: - float shapeParameters // shape parameters. This should be accompanied by a descriptive list of names in the shapeParameterNames collection level metadata, as a vector of strings with the same ordering - float subdetectorEnergies // energy observed in a particular subdetector OneToManyRelations: - edm4hep::Cluster clusters // clusters that have been combined to this cluster - edm4hep::CalorimeterHit hits // hits that have been combined to this cluster - + - edm4hep::ParticleID particleIDs // particle IDs (sorted by their likelihood) + ExtraCode: + includes: "#include " + declaration: " + /// Get the position error value for the two passed dimensions\n + float getPositionError(edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) const { return getPositionError().getValue(dimI, dimJ); }\n + " + MutableExtraCode: + includes: "#include " + declaration: " + /// Set the position error value for the two passed dimensions\n + void setPositionError(float value, edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) { return getPositionError().setValue(value, dimI, dimJ); }\n + " #------------- TrackerHit edm4hep::TrackerHit3D: @@ -383,8 +469,19 @@ datatypes: - float eDep [GeV] // energy deposited on the hit - float eDepError [GeV] // error measured on EDep - edm4hep::Vector3d position [mm] // hit position - - std::array covMatrix // covariance of the position (x,y,z), stored as lower triangle matrix. i.e. cov(x,x) , cov(y,x) , cov(y,y) , cov(z,x) , cov(z,y) , cov(z,z) - + - edm4hep::CovMatrix3f covMatrix // covariance matrix of the position (x,y,z) + ExtraCode: + includes: "#include " + declaration: " + /// Get the position covariance matrix value for the two passed dimensions\n + float getCovMatrix(edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) const { return getCovMatrix().getValue(dimI, dimJ); }\n + " + MutableExtraCode: + includes: "#include " + declaration: " + /// Set the position covariance matrix value for the two passed dimensions\n + void setCovMatrix(float value, edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) { getCovMatrix().setValue(value, dimI, dimJ); }\n + " #------------- TrackerHitPlane edm4hep::TrackerHitPlane: @@ -402,8 +499,19 @@ datatypes: - float du // measurement error along the direction - float dv // measurement error along the direction - edm4hep::Vector3d position [mm] // hit position - - std::array covMatrix // covariance of the position (x,y,z), stored as lower triangle matrix. i.e. cov(x,x) , cov(y,x) , cov(y,y) , cov(z,x) , cov(z,y) , cov(z,z) - + - edm4hep::CovMatrix3f covMatrix // covariance of the position (x,y,z) + ExtraCode: + includes: "#include " + declaration: " + /// Get the position covariance matrix value for the two passed dimensions\n + float getCovMatrix(edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) const { return getCovMatrix().getValue(dimI, dimJ); }\n + " + MutableExtraCode: + includes: "#include " + declaration: " + /// Set the position covariance matrix value for the two passed dimensions\n + void setCovMatrix(float value, edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) { getCovMatrix().setValue(value, dimI, dimJ); }\n + " #---------- RawTimeSeries edm4hep::RawTimeSeries: @@ -446,13 +554,24 @@ datatypes: - float chi2 // chi-squared of the vertex fit - float probability // probability of the vertex fit - edm4hep::Vector3f position // [mm] position of the vertex - - std::array covMatrix // covariance matrix of the position (stored as lower triangle matrix, i.e. cov(xx),cov(y,x),cov(z,x),cov(y,y),... ) + - edm4hep::CovMatrix3f covMatrix // covariance matrix of the position - int32_t algorithmType // type code for the algorithm that has been used to create the vertex VectorMembers: - float parameters // additional parameters related to this vertex OneToOneRelations: - edm4hep::ReconstructedParticle associatedParticle // reconstructed particle associated to this vertex - + ExtraCode: + includes: "#include " + declaration: " + /// Get the position covariance matrix value for the two passed dimensions\n + float getCovMatrix(edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) const { return getCovMatrix().getValue(dimI, dimJ); }\n + " + MutableExtraCode: + includes: "#include " + declaration: " + /// Set the position covariance matrix value for the two passed dimensions\n + void setCovMatrix(float value, edm4hep::Cartesian dimI, edm4hep::Cartesian dimJ) { getCovMatrix().setValue(value, dimI, dimJ); }\n + " #------- ReconstructedParticle edm4hep::ReconstructedParticle: @@ -466,7 +585,7 @@ datatypes: - float charge // charge of the reconstructed particle - float mass [GeV] // mass of the reconstructed particle, set independently from four vector. Four momentum state is not kept consistent internally - float goodnessOfPID // overall goodness of the PID on a scale of [0;1] - - std::array covMatrix // cvariance matrix of the reconstructed particle 4vector (10 parameters). Stored as lower triangle matrix of the four momentum (px,py,pz,E), i.e. cov(px,px), cov(py,## + - edm4hep::CovMatrix4f covMatrix // covariance matrix of the reconstructed particle 4vector OneToOneRelations: - edm4hep::Vertex startVertex // start vertex associated to this particle OneToManyRelations: @@ -474,17 +593,23 @@ datatypes: - edm4hep::Track tracks // tracks that have been used for this particle - edm4hep::ReconstructedParticle particles // reconstructed particles that have been combined to this particle ExtraCode: + includes: "#include " declaration: " bool isCompound() const { return particles_size() > 0 ;}\n [[deprecated(\"use setPDG instead\")]]\n int32_t getType() const { return getPDG(); }\n + /// Get the four momentum covariance matrix value for the two passed dimensions\n + float getCovMatrix(edm4hep::FourMomCoords dimI, edm4hep::FourMomCoords dimJ) const { return getCovMatrix().getValue(dimI, dimJ); }\n " MutableExtraCode: + includes: "#include " declaration: " //vertex where the particle decays. This method actually returns the start vertex from the first daughter particle found.\n //TODO: edm4hep::Vertex getEndVertex() { return edm4hep::Vertex( (getParticles(0).isAvailable() ? getParticles(0).getStartVertex() : edm4hep::Vertex(0,0) ) ) ; }\n [[deprecated(\"use setPDG instead\")]]\n void setType(int32_t pdg) { setPDG(pdg); }\n + /// Set the four momentum covariance matrix value for the two passed dimensions\n + void setCovMatrix(float value, edm4hep::FourMomCoords dimI, edm4hep::FourMomCoords dimJ) { getCovMatrix().setValue(value, dimI, dimJ); }\n " edm4hep::MCRecoParticleAssociation: @@ -603,9 +728,21 @@ datatypes: - float time [ns] // time - float charge [fC] // charge - int16_t quality // quality - - std::array covMatrix // lower triangle covariance matrix of the charge(c) and time(t) measurements + - edm4hep::CovMatrix2f covMatrix // covariance matrix of the charge and time measurements OneToOneRelations: - edm4hep::TimeSeries timeSeries // Optionally, the timeSeries that has been used to create the pulse can be stored with the pulse + ExtraCode: + includes: "#include " + declaration: " + /// Get the position covariance matrix value for the two passed dimensions\n + float getCovMatrix(edm4hep::TrackerPulseDims dimI, edm4hep::TrackerPulseDims dimJ) const { return getCovMatrix().getValue(dimI, dimJ); }\n + " + MutableExtraCode: + includes: "#include " + declaration: " + /// Set the position covariance matrix value for the two passed dimensions\n + void setCovMatrix(float value, edm4hep::TrackerPulseDims dimI, edm4hep::TrackerPulseDims dimJ) { getCovMatrix().setValue(value, dimI, dimJ); }\n + " #---------- RecIonizationCluster edm4hep::RecIonizationCluster: diff --git a/edm4hep/CMakeLists.txt b/edm4hep/CMakeLists.txt index 075eba1f3..57763fe7a 100644 --- a/edm4hep/CMakeLists.txt +++ b/edm4hep/CMakeLists.txt @@ -4,6 +4,10 @@ PODIO_GENERATE_DATAMODEL(edm4hep ../edm4hep.yaml headers sources IO_BACKEND_HANDLERS ${PODIO_IO_HANDLERS}) PODIO_ADD_DATAMODEL_CORE_LIB(edm4hep "${headers}" "${sources}") +target_include_directories(edm4hep PUBLIC + $ + $ +) add_library(EDM4HEP::edm4hep ALIAS edm4hep) file(GLOB_RECURSE top_headers ${PROJECT_SOURCE_DIR}/include/*.h) diff --git a/include/edm4hep/Constants.h b/include/edm4hep/Constants.h index 25abc7d68..d75959942 100644 --- a/include/edm4hep/Constants.h +++ b/include/edm4hep/Constants.h @@ -19,17 +19,37 @@ #ifndef EDM4HEP_CONSTANTS_H #define EDM4HEP_CONSTANTS_H +#include + namespace edm4hep { static constexpr const char* CellIDEncoding = "CellIDEncoding"; static constexpr const char* EventHeaderName = "EventHeader"; static constexpr const char* EventWeights = "EventWeightNames"; static constexpr const char* shapeParameterNames = "shapeParameterNames"; +// Use 16 bits to encode the dimension +// Could go to 8 bits, but would need a fix in podio first +using DimType = std::uint16_t; + +/// The enum for accessing cartesian coordinate values in covariance matrices +enum class Cartesian : DimType { x = 0, y, z }; + +/// The enum for accessing four momentum coordinate values in covariance +/// matrices +enum class FourMomCoords : DimType { x = 0, y, z, t }; + +/// The enum for accessing track parameter values in covariance matrices +enum class TrackParams : DimType { d0 = 0, phi, omega, z0, tanLambda, time }; + +/// Enum for accessing the covariance matrix in the TrackerPulse +enum class TrackerPulseDims : DimType { charge = 0, time }; + /// The collection parameter name for accessing the names of the parameters for /// a ParticleID collection static constexpr const char* pidParameterNames = "ParameterNames"; static constexpr const char* pidAlgoName = "AlgoName"; static constexpr const char* pidAlgoType = "AlgoType"; + } // namespace edm4hep #endif // EDM4HEP_CONSTANTS_H diff --git a/include/edm4hep/detail/CovMatrixCommon.ipp b/include/edm4hep/detail/CovMatrixCommon.ipp new file mode 100644 index 000000000..845928a8f --- /dev/null +++ b/include/edm4hep/detail/CovMatrixCommon.ipp @@ -0,0 +1,90 @@ +// This file is meant to be included inside the declaration part of the +// ExtraCode for the CovMatrixNx components. They live in this file because they +// can be written very generically and reduce the clutter and code repetition in +// the edm4hep.yaml file +// +// NOTE: All of these functions are intended to be member functions, and the +// only member of a CovMatrixNx component is an apropriately sized std::array +// named values. +// +// NOTE: It is also assumed that the edm4hep/utils/cov_matrix_utils.h header is +// included via the corresponding ExtraCode: include directive + +/// Get the i-th element of the underlying storage +/// +/// \note The values are stored in a flat array assuming a lower +/// triangular matrix representation +constexpr float operator[](unsigned i) const { + return values[i]; +} + +/// Get the i-th element of the underlying storage +/// +/// \note The values are stored in a flat array assuming a lower +/// triangular matrix representation +constexpr float& operator[](unsigned i) { + return values[i]; +} + +/// Get the begin iterator to the underlying storage +constexpr auto begin() const { + return values.begin(); +} + +/// Get the begin iterator to the underlying storage +constexpr auto begin() { + return values.begin(); +} + +/// Get the end iterator to the underlying storage +constexpr auto end() const { + return values.end(); +} + +/// Get the end iterator to the underlying storage +constexpr auto end() { + return values.end(); +} + +/// Get a pointer to the underlying storage data +auto* data() { + return values.data(); +} + +/// Get a pointer to the underlying storage data +const auto* data() const { + return values.data(); +} + +/// Get the value of the covariance matrix for the passed dimensions +/// +/// @tparam DimEnum The enum (class) type that describes the dimenstions of this +/// covariance matrix. This will be deduced from the passed +/// arguments! +/// +/// @param dimI The first dimension for which the covariance matrix value should +/// be obtained +/// @param dimJ The second dimension for which the covariance matrix value +/// should be obtained +/// +/// @returns The value of the covariance matrix for dimension dimI and dimJ +template +constexpr float getValue(DimEnum dimI, DimEnum dimJ) const { + return edm4hep::utils::get_cov_value(values, dimI, dimJ); +} + +/// Set the value of the covariance matrix for the passed dimensions +/// +/// @tparam DimEnum The enum (class) type that describes the dimenstions of this +/// covariance matrix. This will be deduced from the passed +/// arguments! +/// +/// @param value The value to be set +/// @param dimI The first dimension for which the covariance matrix value +/// should be obtained +/// @param dimJ The second dimension for which the covariance matrix value +/// should be obtained +template +constexpr void setValue(float value, DimEnum dimI, DimEnum dimJ) { + utils::set_cov_value(value, values, dimI, dimJ); +} diff --git a/test/utils/CMakeLists.txt b/test/utils/CMakeLists.txt index e588c861f..fb84ecda3 100644 --- a/test/utils/CMakeLists.txt +++ b/test/utils/CMakeLists.txt @@ -16,7 +16,8 @@ endif() include(Catch) add_executable(unittests_edm4hep - test_kinematics.cpp test_vector_utils.cpp test_PIDHandler.cpp) + test_kinematics.cpp test_vector_utils.cpp test_covmatrix_utils.cpp test_PIDHandler.cpp) + target_link_libraries(unittests_edm4hep edm4hep EDM4HEP::utils Catch2::Catch2 Catch2::Catch2WithMain) option(SKIP_CATCH_DISCOVERY "Skip the Catch2 test discovery" OFF) diff --git a/test/utils/test_covmatrix_utils.cpp b/test/utils/test_covmatrix_utils.cpp new file mode 100644 index 000000000..6fdb2f76b --- /dev/null +++ b/test/utils/test_covmatrix_utils.cpp @@ -0,0 +1,127 @@ +#include "edm4hep/Constants.h" +#include "edm4hep/CovMatrix3f.h" +#include "edm4hep/MutableTrackerHit3D.h" +#include "edm4hep/TrackState.h" +#include "edm4hep/TrackerHit3D.h" +#include "edm4hep/utils/cov_matrix_utils.h" + +#include + +#include + +TEST_CASE("CovarianceMatrix indexing", "[cov_matrix_utils]") { + using namespace edm4hep::utils::detail; + + STATIC_REQUIRE(get_cov_dim(21) == 6); + STATIC_REQUIRE(get_cov_dim(1) == 1); +#if !__cpp_consteval + // This will fail to compile if we have consteval + REQUIRE_THROWS_AS(get_cov_dim(14), std::invalid_argument); +#endif + + // clang-format off + // For better interpretability of the tests below, these are the indices of a + // 2D matrix in lower triangular form together with the matrix indices + // + // | 0 1 2 3 4 5 + // --+------------------ + // 0 | 0 1 3 6 10 15 + // 1 | 1 2 4 7 11 16 + // 2 | 3 4 5 8 12 17 + // 3 | 6 7 8 9 13 18 + // 4 | 10 11 12 13 14 19 + // 5 | 15 16 17 18 19 20 + // clang-format on + + // diagonal elements + STATIC_REQUIRE(to_lower_tri(0, 0) == 0); + STATIC_REQUIRE(to_lower_tri(1, 1) == 2); + STATIC_REQUIRE(to_lower_tri(2, 2) == 5); + STATIC_REQUIRE(to_lower_tri(3, 3) == 9); + STATIC_REQUIRE(to_lower_tri(4, 4) == 14); + STATIC_REQUIRE(to_lower_tri(5, 5) == 20); + + // some off diagonal elements + STATIC_REQUIRE(to_lower_tri(1, 0) == 1); + STATIC_REQUIRE(to_lower_tri(0, 1) == 1); + STATIC_REQUIRE(to_lower_tri(0, 2) == 3); + STATIC_REQUIRE(to_lower_tri(2, 0) == 3); + STATIC_REQUIRE(to_lower_tri(2, 3) == 8); + STATIC_REQUIRE(to_lower_tri(3, 2) == 8); + STATIC_REQUIRE(to_lower_tri(5, 3) == 18); + STATIC_REQUIRE(to_lower_tri(2, 5) == 17); +} + +TEST_CASE("CovMatrixNf array access", "[cov_matrix_utils]") { + // We use the 3D version here, but since the ExtraCode is effectively + // duplicated for the others as well it shouldn't really matter + auto covMatrix = edm4hep::CovMatrix3f{}; + + covMatrix[3] = 3.14f; + REQUIRE(covMatrix[3] == 3.14f); + + REQUIRE(covMatrix.data()[3] == 3.14f); + covMatrix.data()[2] = 2.13f; + REQUIRE(covMatrix[2] == 2.13f); + + float i = 0.f; + for (auto& v : covMatrix) { + v = i++; + } + i = 0.f; + for (const auto& v : covMatrix) { + REQUIRE(v == i++); + } +} + +TEST_CASE("CovMatrixNf enum access", "[cov_matrix_utils]") { + enum class TestDims : uint32_t { a = 0, b, c }; + + auto covMatrix = edm4hep::CovMatrix3f{}; + covMatrix.setValue(1.23f, TestDims::a, TestDims::c); + REQUIRE(covMatrix.getValue(TestDims::a, TestDims::c) == 1.23f); +} + +TEST_CASE("CovMatrixNf invalid enum access", "[cov_matrix_utils]") { + // Invalid dimensions with too many elements to fit the 3D convariance matrix + enum class InvalidDims : edm4hep::DimType { i = 0, j, k, l, m }; + + auto covMatrix = edm4hep::CovMatrix3f{}; + REQUIRE_THROWS_AS(covMatrix.setValue(1.23f, InvalidDims::k, InvalidDims::l), std::invalid_argument); + REQUIRE_THROWS_AS(covMatrix.getValue(InvalidDims::m, InvalidDims::i), std::invalid_argument); +} + +TEST_CASE("CovMatrixNf equality operators", "[cov_matrix_utils]") { + auto covMatrix = edm4hep::CovMatrix3f{}; + covMatrix[3] = 3.14f; + covMatrix[2] = 2.13f; + REQUIRE(covMatrix == std::array{0, 0, 2.13f, 3.14f, 0, 0}); + REQUIRE(covMatrix != std::array{}); +} + +TEST_CASE("TrackState covariance", "[cov_matrix_utils]") { + auto trackState = edm4hep::TrackState{}; + + trackState.setCovMatrix(1.23f, edm4hep::TrackParams::d0, edm4hep::TrackParams::phi); + // We know the expected index in this case + REQUIRE(trackState.covMatrix.values[1] == 1.23f); + REQUIRE(trackState.getCovMatrix(edm4hep::TrackParams::time, edm4hep::TrackParams::omega) == 0); +} + +TEST_CASE("TrackerHit3D covariance", "[cov_matrix_utils]") { + auto trackerHit = edm4hep::MutableTrackerHit3D{}; + trackerHit.setCovMatrix(3.14f, edm4hep::Cartesian::x, edm4hep::Cartesian::z); + REQUIRE(trackerHit.getCovMatrix(edm4hep::Cartesian::x, edm4hep::Cartesian::z) == 3.14f); + // We can also use the expected index of (x, y) + REQUIRE(trackerHit.getCovMatrix().values[3] == 3.14f); + + auto hit = edm4hep::TrackerHit3D(trackerHit); + REQUIRE(hit.getCovMatrix(edm4hep::Cartesian::x, edm4hep::Cartesian::z) == 3.14f); + + trackerHit.setCovMatrix({1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); + REQUIRE(trackerHit.getCovMatrix() == std::array{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); + + const std::array arrValues = {6.f, 5.f, 4.f, 3.f, 2.f, 1.f}; + trackerHit.setCovMatrix(arrValues); + REQUIRE(trackerHit.getCovMatrix() == std::array{6.f, 5.f, 4.f, 3.f, 2.f, 1.f}); +} diff --git a/test/utils/test_kinematics.py b/test/utils/test_kinematics.py index c563ecf38..f8e025cda 100644 --- a/test/utils/test_kinematics.py +++ b/test/utils/test_kinematics.py @@ -48,7 +48,7 @@ def test_p4(self): 1.0, # charge 125.0, # mass 0.0, # goodnessOfPID - cppyy.gbl.std.array('float', 10)() # covMatrix + edm4hep.CovMatrix4f() # covMatrix ) self.assertEqual(p4(p), LVM(1.0, 2.0, 3.0, 125.0)) diff --git a/utils/include/edm4hep/utils/cov_matrix_utils.h b/utils/include/edm4hep/utils/cov_matrix_utils.h new file mode 100644 index 000000000..609cfd583 --- /dev/null +++ b/utils/include/edm4hep/utils/cov_matrix_utils.h @@ -0,0 +1,128 @@ +#ifndef EDM4HEP_UTILS_COVMATRIXUTILS_H +#define EDM4HEP_UTILS_COVMATRIXUTILS_H + +#include +#include +#include +#include + +namespace edm4hep { + +namespace utils { + + namespace detail { + // From c++23 this is functionality offered by the STL +#if __cpp_lib_to_underlying + using to_index = std::to_underlying; +#else + // Otherwise it is simple enough to roll our own + template + constexpr auto to_index(E e) { + return static_cast>(e); + } +#endif + + /// Cast an index to an enum value + template + constexpr DimEnum to_enum(DimEnum index) { + return static_cast(index); + } + + /// Need a constexpr swap for integers before c++20 + constexpr void swap(int& i, int& j) { + int tmp = j; + j = i; + i = tmp; + } + + /** + * Get the dimension of the covariance matrix from the size of the 1D array in + * which it is stored + * + * **NOTE: to avoid having to do a square root operation and in order to keep + * this constexpr this is currently only implemented for storage sizes up to + * 21 (corresponding to covariance matrix dimensions of 6 x 6)**. This + * function is intended to be called in constexpr or immediate contexts in + * order to fail at compilation already for invalid values of N. + * + * @param N the size of the 1D storage + * + * @returns the dimension of the covariance matrix + */ +#if cpp_consteval + consteval std::size_t get_cov_dim(std::size_t N) { +#else + constexpr std::size_t get_cov_dim(std::size_t N) { +#endif + switch (N) { + case 21: + return 6; + case 15: + return 5; + case 10: + return 4; + case 6: + return 3; + case 3: + return 2; + case 1: + return 1; + } + + // We simply use throwing an exception to make compilation fail in constexpr + // cases. + throw std::invalid_argument( + "Not a valid size for a covariance matrix stored in lower triangular form (N = " + std::to_string(N) + ")"); + } + + /** + * Transform a 2D index to a 1D index in lower triangular storage. + * + * @param i row index + * @param j column index + * + * @returns the index into the 1D storage + */ + constexpr int to_lower_tri(int i, int j) { + if (i < j) { + detail::swap(i, j); + } + return i * (i + 1) / 2 + j; + } + } // namespace detail + + template + constexpr Scalar get_cov_value(const std::array& cov, DimEnum dimI, DimEnum dimJ) { + const auto i = detail::to_index(dimI); + const auto j = detail::to_index(dimJ); + + constexpr auto dim = detail::get_cov_dim(N); + if (i < 0 || j < 0 || i >= dim || j >= dim) { + throw std::invalid_argument("The covariance matrix dimensions (" + std::to_string(dim) + + ") and the passed dimensions are not compatible (dimI = " + std::to_string(i) + + ", dimJ = " + std::to_string(j) + ")"); + } + + return cov[detail::to_lower_tri(i, j)]; + } + + template + constexpr void set_cov_value(Scalar value, std::array& cov, DimEnum dimI, DimEnum dimJ) { + auto i = detail::to_index(dimI); + auto j = detail::to_index(dimJ); + + constexpr auto dim = detail::get_cov_dim(N); + if (i < 0 || j < 0 || i >= dim || j >= dim) { + throw std::invalid_argument("The covariance matrix dimensions (" + std::to_string(dim) + + ") and the passed dimensions are not compatible (dimI = " + std::to_string(i) + + ", dimJ = " + std::to_string(j) + ")"); + } + + // Covariance is in lower triangle this brings us from 2D indices to 1D + cov[detail::to_lower_tri(i, j)] = value; + } +} // namespace utils + +} // namespace edm4hep + +#endif // EDM4HEP_UTILS_COVMATRIXUTILS_H From eddd0511f7d5e783f6c6c42910ce87bc042514a6 Mon Sep 17 00:00:00 2001 From: Mateusz Jakub Fila <37295697+m-fila@users.noreply.github.com> Date: Thu, 2 May 2024 09:54:39 +0200 Subject: [PATCH 09/13] Fix datatypes table formatting and typos (#297) * fix Datatypes table formatting fix links * fix typos --- README.md | 7 +++---- include/edm4hep/detail/CovMatrixCommon.ipp | 6 +++--- test/utils/test_PIDHandler.cpp | 4 ++-- utils/include/edm4hep/utils/ParticleIDUtils.h | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e2ef22b4a..6132330db 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,11 @@ A generic event data model for future HEP collider experiments. | | | | |-|-|-| - | [EventHeader](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L246) | [MCParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L258) | [SimTrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L326) | | [CaloHitContribution](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L368) | [SimCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L380) | [RawCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L392) | -| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L401) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L413) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L426) | -| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L459) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L485) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L515) | -| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L528) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L547) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L575) | +| [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L401) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L413) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L428) | +| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L461) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L487) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L517) | +| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L530) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L549) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L577) | | [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L689) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L723) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L748) | | [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L759) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L771) | | diff --git a/include/edm4hep/detail/CovMatrixCommon.ipp b/include/edm4hep/detail/CovMatrixCommon.ipp index 845928a8f..b13bfa235 100644 --- a/include/edm4hep/detail/CovMatrixCommon.ipp +++ b/include/edm4hep/detail/CovMatrixCommon.ipp @@ -4,7 +4,7 @@ // the edm4hep.yaml file // // NOTE: All of these functions are intended to be member functions, and the -// only member of a CovMatrixNx component is an apropriately sized std::array +// only member of a CovMatrixNx component is an appropriately sized std::array // named values. // // NOTE: It is also assumed that the edm4hep/utils/cov_matrix_utils.h header is @@ -58,7 +58,7 @@ const auto* data() const { /// Get the value of the covariance matrix for the passed dimensions /// -/// @tparam DimEnum The enum (class) type that describes the dimenstions of this +/// @tparam DimEnum The enum (class) type that describes the dimensions of this /// covariance matrix. This will be deduced from the passed /// arguments! /// @@ -75,7 +75,7 @@ constexpr float getValue(DimEnum dimI, DimEnum dimJ) const { /// Set the value of the covariance matrix for the passed dimensions /// -/// @tparam DimEnum The enum (class) type that describes the dimenstions of this +/// @tparam DimEnum The enum (class) type that describes the dimensions of this /// covariance matrix. This will be deduced from the passed /// arguments! /// diff --git a/test/utils/test_PIDHandler.cpp b/test/utils/test_PIDHandler.cpp index 1b018a08e..12eaf3317 100644 --- a/test/utils/test_PIDHandler.cpp +++ b/test/utils/test_PIDHandler.cpp @@ -112,7 +112,7 @@ TEST_CASE("PIDHandler from variadic list of collections", "[pid_utils]") { checkHandlerValidReco(handler, reco); } - SECTION("Unkown reco") { + SECTION("Unknown reco") { const auto reco = edm4hep::ReconstructedParticle(); const auto pids = handler.getPIDs(reco); @@ -184,7 +184,7 @@ TEST_CASE("PIDHandler from Frame w/ metadata", "[pid_utils]") { // Valid algo but invalid parameter name REQUIRE_FALSE(handler.getParamIndex(pidAlgo1, "non-existant-param").has_value()); // Invalid algorithm, the parameter name is not even checked in this case - REQUIRE_FALSE(handler.getParamIndex(-1, "doesnot matter").has_value()); + REQUIRE_FALSE(handler.getParamIndex(-1, "doesn't matter").has_value()); const auto pidInfo = utils::PIDHandler::getAlgoInfo(metadata, "particleIds_1").value(); REQUIRE(pidInfo.algoName == "pidAlgo_1"); diff --git a/utils/include/edm4hep/utils/ParticleIDUtils.h b/utils/include/edm4hep/utils/ParticleIDUtils.h index 0878c3a09..204ee448c 100644 --- a/utils/include/edm4hep/utils/ParticleIDUtils.h +++ b/utils/include/edm4hep/utils/ParticleIDUtils.h @@ -94,7 +94,7 @@ 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 consisent + /// given name is consistent static void setAlgoInfo(podio::Frame& metadata, const std::string& collname, const edm4hep::utils::ParticleIDMeta& pidMetaInfo); From a296139da4e040c2290de3f00be4506b485ea77c Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 7 May 2024 11:14:00 +0200 Subject: [PATCH 10/13] Descriptions: change association descriptions to allow doxygen to link to respective classes (#300) --- edm4hep.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/edm4hep.yaml b/edm4hep.yaml index 9f3544c3e..1d001289d 100644 --- a/edm4hep.yaml +++ b/edm4hep.yaml @@ -613,7 +613,7 @@ datatypes: " edm4hep::MCRecoParticleAssociation: - Description: "Used to keep track of the correspondence between MC and reconstructed particles" + Description: "Association between a ReconstructedParticle and the corresponding MCParticle" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -622,7 +622,7 @@ datatypes: - edm4hep::MCParticle sim // reference to the Monte-Carlo particle edm4hep::MCRecoCaloAssociation: - Description: "Association between a CaloHit and the corresponding simulated CaloHit" + Description: "Association between a CalorimeterHit and the corresponding SimCalorimeterHit" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -631,7 +631,7 @@ datatypes: - edm4hep::SimCalorimeterHit sim // reference to the simulated hit edm4hep::MCRecoTrackerAssociation: - Description: "Association between a TrackerHit and the corresponding simulated TrackerHit" + Description: "Association between a TrackerHit and the corresponding SimTrackerHit" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -640,7 +640,7 @@ datatypes: - edm4hep::SimTrackerHit sim // reference to the simulated hit edm4hep::MCRecoTrackerHitPlaneAssociation: - Description: "Association between a TrackerHitPlane and the corresponding simulated TrackerHit" + Description: "Association between a TrackerHitPlane and the corresponding SimTrackerHit" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -649,7 +649,7 @@ datatypes: - edm4hep::SimTrackerHit sim // reference to the simulated hit edm4hep::MCRecoCaloParticleAssociation: - Description: "Association between a CalorimeterHit and a MCParticle" + Description: "Association between a CalorimeterHit and the corresponding MCParticle" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -658,7 +658,7 @@ datatypes: - edm4hep::MCParticle sim // reference to the Monte-Carlo particle edm4hep::MCRecoClusterParticleAssociation: - Description: "Association between a Cluster and a MCParticle" + Description: "Association between a Cluster and the corresponding MCParticle" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -667,7 +667,7 @@ datatypes: - edm4hep::MCParticle sim // reference to the Monte-Carlo particle edm4hep::MCRecoTrackParticleAssociation: - Description: "Association between a Track and a MCParticle" + Description: "Association between a Track and the corresponding MCParticle" Author: "EDM4hep authors" Members: - float weight // weight of this association @@ -676,7 +676,7 @@ datatypes: - edm4hep::MCParticle sim // reference to the Monte-Carlo particle edm4hep::RecoParticleVertexAssociation: - Description: "Association between a Reconstructed Particle and a Vertex" + Description: "Association between a ReconstructedParticle and a Vertex" Author: "EDM4hep authors" Members: - float weight // weight of this association From 181745967206fa03b1c6a9aa26b947d4b74aeaf4 Mon Sep 17 00:00:00 2001 From: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> Date: Tue, 14 May 2024 21:32:17 +0200 Subject: [PATCH 11/13] Remove ParticleID that was deleted and reintroduced in the last PRs (#304) --- README.md | 16 ++++++++-------- edm4hep.yaml | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6132330db..98c90415e 100644 --- a/README.md +++ b/README.md @@ -26,24 +26,24 @@ A generic event data model for future HEP collider experiments. | [EventHeader](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L246) | [MCParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L258) | [SimTrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L326) | | [CaloHitContribution](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L368) | [SimCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L380) | [RawCalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L392) | | [CalorimeterHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L401) | [ParticleID](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L413) | [Cluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L428) | -| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L461) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L487) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L517) | -| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L530) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L549) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L577) | -| [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L689) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L723) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L748) | -| [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L759) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L771) | | +| [TrackerHit3D](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L460) | [TrackerHitPlane](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L486) | [RawTimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L516) | +| [Track](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L529) | [Vertex](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L548) | [ReconstructedParticle](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L576) | +| [SimPrimaryIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L688) | [TrackerPulse](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L722) | [RecIonizationCluster](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L747) | +| [TimeSeries](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L758) | [RecDqdx](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L770) | | **Associations** | | | | |-|-|-| -| [MCRecoParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L615) | [MCRecoCaloAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L624) | [MCRecoTrackerAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L633) | -| [MCRecoTrackerHitPlaneAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L642) | [MCRecoCaloParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L651) | [MCRecoClusterParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L660) | -| [MCRecoTrackParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L669) | [RecoParticleVertexAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L678) | | +| [MCRecoParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L614) | [MCRecoCaloAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L623) | [MCRecoTrackerAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L632) | +| [MCRecoTrackerHitPlaneAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L641) | [MCRecoCaloParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L650) | [MCRecoClusterParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L659) | +| [MCRecoTrackParticleAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L668) | [RecoParticleVertexAssociation](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L677) | | **Interfaces** | | | | |-|-|-| -| [TrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L785) | | | +| [TrackerHit](https://github.com/key4hep/EDM4hep/blob/main/edm4hep.yaml#L784) | | | The tests and examples in the `tests` directory show how to read, write, and use these types in your code. diff --git a/edm4hep.yaml b/edm4hep.yaml index 1d001289d..0db016be4 100644 --- a/edm4hep.yaml +++ b/edm4hep.yaml @@ -443,7 +443,6 @@ datatypes: OneToManyRelations: - edm4hep::Cluster clusters // clusters that have been combined to this cluster - edm4hep::CalorimeterHit hits // hits that have been combined to this cluster - - edm4hep::ParticleID particleIDs // particle IDs (sorted by their likelihood) ExtraCode: includes: "#include " declaration: " From 0f286fc112c1470a30c8a0d9250689ae7c99f809 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 16 May 2024 16:49:37 +0200 Subject: [PATCH 12/13] Transparently introduce switch to optional param values (#305) --- test/read_events.h | 8 +++++--- utils/src/ParticleIDUtils.cc | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/test/read_events.h b/test/read_events.h index da675d5dd..d3a71d5ae 100644 --- a/test/read_events.h +++ b/test/read_events.h @@ -11,12 +11,10 @@ // podio specific includes #include "podio/Frame.h" +#include "podio/podioVersion.h" // STL -#include -#include #include -#include void processEvent(const podio::Frame& event) { auto& mcps = event.get("MCParticles"); @@ -240,7 +238,11 @@ void processEvent(const podio::Frame& event) { // throw std::runtime_error("Collection 'SimCalorimeterHitContributions' should be present"); // } +#if PODIO_BUILD_VERSION > PODIO_VERSION(0, 99, 0) + const auto evtType = event.getParameter("EventType").value(); +#else const auto& evtType = event.getParameter("EventType"); +#endif std::cout << "Event Type: " << evtType << std::endl; } diff --git a/utils/src/ParticleIDUtils.cc b/utils/src/ParticleIDUtils.cc index 43c1c61ab..8b6337642 100644 --- a/utils/src/ParticleIDUtils.cc +++ b/utils/src/ParticleIDUtils.cc @@ -3,8 +3,8 @@ #include "edm4hep/Constants.h" #include +#include -#include #include #include #include @@ -123,6 +123,20 @@ std::optional PIDHandler::getAlgoInfo(const podi const std::string& collName) { ParticleIDMeta pidInfo{}; +#if PODIO_BUILD_VERSION > PODIO_VERSION(0, 99, 0) + auto maybeAlgoName = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoName)); + if (!maybeAlgoName.has_value()) { + return std::nullopt; + } + + pidInfo.algoName = std::move(maybeAlgoName.value()); + pidInfo.algoType = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoType)).value(); + pidInfo.paramNames = + metadata + .getParameter>(podio::collMetadataParamName(collName, edm4hep::pidParameterNames)) + .value(); + +#else pidInfo.algoName = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoName)); // Use the algoName as proxy to see whether we could actually get the // information from the metadata @@ -133,6 +147,7 @@ std::optional PIDHandler::getAlgoInfo(const podi pidInfo.algoType = metadata.getParameter(podio::collMetadataParamName(collName, edm4hep::pidAlgoType)); pidInfo.paramNames = metadata.getParameter>( podio::collMetadataParamName(collName, edm4hep::pidParameterNames)); +#endif return pidInfo; } From 997ab32b886899253c9bc61adea9a21b57bc5a21 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 May 2024 14:42:09 +0200 Subject: [PATCH 13/13] Update the schema diagram to reflect TrackerHit as interface (#302) --- doc/edm4hep_diagram.svg | 359 ++++++++++++++++++++++++++++++---------- 1 file changed, 272 insertions(+), 87 deletions(-) diff --git a/doc/edm4hep_diagram.svg b/doc/edm4hep_diagram.svg index 409bb6480..9c00daac7 100644 --- a/doc/edm4hep_diagram.svg +++ b/doc/edm4hep_diagram.svg @@ -24,18 +24,22 @@ inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1920" - inkscape:window-height="1016" + inkscape:window-height="1136" id="namedview332" - showgrid="false" - inkscape:zoom="0.86342179" - inkscape:cx="649.74038" - inkscape:cy="258.85379" + showgrid="true" + inkscape:zoom="1.2210628" + inkscape:cx="664.99446" + inkscape:cy="411.52674" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg8" inkscape:pagecheckerboard="0" - inkscape:document-units="mm" /> + inkscape:document-units="mm"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1622,6 +1762,24 @@ d="M 49.61081,110.73342 37.43926,79.359376" id="path6614-2-1" sodipodi:nodetypes="cc" /> + + + TrackerHit Vertex + id="g3121"> + d="m 264.85397,68.952084 c 8.00731,-1.397617 5.13526,-6.168642 -0.30964,-6.16143 -3.59226,0.0047 -8.07578,2.496879 -0.61937,6.048233" + style="fill:none;stroke:#24221c;stroke-width:0.321269;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-9-76671)" /> + d="m 264.85397,68.952084 c 8.00731,-1.397617 5.13526,-6.168642 -0.30964,-6.16143 -3.59226,0.0047 -9.7926,2.345492 -2.33619,5.896846" + style="fill:none;stroke:#24221c;stroke-width:0.321269;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-9-7-23462)" /> + d="m 264.85397,68.952084 c 8.00731,-1.397617 5.13526,-6.168642 -0.30964,-6.16143 -3.59226,0.0047 -11.64468,2.080909 -4.18827,5.632263" + style="fill:none;stroke:#24221c;stroke-width:0.321269;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-9-7-3)" /> @@ -1813,26 +1970,26 @@ style="fill:none;stroke:#000000;stroke-width:0.21700881px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-5-7)" /> + id="g3116"> + id="g31263" + transform="translate(-369.36509,-22.019556)"> + d="m 237.85366,77.865861 -4.68611,16.513173" + style="fill:none;stroke:#000000;stroke-width:0.217009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-6-4-5)" /> + d="m 237.85366,77.865861 -2.24077,14.875903" + style="fill:none;stroke:#000000;stroke-width:0.217009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-5-7-87128)" /> - - - + + + + + @@ -1976,27 +2139,31 @@ d="m 615.276,68.135417 16.64331,-14.56917" style="fill:none;stroke:#000000;stroke-width:0.21700881px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-5-7-3-6)" /> - - - + + + + + + style="fill:none;stroke:#000000;stroke-width:0.217009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-4-9-8-8)" + d="m 154.91496,107.45876 -23.81248,9.09549" + id="path6614-2-5-0-9-7-50-3" + sodipodi:nodetypes="cc" /> + style="fill:none;stroke:#000000;stroke-width:0.217009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-5-7-8-0-4)" + d="m 154.91496,107.45876 -17.19791,10.41842" + id="path6614-2-5-0-1-0-4-8-0" + sodipodi:nodetypes="cc" /> + style="fill:none;stroke:#000000;stroke-width:0.217009px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend-9-9-9-6-4-5-5-4)" + d="m 154.91496,107.45875 -21.16665,10.41842" + id="path6614-2-5-0-3-2-6-2-5" + sodipodi:nodetypes="cc" /> + style="opacity:0.596;fill:none;stroke:#00007e;stroke-width:0.438;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#marker22158)" + d="m 75.53997,113.90843 77.35641,-7.2039" + id="path7862-8-3" + sodipodi:nodetypes="cc" /> MCRecoTrackerAssociation TrackerHit3D + TrackerHitPlane