From 0955220ffc3e2c6046f3a1b73cbc0cd0697f33e2 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 7 Jul 2022 14:45:55 +0200 Subject: [PATCH] feat: EDM4hep IO (#1260) **on track** - add EDM4hep as dependency - use recent `acts/machines` with EDM4hep - use recent `acts/ci-dependencies` with EDM4hep - make geometry service available in `DD4hepDetector` (see `Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp`) - add EDM4hep simhit reader/writer - add EDM4hep particle reader/writer - add EDM4hep measurement reader/writer - add EDM4hep multi trajectory writer **off track** - consistent namespace for `DD4hepDetector` - fix potential memory leak in `Examples/Io/Root/src/RootMaterialTrackReader.cpp` - minor reformatting **known issues** - particle - ID is not persistent - process is not persistent - sim hit - after4 momentum is not persistent - hit index is not persistent - digitization channel is not persistent - measurement - hit index is not persistent - 1D coords are not persistent - segment path is not persistent - trajectory - trajectory state is incomplete and not persistent - curvature param is incorrect - track state local coordinates are written to (D0,Z0) - covariance is incorrect - no preserved relation to the particles --- .github/workflows/analysis.yml | 4 +- .github/workflows/builds.yml | 32 +- .github/workflows/checks.yml | 2 +- .gitlab-ci.yml | 8 +- CMakeLists.txt | 10 +- .../DD4hepDetector/DD4hepDetector.hpp | 22 +- .../DD4hepDetector/src/DD4hepDetector.cpp | 10 +- Examples/Io/CMakeLists.txt | 1 + Examples/Io/Csv/CMakeLists.txt | 2 +- ...ointWriter.cpp => CsvSpacePointWriter.cpp} | 0 Examples/Io/EDM4hep/CMakeLists.txt | 27 ++ .../Io/EDM4hep/EDM4hepMeasurementReader.hpp | 77 +++++ .../Io/EDM4hep/EDM4hepMeasurementWriter.hpp | 77 +++++ .../EDM4hep/EDM4hepMultiTrajectoryWriter.hpp | 71 ++++ .../Io/EDM4hep/EDM4hepParticleReader.hpp | 67 ++++ .../Io/EDM4hep/EDM4hepParticleWriter.hpp | 66 ++++ .../Io/EDM4hep/EDM4hepSimHitReader.hpp | 76 +++++ .../Io/EDM4hep/EDM4hepSimHitWriter.hpp | 74 +++++ .../ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp | 126 +++++++ .../EDM4hep/src/EDM4hepMeasurementReader.cpp | 89 +++++ .../EDM4hep/src/EDM4hepMeasurementWriter.cpp | 81 +++++ .../src/EDM4hepMultiTrajectoryWriter.cpp | 59 ++++ .../Io/EDM4hep/src/EDM4hepParticleReader.cpp | 71 ++++ .../Io/EDM4hep/src/EDM4hepParticleWriter.cpp | 57 ++++ .../Io/EDM4hep/src/EDM4hepSimHitReader.cpp | 116 +++++++ .../Io/EDM4hep/src/EDM4hepSimHitWriter.cpp | 88 +++++ Examples/Io/EDM4hep/src/EDM4hepUtil.cpp | 311 ++++++++++++++++++ .../Io/Root/src/RootMaterialTrackReader.cpp | 5 + Examples/Python/CMakeLists.txt | 8 + .../Python/python/acts/examples/edm4hep.py | 6 + Examples/Python/src/DD4hepComponent.cpp | 22 +- Examples/Python/src/EDM4hep.cpp | 157 +++++++++ Examples/Python/src/EDM4hepStub.cpp | 13 + Examples/Python/src/ModuleEntry.cpp | 2 + Examples/Python/tests/helpers/__init__.py | 9 +- Examples/Python/tests/test_detectors.py | 11 +- Examples/Python/tests/test_examples.py | 8 +- Examples/Python/tests/test_reader.py | 167 +++++++++- Examples/Python/tests/test_writer.py | 126 ++++++- .../DD4hepDigitizationConfigExample.cpp | 4 +- .../DD4hep/DD4hepDigitizationExample.cpp | 3 +- .../Run/Fatras/DD4hep/DD4hepFatrasExample.cpp | 3 +- .../Geometry/DD4hep/DD4hepGeometryExample.cpp | 2 +- .../DD4hep/DD4hepMaterialMapping.cpp | 2 +- .../DD4hep/DD4hepMaterialValidation.cpp | 2 +- .../DD4hep/DD4hepPropagationExample.cpp | 2 +- .../DD4hep/DD4hepRecCKFTracks.cpp | 3 +- .../DD4hep/DD4hepRecTruthTracks.cpp | 3 +- .../DD4hep/DD4hepSeedingExample.cpp | 3 +- pytest.ini | 3 +- 50 files changed, 2111 insertions(+), 77 deletions(-) rename Examples/Io/Csv/src/{CsvSpacepointWriter.cpp => CsvSpacePointWriter.cpp} (100%) create mode 100644 Examples/Io/EDM4hep/CMakeLists.txt create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp create mode 100644 Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepMultiTrajectoryWriter.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepParticleReader.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepParticleWriter.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepSimHitReader.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepSimHitWriter.cpp create mode 100644 Examples/Io/EDM4hep/src/EDM4hepUtil.cpp create mode 100644 Examples/Python/python/acts/examples/edm4hep.py create mode 100644 Examples/Python/src/EDM4hep.cpp create mode 100644 Examples/Python/src/EDM4hepStub.cpp diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index e03a1bd6315..183db2d2f4e 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -21,7 +21,7 @@ env: jobs: build_debug: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 steps: - uses: actions/checkout@v3 @@ -63,7 +63,7 @@ jobs: build_performance: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 19db82df949..d37d2321b6d 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -17,7 +17,7 @@ env: jobs: lcg: runs-on: ubuntu-latest - container: ghcr.io/acts-project/${{ matrix.image }}:v22 + container: ghcr.io/acts-project/${{ matrix.image }}:v26 strategy: matrix: image: @@ -91,10 +91,9 @@ jobs: - name: Downstream run run: ${SETUP} && ./build-downstream/bin/ShowActsVersion - linux_ubuntu: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 env: INSTALL_DIR: ${{ github.workspace }}/install ACTS_LOG_FAILURE_THRESHOLD: WARNING @@ -115,7 +114,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache-linux_ubuntu_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure # setting CMAKE_CXX_STANDARD=17 is a workaround for a bug in the # dd4hep CMake configuration that gets triggered on recent CMake @@ -132,6 +130,7 @@ jobs: -DACTS_BUILD_EVERYTHING=ON -DACTS_BUILD_ODD=ON -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON + -DACTS_BUILD_EXAMPLES_EDM4HEP=ON -DACTS_FORCE_ASSERTIONS=ON -DACTS_BUILD_ANALYSIS_APPS=ON @@ -172,9 +171,8 @@ jobs: linux_examples_test: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 needs: [linux_ubuntu] - steps: - name: Install git lfs run: apt-get install -y git-lfs @@ -206,11 +204,11 @@ jobs: && source build/python/setup.sh && export LD_LIBRARY_PATH=$PWD/build/thirdparty/OpenDataDetector/factory:$LD_LIBRARY_PATH && pip3 install -r Examples/Python/tests/requirements.txt - && pytest -rFs + && pytest -rFsv linux_physmon: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 needs: [linux_ubuntu] steps: @@ -285,7 +283,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache-linux-nodeps_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure run: > cmake -B build -S . @@ -373,7 +370,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure # setting CMAKE_CXX_STANDARD=17 is a workaround for a bug in the # dd4hep CMake configuration that gets triggered on recent CMake @@ -391,6 +387,7 @@ jobs: -DACTS_BUILD_ODD=ON -DACTS_LOG_FAILURE_THRESHOLD=WARNING -DACTS_FORCE_ASSERTIONS=ON + -DACTS_BUILD_EXAMPLES_EDM4HEP=ON - name: Build run: cmake --build build -- - name: Unit tests @@ -415,9 +412,10 @@ jobs: run: cmake --build build-downstream -- - name: Downstream run run: ./build-downstream/bin/ShowActsVersion + cuda: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu1804_cuda:v22 + container: ghcr.io/acts-project/ubuntu1804_cuda:v26 steps: - uses: actions/checkout@v2 @@ -429,7 +427,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache-cuda_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure run: > cmake -B build -S . @@ -442,9 +439,10 @@ jobs: -DACTS_BUILD_UNITTESTS=ON - name: Build run: cmake --build build -- + exatrkx: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004_exatrkx:v22 + container: ghcr.io/acts-project/ubuntu2004_exatrkx:v26 steps: - uses: actions/checkout@v2 @@ -456,7 +454,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache-exatrkx_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure run: > cmake -B build -S . @@ -469,9 +466,10 @@ jobs: -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON - name: Build run: cmake --build build -- + sycl: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004_oneapi:v22 + container: ghcr.io/acts-project/ubuntu2004_oneapi:v26 defaults: run: shell: bash @@ -486,7 +484,6 @@ jobs: restore-keys: | ${{ runner.os }}-ccache-sycl_${{ env.CCACHE_KEY_SUFFIX }}_ - - name: Configure run: > source /opt/intel/oneapi/setvars.sh @@ -503,9 +500,10 @@ jobs: run: > source /opt/intel/oneapi/setvars.sh && cmake --build build -- + docs: runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu2004:v22 + container: ghcr.io/acts-project/ubuntu2004:v26 env: DOXYGEN_WARN_AS_ERROR: YES steps: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7b3788f2c40..eadf64ab50c 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -10,7 +10,7 @@ on: jobs: format: runs-on: ubuntu-latest - container: ghcr.io/acts-project/format10:v22 + container: ghcr.io/acts-project/format10:v26 steps: - uses: actions/checkout@v3 - name: Check diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1534307cb49..0306db2b383 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ clang_tidy: stage: build - image: ghcr.io/acts-project/ubuntu2004:v21 + image: ghcr.io/acts-project/ubuntu2004:v26 artifacts: paths: - src/clang-tidy/ @@ -37,7 +37,7 @@ clang_tidy: build: stage: build - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v22 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v26 tags: - docker variables: @@ -82,12 +82,11 @@ build: -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON - cmake --build build -- - test: stage: test dependencies: - build - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v23 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v26 tags: - docker-gpu-nvidia script: @@ -98,4 +97,3 @@ test: - source build/python/setup.sh - nvidia-smi - python3 src/Examples/Scripts/Python/exatrkx.py - diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7eb7f2f23..ad2704b4801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ option(ACTS_BUILD_ALIGNMENT "Build Alignment package" OFF) # examples related options option(ACTS_BUILD_EXAMPLES "Build standalone examples" OFF) option(ACTS_BUILD_EXAMPLES_DD4HEP "Build DD4hep-based code in the examples" OFF) +option(ACTS_BUILD_EXAMPLES_EDM4HEP "Build EDM4hep-based code in the examples" OFF) option(ACTS_BUILD_EXAMPLES_EXATRKX "Build the Exa.TrkX example code" OFF) option(ACTS_BUILD_EXAMPLES_GEANT4 "Build Geant4-based code in the examples" OFF) option(ACTS_BUILD_EXAMPLES_HEPMC3 "Build HepMC3-based code in the examples" OFF) @@ -94,6 +95,7 @@ set_option_if(ACTS_BUILD_ALIGNMENT ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_EXAMPLES ACTS_BUILD_EXAMPLES_DD4HEP + OR ACTS_BUILD_EXAMPLES_EDM4HEP OR ACTS_BUILD_EXAMPLES_GEANT4 OR ACTS_BUILD_EXAMPLES_HEPMC3 OR ACTS_BUILD_EXAMPLES_PYTHIA8 @@ -102,7 +104,8 @@ set_option_if( # core plugins might be required by examples or depend on each other set_option_if( ACTS_BUILD_PLUGIN_DD4HEP - ACTS_BUILD_EXAMPLES_DD4HEP OR ACTS_BUILD_EVERYTHING) + ACTS_BUILD_EXAMPLES_DD4HEP OR ACTS_BUILD_EVERYTHING OR + ACTS_BUILD_EXAMPLES_EDM4HEP) set_option_if( ACTS_BUILD_PLUGIN_TGEO ACTS_BUILD_PLUGIN_DD4HEP OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) @@ -156,6 +159,7 @@ set(_acts_autodiff_version 0.6) set(_acts_boost_version 1.71.0) set(_acts_dd4hep_version 1.11) set(_acts_actsdd4hep_version 1.0.0) +set(_acts_edm4hep_version v00-04-01) set(_acts_doxygen_version 1.8.15) set(_acts_eigen3_version 3.3.7) set(_acts_hepmc3_version 3.2.1) @@ -249,7 +253,6 @@ if(ACTS_BUILD_PLUGIN_DD4HEP) else() add_subdirectory(thirdparty/actsdd4hep) endif() - endif() if(ACTS_BUILD_PLUGIN_JSON) if(ACTS_USE_SYSTEM_NLOHMANN_JSON) @@ -300,6 +303,9 @@ if(ACTS_BUILD_EXAMPLES_DD4HEP AND ACTS_BUILD_EXAMPLES_GEANT4) elseif(ACTS_BUILD_EXAMPLES_DD4HEP) find_package(DD4hep ${_acts_dd4hep_version} REQUIRED CONFIG COMPONENTS DDCore DDDetectors) endif() +if(ACTS_BUILD_EXAMPLES_EDM4HEP) + find_package(EDM4HEP REQUIRED CONFIG) +endif() if(ACTS_BUILD_EXAMPLES_GEANT4) find_package(Geant4 REQUIRED CONFIG COMPONENTS gdml) endif() diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp index 7e524fdc39e..aa00697c2f9 100644 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp @@ -15,15 +15,23 @@ #include #include -struct DD4hepDetector : public ActsExamples::IBaseDetector { +namespace ActsExamples { +namespace DD4hep { + +struct DD4hepDetector : public IBaseDetector { + std::shared_ptr geometryService; + void addOptions( boost::program_options::options_description& opt) const override; - std::pair - finalize(const boost::program_options::variables_map& vm, - std::shared_ptr mdecorator) override; + std::pair finalize( + const boost::program_options::variables_map& vm, + std::shared_ptr mdecorator) override; - std::pair - finalize(ActsExamples::DD4hep::DD4hepGeometryService::Config cfg, - std::shared_ptr mdecorator); + std::pair finalize( + DD4hepGeometryService::Config cfg, + std::shared_ptr mdecorator); }; + +} // namespace DD4hep +} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp index 6e1c8a8726d..7fcd037783c 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp @@ -16,6 +16,9 @@ #include +namespace ActsExamples { +namespace DD4hep { + void DD4hepDetector::addOptions( boost::program_options::options_description& opt) const { ActsExamples::Options::addDD4hepOptions(opt); @@ -37,10 +40,10 @@ auto DD4hepDetector::finalize( -> std::pair { Acts::GeometryContext dd4HepContext; config.matDecorator = mdecorator; - auto geometrySvc = + geometryService = std::make_shared(config); TrackingGeometryPtr dd4tGeometry = - geometrySvc->trackingGeometry(dd4HepContext); + geometryService->trackingGeometry(dd4HepContext); if (!dd4tGeometry) { throw std::runtime_error{ "Did not receive tracking geometry from DD4hep geometry service"}; @@ -50,3 +53,6 @@ auto DD4hepDetector::finalize( return std::make_pair( std::move(dd4tGeometry), std::move(dd4ContextDeocrators)); } + +} // namespace DD4hep +} // namespace ActsExamples diff --git a/Examples/Io/CMakeLists.txt b/Examples/Io/CMakeLists.txt index a8943dea79f..6cdd23bfeac 100644 --- a/Examples/Io/CMakeLists.txt +++ b/Examples/Io/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(Csv) +add_subdirectory_if(EDM4hep ACTS_BUILD_EXAMPLES_EDM4HEP) add_subdirectory_if(HepMC3 ACTS_BUILD_EXAMPLES_HEPMC3) add_subdirectory(Json) add_subdirectory(NuclearInteractions) diff --git a/Examples/Io/Csv/CMakeLists.txt b/Examples/Io/Csv/CMakeLists.txt index a6e891fcd79..64b409769d5 100644 --- a/Examples/Io/Csv/CMakeLists.txt +++ b/Examples/Io/Csv/CMakeLists.txt @@ -13,7 +13,7 @@ add_library( src/CsvSpacePointReader.cpp src/CsvTrackingGeometryWriter.cpp src/CsvMultiTrajectoryWriter.cpp - src/CsvSpacepointWriter.cpp) + src/CsvSpacePointWriter.cpp) target_include_directories( ActsExamplesIoCsv PUBLIC $) diff --git a/Examples/Io/Csv/src/CsvSpacepointWriter.cpp b/Examples/Io/Csv/src/CsvSpacePointWriter.cpp similarity index 100% rename from Examples/Io/Csv/src/CsvSpacepointWriter.cpp rename to Examples/Io/Csv/src/CsvSpacePointWriter.cpp diff --git a/Examples/Io/EDM4hep/CMakeLists.txt b/Examples/Io/EDM4hep/CMakeLists.txt new file mode 100644 index 00000000000..050d7dbf97a --- /dev/null +++ b/Examples/Io/EDM4hep/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library( + ActsExamplesIoEDM4hep SHARED + src/EDM4hepMeasurementReader.cpp + src/EDM4hepMeasurementWriter.cpp + src/EDM4hepMultiTrajectoryWriter.cpp + src/EDM4hepParticleReader.cpp + src/EDM4hepParticleWriter.cpp + src/EDM4hepSimHitReader.cpp + src/EDM4hepSimHitWriter.cpp + src/EDM4hepUtil.cpp) +target_include_directories( + ActsExamplesIoEDM4hep + PUBLIC $) +target_link_libraries( + ActsExamplesIoEDM4hep + PUBLIC + EDM4HEP::edm4hep + podio::podioRootIO + ActsCore + ActsFatras + ActsExamplesFramework + ActsExamplesDigitization + ActsExamplesDetectorDD4hep) + +install( + TARGETS ActsExamplesIoEDM4hep + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp new file mode 100644 index 00000000000..fbb74bee951 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp @@ -0,0 +1,77 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/Framework/IReader.hpp" + +#include +#include + +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackerHitPlaneCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTReader.h" + +namespace ActsExamples { + +/// Read in a measurement cluster collection from EDM4hep. +/// +/// Inpersistent information: +/// - hit index +/// - 1D local coords? +/// - segment path +/// +/// Known issues: +/// - cluster channels are read from inappropriate fields +/// - local 2D coordinates and time are read from position +class EDM4hepMeasurementReader final : public IReader { + public: + struct Config { + /// Where to read the input file from. + std::string inputPath; + /// Output measurement collection. + std::string outputMeasurements; + /// Output measurement to sim hit collection. + std::string outputMeasurementSimHitsMap; + /// Output source links collection. + std::string outputSourceLinks; + /// Output cluster collection (optional). + std::string outputClusters; + }; + + /// Construct the cluster reader. + /// + /// @param config is the configuration object + /// @param level is the logging level + EDM4hepMeasurementReader(const Config& config, Acts::Logging::Level level); + + std::string name() const final; + + /// Return the available events range. + std::pair availableEvents() const final; + + /// Read out data from the input stream. + ProcessCode read(const ActsExamples::AlgorithmContext& ctx) final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + std::pair m_eventsRange; + std::unique_ptr m_logger; + + podio::ROOTReader m_reader; + podio::EventStore m_store; + + const edm4hep::TrackerHitPlaneCollection* m_trackerHitPlaneCollection; + const edm4hep::TrackerHitCollection* m_trackerHitRawCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp new file mode 100644 index 00000000000..beae9bd45b5 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp @@ -0,0 +1,77 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/Framework/WriterT.hpp" + +#include + +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackerHitPlaneCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTWriter.h" + +namespace ActsExamples { + +/// Write out a measurement cluster collection to EDM4hep. +/// +/// Inpersistent information: +/// - hit index +/// - 1D local coords? +/// - segment path +/// +/// Known issues: +/// - cluster channels are written to inappropriate fields +/// - local 2D coordinates and time are written to position +class EDM4hepMeasurementWriter final : public WriterT { + public: + struct Config { + /// Which measurement collection to write. + std::string inputMeasurements; + /// Which cluster collection to write (optional) + std::string inputClusters; + /// Which simulated (truth) hits collection to use. + std::string inputSimHits; + /// Input collection to map measured hits to simulated hits. + std::string inputMeasurementSimHitsMap; + /// Where to the write the file to. + std::string outputPath; + }; + + /// Constructor with + /// @param config configuration struct + /// @param level logging level + EDM4hepMeasurementWriter(const Config& config, Acts::Logging::Level level); + + ProcessCode endRun() final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + protected: + /// This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + /// + /// @param ctx The Algorithm context with per event information + /// @param measurements is the data to be written out + ProcessCode writeT(const AlgorithmContext& ctx, + const MeasurementContainer& measurements) final; + + private: + Config m_cfg; + + podio::ROOTWriter m_writer; + podio::EventStore m_store; + + edm4hep::TrackerHitPlaneCollection* m_trackerHitPlaneCollection; + edm4hep::TrackerHitCollection* m_trackerHitRawCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp new file mode 100644 index 00000000000..4a5ea1f33f3 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp @@ -0,0 +1,71 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/EventData/Trajectories.hpp" +#include "ActsExamples/Framework/WriterT.hpp" + +#include + +#include "edm4hep/TrackCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTWriter.h" + +namespace ActsExamples { + +/// Write out the tracks reconstructed using Combinatorial Kalman Filter to +/// EDM4hep. +/// +/// Inpersistent information: +/// - trajectory state incomplete +/// - relation to the particles +/// +/// Known issues: +/// - curvature parameter +/// - track state local coordinates are written to (D0,Z0) +/// - covariance incorrect +class EDM4hepMultiTrajectoryWriter : public WriterT { + public: + struct Config { + /// Input trajectory collection + std::string inputTrajectories; + /// Input hit-particles map collection + std::string inputMeasurementParticlesMap; + /// Where to place output file + std::string outputPath; + }; + + /// constructor + /// @param config is the configuration object + /// @param level is the output logging level + EDM4hepMultiTrajectoryWriter( + const Config& config, Acts::Logging::Level level = Acts::Logging::INFO); + + ProcessCode endRun() final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + protected: + /// @brief Write method called by the base class + /// @param [in] context is the algorithm context for consistency + /// @param [in] tracks is the track collection + ProcessCode writeT(const AlgorithmContext& context, + const TrajectoriesContainer& trajectories) final; + + private: + Config m_cfg; + + podio::ROOTWriter m_writer; + podio::EventStore m_store; + + edm4hep::TrackCollection* m_trackCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp new file mode 100644 index 00000000000..f0b34fef227 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp @@ -0,0 +1,67 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/Framework/IReader.hpp" + +#include +#include + +#include "edm4hep/MCParticleCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTReader.h" + +namespace ActsExamples { + +/// Read particles from EDM4hep. +/// +/// Inpersistent information: +/// - particle ID +/// - process +class EDM4hepParticleReader final : public IReader { + public: + struct Config { + /// Where to read input file from. + std::string inputPath; + /// Name of the particle collection in EDM4hep. + std::string inputParticles = "MCParticles"; + /// Which particle collection to read into. + std::string outputParticles; + }; + + /// Construct the particle reader. + /// + /// @param config is the configuration object + /// @param level is the logging level + EDM4hepParticleReader(const Config& config, Acts::Logging::Level level); + + std::string name() const final; + + /// Return the available events range. + std::pair availableEvents() const final; + + /// Read out data from the input stream. + ProcessCode read(const ActsExamples::AlgorithmContext& ctx) final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + std::pair m_eventsRange; + std::unique_ptr m_logger; + + podio::ROOTReader m_reader; + podio::EventStore m_store; + + const edm4hep::MCParticleCollection* m_mcParticleCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp new file mode 100644 index 00000000000..7141dc14f39 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp @@ -0,0 +1,66 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/EventData/SimParticle.hpp" +#include "ActsExamples/Framework/WriterT.hpp" + +#include + +#include "edm4hep/MCParticleCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTWriter.h" + +namespace ActsExamples { + +/// Write particles to EDM4hep +/// +/// Inpersistent information: +/// - particle ID +/// - process +class EDM4hepParticleWriter final : public WriterT { + public: + struct Config { + /// Input particles collection to write. + std::string inputParticles; + /// Where to place the output file. + std::string outputPath; + /// Name of the particle collection in EDM4hep. + std::string outputParticles = "MCParticles"; + }; + + /// Construct the particle writer. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + EDM4hepParticleWriter(const Config& cfg, Acts::Logging::Level lvl); + + ProcessCode endRun() final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] particles are the particle to be written + ProcessCode writeT(const ActsExamples::AlgorithmContext& ctx, + const SimParticleContainer& particles) final; + + private: + Config m_cfg; + + podio::ROOTWriter m_writer; + podio::EventStore m_store; + + edm4hep::MCParticleCollection* m_mcParticleCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp new file mode 100644 index 00000000000..f563f10f075 --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp @@ -0,0 +1,76 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" +#include "ActsExamples/Framework/IReader.hpp" + +#include +#include + +#include "edm4hep/MCParticleCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTReader.h" + +namespace ActsExamples { + +/// Read in a simhit collection from EDM4hep. +/// +/// Inpersistent information: +/// - particle ID +/// - after4 momentum +/// - hit index +/// - digitization channel +class EDM4hepSimHitReader final : public IReader { + public: + struct Config { + /// Where to the read input file from. + std::string inputPath; + /// Name of the particle collection in EDM4hep. + std::string inputParticles = "MCParticles"; + /// Which particle collection to read into. + std::string outputParticles; + /// Output simulated (truth) hits collection. + std::string outputSimHits; + /// DD4hep geometry service for cellID resolution. + std::shared_ptr dd4hepGeometryService; + }; + + /// Construct the simhit reader. + /// + /// @param config is the configuration object + /// @param level is the logging level + EDM4hepSimHitReader(const Config& config, Acts::Logging::Level level); + + std::string name() const final; + + /// Return the available events range. + std::pair availableEvents() const final; + + /// Read out data from the input stream. + ProcessCode read(const ActsExamples::AlgorithmContext& ctx) final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + std::pair m_eventsRange; + std::unique_ptr m_logger; + + podio::ROOTReader m_reader; + podio::EventStore m_store; + + std::vector m_collections; + + const edm4hep::MCParticleCollection* m_mcParticleCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp new file mode 100644 index 00000000000..5755bf9395b --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp @@ -0,0 +1,74 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/Framework/WriterT.hpp" + +#include + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "podio/EventStore.h" +#include "podio/ROOTWriter.h" + +namespace ActsExamples { + +/// Write out a simhit collection to EDM4hep. +/// +/// Inpersistent information: +/// - particle ID +/// - after4 momentum +/// - hit index +/// - digitization channel +class EDM4hepSimHitWriter final : public WriterT { + public: + struct Config { + /// Which simulated (truth) hits collection to use. + std::string inputSimHits; + /// Which simulated (truth) particle collection to use. + std::string inputParticles; + /// WWhere to write the output file to. + std::string outputPath; + /// Name of the particle collection in EDM4hep. + std::string outputParticles = "MCParticles"; + /// Name of the particle collection in EDM4hep. + std::string outputSimTrackerHits = "ActsSimTrackerHits"; + }; + + /// Construct the cluster writer. + /// + /// @param config is the configuration object + /// @param level is the logging level + EDM4hepSimHitWriter(const Config& config, Acts::Logging::Level level); + + ProcessCode endRun() final; + + /// Readonly access to the config + const Config& config() const { return m_cfg; } + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] simHits are the simhits to be written + ProcessCode writeT(const AlgorithmContext& ctx, + const SimHitContainer& simHits) final; + + private: + Config m_cfg; + + podio::ROOTWriter m_writer; + podio::EventStore m_store; + + edm4hep::MCParticleCollection* m_mcParticleCollection; + edm4hep::SimTrackerHitCollection* m_simTrackerHitCollection; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp new file mode 100644 index 00000000000..94915a1404e --- /dev/null +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp @@ -0,0 +1,126 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#pragma once + +#include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/EventData/Trajectories.hpp" +#include "ActsFatras/EventData/Hit.hpp" +#include "ActsFatras/EventData/Particle.hpp" + +#include + +#include "edm4hep/MCParticle.h" +#include "edm4hep/MutableMCParticle.h" +#include "edm4hep/MutableSimTrackerHit.h" +#include "edm4hep/MutableTrack.h" +#include "edm4hep/MutableTrackerHit.h" +#include "edm4hep/MutableTrackerHitPlane.h" +#include "edm4hep/SimTrackerHit.h" +#include "edm4hep/TrackerHit.h" +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackerHitPlane.h" + +namespace ActsExamples { +namespace EDM4hepUtil { + +static constexpr std::int32_t EDM4HEP_ACTS_POSITION_TYPE = 42; + +using MapParticleIdFrom = + std::function; +using MapParticleIdTo = + std::function; + +using MapGeometryIdFrom = + std::function; +using MapGeometryIdTo = + std::function; + +/// Reads a Fatras particle from EDM4hep. +/// +/// Inpersistent information: +/// - particle ID +/// - process +ActsFatras::Particle readParticle(edm4hep::MCParticle from, + MapParticleIdFrom particleMapper); + +/// Write a Fatras particle into EDM4hep. +/// +/// Inpersistent information: +/// - particle ID +/// - process +void writeParticle(const ActsFatras::Particle& from, + edm4hep::MutableMCParticle to); + +/// Reads a Fatras hit from EDM4hep. +/// +/// Inpersistent information: +/// - after4 momentum +/// - hit index +/// - digitization channel +ActsFatras::Hit readSimHit(const edm4hep::SimTrackerHit& from, + MapParticleIdFrom particleMapper, + MapGeometryIdFrom geometryMapper); + +/// Writes a Fatras hit to EDM4hep. +/// +/// Inpersistent information: +/// - after4 momentum +/// - hit index +/// - digitization channel +void writeSimHit(const ActsFatras::Hit& from, edm4hep::MutableSimTrackerHit to, + MapParticleIdTo particleMapper, + MapGeometryIdTo geometryMapper); + +/// Reads a measurement cluster from EDM4hep. +/// +/// Inpersistent information: +/// - hit index +/// - 1D local coords? +/// - segment path +/// +/// Known issues: +/// - cluster channels are read from inappropriate fields +/// - local 2D coordinates and time are read from position +Measurement readMeasurement(edm4hep::TrackerHitPlane from, + const edm4hep::TrackerHitCollection* fromClusters, + Cluster* toCluster, + MapGeometryIdFrom geometryMapper); + +/// Writes a measurement cluster to EDM4hep. +/// +/// Inpersistent information: +/// - hit index +/// - 1D local coords? +/// - segment path +/// +/// Known issues: +/// - cluster channels are written to inappropriate fields +/// - local 2D coordinates and time are written to position +void writeMeasurement(const Measurement& from, + edm4hep::MutableTrackerHitPlane to, + const Cluster* fromCluster, + edm4hep::TrackerHitCollection& toClusters, + MapGeometryIdTo geometryMapper); + +/// Writes a trajectory to EDM4hep. +/// +/// Inpersistent information: +/// - trajectory state incomplete +/// - relation to the particles +/// +/// Known issues: +/// - curvature parameter +/// - track state local coordinates are written to (D0,Z0) +/// - covariance incorrect +void writeTrajectory(const Trajectories& from, edm4hep::MutableTrack to, + std::size_t fromIndex, + const IndexMultimap& hitParticlesMap); + +} // namespace EDM4hepUtil +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp new file mode 100644 index 00000000000..946a1c87672 --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp @@ -0,0 +1,89 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" + +#include +#include + +#include "edm4hep/TrackerHit.h" +#include "edm4hep/TrackerHitPlane.h" + +namespace ActsExamples { + +EDM4hepMeasurementReader::EDM4hepMeasurementReader( + const EDM4hepMeasurementReader::Config& config, Acts::Logging::Level level) + : m_cfg(config), + m_logger(Acts::getDefaultLogger("EDM4hepMeasurementReader", level)) { + if (m_cfg.outputMeasurements.empty()) { + throw std::invalid_argument("Missing measurement output collection"); + } + + m_reader.openFile(m_cfg.inputPath); + m_store.setReader(&m_reader); + + m_eventsRange = std::make_pair(0, m_reader.getEntries()); + + m_trackerHitPlaneCollection = + &m_store.get("ActsTrackerHitsPlane"); + m_trackerHitRawCollection = + &m_store.create("ActsTrackerHitsRaw"); +} + +std::string EDM4hepMeasurementReader::EDM4hepMeasurementReader::name() const { + return "EDM4hepMeasurementReader"; +} + +std::pair EDM4hepMeasurementReader::availableEvents() const { + return m_eventsRange; +} + +ProcessCode EDM4hepMeasurementReader::read(const AlgorithmContext& ctx) { + MeasurementContainer measurements; + ClusterContainer clusters; + // TODO what about those? + IndexMultimap measurementSimHitsMap; + IndexSourceLinkContainer sourceLinks; + std::list sourceLinkStorage; + + m_store.clear(); + m_reader.goToEvent(ctx.eventNumber); + + for (const auto& trackerHitPlane : *m_trackerHitPlaneCollection) { + Cluster cluster; + auto measurement = EDM4hepUtil::readMeasurement( + trackerHitPlane, m_trackerHitRawCollection, &cluster, + [](std::uint64_t cellId) { return Acts::GeometryIdentifier(cellId); }); + + measurements.push_back(std::move(measurement)); + clusters.push_back(std::move(cluster)); + } + + // Write the data to the EventStore + ctx.eventStore.add(m_cfg.outputMeasurements, std::move(measurements)); + ctx.eventStore.add(m_cfg.outputMeasurementSimHitsMap, + std::move(measurementSimHitsMap)); + ctx.eventStore.add(m_cfg.outputSourceLinks, std::move(sourceLinks)); + ctx.eventStore.add(m_cfg.outputSourceLinks + "__storage", + std::move(sourceLinkStorage)); + if (not m_cfg.outputClusters.empty()) { + ctx.eventStore.add(m_cfg.outputClusters, std::move(clusters)); + } + + m_reader.endOfEvent(); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp b/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp new file mode 100644 index 00000000000..4d9f7a174ed --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp @@ -0,0 +1,81 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/Measurement.hpp" +#include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" + +#include + +namespace ActsExamples { + +EDM4hepMeasurementWriter::EDM4hepMeasurementWriter( + const EDM4hepMeasurementWriter::Config& config, Acts::Logging::Level level) + : WriterT(config.inputMeasurements, "EDM4hepMeasurementWriter", level), + m_cfg(config), + m_writer(config.outputPath, &m_store) { + ACTS_VERBOSE("Created output file " << config.outputPath); + + // Input container for measurements is already checked by base constructor + if (m_cfg.inputSimHits.empty()) { + throw std::invalid_argument("Missing simulated hits input collection"); + } + if (m_cfg.inputMeasurementSimHitsMap.empty()) { + throw std::invalid_argument( + "Missing hit-to-simulated-hits map input collection"); + } + + m_trackerHitPlaneCollection = + &m_store.create( + "ActsTrackerHitsPlane"); + m_writer.registerForWrite("ActsTrackerHitsPlane"); + + m_trackerHitRawCollection = + &m_store.create("ActsTrackerHitsRaw"); + m_writer.registerForWrite("ActsTrackerHitsRaw"); +} + +ActsExamples::ProcessCode EDM4hepMeasurementWriter::endRun() { + m_writer.finish(); + + return ProcessCode::SUCCESS; +} + +ActsExamples::ProcessCode EDM4hepMeasurementWriter::writeT( + const AlgorithmContext& ctx, const MeasurementContainer& measurements) { + ClusterContainer clusters; + + if (!m_cfg.inputClusters.empty()) { + ACTS_VERBOSE("Fetch clusters for writing: " << m_cfg.inputClusters); + clusters = ctx.eventStore.get(m_cfg.inputClusters); + } + + ACTS_VERBOSE("Writing " << measurements.size() + << " measurements in this event."); + + for (Index hitIdx = 0u; hitIdx < measurements.size(); ++hitIdx) { + const auto& from = measurements[hitIdx]; + const Cluster* fromCluster = clusters.empty() ? nullptr : &clusters[hitIdx]; + + auto to = m_trackerHitPlaneCollection->create(); + EDM4hepUtil::writeMeasurement( + from, to, fromCluster, *m_trackerHitRawCollection, + [](Acts::GeometryIdentifier id) { return id.value(); }); + } + + m_writer.writeEvent(); + m_store.clearCollections(); + + return ActsExamples::ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepMultiTrajectoryWriter.cpp b/Examples/Io/EDM4hep/src/EDM4hepMultiTrajectoryWriter.cpp new file mode 100644 index 00000000000..e94ebad027d --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepMultiTrajectoryWriter.cpp @@ -0,0 +1,59 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp" + +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" +#include "ActsExamples/Utilities/Paths.hpp" +#include "ActsExamples/Validation/TrackClassification.hpp" + +#include + +namespace ActsExamples { + +EDM4hepMultiTrajectoryWriter::EDM4hepMultiTrajectoryWriter( + const EDM4hepMultiTrajectoryWriter::Config& config, + Acts::Logging::Level level) + : WriterT(config.inputTrajectories, + "EDM4hepMultiTrajectoryWriter", level), + m_cfg(config), + m_writer(config.outputPath, &m_store) { + if (m_cfg.inputTrajectories.empty()) { + throw std::invalid_argument("Missing input trajectories collection"); + } + + m_trackCollection = &m_store.create("ActsTracks"); + m_writer.registerForWrite("ActsTracks"); +} + +ActsExamples::ProcessCode EDM4hepMultiTrajectoryWriter::endRun() { + m_writer.finish(); + + return ProcessCode::SUCCESS; +} + +ProcessCode EDM4hepMultiTrajectoryWriter::writeT( + const AlgorithmContext& ctx, const TrajectoriesContainer& trajectories) { + const auto& hitParticlesMap = + ctx.eventStore.get>( + m_cfg.inputMeasurementParticlesMap); + + for (const auto& from : trajectories) { + for (const std::size_t& trackTip : from.tips()) { + auto to = m_trackCollection->create(); + EDM4hepUtil::writeTrajectory(from, to, trackTip, hitParticlesMap); + } + } + + m_writer.writeEvent(); + m_store.clearCollections(); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepParticleReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepParticleReader.cpp new file mode 100644 index 00000000000..fc2b4070d69 --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepParticleReader.cpp @@ -0,0 +1,71 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" +#include "ActsExamples/Utilities/Paths.hpp" + +#include + +namespace ActsExamples { + +EDM4hepParticleReader::EDM4hepParticleReader( + const EDM4hepParticleReader::Config& config, Acts::Logging::Level level) + : m_cfg(config), + m_logger(Acts::getDefaultLogger("EDM4hepParticleReader", level)) { + if (m_cfg.outputParticles.empty()) { + throw std::invalid_argument("Missing output collection"); + } + + m_reader.openFile(m_cfg.inputPath); + m_store.setReader(&m_reader); + + m_eventsRange = std::make_pair(0, m_reader.getEntries()); + + m_mcParticleCollection = + &m_store.get(m_cfg.inputParticles); +} + +std::string EDM4hepParticleReader::name() const { + return "EDM4hepParticleReader"; +} + +std::pair EDM4hepParticleReader::availableEvents() const { + return m_eventsRange; +} + +ProcessCode EDM4hepParticleReader::read(const AlgorithmContext& ctx) { + m_store.clear(); + m_reader.goToEvent(ctx.eventNumber); + + SimParticleContainer::sequence_type unordered; + + for (const auto& mcParticle : *m_mcParticleCollection) { + auto particle = + EDM4hepUtil::readParticle(mcParticle, [](edm4hep::MCParticle p) { + ActsFatras::Barcode result; + // TODO dont use podio internal id + result.setParticle(p.id()); + return result; + }); + unordered.push_back(particle); + } + + // Write ordered particles container to the EventStore + SimParticleContainer particles; + particles.insert(unordered.begin(), unordered.end()); + ctx.eventStore.add(m_cfg.outputParticles, std::move(particles)); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepParticleWriter.cpp b/Examples/Io/EDM4hep/src/EDM4hepParticleWriter.cpp new file mode 100644 index 00000000000..b0fe8014ec8 --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepParticleWriter.cpp @@ -0,0 +1,57 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" +#include "ActsExamples/Utilities/Paths.hpp" + +#include + +#include "edm4hep/MCParticle.h" + +namespace ActsExamples { + +EDM4hepParticleWriter::EDM4hepParticleWriter( + const EDM4hepParticleWriter::Config& config, Acts::Logging::Level lvl) + : WriterT(config.inputParticles, "EDM4hepParticleWriter", lvl), + m_cfg(config), + m_writer(config.outputPath, &m_store) { + ACTS_VERBOSE("Created output file " << config.outputPath); + + if (m_cfg.inputParticles.empty()) { + throw std::invalid_argument("Missing particles input collection"); + } + + m_mcParticleCollection = + &m_store.create(m_cfg.outputParticles); + m_writer.registerForWrite(m_cfg.outputParticles); +} + +ActsExamples::ProcessCode EDM4hepParticleWriter::endRun() { + m_writer.finish(); + + return ProcessCode::SUCCESS; +} + +ProcessCode EDM4hepParticleWriter::writeT( + const AlgorithmContext&, const SimParticleContainer& particles) { + for (const auto& particle : particles) { + auto p = m_mcParticleCollection->create(); + EDM4hepUtil::writeParticle(particle, p); + } + + m_writer.writeEvent(); + m_store.clearCollections(); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepSimHitReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepSimHitReader.cpp new file mode 100644 index 00000000000..a5c78e2e89d --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepSimHitReader.cpp @@ -0,0 +1,116 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" + +#include "edm4hep/SimTrackerHitCollection.h" + +namespace ActsExamples { + +EDM4hepSimHitReader::EDM4hepSimHitReader( + const EDM4hepSimHitReader::Config& config, Acts::Logging::Level level) + : m_cfg(config), + m_logger(Acts::getDefaultLogger("EDM4hepSimHitReader", level)) { + m_reader.openFile(m_cfg.inputPath); + m_store.setReader(&m_reader); + + m_eventsRange = std::make_pair(0, m_reader.getEntries()); + + m_collections = m_reader.getCollectionIDTable()->names(); + + m_mcParticleCollection = + &m_store.get(m_cfg.inputParticles); +} + +std::string EDM4hepSimHitReader::EDM4hepSimHitReader::name() const { + return "EDM4hepSimHitReader"; +} + +std::pair EDM4hepSimHitReader::availableEvents() const { + return m_eventsRange; +} + +ProcessCode EDM4hepSimHitReader::read(const AlgorithmContext& ctx) { + m_store.clear(); + m_reader.goToEvent(ctx.eventNumber); + + if (!m_cfg.outputParticles.empty()) { + SimParticleContainer::sequence_type unordered; + + for (const auto& mcParticle : *m_mcParticleCollection) { + auto particle = + EDM4hepUtil::readParticle(mcParticle, [](edm4hep::MCParticle p) { + ActsFatras::Barcode result; + // TODO dont use podio internal id + result.setParticle(p.id()); + return result; + }); + unordered.push_back(particle); + } + + // Write ordered particles container to the EventStore + SimParticleContainer particles; + particles.insert(unordered.begin(), unordered.end()); + ctx.eventStore.add(m_cfg.outputParticles, std::move(particles)); + } + + SimHitContainer::sequence_type unordered; + + // TODO does it make sense to query all of them? + for (const auto& name : m_collections) { + auto& collection = m_store.get(name); + + if (!collection.isValid()) { + continue; + } + + if (collection.getValueTypeName() == "edm4hep::SimTrackerHit") { + for (const auto& simTrackerHit : + (const edm4hep::SimTrackerHitCollection&)collection) { + try { + auto hit = EDM4hepUtil::readSimHit( + simTrackerHit, + [](edm4hep::MCParticle particle) { + ActsFatras::Barcode result; + // TODO dont use podio internal id + result.setParticle(particle.id()); + return result; + }, + [&](std::uint64_t cellId) { + auto detElement = m_cfg.dd4hepGeometryService->lcdd() + ->volumeManager() + .lookupDetElement(cellId); + Acts::GeometryIdentifier result = detElement.volumeID(); + return result; + }); + unordered.push_back(std::move(hit)); + } catch (...) { + m_logger->log(Acts::Logging::Level::ERROR, + "EDM4hepSimHitReader: failed to convert SimTrackerHit"); + continue; + } + } + } + } + + m_reader.endOfEvent(); + + SimHitContainer simHits; + simHits.insert(unordered.begin(), unordered.end()); + ctx.eventStore.add(m_cfg.outputSimHits, std::move(simHits)); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepSimHitWriter.cpp b/Examples/Io/EDM4hep/src/EDM4hepSimHitWriter.cpp new file mode 100644 index 00000000000..6dc2391a9c2 --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepSimHitWriter.cpp @@ -0,0 +1,88 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" + +#include + +#include "edm4hep/MCParticle.h" +#include "edm4hep/SimTrackerHit.h" + +namespace ActsExamples { + +EDM4hepSimHitWriter::EDM4hepSimHitWriter( + const EDM4hepSimHitWriter::Config& config, Acts::Logging::Level level) + : WriterT(config.inputSimHits, "CsvSimHitWriter", level), + m_cfg(config), + m_writer(config.outputPath, &m_store) { + ACTS_VERBOSE("Created output file " << config.outputPath); + + if (m_cfg.inputSimHits.empty()) { + throw std::invalid_argument("Missing simulated hits input collection"); + } + + m_mcParticleCollection = + &m_store.create(m_cfg.outputParticles); + m_writer.registerForWrite(m_cfg.outputParticles); + + m_simTrackerHitCollection = &m_store.create( + m_cfg.outputSimTrackerHits); + m_writer.registerForWrite(m_cfg.outputSimTrackerHits); +} + +ActsExamples::ProcessCode EDM4hepSimHitWriter::endRun() { + m_writer.finish(); + + return ProcessCode::SUCCESS; +} + +ProcessCode EDM4hepSimHitWriter::writeT(const AlgorithmContext& ctx, + const SimHitContainer& simHits) { + EDM4hepUtil::MapParticleIdTo particleMapper; + std::unordered_map + particleMap; + + if (!m_cfg.inputParticles.empty()) { + auto particles = + ctx.eventStore.get(m_cfg.inputParticles); + + for (const auto& particle : particles) { + auto p = m_mcParticleCollection->create(); + particleMap[particle.particleId()] = p; + EDM4hepUtil::writeParticle(particle, p); + } + + particleMapper = [&](ActsFatras::Barcode particleId) { + auto it = particleMap.find(particleId); + if (it == particleMap.end()) { + throw std::runtime_error("Particle not found in map"); + } + return it->second; + }; + } + + for (const auto& simHit : simHits) { + auto simTrackerHit = m_simTrackerHitCollection->create(); + EDM4hepUtil::writeSimHit( + simHit, simTrackerHit, particleMapper, + [](Acts::GeometryIdentifier id) { return id.value(); }); + } + + m_writer.writeEvent(); + m_store.clearCollections(); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp new file mode 100644 index 00000000000..52b12c0ccce --- /dev/null +++ b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp @@ -0,0 +1,311 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/MultiTrajectoryHelpers.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "ActsExamples/Digitization/MeasurementCreation.hpp" +#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/Validation/TrackClassification.hpp" + +#include "edm4hep/TrackState.h" + +namespace ActsExamples { + +ActsFatras::Particle EDM4hepUtil::readParticle( + edm4hep::MCParticle from, MapParticleIdFrom particleMapper) { + ActsFatras::Barcode particleId = particleMapper(from); + + ActsFatras::Particle to(particleId, + static_cast(from.getPDG()), + from.getCharge() * Acts::UnitConstants::e, + from.getMass() * Acts::UnitConstants::GeV); + + // TODO do we have that in EDM4hep? + // particle.setProcess(static_cast(data.process)); + + to.setPosition4(from.getVertex()[0] * Acts::UnitConstants::mm, + from.getVertex()[1] * Acts::UnitConstants::mm, + from.getVertex()[2] * Acts::UnitConstants::mm, + from.getTime() * Acts::UnitConstants::ns); + + // Only used for direction; normalization/units do not matter + to.setDirection(from.getMomentum()[0], from.getMomentum()[1], + from.getMomentum()[2]); + + to.setAbsoluteMomentum(std::hypot(from.getMomentum()[0], + from.getMomentum()[1], + from.getMomentum()[2]) * + Acts::UnitConstants::GeV); + + return to; +} + +void EDM4hepUtil::writeParticle(const ActsFatras::Particle& from, + edm4hep::MutableMCParticle to) { + // TODO what about particleId? + + to.setPDG(from.pdg()); + to.setCharge(from.charge() / Acts::UnitConstants::e); + to.setMass(from.mass() / Acts::UnitConstants::GeV); + to.setVertex({from.position().x(), from.position().y(), from.position().z()}); + to.setMomentum({static_cast(from.fourMomentum().x()), + static_cast(from.fourMomentum().y()), + static_cast(from.fourMomentum().z())}); +} + +ActsFatras::Hit EDM4hepUtil::readSimHit(const edm4hep::SimTrackerHit& from, + MapParticleIdFrom particleMapper, + MapGeometryIdFrom geometryMapper) { + ActsFatras::Barcode particleId = particleMapper(from.getMCParticle()); + Acts::GeometryIdentifier geometryId = geometryMapper(from.getCellID()); + + const auto mass = from.getMCParticle().getMass(); + const Acts::ActsVector<3> momentum{ + from.getMomentum().x * Acts::UnitConstants::GeV, + from.getMomentum().y * Acts::UnitConstants::GeV, + from.getMomentum().z * Acts::UnitConstants::GeV, + }; + const auto energy = std::hypot(momentum.norm(), mass); + + ActsFatras::Hit::Vector4 pos4{ + from.getPosition().x * Acts::UnitConstants::mm, + from.getPosition().y * Acts::UnitConstants::mm, + from.getPosition().z * Acts::UnitConstants::mm, + from.getTime() * Acts::UnitConstants::ns, + }; + + ActsFatras::Hit::Vector4 mom4{ + momentum.x(), + momentum.y(), + momentum.z(), + energy, + }; + + // TODO no EDM4hep equivalent? + ActsFatras::Hit::Vector4 delta4{ + 0 * Acts::UnitConstants::GeV, 0 * Acts::UnitConstants::GeV, + 0 * Acts::UnitConstants::GeV, + 0 * Acts::UnitConstants::GeV, // sth.getEDep() + }; + + // TODO no EDM4hep equivalent? + int32_t index = -1; + + return ActsFatras::Hit(geometryId, particleId, pos4, mom4, mom4 + delta4, + index); +} + +void EDM4hepUtil::writeSimHit(const ActsFatras::Hit& from, + edm4hep::MutableSimTrackerHit to, + MapParticleIdTo particleMapper, + MapGeometryIdTo geometryMapper) { + const Acts::Vector4& globalPos4 = from.fourPosition(); + const Acts::Vector4& momentum4Before = from.momentum4Before(); + const auto delta4 = from.momentum4After() - momentum4Before; + + if (particleMapper) { + to.setMCParticle(particleMapper(from.particleId())); + } + + if (geometryMapper) { + // TODO what about the digitization? + to.setCellID(geometryMapper(from.geometryId())); + } + + to.setTime(globalPos4[Acts::eTime] / Acts::UnitConstants::ns); + + to.setPosition({ + globalPos4[Acts::ePos0] / Acts::UnitConstants::mm, + globalPos4[Acts::ePos1] / Acts::UnitConstants::mm, + globalPos4[Acts::ePos2] / Acts::UnitConstants::mm, + }); + + to.setMomentum({ + static_cast(momentum4Before[Acts::eMom0] / + Acts::UnitConstants::GeV), + static_cast(momentum4Before[Acts::eMom1] / + Acts::UnitConstants::GeV), + static_cast(momentum4Before[Acts::eMom2] / + Acts::UnitConstants::GeV), + }); + + to.setEDep(-delta4[Acts::eEnergy] / Acts::UnitConstants::GeV); +} + +Measurement EDM4hepUtil::readMeasurement( + edm4hep::TrackerHitPlane from, + const edm4hep::TrackerHitCollection* fromClusters, Cluster* toCluster, + MapGeometryIdFrom geometryMapper) { + // no need for digitization as we only want to identify the sensor + Acts::GeometryIdentifier geometryId = geometryMapper(from.getCellID()); + + IndexSourceLink sourceLink{geometryId, from.id()}; + + auto pos = from.getPosition(); + auto cov = from.getCovMatrix(); + + DigitizedParameters dParameters; + + dParameters.indices.push_back(Acts::eBoundLoc0); + dParameters.values.push_back(pos.x); + dParameters.variances.push_back(cov[0]); + + // TODO cut this out for 1D + dParameters.indices.push_back(Acts::eBoundLoc1); + dParameters.values.push_back(pos.y); + dParameters.variances.push_back(cov[2]); + + dParameters.indices.push_back(Acts::eBoundTime); + dParameters.values.push_back(pos.z); + dParameters.variances.push_back(cov[5]); + + auto to = createMeasurement(dParameters, sourceLink); + + if (fromClusters) { + for (const auto objectId : from.getRawHits()) { + const auto& c = fromClusters->at(objectId.index); + + // TODO get EDM4hep fixed + // misusing some fields to store ACTS specific information + // don't ask ... + ActsFatras::Channelizer::Bin2D bin{ + static_cast(c.getType()), + static_cast(c.getQuality())}; + ActsFatras::Channelizer::Segment2D path2D; + double activation = c.getTime(); + ActsFatras::Channelizer::ChannelSegment cell{bin, path2D, activation}; + + toCluster->channels.push_back(cell); + } + } + + return to; +} + +void EDM4hepUtil::writeMeasurement(const Measurement& from, + edm4hep::MutableTrackerHitPlane to, + const Cluster* fromCluster, + edm4hep::TrackerHitCollection& toClusters, + MapGeometryIdTo geometryMapper) { + std::visit( + [&](const auto& m) { + Acts::GeometryIdentifier geoId = m.sourceLink().geometryId(); + + if (geometryMapper) { + // no need for digitization as we only want to identify the sensor + to.setCellID(geometryMapper(geoId)); + } + + auto parameters = (m.expander() * m.parameters()).eval(); + + to.setTime(parameters[Acts::eBoundTime] / Acts::UnitConstants::ns); + + to.setType(EDM4hepUtil::EDM4HEP_ACTS_POSITION_TYPE); + // TODO set uv (which are in global spherical coordinates with r=1) + to.setPosition({parameters[Acts::eBoundLoc0], + parameters[Acts::eBoundLoc1], + parameters[Acts::eBoundTime]}); + + auto covariance = + (m.expander() * m.covariance() * m.expander().transpose()).eval(); + to.setCovMatrix({ + static_cast(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)), + static_cast(covariance(Acts::eBoundLoc1, Acts::eBoundLoc0)), + static_cast(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)), + 0, + 0, + 0, + }); + + if (fromCluster) { + for (const auto& c : fromCluster->channels) { + auto toChannel = toClusters.create(); + to.addToRawHits(toChannel.getObjectID()); + + // TODO digitization channel + + // TODO get EDM4hep fixed + // misusing some fields to store ACTS specific information + // don't ask ... + toChannel.setType(c.bin[0]); + toChannel.setQuality(c.bin[1]); + toChannel.setTime(c.activation); + } + } + }, + from); +} + +void EDM4hepUtil::writeTrajectory( + const Trajectories& from, edm4hep::MutableTrack to, std::size_t fromIndex, + const IndexMultimap& hitParticlesMap) { + const auto& multiTrajectory = from.multiTrajectory(); + auto trajectoryState = + Acts::MultiTrajectoryHelpers::trajectoryState(multiTrajectory, fromIndex); + + std::vector particleHitCount; + identifyContributingParticles(hitParticlesMap, from, fromIndex, + particleHitCount); + // TODO use particles + + // TODO write track params + // auto trackParameters = from.trackParameters(fromIndex); + + to.setChi2(trajectoryState.chi2Sum / trajectoryState.NDF); + to.setNdf(trajectoryState.NDF); + + multiTrajectory.visitBackwards(fromIndex, [&](const auto& state) { + // we only fill the track states with non-outlier measurement + auto typeFlags = state.typeFlags(); + if (!typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) { + return true; + } + + Acts::BoundVector meas = state.projector().transpose() * state.parameters(); + Acts::BoundMatrix cov = + state.projector() * state.covariance() * state.projector().transpose(); + + edm4hep::TrackState trackState; + + trackState.D0 = meas[Acts::eBoundLoc0]; + trackState.Z0 = meas[Acts::eBoundLoc1]; + trackState.phi = meas[Acts::eBoundPhi]; + trackState.tanLambda = std::tan(M_PI_2 - meas[Acts::eBoundTheta]); + trackState.omega = meas[Acts::eBoundQOverP]; // TODO convert + trackState.referencePoint = {0, 0, 0}; + + // TODO apply jacobian to covariance + // TODO apply permutation + + // EDM4hep doc: + // lower triangular covariance matrix of the track parameters. the order of + // parameters is d0, phi, omega, z0, tan(lambda). the array is a row-major + // flattening of the matrix. + trackState.covMatrix = { + static_cast(cov(0, 0)), static_cast(cov(1, 0)), + static_cast(cov(1, 1)), static_cast(cov(2, 0)), + static_cast(cov(2, 1)), static_cast(cov(2, 2)), + static_cast(cov(3, 0)), static_cast(cov(3, 1)), + static_cast(cov(3, 2)), static_cast(cov(3, 3)), + static_cast(cov(4, 0)), static_cast(cov(4, 1)), + static_cast(cov(4, 2)), static_cast(cov(4, 3)), + static_cast(cov(4, 4))}; + + to.addToTrackStates(trackState); + + return true; + }); +} + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootMaterialTrackReader.cpp b/Examples/Io/Root/src/RootMaterialTrackReader.cpp index b2da0531d75..bbfea6ab41a 100644 --- a/Examples/Io/Root/src/RootMaterialTrackReader.cpp +++ b/Examples/Io/Root/src/RootMaterialTrackReader.cpp @@ -76,9 +76,14 @@ ActsExamples::RootMaterialTrackReader::RootMaterialTrackReader( } ActsExamples::RootMaterialTrackReader::~RootMaterialTrackReader() { + delete m_inputChain; + delete m_step_x; delete m_step_y; delete m_step_z; + delete m_step_dx; + delete m_step_dy; + delete m_step_dz; delete m_step_length; delete m_step_X0; delete m_step_L0; diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index 0775640a6a1..5596e6cf12b 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -144,6 +144,14 @@ else() target_sources(ActsPythonBindings PRIVATE src/ExaTrkXTrackFindingStub.cpp) endif() +if(ACTS_BUILD_EXAMPLES_EDM4HEP) + target_link_libraries(ActsPythonBindings PUBLIC ActsExamplesIoEDM4hep) + target_sources(ActsPythonBindings PRIVATE src/EDM4hep.cpp) + list(APPEND py_files examples/edm4hep.py) +else() + target_sources(ActsPythonBindings PRIVATE src/EDM4hepStub.cpp) +endif() + add_custom_target(ActsPythonGlueCode) configure_file(setup.sh.in ${_python_dir}/setup.sh @ONLY) install(FILES setup.sh.in DESTINATION "python" RENAME setup.sh) diff --git a/Examples/Python/python/acts/examples/edm4hep.py b/Examples/Python/python/acts/examples/edm4hep.py new file mode 100644 index 00000000000..4deb434f5d7 --- /dev/null +++ b/Examples/Python/python/acts/examples/edm4hep.py @@ -0,0 +1,6 @@ +from acts._adapter import _patch_config +from acts import ActsPythonBindings + +_patch_config(ActsPythonBindings._examples._edm4hep) + +from acts.ActsPythonBindings._examples._edm4hep import * diff --git a/Examples/Python/src/DD4hepComponent.cpp b/Examples/Python/src/DD4hepComponent.cpp index bb542772c81..5e769182f1e 100644 --- a/Examples/Python/src/DD4hepComponent.cpp +++ b/Examples/Python/src/DD4hepComponent.cpp @@ -46,13 +46,17 @@ PYBIND11_MODULE(ActsPythonBindingsDD4hep, m) { } { - auto gd = py::class_>( - m, "DD4hepDetector") - .def(py::init<>()) - .def("finalize", - py::overload_cast< - DD4hep::DD4hepGeometryService::Config, - std::shared_ptr>( - &DD4hepDetector::finalize)); + auto gd = + py::class_>(m, "DD4hepDetector") + .def(py::init<>()) + .def("finalize", + py::overload_cast< + DD4hep::DD4hepGeometryService::Config, + std::shared_ptr>( + &DD4hep::DD4hepDetector::finalize)); + ACTS_PYTHON_STRUCT_BEGIN(gd, DD4hep::DD4hepDetector); + ACTS_PYTHON_MEMBER(geometryService); + ACTS_PYTHON_STRUCT_END(); } -} \ No newline at end of file +} diff --git a/Examples/Python/src/EDM4hep.cpp b/Examples/Python/src/EDM4hep.cpp new file mode 100644 index 00000000000..23256d3e8f9 --- /dev/null +++ b/Examples/Python/src/EDM4hep.cpp @@ -0,0 +1,157 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Python/Utilities.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepMeasurementWriter.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepMultiTrajectoryWriter.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepParticleReader.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepParticleWriter.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepSimHitReader.hpp" +#include "ActsExamples/Io/EDM4hep/EDM4hepSimHitWriter.hpp" + +#include + +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +using namespace Acts; + +namespace Acts::Python { + +void addEDM4hep(Context& ctx) { + auto mex = ctx.get("examples"); + + auto edm4hep = mex.def_submodule("_edm4hep"); + + { + using Reader = ActsExamples::EDM4hepSimHitReader; + using Config = Reader::Config; + auto r = py::class_>( + edm4hep, "EDM4hepSimHitReader") + .def(py::init(), + py::arg("config"), py::arg("level")) + .def_property_readonly("config", &Reader::config); + + auto c = py::class_(r, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputPath); + ACTS_PYTHON_MEMBER(inputParticles); + ACTS_PYTHON_MEMBER(outputSimHits); + ACTS_PYTHON_MEMBER(dd4hepGeometryService); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Writer = ActsExamples::EDM4hepSimHitWriter; + using Config = Writer::Config; + auto w = py::class_>( + edm4hep, "EDM4hepSimHitWriter") + .def(py::init(), + py::arg("config"), py::arg("level")); + + auto c = py::class_(w, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputSimHits); + ACTS_PYTHON_MEMBER(inputParticles); + ACTS_PYTHON_MEMBER(outputPath); + ACTS_PYTHON_MEMBER(outputParticles); + ACTS_PYTHON_MEMBER(outputSimTrackerHits); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Reader = ActsExamples::EDM4hepMeasurementReader; + using Config = Reader::Config; + auto r = py::class_>( + edm4hep, "EDM4hepMeasurementReader") + .def(py::init(), + py::arg("config"), py::arg("level")) + .def_property_readonly("config", &Reader::config); + + auto c = py::class_(r, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputPath); + ACTS_PYTHON_MEMBER(outputMeasurements); + ACTS_PYTHON_MEMBER(outputMeasurementSimHitsMap); + ACTS_PYTHON_MEMBER(outputSourceLinks); + ACTS_PYTHON_MEMBER(outputClusters); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Writer = ActsExamples::EDM4hepMeasurementWriter; + using Config = Writer::Config; + auto w = py::class_>( + edm4hep, "EDM4hepMeasurementWriter") + .def(py::init(), + py::arg("config"), py::arg("level")); + + auto c = py::class_(w, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputMeasurements); + ACTS_PYTHON_MEMBER(inputClusters); + ACTS_PYTHON_MEMBER(inputSimHits); + ACTS_PYTHON_MEMBER(inputMeasurementSimHitsMap); + ACTS_PYTHON_MEMBER(outputPath); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Reader = ActsExamples::EDM4hepParticleReader; + using Config = Reader::Config; + auto r = py::class_>( + edm4hep, "EDM4hepParticleReader") + .def(py::init(), + py::arg("config"), py::arg("level")) + .def_property_readonly("config", &Reader::config); + + auto c = py::class_(r, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputPath); + ACTS_PYTHON_MEMBER(inputParticles); + ACTS_PYTHON_MEMBER(outputParticles); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Writer = ActsExamples::EDM4hepParticleWriter; + using Config = Writer::Config; + auto w = py::class_>( + edm4hep, "EDM4hepParticleWriter") + .def(py::init(), + py::arg("config"), py::arg("level")); + + auto c = py::class_(w, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputParticles); + ACTS_PYTHON_MEMBER(outputPath); + ACTS_PYTHON_MEMBER(outputParticles); + ACTS_PYTHON_STRUCT_END(); + } + + { + using Writer = ActsExamples::EDM4hepMultiTrajectoryWriter; + using Config = Writer::Config; + auto w = py::class_>( + edm4hep, "EDM4hepMultiTrajectoryWriter") + .def(py::init(), + py::arg("config"), py::arg("level")); + + auto c = py::class_(w, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(inputTrajectories); + ACTS_PYTHON_MEMBER(inputMeasurementParticlesMap); + ACTS_PYTHON_MEMBER(outputPath); + ACTS_PYTHON_STRUCT_END(); + } +} + +} // namespace Acts::Python diff --git a/Examples/Python/src/EDM4hepStub.cpp b/Examples/Python/src/EDM4hepStub.cpp new file mode 100644 index 00000000000..0bec96a9fad --- /dev/null +++ b/Examples/Python/src/EDM4hepStub.cpp @@ -0,0 +1,13 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Python/Utilities.hpp" + +namespace Acts::Python { +void addEDM4hep(Context&) {} +} // namespace Acts::Python diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index db114b3c0d5..1fc6584b799 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -92,6 +92,7 @@ void addPythia8(Context& ctx); void addJson(Context& ctx); void addHepMC3(Context& ctx); void addExaTrkXTrackFinding(Context& ctx); +void addEDM4hep(Context& ctx); } // namespace Acts::Python @@ -232,4 +233,5 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addJson(ctx); addHepMC3(ctx); addExaTrkXTrackFinding(ctx); + addEDM4hep(ctx); } diff --git a/Examples/Python/tests/helpers/__init__.py b/Examples/Python/tests/helpers/__init__.py index 9ac9dc3d2c0..03f6125864e 100644 --- a/Examples/Python/tests/helpers/__init__.py +++ b/Examples/Python/tests/helpers/__init__.py @@ -37,9 +37,16 @@ if hepmc3Enabled: try: import acts.examples.hepmc3 - except: + except ImportError: hepmc3Enabled = False +edm4hepEnabled = "EDM4HEP_DIR" in os.environ +if edm4hepEnabled: + try: + import acts.examples.edm4hep + except ImportError: + edm4hepEnabled = False + isCI = os.environ.get("CI", "false") == "true" diff --git a/Examples/Python/tests/test_detectors.py b/Examples/Python/tests/test_detectors.py index 31bafa5b7ae..91ae8925731 100644 --- a/Examples/Python/tests/test_detectors.py +++ b/Examples/Python/tests/test_detectors.py @@ -3,7 +3,11 @@ import pytest from helpers import dd4hepEnabled -from common import getOpenDataDetector + +from common import ( + getOpenDataDetectorDirectory, + getOpenDataDetector, +) import acts.examples @@ -32,11 +36,12 @@ def test_generic_geometry(): @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep is not set up") def test_odd(): - config = acts.MaterialMapJsonConverter.Config() matDeco = acts.JsonMaterialDecorator( rConfig=config, - jFileName="thirdparty/OpenDataDetector/config/odd-material-mapping-config.json", + jFileName=str( + getOpenDataDetectorDirectory() / "config/odd-material-mapping-config.json" + ), level=acts.logging.WARNING, ) diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index e7a3bd8707c..6f43a28adc7 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -527,8 +527,8 @@ def test_material_mapping(material_recording, tmp_path, assert_root_hash): # test the validation as well # we need to destroy the ODD to reload with material - # del trackingGeometry - # del detector + del trackingGeometry + del detector detector, trackingGeometry, decorators = getOpenDataDetector( mdecorator=acts.IMaterialDecorator.fromFile(mat_file) @@ -604,8 +604,8 @@ def test_volume_material_mapping(material_recording, tmp_path, assert_root_hash) # test the validation as well # we need to destroy the ODD to reload with material - # del trackingGeometry - # del detector + del trackingGeometry + del detector detector, trackingGeometry, decorators = getOpenDataDetector( mdecorator=acts.IMaterialDecorator.fromFile(mat_file) diff --git a/Examples/Python/tests/test_reader.py b/Examples/Python/tests/test_reader.py index 1cdfb9b7664..a9525e9c6ff 100644 --- a/Examples/Python/tests/test_reader.py +++ b/Examples/Python/tests/test_reader.py @@ -1,12 +1,19 @@ -from typing import Type -import inspect - import pytest +import os +import multiprocessing -from helpers import geant4Enabled, AssertCollectionExistsAlg +from helpers import ( + geant4Enabled, + edm4hepEnabled, + AssertCollectionExistsAlg, +) -import acts +from common import ( + getOpenDataDetectorDirectory, + getOpenDataDetector, +) +import acts from acts import PlanarModuleStepper from acts.examples import ( RootParticleWriter, @@ -22,11 +29,11 @@ CsvPlanarClusterWriter, CsvPlanarClusterReader, PlanarSteppingAlgorithm, - BareAlgorithm, Sequencer, ) +@pytest.mark.root def test_root_particle_reader(tmp_path, conf_const, ptcl_gun): # need to write out some particles first s = Sequencer(numThreads=1, events=10, logLevel=acts.logging.WARNING) @@ -69,6 +76,7 @@ def test_root_particle_reader(tmp_path, conf_const, ptcl_gun): assert alg.events_seen == 10 +@pytest.mark.csv def test_csv_particle_reader(tmp_path, conf_const, ptcl_gun): s = Sequencer(numThreads=1, events=10, logLevel=acts.logging.WARNING) evGen = ptcl_gun(s) @@ -303,3 +311,150 @@ def test_csv_clusters_reader(tmp_path, fatras, conf_const, trk_geo, rng): for alg in algs: assert alg.events_seen == 10 + + +def generate_input_test_edm4hep_simhit_reader(input, output): + from DDSim.DD4hepSimulation import DD4hepSimulation + + ddsim = DD4hepSimulation() + ddsim.compactFile = input + ddsim.enableGun = True + ddsim.gun.direction = (1, 0, 0) + ddsim.gun.distribution = "eta" + ddsim.numberOfEvents = 10 + ddsim.outputFile = output + ddsim.run() + + +@pytest.mark.slow +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_simhit_reader(tmp_path): + from acts.examples.edm4hep import EDM4hepSimHitReader + + tmp_file = str(tmp_path / "output_edm4hep.root") + odd_xml_file = str(getOpenDataDetectorDirectory() / "xml" / "OpenDataDetector.xml") + + with multiprocessing.get_context("spawn").Pool() as pool: + pool.apply(generate_input_test_edm4hep_simhit_reader, (odd_xml_file, tmp_file)) + + assert os.path.exists(tmp_file) + + detector, _, _ = getOpenDataDetector() + + s = Sequencer(numThreads=1) + + s.addReader( + EDM4hepSimHitReader( + level=acts.logging.INFO, + inputPath=tmp_file, + outputSimHits="simhits", + dd4hepGeometryService=detector.geometryService, + ) + ) + + alg = AssertCollectionExistsAlg("simhits", "check_alg", acts.logging.WARNING) + s.addAlgorithm(alg) + + s.run() + + assert alg.events_seen == 10 + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_measurement_reader(tmp_path, fatras, conf_const): + from acts.examples.edm4hep import ( + EDM4hepMeasurementWriter, + EDM4hepMeasurementReader, + ) + + s = Sequencer(numThreads=1, events=10) + _, simAlg, digiAlg = fatras(s) + + out = tmp_path / "measurements_edm4hep.root" + + config = EDM4hepMeasurementWriter.Config( + inputMeasurements=digiAlg.config.outputMeasurements, + inputClusters=digiAlg.config.outputClusters, + inputSimHits=simAlg.config.outputSimHits, + inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap, + outputPath=str(out), + ) + s.addWriter(EDM4hepMeasurementWriter(level=acts.logging.INFO, config=config)) + s.run() + + # read back in + s = Sequencer(numThreads=1) + + s.addReader( + conf_const( + EDM4hepMeasurementReader, + level=acts.logging.WARNING, + outputMeasurements="measurements", + outputMeasurementSimHitsMap="simhitsmap", + outputSourceLinks="sourcelinks", + inputPath=str(out), + ) + ) + + algs = [ + AssertCollectionExistsAlg(k, f"check_alg_{k}", acts.logging.WARNING) + for k in ("measurements", "simhitsmap", "sourcelinks") + ] + for alg in algs: + s.addAlgorithm(alg) + + s.run() + + for alg in algs: + assert alg.events_seen == 10 + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_particle_reader(tmp_path, conf_const, ptcl_gun): + from acts.examples.edm4hep import ( + EDM4hepParticleWriter, + EDM4hepParticleReader, + ) + + s = Sequencer(numThreads=1, events=10, logLevel=acts.logging.WARNING) + evGen = ptcl_gun(s) + + out = tmp_path / "particles_edm4hep.root" + + out.mkdir() + + s.addWriter( + conf_const( + EDM4hepParticleWriter, + acts.logging.WARNING, + inputParticles=evGen.config.outputParticles, + outputPath=str(out), + ) + ) + + s.run() + + # reset the seeder + s = Sequencer(numThreads=1, logLevel=acts.logging.WARNING) + + s.addReader( + conf_const( + EDM4hepParticleReader, + acts.logging.WARNING, + inputPath=str(out), + outputParticles="input_particles", + ) + ) + + alg = AssertCollectionExistsAlg( + "input_particles", "check_alg", acts.logging.WARNING + ) + + s.addAlgorithm(alg) + + s.run() + + assert alg.events_seen == 10 diff --git a/Examples/Python/tests/test_writer.py b/Examples/Python/tests/test_writer.py index c46597c1e1b..89dedaba9d9 100644 --- a/Examples/Python/tests/test_writer.py +++ b/Examples/Python/tests/test_writer.py @@ -11,10 +11,10 @@ dd4hepEnabled, hepmc3Enabled, geant4Enabled, + edm4hepEnabled, AssertCollectionExistsAlg, ) - import acts from common import getOpenDataDetectorDirectory @@ -43,13 +43,11 @@ CsvMultiTrajectoryWriter, CsvTrackingGeometryWriter, CsvMeasurementWriter, - TrackParamsEstimationAlgorithm, PlanarSteppingAlgorithm, JsonMaterialWriter, JsonFormat, Sequencer, GenericDetector, - RootNuclearInteractionParametersWriter, ) @@ -499,7 +497,6 @@ def test_csv_multitrajectory_writer(tmp_path): @pytest.fixture(scope="session") def hepmc_data_impl(tmp_path_factory): - import subprocess script = ( @@ -540,7 +537,6 @@ def hepmc_data(hepmc_data_impl: Path, tmp_path): @pytest.mark.skipif(not geant4Enabled, reason="Geant4 not set up") @pytest.mark.slow def test_hepmc3_histogram(hepmc_data, tmp_path): - from acts.examples.hepmc3 import ( HepMC3AsciiReader, HepMCProcessExtractor, @@ -578,3 +574,123 @@ def test_hepmc3_histogram(hepmc_data, tmp_path): s.addAlgorithm(alg) s.run() + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_measurement_writer(tmp_path, fatras): + from acts.examples.edm4hep import EDM4hepMeasurementWriter + + s = Sequencer(numThreads=1, events=10) + _, simAlg, digiAlg = fatras(s) + + out = tmp_path / "measurements_edm4hep.root" + + s.addWriter( + EDM4hepMeasurementWriter( + level=acts.logging.VERBOSE, + inputMeasurements=digiAlg.config.outputMeasurements, + inputClusters=digiAlg.config.outputClusters, + inputSimHits=simAlg.config.outputSimHits, + inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap, + outputPath=str(out), + ) + ) + + s.run() + + assert os.path.isfile(out) + assert os.stat(out).st_size > 10 + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_simhit_writer(tmp_path, fatras, conf_const): + from acts.examples.edm4hep import EDM4hepSimHitWriter + + s = Sequencer(numThreads=1, events=10) + _, simAlg, _ = fatras(s) + + out = tmp_path / "simhits_edm4hep.root" + + s.addWriter( + conf_const( + EDM4hepSimHitWriter, + level=acts.logging.INFO, + inputSimHits=simAlg.config.outputSimHits, + outputPath=str(out), + ) + ) + + s.run() + + assert os.path.isfile(out) + assert os.stat(out).st_size > 200 + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_particle_writer(tmp_path, conf_const, ptcl_gun): + from acts.examples.edm4hep import EDM4hepParticleWriter + + s = Sequencer(numThreads=1, events=10) + evGen = ptcl_gun(s) + + out = tmp_path / "particles_edm4hep.root" + + out.mkdir() + + s.addWriter( + conf_const( + EDM4hepParticleWriter, + acts.logging.INFO, + inputParticles=evGen.config.outputParticles, + outputPath=str(out), + ) + ) + + s.run() + + assert os.path.isfile(out) + assert os.stat(out).st_size > 200 + + +@pytest.mark.edm4hep +@pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up") +def test_edm4hep_multitrajectory_writer(tmp_path): + from acts.examples.edm4hep import EDM4hepMultiTrajectoryWriter + + detector, trackingGeometry, decorators = GenericDetector.create() + field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) + + from truth_tracking_kalman import runTruthTrackingKalman + + s = Sequencer(numThreads=1, events=10) + runTruthTrackingKalman( + trackingGeometry, + field, + digiConfigFile=Path( + str( + Path(__file__).parent.parent.parent.parent + / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" + ) + ), + outputDir=tmp_path, + s=s, + ) + + out = tmp_path / "trajectories_edm4hep.root" + + s.addWriter( + EDM4hepMultiTrajectoryWriter( + level=acts.logging.VERBOSE, + inputTrajectories="trajectories", + inputMeasurementParticlesMap="measurement_particles_map", + outputPath=str(out), + ) + ) + + s.run() + + assert os.path.isfile(out) + assert os.stat(out).st_size > 200 diff --git a/Examples/Run/Digitization/DD4hep/DD4hepDigitizationConfigExample.cpp b/Examples/Run/Digitization/DD4hep/DD4hepDigitizationConfigExample.cpp index be26dad9cee..8e2095b6d7c 100644 --- a/Examples/Run/Digitization/DD4hep/DD4hepDigitizationConfigExample.cpp +++ b/Examples/Run/Digitization/DD4hep/DD4hepDigitizationConfigExample.cpp @@ -11,6 +11,6 @@ #include "DigitizationConfigExample.hpp" int main(int argc, char* argv[]) { - return runDigitizationConfigExample(argc, argv, - std::make_shared()); + return runDigitizationConfigExample( + argc, argv, std::make_shared()); } diff --git a/Examples/Run/Digitization/DD4hep/DD4hepDigitizationExample.cpp b/Examples/Run/Digitization/DD4hep/DD4hepDigitizationExample.cpp index 67fb8db71c5..8083ba6c2bc 100644 --- a/Examples/Run/Digitization/DD4hep/DD4hepDigitizationExample.cpp +++ b/Examples/Run/Digitization/DD4hep/DD4hepDigitizationExample.cpp @@ -11,5 +11,6 @@ #include "DigitizationExample.hpp" int main(int argc, char* argv[]) { - return runDigitizationExample(argc, argv, std::make_shared()); + return runDigitizationExample( + argc, argv, std::make_shared()); } diff --git a/Examples/Run/Fatras/DD4hep/DD4hepFatrasExample.cpp b/Examples/Run/Fatras/DD4hep/DD4hepFatrasExample.cpp index 3b30ba4021f..25257400ad3 100644 --- a/Examples/Run/Fatras/DD4hep/DD4hepFatrasExample.cpp +++ b/Examples/Run/Fatras/DD4hep/DD4hepFatrasExample.cpp @@ -11,5 +11,6 @@ #include "Fatras.hpp" int main(int argc, char* argv[]) { - return runFatras(argc, argv, std::make_shared()); + return runFatras(argc, argv, + std::make_shared()); } diff --git a/Examples/Run/Geometry/DD4hep/DD4hepGeometryExample.cpp b/Examples/Run/Geometry/DD4hep/DD4hepGeometryExample.cpp index 1d2b7bf35e0..6ba201b0262 100644 --- a/Examples/Run/Geometry/DD4hep/DD4hepGeometryExample.cpp +++ b/Examples/Run/Geometry/DD4hep/DD4hepGeometryExample.cpp @@ -15,7 +15,7 @@ /// @param argv The argument list int main(int argc, char* argv[]) { // -------------------------------------------------------------------------------- - DD4hepDetector detector; + ActsExamples::DD4hep::DD4hepDetector detector; // now process it return processGeometry(argc, argv, detector); } diff --git a/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialMapping.cpp b/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialMapping.cpp index 75a8092c09e..0051ef41b59 100644 --- a/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialMapping.cpp +++ b/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialMapping.cpp @@ -15,7 +15,7 @@ /// @param argv The argument list int main(int argc, char* argv[]) { // -------------------------------------------------------------------------------- - DD4hepDetector detector; + ActsExamples::DD4hep::DD4hepDetector detector; // now process it return runMaterialMapping(argc, argv, detector); } diff --git a/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialValidation.cpp b/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialValidation.cpp index 87429fb5c13..9b6950090b7 100644 --- a/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialValidation.cpp +++ b/Examples/Run/MaterialMapping/DD4hep/DD4hepMaterialValidation.cpp @@ -15,7 +15,7 @@ /// @param argv The argument list int main(int argc, char* argv[]) { // -------------------------------------------------------------------------------- - DD4hepDetector detector; + ActsExamples::DD4hep::DD4hepDetector detector; // now process it return materialValidationExample(argc, argv, detector); } diff --git a/Examples/Run/Propagation/DD4hep/DD4hepPropagationExample.cpp b/Examples/Run/Propagation/DD4hep/DD4hepPropagationExample.cpp index c7169690604..a1fd44a9afa 100644 --- a/Examples/Run/Propagation/DD4hep/DD4hepPropagationExample.cpp +++ b/Examples/Run/Propagation/DD4hep/DD4hepPropagationExample.cpp @@ -15,7 +15,7 @@ /// @param argv The argument list int main(int argc, char* argv[]) { // -------------------------------------------------------------------------------- - DD4hepDetector detector; + ActsExamples::DD4hep::DD4hepDetector detector; // now process it return propagationExample(argc, argv, detector); diff --git a/Examples/Run/Reconstruction/DD4hep/DD4hepRecCKFTracks.cpp b/Examples/Run/Reconstruction/DD4hep/DD4hepRecCKFTracks.cpp index 7c9c4fdef1f..bc62f84bbb0 100644 --- a/Examples/Run/Reconstruction/DD4hep/DD4hepRecCKFTracks.cpp +++ b/Examples/Run/Reconstruction/DD4hep/DD4hepRecCKFTracks.cpp @@ -11,5 +11,6 @@ #include "RecCKFTracks.hpp" int main(int argc, char* argv[]) { - return runRecCKFTracks(argc, argv, std::make_shared()); + return runRecCKFTracks( + argc, argv, std::make_shared()); } diff --git a/Examples/Run/Reconstruction/DD4hep/DD4hepRecTruthTracks.cpp b/Examples/Run/Reconstruction/DD4hep/DD4hepRecTruthTracks.cpp index 41e439f7e92..408cc5144a5 100644 --- a/Examples/Run/Reconstruction/DD4hep/DD4hepRecTruthTracks.cpp +++ b/Examples/Run/Reconstruction/DD4hep/DD4hepRecTruthTracks.cpp @@ -11,5 +11,6 @@ #include "RecTruthTracks.hpp" int main(int argc, char* argv[]) { - return runRecTruthTracks(argc, argv, std::make_shared()); + return runRecTruthTracks( + argc, argv, std::make_shared()); } diff --git a/Examples/Run/Reconstruction/DD4hep/DD4hepSeedingExample.cpp b/Examples/Run/Reconstruction/DD4hep/DD4hepSeedingExample.cpp index b5e56e77195..b6a2b4d6dc9 100644 --- a/Examples/Run/Reconstruction/DD4hep/DD4hepSeedingExample.cpp +++ b/Examples/Run/Reconstruction/DD4hep/DD4hepSeedingExample.cpp @@ -11,5 +11,6 @@ #include "SeedingExample.hpp" int main(int argc, char* argv[]) { - return runSeedingExample(argc, argv, std::make_shared()); + return runSeedingExample( + argc, argv, std::make_shared()); } diff --git a/pytest.ini b/pytest.ini index 203ca7b794e..a2aadae6e09 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,4 +7,5 @@ markers = obj root json - slow \ No newline at end of file + slow + edm4hep \ No newline at end of file