diff --git a/.github/workflows/linux-eic-shell.yml b/.github/workflows/linux-eic-shell.yml index 97c073353..4f4e8bfed 100644 --- a/.github/workflows/linux-eic-shell.yml +++ b/.github/workflows/linux-eic-shell.yml @@ -49,7 +49,7 @@ jobs: env: # env cannot be used in matrix, but outputs can # so this job turns env into outputs - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: platform_json: ${{ steps.define.outputs.platform_json }} release_json: ${{ steps.define.outputs.release_json }} @@ -60,7 +60,7 @@ jobs: echo "release_json=[\"${{ env.release }}\"]" >> $GITHUB_OUTPUT build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: env strategy: # include multiple compilers for one release version, @@ -125,7 +125,7 @@ jobs: echo "cache_dir=${{ github.workspace }}/.ccache" > ~/.ccache/ccache.conf echo "max_size=1500MB" >> ~/.ccache/ccache.conf echo "compression=true" >> ~/.ccache/ccache.conf - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Build and install uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -175,13 +175,13 @@ jobs: if-no-files-found: error clang-tidy-iwyu: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build steps: - uses: actions/checkout@v4 with: filter: "tree:0" - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Download build artifact uses: actions/download-artifact@v4 with: @@ -210,7 +210,7 @@ jobs: path: clang_tidy_fixes.yml if-no-files-found: ignore - name: Suggest clang-tidy fixes as PR comments - uses: platisd/clang-tidy-pr-comments@v1.6.0 + uses: platisd/clang-tidy-pr-comments@v1.6.1 if: ${{ github.event_name == 'pull_request' }} with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -252,7 +252,7 @@ jobs: run: git diff --exit-code llvm-cov: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build permissions: statuses: write @@ -311,11 +311,11 @@ jobs: path: build/codecov_report/ detector-info: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: hash: ${{ steps.detector-info.outputs.hash }} steps: - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Get detector info id: detector-info run: | @@ -326,7 +326,7 @@ jobs: echo "hash=${hash%% *}" | tee $GITHUB_OUTPUT npsim-gun: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - detector-info strategy: @@ -334,7 +334,7 @@ jobs: particle: [pi, e] detector_config: [craterlake] steps: - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Retrieve simulation files id: retrieve_simulation_files uses: actions/cache@v4 @@ -356,7 +356,7 @@ jobs: if-no-files-found: error npsim-gun-EcalLumiSpec: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - detector-info strategy: @@ -367,7 +367,7 @@ jobs: - uses: actions/checkout@v4 with: filter: "tree:0" - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Retrieve simulation files id: retrieve_simulation_files uses: actions/cache@v4 @@ -390,7 +390,7 @@ jobs: if-no-files-found: error npsim-dis: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - detector-info strategy: @@ -403,7 +403,7 @@ jobs: minq2: 1000 detector_config: craterlake_18x275 steps: - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Retrieve simulation files id: retrieve_simulation_files uses: actions/cache@v4 @@ -426,7 +426,7 @@ jobs: if-no-files-found: error npsim-minbias: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - detector-info strategy: @@ -439,7 +439,7 @@ jobs: - beam: 18x275 detector_config: craterlake_18x275 steps: - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Retrieve simulation files id: retrieve_simulation_files uses: actions/cache@v4 @@ -462,7 +462,7 @@ jobs: if-no-files-found: error eicrecon-two-stage-running: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun @@ -491,7 +491,7 @@ jobs: with: name: sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - name: Setup cvmfs - uses: cvmfs-contrib/github-action-cvmfs@v4 + uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon (digitization) uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -526,7 +526,7 @@ jobs: if-no-files-found: error eicrecon-eicmkplugin: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun @@ -553,7 +553,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -566,10 +566,10 @@ jobs: $PWD/install/bin/eicmkplugin.py MyCustomPlugin cmake -S MyCustomPlugin -B MyCustomPlugin/build -DEICrecon_ROOT=$PWD/install -DUSER_PLUGIN_OUTPUT_DIRECTORY=$PWD/install/lib/EICrecon/plugins cmake --build MyCustomPlugin/build -j $(getconf _NPROCESSORS_ONLN) --target install - $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=MyCustomPlugin -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root -Pplugins=dump_flags,janadot -Pdump_flags:json=${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json + $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=MyCustomPlugin -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root eicrecon-test-plugins: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun @@ -600,7 +600,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -610,7 +610,7 @@ jobs: export DETECTOR_CONFIG=${DETECTOR}_${{ matrix.detector_config }} export LD_LIBRARY_PATH=$PWD/install/lib:$LD_LIBRARY_PATH export JANA_PLUGIN_PATH=$PWD/install/lib/EICrecon/plugins${JANA_PLUGIN_PATH:+:${JANA_PLUGIN_PATH}} - $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=${{ matrix.test_plugins }} -Phistsfile=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.test_plugins }}.hists.root -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root -Pplugins=dump_flags,janadot -Pdump_flags:json=${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json + $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=${{ matrix.test_plugins }} -Phistsfile=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.test_plugins }}.hists.root -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - uses: actions/upload-artifact@v4 with: name: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.test_plugins }}.hists.root @@ -618,7 +618,7 @@ jobs: if-no-files-found: error eicrecon-benchmarks-plugins: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun @@ -653,7 +653,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -663,7 +663,7 @@ jobs: export DETECTOR_CONFIG=${DETECTOR}_${{ matrix.detector_config }} export LD_LIBRARY_PATH=$PWD/install/lib:$LD_LIBRARY_PATH export JANA_PLUGIN_PATH=$PWD/install/lib/EICrecon/plugins${JANA_PLUGIN_PATH:+:${JANA_PLUGIN_PATH}} - $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=${{ matrix.benchmark_plugins }} -Phistsfile=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.benchmark_plugins }}.hists.root -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root -Pplugins=dump_flags,janadot -Pdump_flags:json=${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json + $PWD/install/bin/eicrecon $JANA_OPTIONS -Pplugins=${{ matrix.benchmark_plugins }} -Phistsfile=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.benchmark_plugins }}.hists.root -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - uses: actions/upload-artifact@v4 with: name: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}_${{ matrix.benchmark_plugins }}.hists.root @@ -671,7 +671,7 @@ jobs: if-no-files-found: error eicrecon-gun: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun @@ -698,7 +698,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Check dynamic library loader paths uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -754,7 +754,7 @@ jobs: if-no-files-found: error - name: Download previous artifact id: download_previous_artifact - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 with: branch: ${{ github.base_ref || github.event.merge_group.base_ref || github.ref_name }} path: ref/ @@ -785,7 +785,7 @@ jobs: if-no-files-found: error eicrecon-gun-EcalLumiSpec: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-gun-EcalLumiSpec @@ -812,7 +812,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_${{ matrix.particle }}_EcalLumiSpec_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -852,7 +852,7 @@ jobs: if-no-files-found: error eicrecon-dis: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build - npsim-dis @@ -898,7 +898,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: sim_dis_${{ matrix.beam }}_minQ2=${{ matrix.minq2 }}_${{ matrix.detector_config }}.edm4hep.root - - uses: cvmfs-contrib/github-action-cvmfs@v4 + - uses: cvmfs-contrib/github-action-cvmfs@v5 - name: Run EICrecon uses: eic/run-cvmfs-osg-eic-shell@main with: @@ -954,7 +954,7 @@ jobs: if-no-files-found: error - name: Download previous artifact id: download_previous_artifact - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 with: branch: ${{ github.base_ref || github.event.merge_group.base_ref || github.ref_name }} path: ref/ @@ -985,7 +985,7 @@ jobs: if-no-files-found: error trigger-container: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: ${{ github.event_name != 'merge_group' && github.event_name != 'schedule' && github.actor != 'dependabot[bot]' }} needs: - eicrecon-gun @@ -1017,7 +1017,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-docs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - eicrecon-gun - eicrecon-dis @@ -1055,7 +1055,7 @@ jobs: if-no-files-found: error list-open-prs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: json: ${{ steps.remap.outputs.json }} steps: @@ -1067,14 +1067,14 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Remap open PRs id: remap - uses: nickofthyme/object-remap@v2 + uses: nickofthyme/object-remap@v3 with: __case: snake include.*.pr: ${{ toJSON(fromJSON(steps.query.outputs.data).*.number) }} include.*.head_sha: ${{ toJSON(fromJSON(steps.query.outputs.data).*.head.sha) }} get-docs-from-open-prs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build-docs - list-open-prs @@ -1084,7 +1084,7 @@ jobs: max-parallel: 4 steps: - name: Download docs artifact (other PRs) - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 if: github.event.pull_request.number != matrix.pr with: commit: ${{ matrix.head_sha }} @@ -1101,7 +1101,7 @@ jobs: path: publishing_docs/pr/${{ matrix.pr }}/ - name: Download capybara artifact (other PRs) id: download_capybara - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 if: github.event.pull_request.number != matrix.pr with: commit: ${{ matrix.head_sha }} @@ -1125,7 +1125,7 @@ jobs: if-no-files-found: ignore get-docs-from-main: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - build-docs steps: @@ -1133,7 +1133,7 @@ jobs: # - If we run this on a non-main branch, we download from main with action-download-artifact. # - If we run this on the main branch, we have to download from this pipeline with download-artifact. - name: Download docs artifact (in PR) - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 if: github.ref_name != 'main' with: branch: main @@ -1150,7 +1150,7 @@ jobs: path: publishing_docs/ - name: Download capybara artifact (in PR) id: download_capybara - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v7 if: github.ref_name != 'main' with: commit: ${{ matrix.head_sha }} @@ -1182,7 +1182,7 @@ jobs: if-no-files-found: error collect-docs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: - get-docs-from-main - get-docs-from-open-prs @@ -1241,7 +1241,7 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Deploy to GitHub Pages id: deployment diff --git a/CMakeLists.txt b/CMakeLists.txt index bbfa5d22b..3983d19d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,9 @@ find_package(EDM4EIC 5.0 REQUIRED) # spdlog find_package(spdlog REQUIRED) +# fmt +find_package(fmt 9.0.0 REQUIRED) + # Guidelines Support Library find_package(Microsoft.GSL CONFIG) diff --git a/cmake/jana_plugin.cmake b/cmake/jana_plugin.cmake index b2616f916..76d9deee4 100644 --- a/cmake/jana_plugin.cmake +++ b/cmake/jana_plugin.cmake @@ -23,25 +23,6 @@ macro(plugin_add _name) endif() endforeach() - # Include JANA by default - find_package(JANA REQUIRED) - - # TODO: NWB: This really needs to be a dependency of JANA itself. If we don't - # do this here, CMake will later refuse to accept that podio is indeed a - # dependency of JANA and aggressively reorders my target_link_list to reflect - # this misapprehension. - # https://gitlab.kitware.com/cmake/cmake/blob/v3.13.2/Source/cmComputeLinkDepends.cxx - find_package(podio REQUIRED) - - # include logging by default - find_package(spdlog REQUIRED) - - # include fmt by default - find_package(fmt 9.0.0 REQUIRED) - - # include gsl by default - find_package(Microsoft.GSL CONFIG) - # Define plugin if(${_name}_WITH_PLUGIN) add_library(${_name}_plugin SHARED ${PLUGIN_SOURCES}) @@ -57,6 +38,11 @@ macro(plugin_add _name) PROPERTIES PREFIX "" OUTPUT_NAME "${_name}" SUFFIX ".so") + target_compile_definitions( + ${PLUGIN_NAME}_plugin + PRIVATE "JANA_VERSION_MAJOR=${JANA_VERSION_MAJOR}" + "JANA_VERSION_MINOR=${JANA_VERSION_MINOR}" + "JANA_VERSION_PATCH=${JANA_VERSION_PATCH}") target_link_libraries(${_name}_plugin ${JANA_LIB} podio::podio podio::podioRootIO spdlog::spdlog fmt::fmt) target_link_libraries(${_name}_plugin Microsoft.GSL::GSL) @@ -82,6 +68,11 @@ macro(plugin_add _name) PROPERTIES PREFIX "lib" OUTPUT_NAME "${_name}" SUFFIX ${suffix}) + target_compile_definitions( + ${PLUGIN_NAME}_library + PRIVATE "JANA_VERSION_MAJOR=${JANA_VERSION_MAJOR}" + "JANA_VERSION_MINOR=${JANA_VERSION_MINOR}" + "JANA_VERSION_PATCH=${JANA_VERSION_PATCH}") target_include_directories( ${_name}_library diff --git a/src/algorithms/onnx/CalorimeterParticleIDPostML.cc b/src/algorithms/onnx/CalorimeterParticleIDPostML.cc new file mode 100644 index 000000000..7c2b2199a --- /dev/null +++ b/src/algorithms/onnx/CalorimeterParticleIDPostML.cc @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024 Dmitry Kalinkin + +#include + +#if EDM4EIC_VERSION_MAJOR >= 8 +#include +#include +#include +#include + +#include "CalorimeterParticleIDPostML.h" + +namespace eicrecon { + + void CalorimeterParticleIDPostML::init() { + // Nothing + } + + void CalorimeterParticleIDPostML::process( + const CalorimeterParticleIDPostML::Input& input, + const CalorimeterParticleIDPostML::Output& output) const { + + const auto [in_clusters, in_assocs, prediction_tensors] = input; + auto [out_clusters, out_assocs, out_particle_ids] = output; + + if (prediction_tensors->size() != 1) { + error("Expected to find a single tensor, found {}", prediction_tensors->size()); + throw std::runtime_error(""); + } + edm4eic::Tensor prediction_tensor = (*prediction_tensors)[0]; + + if (prediction_tensor.shape_size() != 2) { + error("Expected tensor rank to be 2, but it is {}", prediction_tensor.shape_size()); + throw std::runtime_error(fmt::format("Expected tensor rank to be 2, but it is {}", prediction_tensor.shape_size())); + } + + if (prediction_tensor.getShape(0) != in_clusters->size()) { + error("Length mismatch between tensor's 0th axis and number of clusters: {} != {}", prediction_tensor.getShape(0), in_clusters->size()); + throw std::runtime_error(fmt::format("Length mismatch between tensor's 0th axis and number of clusters: {} != {}", prediction_tensor.getShape(0), in_clusters->size())); + } + + if (prediction_tensor.getShape(1) != 2) { + error("Expected 2 values per cluster in the output tensor, got {}", prediction_tensor.getShape(0)); + throw std::runtime_error(fmt::format("Expected 2 values per cluster in the output tensor, got {}", prediction_tensor.getShape(0))); + } + + if (prediction_tensor.getElementType() != 1) { // 1 - float + error("Expected a tensor of floats, but element type is {}", prediction_tensor.getElementType()); + throw std::runtime_error(fmt::format("Expected a tensor of floats, but element type is {}", prediction_tensor.getElementType())); + } + + for (size_t cluster_ix = 0; cluster_ix < in_clusters->size(); cluster_ix++) { + edm4eic::Cluster in_cluster = (*in_clusters)[cluster_ix]; + edm4eic::MutableCluster out_cluster = in_cluster.clone(); + out_clusters->push_back(out_cluster); + + float prob_pion = prediction_tensor.getFloatData(cluster_ix * prediction_tensor.getShape(1) + 0); + float prob_electron = prediction_tensor.getFloatData(cluster_ix * prediction_tensor.getShape(1) + 1); + + out_cluster.addToParticleIDs(out_particle_ids->create( + 0, // std::int32_t type + 211, // std::int32_t PDG + 0, // std::int32_t algorithmType + prob_pion // float likelihood + )); + out_cluster.addToParticleIDs(out_particle_ids->create( + 0, // std::int32_t type + 11, // std::int32_t PDG + 0, // std::int32_t algorithmType + prob_electron // float likelihood + )); + + // propagate associations + for (auto in_assoc : *in_assocs) { + if (in_assoc.getRec() == in_cluster) { + auto out_assoc = in_assoc.clone(); + out_assoc.setRec(out_cluster); + out_assocs->push_back(out_assoc); + } + } + } + } + +} // namespace eicrecon +#endif diff --git a/src/algorithms/onnx/CalorimeterParticleIDPostML.h b/src/algorithms/onnx/CalorimeterParticleIDPostML.h new file mode 100644 index 000000000..dbc2cb93e --- /dev/null +++ b/src/algorithms/onnx/CalorimeterParticleIDPostML.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024 Dmitry Kalinkin + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "algorithms/interfaces/WithPodConfig.h" + +namespace eicrecon { + +using CalorimeterParticleIDPostMLAlgorithm = + algorithms::Algorithm< + algorithms::Input, + edm4eic::TensorCollection>, + algorithms::Output, + edm4hep::ParticleIDCollection> + >; + +class CalorimeterParticleIDPostML : public CalorimeterParticleIDPostMLAlgorithm, + public WithPodConfig { + +public: + CalorimeterParticleIDPostML(std::string_view name) + : CalorimeterParticleIDPostMLAlgorithm{name, + {"inputClusters", "inputClusterAssociations", "inputPredictionsTensor"}, + {"outputClusters", "outputClusterAssociations", "outputParticleIDs"}, + ""} { + } + + void init() final; + void process(const Input&, const Output&) const final; +}; + +} // namespace eicrecon diff --git a/src/algorithms/onnx/CalorimeterParticleIDPreML.cc b/src/algorithms/onnx/CalorimeterParticleIDPreML.cc new file mode 100644 index 000000000..89d2175d7 --- /dev/null +++ b/src/algorithms/onnx/CalorimeterParticleIDPreML.cc @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024 Dmitry Kalinkin + +#include + +#if EDM4EIC_VERSION_MAJOR >= 8 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CalorimeterParticleIDPreML.h" + +namespace eicrecon { + + void CalorimeterParticleIDPreML::init() { + // Nothing + } + + void CalorimeterParticleIDPreML::process( + const CalorimeterParticleIDPreML::Input& input, + const CalorimeterParticleIDPreML::Output& output) const { + + const auto [clusters, cluster_assocs] = input; + auto [feature_tensors, target_tensors] = output; + + edm4eic::MutableTensor feature_tensor = feature_tensors->create(); + feature_tensor.addToShape(clusters->size()); + feature_tensor.addToShape(11); // p, E/p, azimuthal, polar, 7 shape parameters + feature_tensor.setElementType(1); // 1 - float + + edm4eic::MutableTensor target_tensor; + if (cluster_assocs) { + target_tensor = target_tensors->create(); + target_tensor.addToShape(clusters->size()); + target_tensor.addToShape(2); // is electron, is hadron + target_tensor.setElementType(7); // 7 - int64 + } + + for (edm4eic::Cluster cluster : *clusters) { + double momentum; + { + // FIXME: use track momentum once matching to tracks becomes available + edm4eic::MCRecoClusterParticleAssociation best_assoc; + for (auto assoc : *cluster_assocs) { + if (assoc.getRec() == cluster) { + if ((not best_assoc.isAvailable()) || (assoc.getWeight() > best_assoc.getWeight())) { + best_assoc = assoc; + } + } + } + if (best_assoc.isAvailable()) { + momentum = edm4hep::utils::magnitude(best_assoc.getSim().getMomentum()); + } else { + warning("Can't find association for cluster. Skipping..."); + continue; + } + } + + feature_tensor.addToFloatData(momentum); + feature_tensor.addToFloatData(cluster.getEnergy() / momentum); + auto pos = cluster.getPosition(); + feature_tensor.addToFloatData(edm4hep::utils::anglePolar(pos)); + feature_tensor.addToFloatData(edm4hep::utils::angleAzimuthal(pos)); + for (int par_ix = 0; par_ix < cluster.shapeParameters_size(); par_ix++) { + feature_tensor.addToFloatData(cluster.getShapeParameters(par_ix)); + } + + if (cluster_assocs) { + edm4eic::MCRecoClusterParticleAssociation best_assoc; + for (auto assoc : *cluster_assocs) { + if (assoc.getRec() == cluster) { + if ((not best_assoc.isAvailable()) || (assoc.getWeight() > best_assoc.getWeight())) { + best_assoc = assoc; + } + } + } + int64_t is_electron = 0, is_pion = 0; + if (best_assoc.isAvailable()) { + is_electron = best_assoc.getSim().getPDG() == 11; + is_pion = best_assoc.getSim().getPDG() != 11; + } + target_tensor.addToInt64Data(is_pion); + target_tensor.addToInt64Data(is_electron); + } + } + + size_t expected_num_entries = feature_tensor.getShape(0) * feature_tensor.getShape(1); + if (feature_tensor.floatData_size() != expected_num_entries) { + error("Inconsistent output tensor shape and element count: {} != {}", feature_tensor.floatData_size(), expected_num_entries); + throw std::runtime_error(fmt::format("Inconsistent output tensor shape and element count: {} != {}", feature_tensor.floatData_size(), expected_num_entries)); + } + } + +} // namespace eicrecon +#endif diff --git a/src/algorithms/onnx/CalorimeterParticleIDPreML.h b/src/algorithms/onnx/CalorimeterParticleIDPreML.h new file mode 100644 index 000000000..bc4b757eb --- /dev/null +++ b/src/algorithms/onnx/CalorimeterParticleIDPreML.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024 Dmitry Kalinkin + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "algorithms/interfaces/WithPodConfig.h" + +namespace eicrecon { + +using CalorimeterParticleIDPreMLAlgorithm = + algorithms::Algorithm>, + algorithms::Output>>; + +class CalorimeterParticleIDPreML : public CalorimeterParticleIDPreMLAlgorithm, + public WithPodConfig { + +public: + CalorimeterParticleIDPreML(std::string_view name) + : CalorimeterParticleIDPreMLAlgorithm{name, + {"inputClusters"}, + {"outputFeatureTensor", "outputTargetTensor"}, + ""} { + } + + void init() final; + void process(const Input&, const Output&) const final; +}; + +} // namespace eicrecon diff --git a/src/algorithms/onnx/InclusiveKinematicsML.cc b/src/algorithms/onnx/InclusiveKinematicsML.cc index 2483d6c62..091e637ef 100644 --- a/src/algorithms/onnx/InclusiveKinematicsML.cc +++ b/src/algorithms/onnx/InclusiveKinematicsML.cc @@ -34,6 +34,8 @@ namespace eicrecon { // onnxruntime setup m_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "inclusive-kinematics-ml"); Ort::SessionOptions session_options; + session_options.SetInterOpNumThreads(1); + session_options.SetIntraOpNumThreads(1); try { m_session = Ort::Session(m_env, m_cfg.modelPath.c_str(), session_options); diff --git a/src/algorithms/onnx/ONNXInference.cc b/src/algorithms/onnx/ONNXInference.cc new file mode 100644 index 000000000..00665cd85 --- /dev/null +++ b/src/algorithms/onnx/ONNXInference.cc @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2022 - 2024 Wouter Deconinck, Tooba Ali, Dmitry Kalinkin + +#include + +#if EDM4EIC_VERSION_MAJOR >= 8 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ONNXInference.h" + +namespace eicrecon { + + static std::string print_shape(const std::vector& v) { + std::stringstream ss(""); + for (std::size_t i = 0; i < v.size() - 1; i++) ss << v[i] << " x "; + ss << v[v.size() - 1]; + return ss.str(); + } + + static bool check_shape_consistency(const std::vector& shape1, const std::vector& shape2) { + if (shape2.size() != shape1.size()) { + return false; + } + for (size_t ix = 0; ix < shape1.size(); ix++) { + if ((shape1[ix] != -1) && (shape2[ix] != -1) && (shape1[ix] != shape2[ix])) { + return false; + } + } + return true; + } + + template + static Ort::Value iters_to_tensor( + typename std::vector::const_iterator data_begin, + typename std::vector::const_iterator data_end, + std::vector::const_iterator shape_begin, + std::vector::const_iterator shape_end + ) { + Ort::MemoryInfo mem_info = + Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + auto tensor = Ort::Value::CreateTensor(mem_info, const_cast(&*data_begin), data_end - data_begin, &*shape_begin, shape_end - shape_begin); + return tensor; + } + + void ONNXInference::init() { + // onnxruntime setup + m_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, name().data()); + Ort::SessionOptions session_options; + session_options.SetInterOpNumThreads(1); + session_options.SetIntraOpNumThreads(1); + try { + m_session = Ort::Session(m_env, m_cfg.modelPath.c_str(), session_options); + Ort::AllocatorWithDefaultOptions allocator; + + // print name/shape of inputs + debug("Input Node Name/Shape:"); + for (std::size_t i = 0; i < m_session.GetInputCount(); i++) { + m_input_names.emplace_back(m_session.GetInputNameAllocated(i, allocator).get()); + m_input_shapes.emplace_back(m_session.GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + debug("\t{} : {}", m_input_names.at(i), print_shape(m_input_shapes.at(i))); + } + + // print name/shape of outputs + debug("Output Node Name/Shape: {}", m_session.GetOutputCount()); + for (std::size_t i = 0; i < m_session.GetOutputCount(); i++) { + m_output_names.emplace_back(m_session.GetOutputNameAllocated(i, allocator).get()); + + if (m_session.GetOutputTypeInfo(i).GetONNXType() != ONNX_TYPE_TENSOR) { + m_output_shapes.emplace_back(); + debug("\t{} : not a tensor", m_output_names.at(i)); + } else { + m_output_shapes.emplace_back(m_session.GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + debug("\t{} : {}", m_output_names.at(i), print_shape(m_output_shapes.at(i))); + } + } + + // convert names to char* + m_input_names_char.resize(m_input_names.size(), nullptr); + std::transform(std::begin(m_input_names), std::end(m_input_names), std::begin(m_input_names_char), + [&](const std::string& str) { return str.c_str(); }); + m_output_names_char.resize(m_output_names.size(), nullptr); + std::transform(std::begin(m_output_names), std::end(m_output_names), std::begin(m_output_names_char), + [&](const std::string& str) { return str.c_str(); }); + + } catch(const Ort::Exception& exception) { + error("ONNX error {}", exception.what()); + throw; + } + } + + void ONNXInference::process( + const ONNXInference::Input& input, + const ONNXInference::Output& output) const { + + const auto [in_tensors] = input; + auto [out_tensors] = output; + + // Require valid inputs + if (in_tensors.size() != m_input_names.size()) { + error("The ONNX model requires {} tensors, whereas {} were provided", m_input_names.size(), in_tensors.size()); + throw std::runtime_error(fmt::format("The ONNX model requires {} tensors, whereas {} were provided", m_input_names.size(), in_tensors.size())); + } + + // Prepare input tensor + std::vector input_tensor_values; + std::vector input_tensors; + + for (int ix = 0; ix < m_input_names.size(); ix++) { + edm4eic::Tensor in_tensor = in_tensors[ix]->at(0); + if (in_tensor.getElementType() == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { + input_tensors.emplace_back(iters_to_tensor( + in_tensor.floatData_begin(), + in_tensor.floatData_end(), + in_tensor.shape_begin(), + in_tensor.shape_end() + )); + } else if (in_tensor.getElementType() == ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64) { + input_tensors.emplace_back(iters_to_tensor( + in_tensor.int64Data_begin(), + in_tensor.int64Data_end(), + in_tensor.shape_begin(), + in_tensor.shape_end() + )); + } + + auto input_shape = input_tensors[ix].GetTensorTypeAndShapeInfo().GetShape(); + std::vector input_expected_shape = m_input_shapes[ix]; + if (!check_shape_consistency(input_shape, input_expected_shape)) { + error("Input tensor shape incorrect {} != {}", print_shape(input_shape), print_shape(input_expected_shape)); + throw std::runtime_error(fmt::format("Input tensor shape incorrect {} != {}", print_shape(input_shape), print_shape(input_expected_shape))); + } + } + + // Attempt inference + std::vector onnx_values; + try { + onnx_values = m_session.Run(Ort::RunOptions{nullptr}, m_input_names_char.data(), input_tensors.data(), + m_input_names_char.size(), m_output_names_char.data(), m_output_names_char.size()); + } catch (const Ort::Exception& exception) { + error("Error running model inference: {}", exception.what()); + throw; + } + + try { + for (size_t ix = 0; ix < onnx_values.size(); ix++) { + Ort::Value &onnx_tensor = onnx_values[ix]; + if (!onnx_tensor.IsTensor()) { + error("The output \"{}\" is not a tensor. ONNXType {} is not yet supported. Skipping...", + m_output_names_char[ix], + static_cast(onnx_tensor.GetTypeInfo().GetONNXType())); + continue; + } + auto onnx_tensor_type = onnx_tensor.GetTensorTypeAndShapeInfo(); + edm4eic::MutableTensor out_tensor = out_tensors[ix]->create(); + out_tensor.setElementType(static_cast(onnx_tensor_type.GetElementType())); + size_t num_values = 1; + for (int64_t dim_size : onnx_tensor_type.GetShape()) { + out_tensor.addToShape(dim_size); + num_values *= dim_size; + } + if (onnx_tensor_type.GetElementType() == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { + auto *data = onnx_tensor.GetTensorMutableData(); + for (size_t value_ix = 0; value_ix < num_values; value_ix++) { + out_tensor.addToFloatData(data[value_ix]); + } + } else if (onnx_tensor_type.GetElementType() == ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64) { + auto *data = onnx_tensor.GetTensorMutableData(); + for (size_t value_ix = 0; value_ix < num_values; value_ix++) { + out_tensor.addToInt64Data(data[value_ix]); + } + } else { + error("Unsupported ONNXTensorElementDataType {}", static_cast(onnx_tensor_type.GetElementType())); + } + } + } catch (const Ort::Exception& exception) { + error("Error running model inference: {}", exception.what()); + throw; + } + } + +} // namespace eicrecon +#endif diff --git a/src/algorithms/onnx/ONNXInference.h b/src/algorithms/onnx/ONNXInference.h new file mode 100644 index 000000000..8cc8e9123 --- /dev/null +++ b/src/algorithms/onnx/ONNXInference.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2022 - 2024 Sylvester Joosten, Dmitry Romanov, Wouter Deconinck, Dmitry Kalinkin + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "algorithms/interfaces/WithPodConfig.h" +#include "algorithms/onnx/ONNXInferenceConfig.h" + +namespace eicrecon { + +using ONNXInferenceAlgorithm = + algorithms::Algorithm>, + algorithms::Output>>; + +class ONNXInference : public ONNXInferenceAlgorithm, + public WithPodConfig { + +public: + ONNXInference(std::string_view name) + : ONNXInferenceAlgorithm{name, + {"inputTensors"}, + {"outputTensors"}, + ""} { + } + + void init() final; + void process(const Input&, const Output&) const final; + +private: + mutable Ort::Env m_env{nullptr}; + mutable Ort::Session m_session{nullptr}; + + std::vector m_input_names; + std::vector m_input_names_char; + std::vector> m_input_shapes; + + std::vector m_output_names; + std::vector m_output_names_char; + std::vector> m_output_shapes; +}; + +} // namespace eicrecon diff --git a/src/algorithms/onnx/ONNXInferenceConfig.h b/src/algorithms/onnx/ONNXInferenceConfig.h new file mode 100644 index 000000000..a6e98204a --- /dev/null +++ b/src/algorithms/onnx/ONNXInferenceConfig.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024 Wouter Deconinck, Dmitry Kalinkin + +#pragma once + +#include + +namespace eicrecon { + + struct ONNXInferenceConfig { + + std::string modelPath; + + }; + +} // eicrecon diff --git a/src/algorithms/reco/UndoAfterBurner.cc b/src/algorithms/reco/UndoAfterBurner.cc index 69430e97e..129674f47 100644 --- a/src/algorithms/reco/UndoAfterBurner.cc +++ b/src/algorithms/reco/UndoAfterBurner.cc @@ -115,6 +115,9 @@ void eicrecon::UndoAfterBurner::process( // Now, loop through events and apply operations to the MCparticles for (const auto& p: *mcparts) { + if (p.isCreatedInSimulation()) { + continue; + } ROOT::Math::PxPyPzEVector mc(p.getMomentum().x, p.getMomentum().y, p.getMomentum().z, p.getEnergy()); diff --git a/src/algorithms/tracking/CKFTracking.cc b/src/algorithms/tracking/CKFTracking.cc index 0f809fff0..ba07f9356 100644 --- a/src/algorithms/tracking/CKFTracking.cc +++ b/src/algorithms/tracking/CKFTracking.cc @@ -7,6 +7,10 @@ #include #include #include +#if Acts_VERSION_MAJOR >= 32 +#include +#endif +#include #if Acts_VERSION_MAJOR < 36 #include #endif @@ -56,6 +60,7 @@ #include #include #include +#include #include #include "ActsGeometryProvider.h" @@ -260,6 +265,7 @@ namespace eicrecon { #else Acts::TrackAccessor seedNumber("seed"); #endif + std::vector failed_tracks; // Loop over seeds for (std::size_t iseed = 0; iseed < acts_init_trk_params.size(); ++iseed) { @@ -291,6 +297,7 @@ namespace eicrecon { ACTS_ERROR("Extrapolation for seed " << iseed << " and track " << track.index() << " failed with error " << extrapolationResult.error()); + failed_tracks.push_back(track.index()); continue; } #endif @@ -299,6 +306,18 @@ namespace eicrecon { } } + for (Acts::TrackIndexType track_index : std::ranges::reverse_view(failed_tracks)) { + // NOTE This does not remove track states corresponding to the + // removed tracks. Doing so would require implementing some garbage + // collection. We'll just assume no algorithm will access them + // directly. + acts_tracks.removeTrack(track_index); +#if Acts_VERSION_MAJOR < 36 + // Workaround an upstream bug in Acts::VectorTrackContainer::removeTrack_impl() + // https://github.com/acts-project/acts/commit/94cf81f3f1109210b963977e0904516b949b1154 + trackContainer->m_particleHypothesis.erase(trackContainer->m_particleHypothesis.begin() + track_index); +#endif + } // Move track states and track container to const containers // NOTE Using the non-const containers leads to references to @@ -336,15 +355,6 @@ namespace eicrecon { std::optional lastSeed; for (const auto& track : constTracks) { -#if Acts_VERSION_MAJOR >= 34 - // Some B0 tracks fail to extrapolate to the perigee surface. The - // Acts::extrapolateTrackToReferenceSurface will not set - // referenceSurface in that case, which is what we check here. - if (not track.hasReferenceSurface()) { - m_log->warn("Skipping a track not on perigee surface"); - continue; - } -#endif if (!lastSeed) { lastSeed = constSeedNumber(track); } diff --git a/src/algorithms/tracking/OrthogonalTrackSeedingConfig.h b/src/algorithms/tracking/OrthogonalTrackSeedingConfig.h index 048be3973..533997784 100644 --- a/src/algorithms/tracking/OrthogonalTrackSeedingConfig.h +++ b/src/algorithms/tracking/OrthogonalTrackSeedingConfig.h @@ -38,6 +38,8 @@ namespace eicrecon { float rMinMiddle = 20. * Acts::UnitConstants::mm; // Middle spacepoint must fall between these two radii float rMaxMiddle = 400. * Acts::UnitConstants::mm; + float deltaPhiMax = 0.085; // Max difference in phi between middle and either top or bottom sp + ////////////////////////////////////////////////////////////////////////// /// SEED FILTER GENERAL PARAMETERS /// The parameters below control the process of filtering out seeds before diff --git a/src/algorithms/tracking/TrackProjector.cc b/src/algorithms/tracking/TrackProjector.cc index fbf9d8fd4..7a5815dc5 100644 --- a/src/algorithms/tracking/TrackProjector.cc +++ b/src/algorithms/tracking/TrackProjector.cc @@ -153,7 +153,7 @@ namespace eicrecon { static_cast(boundCov(Acts::eBoundPhi, Acts::eBoundQOverP)) }; const float time{static_cast(boundParams(Acts::eBoundTime))}; - const float timeError{sqrt(static_cast(boundCov(Acts::eBoundTime, Acts::eBoundTime)))}; + const float timeError{static_cast(sqrt(boundCov(Acts::eBoundTime, Acts::eBoundTime)))}; const float theta(boundParams[Acts::eBoundTheta]); const float phi(boundParams[Acts::eBoundPhi]); const decltype(edm4eic::TrackPoint::directionError) directionError{ diff --git a/src/algorithms/tracking/TrackPropagation.cc b/src/algorithms/tracking/TrackPropagation.cc index bafe82f42..6ad4e9813 100644 --- a/src/algorithms/tracking/TrackPropagation.cc +++ b/src/algorithms/tracking/TrackPropagation.cc @@ -295,7 +295,7 @@ void TrackPropagation::propagateToSurfaceList( // time const float time{static_cast(parameter(Acts::eBoundTime))}; - const float timeError{sqrt(static_cast(covariance(Acts::eBoundTime, Acts::eBoundTime)))}; + const float timeError{static_cast(sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)))}; // Direction const float theta(parameter[Acts::eBoundTheta]); diff --git a/src/algorithms/tracking/TrackSeeding.cc b/src/algorithms/tracking/TrackSeeding.cc index c1094e78c..3c285d631 100644 --- a/src/algorithms/tracking/TrackSeeding.cc +++ b/src/algorithms/tracking/TrackSeeding.cc @@ -104,6 +104,7 @@ void eicrecon::TrackSeeding::configure() { m_seedFinderConfig.impactMax = m_cfg.impactMax; m_seedFinderConfig.rMinMiddle = m_cfg.rMinMiddle; m_seedFinderConfig.rMaxMiddle = m_cfg.rMaxMiddle; + m_seedFinderConfig.deltaPhiMax = m_cfg.deltaPhiMax; m_seedFinderOptions.beamPos = Acts::Vector2(m_cfg.beamPosX, m_cfg.beamPosY); m_seedFinderOptions.bFieldInZ = m_cfg.bFieldInZ; diff --git a/src/detectors/EEMC/CMakeLists.txt b/src/detectors/EEMC/CMakeLists.txt index 90d48a9b0..7478a5546 100644 --- a/src/detectors/EEMC/CMakeLists.txt +++ b/src/detectors/EEMC/CMakeLists.txt @@ -15,6 +15,7 @@ plugin_glob_all(${PLUGIN_NAME}) # Find dependencies plugin_add_dd4hep(${PLUGIN_NAME}) plugin_add_event_model(${PLUGIN_NAME}) +plugin_add_onnxruntime(${PLUGIN_NAME}) # Add include directories (works same as target_include_directories) # plugin_include_directories(${PLUGIN_NAME} SYSTEM PUBLIC ... ) diff --git a/src/detectors/EEMC/EEMC.cc b/src/detectors/EEMC/EEMC.cc index 8908c7ac7..035b4ab3d 100644 --- a/src/detectors/EEMC/EEMC.cc +++ b/src/detectors/EEMC/EEMC.cc @@ -15,8 +15,15 @@ #include "factories/calorimetry/CalorimeterHitDigi_factory.h" #include "factories/calorimetry/CalorimeterHitReco_factory.h" #include "factories/calorimetry/CalorimeterIslandCluster_factory.h" +#if EDM4EIC_VERSION_MAJOR >= 8 +#include "factories/calorimetry/CalorimeterParticleIDPostML_factory.h" +#include "factories/calorimetry/CalorimeterParticleIDPreML_factory.h" +#endif #include "factories/calorimetry/CalorimeterTruthClustering_factory.h" #include "factories/calorimetry/TrackClusterMergeSplitter_factory.h" +#if EDM4EIC_VERSION_MAJOR >= 8 +#include "factories/meta/ONNXInference_factory.h" +#endif extern "C" { void InitPlugin(JApplication *app) { @@ -112,15 +119,24 @@ extern "C" { app->Add( new JOmniFactoryGeneratorT( +#if EDM4EIC_VERSION_MAJOR >= 8 + "EcalEndcapNClustersWithoutPID", +#else "EcalEndcapNClusters", +#endif {"EcalEndcapNIslandProtoClusters", // edm4eic::ProtoClusterCollection #if EDM4EIC_VERSION_MAJOR >= 7 "EcalEndcapNRawHitAssociations"}, // edm4eic::MCRecoCalorimeterHitAssociationCollection #else "EcalEndcapNHits"}, // edm4hep::SimCalorimeterHitCollection #endif +#if EDM4EIC_VERSION_MAJOR >= 8 + {"EcalEndcapNClustersWithoutPID", // edm4eic::Cluster + "EcalEndcapNClusterAssociationsWithoutPID"}, // edm4eic::MCRecoClusterParticleAssociation +#else {"EcalEndcapNClusters", // edm4eic::Cluster "EcalEndcapNClusterAssociations"}, // edm4eic::MCRecoClusterParticleAssociation +#endif { .energyWeight = "log", .sampFrac = 1.0, @@ -150,6 +166,49 @@ extern "C" { ) ); +#if EDM4EIC_VERSION_MAJOR >= 8 + app->Add(new JOmniFactoryGeneratorT( + "EcalEndcapNParticleIDPreML", + { + "EcalEndcapNClustersWithoutPID", + "EcalEndcapNClusterAssociationsWithoutPID", + }, + { + "EcalEndcapNParticleIDInput_features", + "EcalEndcapNParticleIDTarget", + }, + app + )); + app->Add(new JOmniFactoryGeneratorT( + "EcalEndcapNParticleIDInference", + { + "EcalEndcapNParticleIDInput_features", + }, + { + "EcalEndcapNParticleIDOutput_label", + "EcalEndcapNParticleIDOutput_probability_tensor", + }, + { + .modelPath = "calibrations/onnx/EcalEndcapN_pi_rejection.onnx", + }, + app + )); + app->Add(new JOmniFactoryGeneratorT( + "EcalEndcapNParticleIDPostML", + { + "EcalEndcapNClustersWithoutPID", + "EcalEndcapNClusterAssociationsWithoutPID", + "EcalEndcapNParticleIDOutput_probability_tensor", + }, + { + "EcalEndcapNClusters", + "EcalEndcapNClusterAssociations", + "EcalEndcapNClusterParticleIDs", + }, + app + )); +#endif + app->Add( new JOmniFactoryGeneratorT( "EcalEndcapNSplitMergeClusters", diff --git a/src/factories/calorimetry/CalorimeterParticleIDPostML_factory.h b/src/factories/calorimetry/CalorimeterParticleIDPostML_factory.h new file mode 100644 index 000000000..51769c13c --- /dev/null +++ b/src/factories/calorimetry/CalorimeterParticleIDPostML_factory.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024, Dmitry Kalinkin + +#pragma once + +#include "algorithms/onnx/CalorimeterParticleIDPostML.h" +#include "services/algorithms_init/AlgorithmsInit_service.h" +#include "extensions/jana/JOmniFactory.h" + + +namespace eicrecon { + +class CalorimeterParticleIDPostML_factory : public JOmniFactory { + +public: + using AlgoT = eicrecon::CalorimeterParticleIDPostML; +private: + std::unique_ptr m_algo; + + PodioInput m_cluster_input {this}; + PodioInput m_cluster_assoc_input {this}; + PodioInput m_prediction_tensor_input {this}; + + PodioOutput m_cluster_output {this}; + PodioOutput m_cluster_assoc_output {this}; + PodioOutput m_particle_id_output {this}; + +public: + void Configure() { + m_algo = std::make_unique(GetPrefix()); + m_algo->level(static_cast(logger()->level())); + m_algo->applyConfig(config()); + m_algo->init(); + } + + void ChangeRun(int64_t run_number) { + } + + void Process(int64_t run_number, uint64_t event_number) { + m_algo->process({m_cluster_input(), m_cluster_assoc_input(), m_prediction_tensor_input()}, + {m_cluster_output().get(), m_cluster_assoc_output().get(), m_particle_id_output().get()}); + } +}; + +} // eicrecon diff --git a/src/factories/calorimetry/CalorimeterParticleIDPreML_factory.h b/src/factories/calorimetry/CalorimeterParticleIDPreML_factory.h new file mode 100644 index 000000000..253abf1f6 --- /dev/null +++ b/src/factories/calorimetry/CalorimeterParticleIDPreML_factory.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024, Dmitry Kalinkin + +#pragma once + +#include "algorithms/onnx/CalorimeterParticleIDPreML.h" +#include "services/algorithms_init/AlgorithmsInit_service.h" +#include "extensions/jana/JOmniFactory.h" + + +namespace eicrecon { + +class CalorimeterParticleIDPreML_factory : public JOmniFactory { + +public: + using AlgoT = eicrecon::CalorimeterParticleIDPreML; +private: + std::unique_ptr m_algo; + + PodioInput m_cluster_input {this}; + PodioInput m_cluster_assoc_input {this}; + + PodioOutput m_feature_tensor_output {this}; + PodioOutput m_target_tensor_output {this}; + +public: + void Configure() { + m_algo = std::make_unique(GetPrefix()); + m_algo->level(static_cast(logger()->level())); + m_algo->applyConfig(config()); + m_algo->init(); + } + + void ChangeRun(int64_t run_number) { + } + + void Process(int64_t run_number, uint64_t event_number) { + m_algo->process({m_cluster_input(), m_cluster_assoc_input()}, + {m_feature_tensor_output().get(), m_target_tensor_output().get()}); + } +}; + +} // eicrecon diff --git a/src/factories/meta/ONNXInference_factory.h b/src/factories/meta/ONNXInference_factory.h new file mode 100644 index 000000000..683778351 --- /dev/null +++ b/src/factories/meta/ONNXInference_factory.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2023 - 2024, Wouter Deconinck, Simon Gardener, Dmitry Kalinkin + +#pragma once + +#include "algorithms/onnx/ONNXInference.h" +#include "services/algorithms_init/AlgorithmsInit_service.h" +#include "extensions/jana/JOmniFactory.h" + + +namespace eicrecon { + +class ONNXInference_factory : public JOmniFactory { + +public: + using AlgoT = eicrecon::ONNXInference; +private: + std::unique_ptr m_algo; + + VariadicPodioInput m_input_tensors {this}; + + VariadicPodioOutput m_output_tensors {this}; + + ParameterRef m_modelPath {this, "modelPath", config().modelPath}; + + Service m_algorithmsInit {this}; + +public: + void Configure() { + m_algo = std::make_unique(GetPrefix()); + m_algo->level(static_cast(logger()->level())); + m_algo->applyConfig(config()); + m_algo->init(); + } + + void ChangeRun(int64_t run_number) { + } + + void Process(int64_t run_number, uint64_t event_number) { + std::vector> in_collections; + for (const auto& in_collection : m_input_tensors()) { + in_collections.push_back(gsl::not_null{in_collection}); + } + + std::vector> out_collections; + for (const auto& out_collection : m_output_tensors()) { + out_collections.push_back(gsl::not_null{out_collection.get()}); + } + + m_algo->process(in_collections, + out_collections); + } +}; + +} // eicrecon diff --git a/src/global/tracking/TrackSeeding_factory.h b/src/global/tracking/TrackSeeding_factory.h index 181e36cdb..12695bc72 100644 --- a/src/global/tracking/TrackSeeding_factory.h +++ b/src/global/tracking/TrackSeeding_factory.h @@ -50,6 +50,7 @@ class TrackSeeding_factory : ParameterRef m_impactMax {this, "impactMax", config().impactMax, "maximum impact parameter allowed for seeds for Acts::OrthogonalSeedFinder. rMin should be larger than impactMax."}; ParameterRef m_rMinMiddle {this, "rMinMiddle", config().rMinMiddle, "min radius for middle space point for Acts::OrthogonalSeedFinder"}; ParameterRef m_rMaxMiddle {this, "rMaxMiddle", config().rMaxMiddle, "max radius for middle space point for Acts::OrthogonalSeedFinder"}; + ParameterRef m_deltaPhiMax {this, "deltaPhiMax", config().deltaPhiMax, "Max phi difference between middle and top/bottom space point"}; ParameterRef m_locaError {this, "loc_a_Error", config().locaError, "Error on Loc a for Acts::OrthogonalSeedFinder"}; ParameterRef m_locbError {this, "loc_b_Error", config().locbError, "Error on Loc b for Acts::OrthogonalSeedFinder"}; ParameterRef m_phiError {this, "phi_Error", config().phiError, "Error on phi for Acts::OrthogonalSeedFinder"}; diff --git a/src/services/io/podio/JEventSourcePODIO.cc b/src/services/io/podio/JEventSourcePODIO.cc index 1d60140ff..faf3f4ba3 100644 --- a/src/services/io/podio/JEventSourcePODIO.cc +++ b/src/services/io/podio/JEventSourcePODIO.cc @@ -69,6 +69,9 @@ struct InsertingVisitor { //------------------------------------------------------------------------------ JEventSourcePODIO::JEventSourcePODIO(std::string resource_name, JApplication* app) : JEventSource(resource_name, app) { SetTypeName(NAME_OF_THIS); // Provide JANA with class name +#if JANA_NEW_CALLBACK_STYLE + SetCallbackStyle(CallbackStyle::ExpertMode); // Use new, exception-free Emit() callback +#endif // Tell JANA that we want it to call the FinishEvent() method. // EnableFinishEvent(); @@ -174,7 +177,7 @@ void JEventSourcePODIO::Open() { //------------------------------------------------------------------------------ void JEventSourcePODIO::Close() { // m_reader.close(); - // TODO: ROOTFrameReader does not appear to have a close() method. + // TODO: ROOTReader does not appear to have a close() method. } @@ -185,7 +188,12 @@ void JEventSourcePODIO::Close() { /// /// \param event //------------------------------------------------------------------------------ -void JEventSourcePODIO::GetEvent(std::shared_ptr event) { +#if JANA_NEW_CALLBACK_STYLE +JEventSourcePODIO::Result JEventSourcePODIO::Emit(JEvent& event) { +#else +void JEventSourcePODIO::GetEvent(std::shared_ptr _event) { + auto &event = *_event; +#endif /// Calls to GetEvent are synchronized with each other, which means they can /// read and write state on the JEventSource without causing race conditions. @@ -194,10 +202,12 @@ void JEventSourcePODIO::GetEvent(std::shared_ptr event) { if( Nevents_read >= Nevents_in_file ) { if( m_run_forever ){ Nevents_read = 0; - }else{ - // m_reader.close(); - // TODO:: ROOTFrameReader does not appear to have a close() method. + } else { +#if JANA_NEW_CALLBACK_STYLE + return Result::FailureFinished; +#else throw RETURN_STATUS::kNO_MORE_EVENTS; +#endif } } @@ -208,19 +218,22 @@ void JEventSourcePODIO::GetEvent(std::shared_ptr event) { if (event_headers.size() != 1) { throw JException("Bad event headers: Entry %d contains %d items, but 1 expected.", Nevents_read, event_headers.size()); } - event->SetEventNumber(event_headers[0].getEventNumber()); - event->SetRunNumber(event_headers[0].getRunNumber()); + event.SetEventNumber(event_headers[0].getEventNumber()); + event.SetRunNumber(event_headers[0].getRunNumber()); // Insert contents odf frame into JFactories VisitPodioCollection visit; for (const std::string& coll_name : frame->getAvailableCollections()) { const podio::CollectionBase* collection = frame->get(coll_name); - InsertingVisitor visitor(*event, coll_name); + InsertingVisitor visitor(event, coll_name); visit(visitor, *collection); } - event->Insert(frame.release()); // Transfer ownership from unique_ptr to JFactoryT + event.Insert(frame.release()); // Transfer ownership from unique_ptr to JFactoryT Nevents_read += 1; +#if JANA_NEW_CALLBACK_STYLE + return Result::Success; +#endif } //------------------------------------------------------------------------------ diff --git a/src/services/io/podio/JEventSourcePODIO.h b/src/services/io/podio/JEventSourcePODIO.h index 21dc45c99..6c81ede7f 100644 --- a/src/services/io/podio/JEventSourcePODIO.h +++ b/src/services/io/podio/JEventSourcePODIO.h @@ -8,12 +8,23 @@ #include #include #include +#include +#if podio_VERSION >= PODIO_VERSION(0, 99, 0) +#include +#else #include +#endif #include #include #include #include +#if ((JANA_VERSION_MAJOR == 2) && (JANA_VERSION_MINOR >= 3)) || (JANA_VERSION_MAJOR > 2) +#define JANA_NEW_CALLBACK_STYLE 1 +#else +#define JANA_NEW_CALLBACK_STYLE 0 +#endif + class JEventSourcePODIO : public JEventSource { public: @@ -25,14 +36,23 @@ class JEventSourcePODIO : public JEventSource { void Close() override; +#if JANA_NEW_CALLBACK_STYLE + Result Emit(JEvent& event) override; +#else void GetEvent(std::shared_ptr) override; +#endif static std::string GetDescription(); void PrintCollectionTypeTable(void); protected: +#if podio_VERSION >= PODIO_VERSION(0, 99, 0) + podio::ROOTReader m_reader; +#else podio::ROOTFrameReader m_reader; +#endif + size_t Nevents_in_file = 0; size_t Nevents_read = 0; diff --git a/src/utilities/eicrecon/eicrecon_cli.cpp b/src/utilities/eicrecon/eicrecon_cli.cpp index 3b3720906..eaabf77fb 100644 --- a/src/utilities/eicrecon/eicrecon_cli.cpp +++ b/src/utilities/eicrecon/eicrecon_cli.cpp @@ -410,6 +410,9 @@ int Execute(JApplication* app, UserOptions& options) { app->GetJParameterManager()->SetParameter("jana:parameter_verbosity", 0); } } + if (not app->GetJParameterManager()->Exists("jana:parameter_strictness")) { + app->GetJParameterManager()->SetParameter("jana:parameter_strictness", 2); + } // Run JANA in normal mode try { JSignalHandler::register_handlers(app);