From 5b9815e29a34281a0968ca9455c5c677fc55fba8 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Tue, 26 Sep 2023 16:23:27 +0200 Subject: [PATCH 1/8] Add test utilities to create EDM4hep test data More or less copied from k4MarlinWrapper tests --- tests/CMakeLists.txt | 11 ++ tests/edm4hep_to_lcio.cpp | 28 +++ tests/src/EDM4hep2LCIOUtilities.cc | 269 +++++++++++++++++++++++++++++ tests/src/EDM4hep2LCIOUtilities.h | 143 +++++++++++++++ 4 files changed, 451 insertions(+) create mode 100644 tests/edm4hep_to_lcio.cpp create mode 100644 tests/src/EDM4hep2LCIOUtilities.cc create mode 100644 tests/src/EDM4hep2LCIOUtilities.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7f5ba239..d07d32df 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,11 +2,22 @@ add_library(edmCompare SHARED src/CompareEDM4hepLCIO.cc src/ObjectMapping.cc) target_link_libraries(edmCompare PUBLIC EDM4HEP::edm4hep ${LCIO_LIBRARIES}) target_include_directories(edmCompare PUBLIC ${LCIO_INCLUDE_DIRS}) +add_library(TestUtils SHARED src/EDM4hep2LCIOUtilities.cc) +target_link_libraries(TestUtils PUBLIC EDM4HEP::edm4hep ${LCIO_LIBRARIES}) +target_include_directories(TestUtils PUBLIC ${LCIO_INCLUDE_DIRS}) + add_executable(compare-contents compare_contents.cpp) target_link_libraries(compare-contents PRIVATE edmCompare podio::podioRootIO) target_include_directories(compare-contents PRIVATE $) +add_executable(edm4hep_to_lcio edm4hep_to_lcio.cpp) +target_link_libraries(edm4hep_to_lcio PRIVATE edmCompare TestUtils k4EDM4hep2LcioConv) +target_include_directories(edm4hep_to_lcio PRIVATE + $) + +add_test(NAME edm4hep_to_lcio COMMAND edm4hep_to_lcio) + find_program(BASH_PROGRAM bash) add_test(fetch_test_inputs ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/get_test_data.sh) diff --git a/tests/edm4hep_to_lcio.cpp b/tests/edm4hep_to_lcio.cpp new file mode 100644 index 00000000..6cc4acf3 --- /dev/null +++ b/tests/edm4hep_to_lcio.cpp @@ -0,0 +1,28 @@ +#include "EDM4hep2LCIOUtilities.h" +#include "CompareEDM4hepLCIO.h" + +#include "k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h" + +#include "podio/Frame.h" + +#include + +template +struct TD; + +int main() +{ + auto convObjMaps = EDM4hep2LCIOConv::CollectionsPairVectors {}; + auto objectMaps = ObjectMappings {}; + + const auto caloHitColl = createCalorimeterHits(10); + const auto lcioColl = EDM4hep2LCIOConv::convCalorimeterHits(&caloHitColl, "", convObjMaps.calohits); + + const auto edmEvent = createExampleEvent(); + + if (!compare(lcioColl, caloHitColl, objectMaps)) { + return 1; + } + + return 0; +} diff --git a/tests/src/EDM4hep2LCIOUtilities.cc b/tests/src/EDM4hep2LCIOUtilities.cc new file mode 100644 index 00000000..2bceb13d --- /dev/null +++ b/tests/src/EDM4hep2LCIOUtilities.cc @@ -0,0 +1,269 @@ +#include "EDM4hep2LCIOUtilities.h" + +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/RawCalorimeterHitCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include +#include "edm4hep/TrackCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/CaloHitContributionCollection.h" + +#include "podio/Frame.h" + +#include +#include +#include + +constexpr std::uint64_t operator""_u64(unsigned long long num) { return static_cast(num); } + +// Some pre-defined cellIDs that can be used below +constexpr static std::array CELLIDS = {0xcaffee_u64, 0xbeef_u64, 0xfe47_u64, 0x12345678_u64, 0_u64, -1_u64}; + +constexpr static uint64_t createCellID(int i) { return CELLIDS[i % CELLIDS.size()]; } + +/// Create a covariance matrix for N dimensions in lower triangular form +template +constexpr auto createCov() +{ + std::array result {}; + + // Calculate the flat index from the 2D index + const auto to_lower_tri = [](int i, int j) { + if (j < i) { + std::swap(i, j); + } + return i * (2 * N - i - 1) / 2 + j; + }; + + for (int i = 0; i < N; ++i) { + for (int j = 0; j < i; ++j) { + const auto index = to_lower_tri(i, j); + result[index] = i + 10 * j; + } + } + + return result; +} + +edm4hep::MCParticleCollection createMCParticles( + const int num_elements, + const std::vector& mcp_parents_idx) +{ + auto coll = edm4hep::MCParticleCollection {}; + + for (int i = 0; i < num_elements; ++i) { + auto elem = coll.create(); + elem.setPDG(i); + elem.setGeneratorStatus(i + 42); + elem.setVertex({i * 10., i * 20., i * 30.}); + elem.setTime(i * 100); + elem.setEndpoint({i * 30., i * 20., i * 30.}); + elem.setMomentum({i * 1.f, i * 2.f, i * 3.f}); + elem.setMomentumAtEndpoint({i * 3.f, i * 2.f, i * 1.f}); + elem.setMass(125. * i); + elem.setSpin({i * 0.5f, i * 0.25f, i * 0.25f}); + elem.setColorFlow({i, i * 2}); + elem.setCreatedInSimulation(1); + elem.setBackscatter(0); + elem.setVertexIsNotEndpointOfParent(1); + elem.setDecayedInTracker(0); + elem.setDecayedInCalorimeter(1); + elem.setHasLeftDetector(0); + elem.setStopped(1); + elem.setOverlay(0); + } + + for (const auto& [orig_idx, link_idx] : mcp_parents_idx) { + coll[orig_idx].addToParents(coll[link_idx]); + coll[link_idx].addToDaughters(coll[orig_idx]); + } + + return coll; +} + +edm4hep::CalorimeterHitCollection createCalorimeterHits(const int num_elements) +{ + edm4hep::CalorimeterHitCollection coll {}; + for (int i = 0; i < num_elements; ++i) { + auto elem = coll.create(); + elem.setCellID(createCellID(i)); + elem.setEnergy(i); + elem.setEnergyError(i / std::sqrt(i + 1)); + elem.setTime(i * 10); + elem.setPosition({i * 20.f, i * 30.f, i * 40.f}); + elem.setType(i * 123); + } + + return coll; +} + +edm4hep::RawCalorimeterHitCollection createRawCalorimeterHits(const int num_elements) +{ + edm4hep::RawCalorimeterHitCollection coll {}; + for (int i = 0; i < num_elements; ++i) { + auto elem = coll.create(); + elem.setCellID(createCellID(i)); + elem.setAmplitude(i * 1000); + elem.setTimeStamp(i * 100); + } + + return coll; +} + +edm4hep::RawTimeSeriesCollection createTPCHits(const int num_elements, const int num_rawwords) +{ + edm4hep::RawTimeSeriesCollection coll {}; + for (int i = 0; i < num_elements; ++i) { + auto elem = coll.create(); + + elem.setCellID(createCellID(i)); + elem.setQuality(i); + elem.setTime(i * 10.f); + elem.setCharge(i * 0.1f); + + for (int j = 0; j < num_rawwords; ++j) { + elem.addToAdcCounts((i + 10) * j); + } + } + return coll; +} + +edm4hep::TrackerHitCollection createTrackerHits(const int num_elements) +{ + edm4hep::TrackerHitCollection coll {}; + + for (int i = 0; i < num_elements; ++i) { + auto elem = coll.create(); + + elem.setCellID(createCellID(i)); + elem.setType(i * 1234); + elem.setQuality(i * 321); + elem.setTime(i * 100.f); + elem.setEDep(i * 1111.f); + elem.setEDepError(i * sqrt(10.f)); + elem.setPosition({i * 10., i * 20., i * 30.}); + elem.setCovMatrix(createCov<3>()); + } + return coll; +} + +edm4hep::TrackCollection createTracks( + const int num_elements, + const int subdetectorhitnumbers, + const int num_track_states, + const edm4hep::TrackerHitCollection& trackerHits, + const std::vector& link_trackerhit_idcs, + const std::vector& track_link_tracks_idcs) +{ + // edm4hep::Track + auto track_coll = edm4hep::TrackCollection(); + + for (int i = 0; i < num_elements; ++i) { + auto elem = track_coll.create(); + + elem.setType(2); // TODO specific type + elem.setChi2(i * 10.f); + elem.setNdf(i * 12); + elem.setDEdx(i); + elem.setDEdxError(i / std::sqrt(i + 1)); + elem.setRadiusOfInnermostHit(i * 5.f); + + for (int j = 0; j < subdetectorhitnumbers; ++j) { + elem.addToSubdetectorHitNumbers(i + 10 * j); + } + + for (auto& idx : link_trackerhit_idcs) { + elem.addToTrackerHits(trackerHits[idx]); + } + + for (int j = 0; j < num_track_states; ++j) { + edm4hep::TrackState trackstate; + + trackstate.location = j; + trackstate.D0 = (i + j) * 2; + trackstate.phi = (i - j) * 2; + trackstate.omega = (i * j) * 2; + trackstate.Z0 = (i + j) * 0.5; + trackstate.tanLambda = j * 2; + // edm4hep::Vector3f test_vec {float_cnt++, float_cnt++, float_cnt++}; + trackstate.referencePoint = {j * 1.f, i * 1.f, (j + i) * 1.f}; + trackstate.covMatrix = createCov<6>(); + + elem.addToTrackStates(trackstate); + } + } + + for (const auto& [orig_idx, link_idx] : track_link_tracks_idcs) { + track_coll[orig_idx].addToTracks(track_coll[link_idx]); + } + + return track_coll; +} + +std::pair createSimCalorimeterHits( + const int num_elements, + const int num_contributions, + const edm4hep::MCParticleCollection& mcParticles, + const std::vector& link_mcparticles_idcs) +{ + auto simcalohit_coll = edm4hep::SimCalorimeterHitCollection(); + auto contrib_coll = edm4hep::CaloHitContributionCollection {}; + + for (int i = 0; i < num_elements; ++i) { + // auto* elem = new edm4hep::SimCalorimeterHit(); + auto elem = simcalohit_coll.create(); + + elem.setCellID(createCellID(i)); + elem.setEnergy(i * 1000.); + elem.setPosition({i * 10.f, i * 20.f, i * 30.f}); + + for (int j = 0; j < num_contributions; j++) { + auto contrib = contrib_coll.create(); + contrib.setPDG(j * 42); + contrib.setEnergy(j + i * 1000.f); + contrib.setTime(j * 1000 - i); + contrib.setStepPosition({j * 1.f, j * 2.f, j * 3.f}); + + // add the corresponding mcparticle + for (const auto& [simch_idx, contrib_idx, mcpart_idx] : link_mcparticles_idcs) { + if (i == simch_idx && j == contrib_idx) { + contrib.setParticle(mcParticles[mcpart_idx]); + } + } + + elem.addToContributions(contrib); + } + } + + return {std::move(simcalohit_coll), std::move(contrib_coll)}; +} + +podio::Frame createExampleEvent() +{ + podio::Frame event; + + const auto& mcParticles = + event.put(createMCParticles(test_config::nMCParticles, test_config::mcpParentIdcs), "mcParticles"); + const auto& caloHits = event.put(createCalorimeterHits(test_config::nCaloHits), "caloHits"); + const auto& rawCaloHits = event.put(createRawCalorimeterHits(test_config::nRawCaloHits), "rawCaloHits"); + const auto& tpcHits = event.put(createTPCHits(test_config::nTPCHits, test_config::nTPCRawWords), "tpcHits"); + const auto& trackerHits = event.put(createTrackerHits(test_config::nTrackerHits), "trackerHits"); + const auto& tracks = event.put( + createTracks( + test_config::nTracks, + test_config::nSubdetectorHitNumbers, + test_config::nTrackStates, + trackerHits, + test_config::trackTrackerHitIdcs, + test_config::trackTrackIdcs), + "tracks"); + + auto [tmpSimCaloHits, tmpCaloHitConts] = createSimCalorimeterHits( + test_config::nSimCaloHits, test_config::nCaloHitContributions, mcParticles, test_config::simCaloHitMCIdcs); + const auto& simCaloHits = event.put(std::move(tmpSimCaloHits), "simCaloHits"); + event.put(std::move(tmpCaloHitConts), "caloHitContributions"); + + return event; +} diff --git a/tests/src/EDM4hep2LCIOUtilities.h b/tests/src/EDM4hep2LCIOUtilities.h new file mode 100644 index 00000000..dd4dc4fc --- /dev/null +++ b/tests/src/EDM4hep2LCIOUtilities.h @@ -0,0 +1,143 @@ +#ifndef K4EDM4HEP2LCIOCONV_TEST_EDM4HEP2LCIOUTILITIES_H +#define K4EDM4HEP2LCIOCONV_TEST_EDM4HEP2LCIOUTILITIES_H + +#include +#include +#include +#include + +namespace edm4hep { + class CalorimeterHitCollection; + class MCParticleCollection; + class RawCalorimeterHitCollection; + class RawTimeSeriesCollection; + class TrackerHitCollection; + class TrackCollection; + class SimCalorimeterHitCollection; + class CaloHitContributionCollection; +} // namespace edm4hep + +namespace podio { + class Frame; +} + +namespace test_config { + constexpr static int nMCParticles = 5; ///< The number of MCParticles to generate + + using IdxPair = std::pair; + /// How to create the MC particle hierarchy, e.g. {4, 0} means that mc[4] will + /// have mc[0] as a parente, and mc[0] will get mc[4] as a daughter + const static std::vector mcpParentIdcs = {{4, 0}, {4, 1}, {3, 2}, {3, 0}, {3, 1}, {2, 1}}; + + constexpr static int nCaloHits = 2; ///< The number of CalorimeterHits to generate + constexpr static int nRawCaloHits = 2; ///< The number of RawCalorimeterHits to generate + + constexpr static int nTPCHits = 4; ///< The number of TPCHits (RawTimeSeries) to generate + constexpr static int nTPCRawWords = 6; ///< The number of raw words to put into each TPCHit + + constexpr static int nTrackerHits = 5; ///< The number of TrackerHits to generate + + constexpr static int nTracks = 4; ///< The number of tracks to generate + constexpr static int nSubdetectorHitNumbers = 4; ///< The number of subdetector hits for each track + /// The tracker hits that should be added to each track + const static std::vector trackTrackerHitIdcs = {0, 2, 4}; + constexpr static int nTrackStates = 5; ///< The number of track states for each track + /// The tracks that should be linked, first index is the track to which the + /// second index will be added + const static std::vector trackTrackIdcs = {{0, 2}, {1, 3}, {2, 3}, {3, 2}, {3, 0}}; + + constexpr static int nSimCaloHits = 3; ///< The number of SimCalorimeterHits + constexpr static int nCaloHitContributions = 4; ///< The number of CalorimeterHit Contributions + /// idcs to setup relations between SimCalorimeterHits, CaloHitContributions + /// and MCParticles (in this order) + using CaloContIdx = std::tuple; + /// The index values to use for setting up the relations + const static std::vector simCaloHitMCIdcs = { + {0, 0, 0}, + {0, 1, 2}, + {0, 2, 1}, + {0, 3, 4}, + {1, 0, 1}, + {1, 1, 3}, + {1, 2, 4}, + {1, 3, 4}, + {2, 0, 0}, + {2, 1, 3}, + {2, 2, 2}, + {2, 3, 0}}; + +} // namespace test_config + +/** + * Create an MCParticle collection with num_elements and create a parent + * hierarchy using the passed mcp_parents_idx indices. The .first index will be + * used to determine the daughter element, while the .second index will be used + * to determine the parent. + */ +edm4hep::MCParticleCollection createMCParticles( + const int num_elements, + const std::vector& mcp_parents_idx); + +/** + * Create a CalorimeterHit collection + */ +edm4hep::CalorimeterHitCollection createCalorimeterHits(const int num_elements); + +/** + * Create a RawCalorimeterHit collection + */ +edm4hep::RawCalorimeterHitCollection createRawCalorimeterHits(const int num_elements); + +/** + * Create a TPCHit collection + */ +edm4hep::RawTimeSeriesCollection createTPCHits(const int num_elements, const int num_rawwords); + +/** + * Create a TrackerHit collection + */ +edm4hep::TrackerHitCollection createTrackerHits(const int num_elements); + +/** + * Create a track collection with tracks that have links to other tracks (in the + * same collection) and tracker hits + */ +edm4hep::TrackCollection createTracks( + const int num_elements, + const int subdetectorhitnumbers, + const int num_track_states, + const edm4hep::TrackerHitCollection& trackerHits, + const std::vector& link_trackerhit_idcs, + const std::vector& track_link_tracks_idcs); + +/** + * Create a SimCalorimeterHit (and an accompanying CaloHitContribution) + * collection with links to MCParticles + */ +std::pair createSimCalorimeterHits( + const int num_elements, + const int num_contributions, + const edm4hep::MCParticleCollection& mcParticles, + const std::vector& link_mcparticles_idcs); + +/** + * Create an example event that can be used to test the converter. + * + * Content: + * + * The following table gives an overview of the contents. The arguments for + * calling the individual creation functions are taken from the test_config + * namespace. + * | Name | Data type | comment | + * |----------------------+---------------------+--------------------------| + * | mcParticles | MCParticle | createMCParticles | + * | caloHits | CalorimeterHit | createCalorimeterHits | + * | rawCaloHits | RawCalorimeterHit | createRawCalorimeterHits | + * | tpcHits | RawTimeSeries | createTPCHits | + * | trackerHits | TrackerHit | createTrackerHits | + * | simCaloHits | SimCalorimeterHit | createSimCalorimeterHits | + * | caloHitContributions | CaloHitContribution | createSimCalorimeterHits | + */ +podio::Frame createExampleEvent(); + +#endif // K4EDM4HEP2LCIOCONV_TEST_COMPAREEDM4HEPLCIO_H From b9b2029c4f136a5314b75d388b0a16ba7d5a0c90 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Tue, 26 Sep 2023 17:57:18 +0200 Subject: [PATCH 2/8] Add a convEvent function to convert complete Frames --- .../k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h | 11 +++ k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp | 81 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h b/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h index 1a896007..a828af30 100644 --- a/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h +++ b/k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h @@ -36,6 +36,8 @@ namespace edm4hep { #include #include +#include "podio/Frame.h" + // LCIO #include #include @@ -57,6 +59,8 @@ namespace edm4hep { #include #include +#include + // Preprocessor symbol that can be used in downstream code to switch on the // namespace for the conversion #define EDM4HEP2LCIOCONV_NAMESPACE 1 @@ -142,6 +146,13 @@ namespace EDM4hep2LCIOConv { bool collectionExist(const std::string& collection_name, const lcio::LCEventImpl* lcio_event); + /** + * Convert an edm4hep event to an LCEvent + */ + std::unique_ptr convEvent( + const podio::Frame& edmEvent, + const podio::Frame& metadata = podio::Frame {}); + } // namespace EDM4hep2LCIOConv #endif diff --git a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp index f9299b74..d0f4ef92 100644 --- a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp +++ b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp @@ -1,5 +1,7 @@ #include "k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h" +#include "IMPL/LCEventImpl.h" + #if __has_include("edm4hep/EDM4hepVersion.h") #include "edm4hep/EDM4hepVersion.h" #else @@ -901,4 +903,83 @@ namespace EDM4hep2LCIOConv { return false; } + std::unique_ptr convEvent(const podio::Frame& edmEvent, const podio::Frame& metadata) + { + auto lcioEvent = std::make_unique(); + auto objectMappings = CollectionsPairVectors {}; + + const auto& collections = edmEvent.getAvailableCollections(); + for (const auto& name : collections) { + const auto edmCollection = edmEvent.get(name); + + const auto& cellIDStr = metadata.getParameter(podio::collMetadataParamName(name, "CellIDEncoding")); + + if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convTracks(coll, objectMappings.tracks, objectMappings.trackerhits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convTrackerHits(coll, cellIDStr, objectMappings.trackerhits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convSimTrackerHits(coll, cellIDStr, objectMappings.simtrackerhits, objectMappings.mcparticles); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convCalorimeterHits(coll, cellIDStr, objectMappings.calohits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convRawCalorimeterHits(coll, objectMappings.rawcalohits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convSimCalorimeterHits(coll, cellIDStr, objectMappings.simcalohits, objectMappings.mcparticles); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convTPCHits(coll, objectMappings.tpchits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convClusters(coll, objectMappings.clusters, objectMappings.calohits); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convVertices(coll, objectMappings.vertices, objectMappings.recoparticles); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convMCParticles(coll, objectMappings.mcparticles); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + auto lcColl = convReconstructedParticles( + coll, objectMappings.recoparticles, objectMappings.tracks, objectMappings.vertices, objectMappings.clusters); + lcioEvent->addCollection(lcColl, name); + } + else if (auto coll = dynamic_cast(edmCollection)) { + convEventHeader(coll, lcioEvent.get()); + } + else if (auto coll = dynamic_cast(edmCollection)) { + // "converted" as part of FillMissingCollectoins at the end + continue; + } + else { + std::cerr << "Error trying to convert requested " << edmCollection->getValueTypeName() << " with name " << name + << "\n" + << "List of supported types: " + << "Track, TrackerHit, SimTrackerHit, " + << "Cluster, CalorimeterHit, RawCalorimeterHit, " + << "SimCalorimeterHit, Vertex, ReconstructedParticle, " + << "MCParticle." << std::endl; + } + } + + FillMissingCollections(objectMappings); + + return lcioEvent; + } + } // namespace EDM4hep2LCIOConv From 18515c45f9cd4f895b387ec5dfdcebc4dedc1a51 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 27 Sep 2023 10:10:36 +0200 Subject: [PATCH 3/8] Move macro to utility header --- tests/compare_contents.cpp | 10 +--------- tests/src/ComparisonUtils.h | 9 +++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/compare_contents.cpp b/tests/compare_contents.cpp index 754d3eab..abf265f6 100644 --- a/tests/compare_contents.cpp +++ b/tests/compare_contents.cpp @@ -1,5 +1,6 @@ #include "CompareEDM4hepLCIO.h" #include "ObjectMapping.h" +#include "ComparisonUtils.h" #include "podio/ROOTFrameReader.h" #include "podio/Frame.h" @@ -9,15 +10,6 @@ #include #include -#define ASSERT_COMPARE_OR_EXIT(collType) \ - if (type == #collType) { \ - auto& edmcoll = edmEvent.get(name); \ - if (!compare(lcioColl, edmcoll, objectMapping)) { \ - std::cerr << "in collection: " << name << std::endl; \ - return 1; \ - } \ - } - constexpr auto usageMsg = R"(usage: compare-contents lciofile edm4hepfile)"; int main(int argc, char* argv[]) diff --git a/tests/src/ComparisonUtils.h b/tests/src/ComparisonUtils.h index 046c1f63..14e2c115 100644 --- a/tests/src/ComparisonUtils.h +++ b/tests/src/ComparisonUtils.h @@ -244,4 +244,13 @@ bool compareCollection( return true; } +#define ASSERT_COMPARE_OR_EXIT(collType) \ + if (type == #collType) { \ + auto& edmcoll = edmEvent.get(name); \ + if (!compare(lcioColl, edmcoll, objectMapping)) { \ + std::cerr << "in collection: " << name << std::endl; \ + return 1; \ + } \ + } + #endif // K4EDM4HEP2LCIOCONV_TEST_COMPARISONUTILS_H From 432fced1590e4d73545faeff9f10b88ac21c4473 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 27 Sep 2023 10:10:58 +0200 Subject: [PATCH 4/8] Add test for EDM4hep to LCIO conversion --- tests/edm4hep_to_lcio.cpp | 52 +++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/tests/edm4hep_to_lcio.cpp b/tests/edm4hep_to_lcio.cpp index 6cc4acf3..a8c96cf0 100644 --- a/tests/edm4hep_to_lcio.cpp +++ b/tests/edm4hep_to_lcio.cpp @@ -1,8 +1,12 @@ #include "EDM4hep2LCIOUtilities.h" #include "CompareEDM4hepLCIO.h" +#include "ObjectMapping.h" +#include "ComparisonUtils.h" #include "k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h" +#include "Exceptions.h" + #include "podio/Frame.h" #include @@ -12,16 +16,50 @@ struct TD; int main() { - auto convObjMaps = EDM4hep2LCIOConv::CollectionsPairVectors {}; - auto objectMaps = ObjectMappings {}; + const auto edmEvent = createExampleEvent(); + const auto lcioEvent = EDM4hep2LCIOConv::convEvent(edmEvent); - const auto caloHitColl = createCalorimeterHits(10); - const auto lcioColl = EDM4hep2LCIOConv::convCalorimeterHits(&caloHitColl, "", convObjMaps.calohits); + // if (!compareEventHeader(lcioEvent.get(), &edmEvent)) { + // return 1; + // } - const auto edmEvent = createExampleEvent(); + for (const auto& name : edmEvent.getAvailableCollections()) { + const auto edmColl = edmEvent.get(name); + const auto typeName = edmColl->getValueTypeName(); + if (typeName == "edm4hep::CaloHitContribution" || typeName == "edm4hep::ParticleID") { + continue; + } + try { + const auto* lcColl = lcioEvent->getCollection(name); + if (lcColl->getNumberOfElements() != edmColl->size()) { + std::cerr << "Collection " << name << " has different sizes. EDM4hep: " << edmColl->size() + << ", LCIO: " << lcColl->getNumberOfElements() << std::endl; + return 1; + } + } catch (EVENT::DataNotAvailableException& ex) { + std::cerr << "Collection " << name << " is not available in LCEvent" << std::endl; + return 1; + } + } + + const auto objectMapping = ObjectMappings::fromEvent(lcioEvent.get(), edmEvent); + + for (const auto& name : edmEvent.getAvailableCollections()) { + const auto* lcioColl = lcioEvent->getCollection(name); + const auto type = edmEvent.get(name)->getTypeName(); - if (!compare(lcioColl, caloHitColl, objectMaps)) { - return 1; + ASSERT_COMPARE_OR_EXIT(edm4hep::MCParticleCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::ReconstructedParticleCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::TrackCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::TrackerHitCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::TrackerHitPlaneCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::SimTrackerHitCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::CalorimeterHitCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::RawCalorimeterHitCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::SimCalorimeterHitCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::RawTimeSeriesCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::ClusterCollection) + ASSERT_COMPARE_OR_EXIT(edm4hep::VertexCollection) } return 0; From d2dffaba0271349a3dd7d2c8266859ece0c974bc Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 27 Sep 2023 11:27:44 +0200 Subject: [PATCH 5/8] Fix minor issues uncovered by tests - Reset SimCalorimeterHit energy after adding Contributions as adding contributions changes the energy - Make sure to populate daughters in the right order in the example MCCollection creation --- k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp | 6 +++++- tests/edm4hep_to_lcio.cpp | 10 ++++------ tests/src/EDM4hep2LCIOUtilities.cc | 16 ++++++++++++++-- tests/src/EDM4hep2LCIOUtilities.h | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp index d0f4ef92..32ceceea 100644 --- a/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp +++ b/k4EDM4hep2LcioConv/src/k4EDM4hep2LcioConv.cpp @@ -873,7 +873,11 @@ namespace EDM4hep2LCIOConv { // << std::endl; } } // all emd4hep contributions - } // SimCaloHit + + // We need to reset the energy to the original one, because adding + // contributions alters the energy in LCIO + lcio_sch->setEnergy(edm_sch.getEnergy()); + } // SimCaloHit // Fill missing SimTrackerHit collections for (auto& [lcio_strh, edm_strh] : collection_pairs.simtrackerhits) { diff --git a/tests/edm4hep_to_lcio.cpp b/tests/edm4hep_to_lcio.cpp index a8c96cf0..2ee20461 100644 --- a/tests/edm4hep_to_lcio.cpp +++ b/tests/edm4hep_to_lcio.cpp @@ -9,11 +9,6 @@ #include "podio/Frame.h" -#include - -template -struct TD; - int main() { const auto edmEvent = createExampleEvent(); @@ -45,8 +40,11 @@ int main() const auto objectMapping = ObjectMappings::fromEvent(lcioEvent.get(), edmEvent); for (const auto& name : edmEvent.getAvailableCollections()) { - const auto* lcioColl = lcioEvent->getCollection(name); const auto type = edmEvent.get(name)->getTypeName(); + if (type == "edm4hep::CaloHitContributionCollection" || type == "edm4hep::ParticleIDCollection") { + continue; + } + const auto* lcioColl = lcioEvent->getCollection(name); ASSERT_COMPARE_OR_EXIT(edm4hep::MCParticleCollection) ASSERT_COMPARE_OR_EXIT(edm4hep::ReconstructedParticleCollection) diff --git a/tests/src/EDM4hep2LCIOUtilities.cc b/tests/src/EDM4hep2LCIOUtilities.cc index 2bceb13d..dce03cb5 100644 --- a/tests/src/EDM4hep2LCIOUtilities.cc +++ b/tests/src/EDM4hep2LCIOUtilities.cc @@ -77,7 +77,16 @@ edm4hep::MCParticleCollection createMCParticles( for (const auto& [orig_idx, link_idx] : mcp_parents_idx) { coll[orig_idx].addToParents(coll[link_idx]); - coll[link_idx].addToDaughters(coll[orig_idx]); + } + // We assign the daughters after all the parents are assigned simply because + // LCIO adds the daughters in the call to add parents and relation comparison + // in the tests assume that all relations are in the same order + for (auto particle : coll) { + // Workaround as proposed in https://github.com/AIDASoft/podio/issues/347 + for (auto p : particle.getParents()) { + auto parent = coll[p.getObjectID().index]; + parent.addToDaughters(particle); + } } return coll; @@ -166,9 +175,12 @@ edm4hep::TrackCollection createTracks( elem.setType(2); // TODO specific type elem.setChi2(i * 10.f); elem.setNdf(i * 12); + elem.setRadiusOfInnermostHit(i * 5.f); + elem.setDEdx(i); elem.setDEdxError(i / std::sqrt(i + 1)); - elem.setRadiusOfInnermostHit(i * 5.f); + // Also add a DxQuantity since the comparison expects that + elem.addToDxQuantities({0, i * 1.f, i / std::sqrt(i + 1.f)}); for (int j = 0; j < subdetectorhitnumbers; ++j) { elem.addToSubdetectorHitNumbers(i + 10 * j); diff --git a/tests/src/EDM4hep2LCIOUtilities.h b/tests/src/EDM4hep2LCIOUtilities.h index dd4dc4fc..783bdb2f 100644 --- a/tests/src/EDM4hep2LCIOUtilities.h +++ b/tests/src/EDM4hep2LCIOUtilities.h @@ -26,7 +26,7 @@ namespace test_config { using IdxPair = std::pair; /// How to create the MC particle hierarchy, e.g. {4, 0} means that mc[4] will - /// have mc[0] as a parente, and mc[0] will get mc[4] as a daughter + /// have mc[0] as a parent, and mc[0] will get mc[4] as a daughter const static std::vector mcpParentIdcs = {{4, 0}, {4, 1}, {3, 2}, {3, 0}, {3, 1}, {2, 1}}; constexpr static int nCaloHits = 2; ///< The number of CalorimeterHits to generate From 15f3ae373a879c35ef014602f5eb9e94e4d90190 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 27 Sep 2023 12:42:07 +0200 Subject: [PATCH 6/8] Mark all utility functionalities inline to avoid linker issues --- tests/src/ComparisonUtils.h | 55 +++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/src/ComparisonUtils.h b/tests/src/ComparisonUtils.h index 14e2c115..0ef4fe75 100644 --- a/tests/src/ComparisonUtils.h +++ b/tests/src/ComparisonUtils.h @@ -21,7 +21,7 @@ #include template -std::ostream& printContainer(std::ostream& os, const T& cont) +inline std::ostream& printContainer(std::ostream& os, const T& cont) { os << "("; if (!cont.empty()) { @@ -35,25 +35,25 @@ std::ostream& printContainer(std::ostream& os, const T& cont) } template -std::ostream& operator<<(std::ostream& os, const std::vector& vec) +inline std::ostream& operator<<(std::ostream& os, const std::vector& vec) { return printContainer(os, vec); } template -std::ostream& operator<<(std::ostream& os, const std::array& arr) +inline std::ostream& operator<<(std::ostream& os, const std::array& arr) { return printContainer(os, arr); } template -std::ostream& operator<<(std::ostream& os, const podio::RelationRange& range) +inline std::ostream& operator<<(std::ostream& os, const podio::RelationRange& range) { return printContainer(os, range); } template -bool operator==(const std::vector& vec, const std::array& arr) +inline bool operator==(const std::vector& vec, const std::array& arr) { if (vec.size() != N) { return false; @@ -67,13 +67,13 @@ bool operator==(const std::vector& vec, const std::array& arr) } template -bool operator!=(const std::vector& vec, const std::array& arr) +inline bool operator!=(const std::vector& vec, const std::array& arr) { return !(vec == arr); } template -bool operator==(const std::vector& vec, const podio::RelationRange& range) +inline bool operator==(const std::vector& vec, const podio::RelationRange& range) { if (vec.size() != range.size()) { return false; @@ -87,13 +87,13 @@ bool operator==(const std::vector& vec, const podio::RelationRange& range) } template -bool operator!=(const std::vector& vec, const podio::RelationRange& range) +inline bool operator!=(const std::vector& vec, const podio::RelationRange& range) { return !(vec == range); } template -bool nanSafeComp(T x, U y) +inline bool nanSafeComp(T x, U y) { return (x == y) || (std::isnan(x) && std::isnan(y)); } @@ -101,19 +101,19 @@ bool nanSafeComp(T x, U y) // Macro for defining the comparison operators for edm4hep::Vector3X and // different return types (X* or vector from LCIO) #define VECTOR3_COMPARE(FT, VT) \ - bool operator==(const FT* vals, const VT& vec) \ + inline bool operator==(const FT* vals, const VT& vec) \ { \ return nanSafeComp(vals[0], vec[0]) && nanSafeComp(vals[1], vec[1]) && nanSafeComp(vals[2], vec[2]); \ } \ - bool operator!=(const FT* vals, const VT& vec) { return !(vals == vec); } \ - bool operator==(const std::vector& vals, const VT& vec) \ + inline bool operator!=(const FT* vals, const VT& vec) { return !(vals == vec); } \ + inline bool operator==(const std::vector& vals, const VT& vec) \ { \ if (vals.size() != 3) { \ return false; \ } \ return vals.data() == vec; \ } \ - bool operator!=(const std::vector& vals, const VT& vec) { return !(vals == vec); } + inline bool operator!=(const std::vector& vals, const VT& vec) { return !(vals == vec); } VECTOR3_COMPARE(float, edm4hep::Vector3f) VECTOR3_COMPARE(double, edm4hep::Vector3d) @@ -123,17 +123,17 @@ VECTOR3_COMPARE(double, edm4hep::Vector3f) // Macro for defining the comparison operators for edm4hep::Vector3X and // different return types (X* or vector from LCIO) -#define VECTOR2_COMPARE(FT, VT) \ - bool operator==(const FT* vals, const VT& vec) { return vals[0] == vec[0] && vals[1] == vec[1]; } \ - bool operator!=(const FT* vals, const VT& vec) { return !(vals == vec); } \ - bool operator==(const std::vector& vals, const VT& vec) \ - { \ - if (vals.size() != 2) { \ - return false; \ - } \ - return vals.data() == vec; \ - } \ - bool operator!=(const std::vector& vals, const VT& vec) { return !(vals == vec); } +#define VECTOR2_COMPARE(FT, VT) \ + inline bool operator==(const FT* vals, const VT& vec) { return vals[0] == vec[0] && vals[1] == vec[1]; } \ + inline bool operator!=(const FT* vals, const VT& vec) { return !(vals == vec); } \ + inline bool operator==(const std::vector& vals, const VT& vec) \ + { \ + if (vals.size() != 2) { \ + return false; \ + } \ + return vals.data() == vec; \ + } \ + inline bool operator!=(const std::vector& vals, const VT& vec) { return !(vals == vec); } VECTOR2_COMPARE(int, edm4hep::Vector2i) VECTOR2_COMPARE(float, edm4hep::Vector2f) @@ -159,7 +159,8 @@ VECTOR2_COMPARE(float, edm4hep::Vector2f) * correct EDM4hep element (using the ObjectIDs) */ template -bool compareRelation(const LcioT* lcioElem, const EDM4hepT& edm4hepElem, const MapT& objectMap, const std::string& msg) +inline bool +compareRelation(const LcioT* lcioElem, const EDM4hepT& edm4hepElem, const MapT& objectMap, const std::string& msg) { if (lcioElem == nullptr && edm4hepElem.isAvailable()) { std::cerr << msg << " LCIO element is empty but edm4hep element is not" << std::endl; @@ -188,7 +189,7 @@ bool compareRelation(const LcioT* lcioElem, const EDM4hepT& edm4hepElem, const M * that dispatches this. */ template -bool compareRelation( +inline bool compareRelation( const std::vector& lcioRange, const podio::RelationRange& edm4hepRange, const MapT& objectMap, @@ -226,7 +227,7 @@ bool compareRelation( // Compare an LCIO collection and an EDM4hep collection. Assumes that a compare // function working with the element types is available template -bool compareCollection( +inline bool compareCollection( const lcio::LCCollection* lcioCollection, const EDM4hepCollT& edm4hepCollection, const ObjectMappings& objectMaps) From 55e117c47043d47b140e94dae23f439205dab9a3 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Wed, 27 Sep 2023 14:22:49 +0200 Subject: [PATCH 7/8] Add the roundtrip tests from k4MarlinWrapper --- tests/CMakeLists.txt | 9 +- tests/edm4hep_roundtrip.cpp | 30 ++++ tests/src/CompareEDM4hepEDM4hep.cc | 218 +++++++++++++++++++++++++++++ tests/src/CompareEDM4hepEDM4hep.h | 18 +++ 4 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 tests/edm4hep_roundtrip.cpp create mode 100644 tests/src/CompareEDM4hepEDM4hep.cc create mode 100644 tests/src/CompareEDM4hepEDM4hep.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d07d32df..c928ab26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(edmCompare SHARED src/CompareEDM4hepLCIO.cc src/ObjectMapping.cc) +add_library(edmCompare SHARED src/CompareEDM4hepLCIO.cc src/ObjectMapping.cc src/CompareEDM4hepEDM4hep.cc) target_link_libraries(edmCompare PUBLIC EDM4HEP::edm4hep ${LCIO_LIBRARIES}) target_include_directories(edmCompare PUBLIC ${LCIO_INCLUDE_DIRS}) @@ -18,6 +18,13 @@ target_include_directories(edm4hep_to_lcio PRIVATE add_test(NAME edm4hep_to_lcio COMMAND edm4hep_to_lcio) +add_executable(edm4hep_roundtrip edm4hep_roundtrip.cpp) +target_link_libraries(edm4hep_roundtrip PRIVATE k4EDM4hep2LcioConv TestUtils edmCompare) +target_include_directories(edm4hep_roundtrip PRIVATE + $) + +add_test(NAME edm4hep_roundtrip COMMAND edm4hep_roundtrip) + find_program(BASH_PROGRAM bash) add_test(fetch_test_inputs ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/get_test_data.sh) diff --git a/tests/edm4hep_roundtrip.cpp b/tests/edm4hep_roundtrip.cpp new file mode 100644 index 00000000..a4b0f960 --- /dev/null +++ b/tests/edm4hep_roundtrip.cpp @@ -0,0 +1,30 @@ +#include "CompareEDM4hepEDM4hep.h" +#include "EDM4hep2LCIOUtilities.h" + +#include "k4EDM4hep2LcioConv/k4EDM4hep2LcioConv.h" +#include "k4EDM4hep2LcioConv/k4Lcio2EDM4hepConv.h" + +#include "podio/Frame.h" + +#include + +#define ASSERT_SAME_OR_ABORT(type, name) \ + if (!compare(origEvent.get(name), roundtripEvent.get(name))) { \ + std::cerr << "Comparison failure in " << name << std::endl; \ + return 1; \ + } + +int main() +{ + const auto origEvent = createExampleEvent(); + const auto lcioEvent = EDM4hep2LCIOConv::convEvent(origEvent); + const auto roundtripEvent = LCIO2EDM4hepConv::convertEvent(lcioEvent.get()); + + ASSERT_SAME_OR_ABORT(edm4hep::CalorimeterHitCollection, "caloHits"); + ASSERT_SAME_OR_ABORT(edm4hep::MCParticleCollection, "mcParticles"); + ASSERT_SAME_OR_ABORT(edm4hep::SimCalorimeterHitCollection, "simCaloHits"); + ASSERT_SAME_OR_ABORT(edm4hep::TrackCollection, "tracks"); + ASSERT_SAME_OR_ABORT(edm4hep::TrackerHitCollection, "trackerHits"); + + return 0; +} diff --git a/tests/src/CompareEDM4hepEDM4hep.cc b/tests/src/CompareEDM4hepEDM4hep.cc new file mode 100644 index 00000000..be72e9e7 --- /dev/null +++ b/tests/src/CompareEDM4hepEDM4hep.cc @@ -0,0 +1,218 @@ +#include "CompareEDM4hepEDM4hep.h" +#include "ComparisonUtils.h" + +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/TrackerHitCollection.h" + +#include +#include +#include + +#define REQUIRE_SAME(expected, actual, msg) \ + { \ + if (!((expected) == (actual))) { \ + std::cerr << msg << " are not the same (expected: " << (expected) << ", actual: " << (actual) << ")" \ + << std::endl; \ + return false; \ + } \ + } + +bool compare(const edm4hep::CalorimeterHitCollection& origColl, const edm4hep::CalorimeterHitCollection& roundtripColl) +{ + REQUIRE_SAME(origColl.size(), roundtripColl.size(), "collection sizes"); + for (size_t i = 0; i < origColl.size(); ++i) { + const auto origHit = origColl[i]; + const auto hit = roundtripColl[i]; + + REQUIRE_SAME(origHit.getCellID(), hit.getCellID(), "cellID in hit " << i); + REQUIRE_SAME(origHit.getEnergy(), hit.getEnergy(), "energy in hit " << i); + REQUIRE_SAME(origHit.getEnergyError(), hit.getEnergyError(), "energyError in hit " << i); + REQUIRE_SAME(origHit.getTime(), hit.getTime(), "time in hit " << i); + REQUIRE_SAME(origHit.getPosition(), hit.getPosition(), "position in hit " << i); + } + + return true; +} + +bool compare(const edm4hep::MCParticleCollection& origColl, const edm4hep::MCParticleCollection& roundtripColl) +{ + REQUIRE_SAME(origColl.size(), roundtripColl.size(), "collection sizes"); + + for (size_t i = 0; i < origColl.size(); ++i) { + auto origPart = origColl[i]; + auto part = roundtripColl[i]; + + REQUIRE_SAME(origPart.getPDG(), part.getPDG(), "pdg in particle " << i); + REQUIRE_SAME(origPart.getGeneratorStatus(), part.getGeneratorStatus(), "generatorStatus in particle " << i); + REQUIRE_SAME(origPart.getVertex(), part.getVertex(), "vertex in particle " << i); + REQUIRE_SAME(origPart.getTime(), part.getTime(), "time in particle " << i); + REQUIRE_SAME(origPart.getEndpoint(), part.getEndpoint(), "endpoint in particle " << i); + REQUIRE_SAME(origPart.getMomentum(), part.getMomentum(), "momentum in particle " << i); + REQUIRE_SAME( + origPart.getMomentumAtEndpoint(), part.getMomentumAtEndpoint(), "momentumAtEndpoint in particle " << i); + REQUIRE_SAME(origPart.getMass(), part.getMass(), "mass in particle " << i); + REQUIRE_SAME(origPart.getCharge(), part.getCharge(), "charge in particle " << i); + REQUIRE_SAME(origPart.getSpin(), part.getSpin(), "spin in particle " << i); + REQUIRE_SAME(origPart.getColorFlow(), part.getColorFlow(), "colorFlow in particle " << i); + + REQUIRE_SAME(origPart.isCreatedInSimulation(), part.isCreatedInSimulation(), " in particle " << i); + REQUIRE_SAME(origPart.isBackscatter(), part.isBackscatter(), " in particle " << i); + REQUIRE_SAME(origPart.vertexIsNotEndpointOfParent(), part.vertexIsNotEndpointOfParent(), " in particle " << i); + REQUIRE_SAME(origPart.isDecayedInTracker(), part.isDecayedInTracker(), " in particle " << i); + REQUIRE_SAME(origPart.isDecayedInCalorimeter(), part.isDecayedInCalorimeter(), " in particle " << i); + REQUIRE_SAME(origPart.hasLeftDetector(), part.hasLeftDetector(), " in particle " << i); + REQUIRE_SAME(origPart.isStopped(), part.isStopped(), " in particle " << i); + REQUIRE_SAME(origPart.isOverlay(), part.isOverlay(), " in particle " << i); + + // Check the mother / daughter relations. We use the ObjectIDs here + // (assuming that the collection names are the same!) + REQUIRE_SAME(origPart.getParents().size(), part.getParents().size(), "size of parents in particle " << i); + for (size_t iP = 0; iP < origPart.getParents().size(); ++iP) { + REQUIRE_SAME( + origPart.getParents()[iP].getObjectID(), + part.getParents()[iP].getObjectID(), + " parent " << iP << " in particle " << i); + } + REQUIRE_SAME(origPart.getDaughters().size(), part.getDaughters().size(), "size of daughters in particle " << i); + for (size_t iD = 0; iD < origPart.getDaughters().size(); ++iD) { + REQUIRE_SAME( + origPart.getDaughters()[iD].getObjectID(), + part.getDaughters()[iD].getObjectID(), + " daughter " << iD << " in particle " << i); + } + } + + return true; +} + +bool compare( + const edm4hep::SimCalorimeterHitCollection& origColl, + const edm4hep::SimCalorimeterHitCollection& roundtripColl) +{ + REQUIRE_SAME(origColl.size(), roundtripColl.size(), "collection sizes"); + for (size_t i = 0; i < origColl.size(); ++i) { + auto origHit = origColl[i]; + auto hit = roundtripColl[i]; + + REQUIRE_SAME(origHit.getCellID(), hit.getCellID(), "cellID in hit " << i); + REQUIRE_SAME(origHit.getEnergy(), hit.getEnergy(), "energy in hit " << i); + REQUIRE_SAME(origHit.getPosition(), hit.getPosition(), "position in hit " << i); + + // Check the contributions + const auto origContribs = origHit.getContributions(); + const auto contribs = hit.getContributions(); + REQUIRE_SAME(origContribs.size(), contribs.size(), "size of contributions in hit " << i); + for (size_t iC = 0; iC < origHit.getContributions().size(); ++iC) { + const auto origCont = origContribs[iC]; + const auto cont = contribs[iC]; + REQUIRE_SAME(origCont.getPDG(), cont.getPDG(), "pdg of contribution " << iC << " in hit " << i); + REQUIRE_SAME(origCont.getEnergy(), cont.getEnergy(), "energy of contribution " << iC << " in hit " << i); + REQUIRE_SAME(origCont.getTime(), cont.getTime(), "time of contribution " << iC << " in hit " << i); + REQUIRE_SAME( + origCont.getStepPosition(), cont.getStepPosition(), "stepPosition of contribution " << iC << " in hit " << i); + + // Check the MCParticles via ObjectID (asssuming collection names remain + // unchanged) + REQUIRE_SAME( + origCont.getParticle().getObjectID(), + cont.getParticle().getObjectID(), + "particle of contribution " << iC << " in hit " << i); + } + } + + return true; +} + +bool compare(const edm4hep::TrackState& orig, const edm4hep::TrackState& roundtrip) +{ + REQUIRE_SAME(orig.location, roundtrip.location, "location in TrackState"); + REQUIRE_SAME(orig.D0, roundtrip.D0, "D0 in TrackState"); + REQUIRE_SAME(orig.Z0, roundtrip.Z0, "Z0 in TrackState"); + REQUIRE_SAME(orig.phi, roundtrip.phi, "phi in TrackState"); + REQUIRE_SAME(orig.omega, roundtrip.omega, "omega in TrackState"); + REQUIRE_SAME(orig.tanLambda, roundtrip.tanLambda, "tanLambda in TrackState"); + REQUIRE_SAME(orig.referencePoint, roundtrip.referencePoint, "referencePoint in TrackState"); + // The time related covariance matrix elements are lost in the EDM4hep -> LCIO + // conversion and set to zero in the LCIO -> EDM4hep conversion + const auto redOrigCov = std::vector(orig.covMatrix.begin(), orig.covMatrix.begin() + 15); + const auto redCov = std::vector(roundtrip.covMatrix.begin(), roundtrip.covMatrix.begin() + 15); + REQUIRE_SAME(redOrigCov, redCov, "covMatrix in TrackState"); + + return true; +} + +bool compare(const edm4hep::TrackCollection& origColl, const edm4hep::TrackCollection& roundtripColl) +{ + REQUIRE_SAME(origColl.size(), roundtripColl.size(), "collection sizes"); + + for (size_t i = 0; i < origColl.size(); ++i) { + auto origTrack = origColl[i]; + auto track = roundtripColl[i]; + + REQUIRE_SAME(origTrack.getChi2(), track.getChi2(), "chi2 in track " << i); + REQUIRE_SAME(origTrack.getNdf(), track.getNdf(), "chi2 in track " << i); + REQUIRE_SAME(origTrack.getDEdx(), track.getDEdx(), "dEdx in track " << i); + REQUIRE_SAME(origTrack.getDEdxError(), track.getDEdxError(), "dEdxError in track " << i); + + const auto origSubDetHitNumbers = origTrack.getSubdetectorHitNumbers(); + const auto subDetHitNumbers = track.getSubdetectorHitNumbers(); + // Resizing in EDM4hep to LCIO conversion causes the roundtripped version to + // have 50 hits. So here we just compare the ones that are available in the + // original + for (size_t iSN = 0; iSN < origSubDetHitNumbers.size(); ++iSN) { + REQUIRE_SAME( + origSubDetHitNumbers[iSN], subDetHitNumbers[iSN], "subdetector hit numbers " << iSN << " in track " << i); + } + for (size_t iSN = origSubDetHitNumbers.size(); iSN < 50; ++iSN) { + REQUIRE_SAME(0, subDetHitNumbers[iSN], "additional subdetector hit number in track " << i); + } + + const auto origHits = origTrack.getTrackerHits(); + const auto hits = track.getTrackerHits(); + REQUIRE_SAME(origHits.size(), hits.size(), "number of hits in track " << i); + for (size_t iH = 0; iH < origHits.size(); ++iH) { + REQUIRE_SAME(origHits[iH].getObjectID(), hits[iH].getObjectID(), "tracker hit " << iH << " in track " << i); + } + + const auto origStates = origTrack.getTrackStates(); + const auto states = track.getTrackStates(); + REQUIRE_SAME(origStates.size(), states.size(), "number of track states in track " << i); + for (size_t iS = 0; iS < origStates.size(); ++iS) { + if (!compare(origStates[iS], states[iS])) { + std::cerr << "in track state " << iS << " in track " << i << std::endl; + return false; + } + } + + const auto origTracks = origTrack.getTracks(); + const auto tracks = track.getTracks(); + REQUIRE_SAME(origTracks.size(), tracks.size(), "number of tracks in track " << i); + for (size_t iT = 0; iT < origTracks.size(); ++iT) { + REQUIRE_SAME(origTracks[iT].getObjectID(), tracks[iT].getObjectID(), "track " << iT << " in track " << i); + } + } + return true; +} + +bool compare(const edm4hep::TrackerHitCollection& origColl, const edm4hep::TrackerHitCollection& roundtripColl) +{ + REQUIRE_SAME(origColl.size(), roundtripColl.size(), "collection sizes"); + for (size_t i = 0; i < origColl.size(); ++i) { + auto origHit = origColl[i]; + auto hit = roundtripColl[i]; + + REQUIRE_SAME(origHit.getCellID(), hit.getCellID(), "cellID in hit " << i); + REQUIRE_SAME(origHit.getType(), hit.getType(), "type in hit " << i); + REQUIRE_SAME(origHit.getQuality(), hit.getQuality(), "quality in hit " << i); + REQUIRE_SAME(origHit.getTime(), hit.getTime(), "time in hit " << i); + REQUIRE_SAME(origHit.getEDep(), hit.getEDep(), "EDep in hit " << i); + REQUIRE_SAME(origHit.getEDepError(), hit.getEDepError(), "EDepError in hit " << i); + REQUIRE_SAME(origHit.getPosition(), hit.getPosition(), "Position in hit " << i); + REQUIRE_SAME(origHit.getCovMatrix(), hit.getCovMatrix(), "CovMatrix in hit " << i); + } + + return true; +} diff --git a/tests/src/CompareEDM4hepEDM4hep.h b/tests/src/CompareEDM4hepEDM4hep.h new file mode 100644 index 00000000..e9a5924d --- /dev/null +++ b/tests/src/CompareEDM4hepEDM4hep.h @@ -0,0 +1,18 @@ +#ifndef K4EDM4HEP2LCIOCONV_TEST_COMPAREEDM4HEPEDM4HEP_H +#define K4EDM4HEP2LCIOCONV_TEST_COMPAREEDM4HEPEDM4HEP_H + +#include "EDM4hep2LCIOUtilities.h" + +bool compare(const edm4hep::CalorimeterHitCollection& origColl, const edm4hep::CalorimeterHitCollection& roundtripColl); + +bool compare(const edm4hep::MCParticleCollection& origColl, const edm4hep::MCParticleCollection& roundtripColl); + +bool compare( + const edm4hep::SimCalorimeterHitCollection& origColl, + const edm4hep::SimCalorimeterHitCollection& roundtripColl); + +bool compare(const edm4hep::TrackCollection& origColl, const edm4hep::TrackCollection& roundtripColl); + +bool compare(const edm4hep::TrackerHitCollection& origColl, const edm4hep::TrackerHitCollection& roundtripColl); + +#endif // K4EDM4HEP2LCIOCONV_TEST_COMPAREEDM4HEPEDM4HEP_H From caff3e910acf033ed0e9c4c3e7ee558e86a435e1 Mon Sep 17 00:00:00 2001 From: tmadlener Date: Thu, 5 Oct 2023 09:13:12 +0200 Subject: [PATCH 8/8] Create an EventHeader and also compare it --- tests/edm4hep_to_lcio.cpp | 14 +++++++++----- tests/src/EDM4hep2LCIOUtilities.cc | 15 +++++++++++++++ tests/src/EDM4hep2LCIOUtilities.h | 3 +++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/edm4hep_to_lcio.cpp b/tests/edm4hep_to_lcio.cpp index 2ee20461..587c1467 100644 --- a/tests/edm4hep_to_lcio.cpp +++ b/tests/edm4hep_to_lcio.cpp @@ -14,14 +14,16 @@ int main() const auto edmEvent = createExampleEvent(); const auto lcioEvent = EDM4hep2LCIOConv::convEvent(edmEvent); - // if (!compareEventHeader(lcioEvent.get(), &edmEvent)) { - // return 1; - // } + if (!compareEventHeader(lcioEvent.get(), &edmEvent)) { + return 1; + } for (const auto& name : edmEvent.getAvailableCollections()) { const auto edmColl = edmEvent.get(name); const auto typeName = edmColl->getValueTypeName(); - if (typeName == "edm4hep::CaloHitContribution" || typeName == "edm4hep::ParticleID") { + if ( + typeName == "edm4hep::CaloHitContribution" || typeName == "edm4hep::ParticleID" || + typeName == "edm4hep::EventHeader") { continue; } try { @@ -41,7 +43,9 @@ int main() for (const auto& name : edmEvent.getAvailableCollections()) { const auto type = edmEvent.get(name)->getTypeName(); - if (type == "edm4hep::CaloHitContributionCollection" || type == "edm4hep::ParticleIDCollection") { + if ( + type == "edm4hep::CaloHitContributionCollection" || type == "edm4hep::ParticleIDCollection" || + type == "edm4hep::EventHeaderCollection") { continue; } const auto* lcioColl = lcioEvent->getCollection(name); diff --git a/tests/src/EDM4hep2LCIOUtilities.cc b/tests/src/EDM4hep2LCIOUtilities.cc index dce03cb5..d3a3edfb 100644 --- a/tests/src/EDM4hep2LCIOUtilities.cc +++ b/tests/src/EDM4hep2LCIOUtilities.cc @@ -5,6 +5,7 @@ #include "edm4hep/RawCalorimeterHitCollection.h" #include "edm4hep/SimCalorimeterHitCollection.h" #include "edm4hep/TrackerHitCollection.h" +#include #include #include "edm4hep/TrackCollection.h" #include "edm4hep/SimCalorimeterHitCollection.h" @@ -252,10 +253,24 @@ std::pair& link_mcparticles_idcs); +edm4hep::EventHeaderCollection createEventHeader(); + /** * Create an example event that can be used to test the converter. *