diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 00105903a32..ea1ef7ec79a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -86,3 +86,10 @@ repos: language: system entry: docs/parse_cmake_options.py --write docs/getting_started.md files: ^CMakeLists.txt$ + + - repo: local + hooks: + - id: leftover_conflict_markers + name: Leftover conflict markers + language: system + entry: git diff --staged --check diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index fa3027bb8f6..841fad5c376 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -20,8 +20,8 @@ shopt -s extglob mode=${1:-all} -if ! [[ $mode = @(all|kf|gsf|gx2f|fullchains|simulation) ]]; then - echo "Usage: $0 (outdir)" +if ! [[ $mode = @(all|kf|gsf|gx2f|refit_kf|refit_gsf|fullchains|simulation) ]]; then + echo "Usage: $0 (outdir)" exit 1 fi @@ -152,6 +152,12 @@ fi if [[ "$mode" == "all" || "$mode" == "gx2f" ]]; then run_physmon_gen "Truth Tracking GX2F" "trackfitting_gx2f" fi +if [[ "$mode" == "all" || "$mode" == "refit_kf" ]]; then + run_physmon_gen "Truth Tracking KF refit" "trackrefitting_kf" +fi +if [[ "$mode" == "all" || "$mode" == "refit_gsf" ]]; then + run_physmon_gen "Truth Tracking GSF refit" "trackrefitting_gsf" +fi if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then run_physmon_gen "CKF single muon" "trackfinding_1muon" run_physmon_gen "CKF muon 50" "trackfinding_4muon_50vertices" @@ -411,6 +417,26 @@ if [[ "$mode" == "all" || "$mode" == "gx2f" ]]; then --config CI/physmon/config/trackfitting_gx2f.yml fi +if [[ "$mode" == "all" || "$mode" == "kf_refit" ]]; then + run_histcmp \ + $outdir/data/trackrefitting_kf/performance_trackrefitting.root \ + $refdir/trackrefitting_kf/performance_trackrefitting.root \ + "Truth tracking (KF refit)" \ + trackrefitting_kf/performance_trackrefitting.html \ + trackrefitting_kf/performance_trackrefitting_plots \ + --config CI/physmon/config/trackfitting_kf.yml +fi + +if [[ "$mode" == "all" || "$mode" == "gsf_refit" ]]; then + run_histcmp \ + $outdir/data/trackrefitting_gsf/performance_trackrefitting.root \ + $refdir/trackrefitting_gsf/performance_trackrefitting.root \ + "Truth tracking (GSF refit)" \ + trackrefitting_gsf/performance_trackrefitting.html \ + trackrefitting_gsf/performance_trackrefitting_plots \ + --config CI/physmon/config/trackfitting_gsf.yml +fi + if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then trackfinding "trackfinding | single muon | truth smeared seeding" trackfinding_1muon/truth_smeared trackfinding "trackfinding | single muon | truth estimated seeding" trackfinding_1muon/truth_estimated diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf.root index f7fb66f24ab..2c7626e0516 100644 Binary files a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf.root and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf.root differ diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf_ambi.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf_ambi.root index 8fbd66c0abb..c5d08a93db0 100644 Binary files a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf_ambi.root and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_ckf_ambi.root differ diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_gauss_notime_hist.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_gauss_notime_hist.root index cb71ba79000..64abc1fadaf 100644 Binary files a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_gauss_notime_hist.root and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_gauss_notime_hist.root differ diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_grid_time_hist.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_grid_time_hist.root index 0980823e57c..8181f0608c4 100644 Binary files a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_grid_time_hist.root and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_vertexing_amvf_grid_time_hist.root differ diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/tracksummary_ckf_hist.root b/CI/physmon/reference/trackfinding_ttbar_pu200/tracksummary_ckf_hist.root index dd9bae09c79..e964b96fcab 100644 Binary files a/CI/physmon/reference/trackfinding_ttbar_pu200/tracksummary_ckf_hist.root and b/CI/physmon/reference/trackfinding_ttbar_pu200/tracksummary_ckf_hist.root differ diff --git a/CI/physmon/reference/trackrefitting_gsf/performance_trackrefitting.root b/CI/physmon/reference/trackrefitting_gsf/performance_trackrefitting.root new file mode 100644 index 00000000000..d888ad406d8 Binary files /dev/null and b/CI/physmon/reference/trackrefitting_gsf/performance_trackrefitting.root differ diff --git a/CI/physmon/reference/trackrefitting_kf/performance_trackrefitting.root b/CI/physmon/reference/trackrefitting_kf/performance_trackrefitting.root new file mode 100644 index 00000000000..aade25ba5a2 Binary files /dev/null and b/CI/physmon/reference/trackrefitting_kf/performance_trackrefitting.root differ diff --git a/CI/physmon/workflows/physmon_trackfinding_1muon.py b/CI/physmon/workflows/physmon_trackfinding_1muon.py index d06f99fbdb6..3c87bdee32d 100755 --- a/CI/physmon/workflows/physmon_trackfinding_1muon.py +++ b/CI/physmon/workflows/physmon_trackfinding_1muon.py @@ -11,13 +11,13 @@ EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, ParticleSmearingSigmas, SeedFinderConfigArg, SeedFinderOptionsArg, @@ -72,6 +72,11 @@ def run_ckf_tracking(label, seeding): setup.field, enableInteractions=True, rnd=rnd, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + measurements=(9, None), + removeNeutral=True, + ), ) addDigitization( @@ -86,7 +91,6 @@ def run_ckf_tracking(label, seeding): s, setup.trackingGeometry, setup.field, - TruthSeedRanges(pt=(500 * u.MeV, None), nHits=(9, None)), ParticleSmearingSigmas( # only used by SeedingAlgorithm.TruthSmeared # zero eveything so the CKF has a chance to find the measurements d0=0, @@ -134,7 +138,7 @@ def run_ckf_tracking(label, seeding): setup.trackingGeometry, setup.field, TrackSelectorConfig( - pt=(500 * u.MeV, None), + pt=(0.9 * u.GeV, None), loc0=(-4.0 * u.mm, 4.0 * u.mm), nMeasurementsMin=6, maxHoles=2, diff --git a/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py b/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py index e1ece122c45..f6c3fe110fd 100755 --- a/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py +++ b/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py @@ -11,12 +11,12 @@ EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, SeedFinderConfigArg, SeedFinderOptionsArg, SeedingAlgorithm, @@ -69,6 +69,11 @@ setup.trackingGeometry, setup.field, rnd=rnd, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + measurements=(9, None), + removeNeutral=True, + ), ) addDigitization( @@ -83,7 +88,6 @@ s, setup.trackingGeometry, setup.field, - TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), SeedFinderConfigArg( r=(33 * u.mm, 200 * u.mm), deltaR=(1 * u.mm, 60 * u.mm), @@ -116,7 +120,7 @@ setup.trackingGeometry, setup.field, TrackSelectorConfig( - pt=(500 * u.MeV, None), + pt=(0.9 * u.GeV, None), loc0=(-4.0 * u.mm, 4.0 * u.mm), nMeasurementsMin=6, maxHoles=2, diff --git a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py index 172fa55270a..c8b163c9022 100755 --- a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py +++ b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py @@ -13,7 +13,6 @@ ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, SeedFinderConfigArg, SeedFinderOptionsArg, SeedingAlgorithm, @@ -68,6 +67,11 @@ rho=(0.0, 24 * u.mm), absZ=(0.0, 1.0 * u.m), ), + postSelectParticles=ParticleSelectorConfig( + pt=(0.5 * u.GeV, None), + measurements=(9, None), + removeNeutral=True, + ), ) addDigitization( @@ -82,7 +86,6 @@ s, setup.trackingGeometry, setup.field, - TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), SeedFinderConfigArg( r=(33 * u.mm, 200 * u.mm), deltaR=(1 * u.mm, 60 * u.mm), diff --git a/CI/physmon/workflows/physmon_trackrefitting_gsf.py b/CI/physmon/workflows/physmon_trackrefitting_gsf.py new file mode 100755 index 00000000000..5477121942a --- /dev/null +++ b/CI/physmon/workflows/physmon_trackrefitting_gsf.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import tempfile +from pathlib import Path +import shutil + +import acts +from truth_tracking_gsf_refitting import runRefittingGsf + +from physmon_common import makeSetup + +setup = makeSetup() + +with tempfile.TemporaryDirectory() as temp: + s = acts.examples.Sequencer( + events=10000, + numThreads=-1, + logLevel=acts.logging.INFO, + ) + + tp = Path(temp) + runRefittingGsf( + trackingGeometry=setup.trackingGeometry, + field=setup.field, + digiConfigFile=setup.digiConfig, + outputDir=tp, + s=s, + ) + + s.run() + + perf_file = tp / "performance_gsf_refit.root" + assert perf_file.exists(), "Performance file not found" + shutil.copy(perf_file, setup.outdir / "performance_trackrefitting.root") diff --git a/CI/physmon/workflows/physmon_trackrefitting_kf.py b/CI/physmon/workflows/physmon_trackrefitting_kf.py new file mode 100755 index 00000000000..ea82d26c701 --- /dev/null +++ b/CI/physmon/workflows/physmon_trackrefitting_kf.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import tempfile +from pathlib import Path +import shutil + +import acts +from truth_tracking_kalman_refitting import runRefittingKf + +from physmon_common import makeSetup + +setup = makeSetup() + +with tempfile.TemporaryDirectory() as temp: + s = acts.examples.Sequencer( + events=10000, + numThreads=-1, + logLevel=acts.logging.INFO, + ) + + tp = Path(temp) + runRefittingKf( + trackingGeometry=setup.trackingGeometry, + field=setup.field, + digiConfigFile=setup.digiConfig, + outputDir=tp, + s=s, + ) + + s.run() + + perf_file = tp / "performance_kf_refit.root" + assert perf_file.exists(), "Performance file not found" + shutil.copy(perf_file, setup.outdir / "performance_trackrefitting.root") diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bbfae3efa8..e8e9ab3f583 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,6 @@ option(ACTS_FORCE_ASSERTIONS "Force assertions regardless of build type" OFF) option(ACTS_USE_SYSTEM_LIBS "Use system libraries by default" OFF) # plugins related options option(ACTS_USE_SYSTEM_ACTSVG "Use the ActSVG system library" ${ACTS_USE_SYSTEM_LIBS}) -option(ACTS_USE_SYSTEM_GEOMODEL "Use a system-provided GeoModel installation" ${ACTS_USE_SYSTEM_LIBS}) option(ACTS_USE_SYSTEM_COVFIE "Use a system-provided covfie installation" ${ACTS_USE_SYSTEM_LIBS}) option(ACTS_USE_SYSTEM_DETRAY "Use a system-provided detray installation" ${ACTS_USE_SYSTEM_LIBS}) option(ACTS_USE_SYSTEM_TRACCC "Use a system-provided traccc installation" ${ACTS_USE_SYSTEM_LIBS}) @@ -229,7 +228,6 @@ set(_acts_actsvg_version 0.4.50) set(_acts_boost_version 1.71.0) set(_acts_dd4hep_version 1.21) set(_acts_edm4hep_version 0.7) -set(_acts_geomodel_version 6.3.0) set(_acts_eigen3_version 3.4.0) set(_acts_podio_version 1.0.1) # will try this first set(_acts_podio_fallback_version 0.16) # if not found, will try this one @@ -374,8 +372,24 @@ if(ACTS_BUILD_PLUGIN_JSON) endif() endif() if(ACTS_BUILD_PLUGIN_GEOMODEL) - find_package(GeoModelCore ${_acts_geomodel_version} REQUIRED CONFIG) - find_package(GeoModelIO ${_acts_geomodel_version} REQUIRED CONFIG) + find_package(GeoModelCore CONFIG) + if(NOT GeoModelCore_FOUND) + message( + FATAL_ERROR + "GeoModel not found. Please install GeoModel or set ACTS_BUILD_PLUGIN_GEOMODEL to OFF." + ) + endif() + + set(_gm_ver_min 6.3.0) + + if(GeoModelCore_VERSION VERSION_LESS _gm_ver_min) + message( + FATAL_ERROR + "GeoModel version ${GeoModelCore_VERSION} is insufficient. Please install GeoModel version ${_gm_ver_min} or newer." + ) + endif() + # find other GeoModel components of EXACT same version + find_package(GeoModelIO ${GeoModelCore_VERSION} REQUIRED EXACT CONFIG) endif() if(ACTS_BUILD_PLUGIN_TGEO) find_package( diff --git a/CMakePresets.json b/CMakePresets.json index bee5ab514df..2aef0f3fe94 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -46,7 +46,7 @@ "inherits": "common", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_FLAGS": "-Werror", + "CMAKE_COMPILE_WARNING_AS_ERROR": "ON", "ACTS_FORCE_ASSERTIONS": "ON", "ACTS_ENABLE_LOG_FAILURE_THRESHOLD": "ON", "ACTS_BUILD_BENCHMARKS": "ON", diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index eeb6bc8b92a..554656f514c 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -110,6 +110,7 @@ add_subdirectory(src/MagneticField) add_subdirectory(src/Material) add_subdirectory(src/Navigation) add_subdirectory(src/Propagator) +add_subdirectory(src/Seeding) add_subdirectory(src/Surfaces) add_subdirectory(src/TrackFinding) add_subdirectory(src/TrackFitting) diff --git a/Core/include/Acts/Clusterization/Clusterization.hpp b/Core/include/Acts/Clusterization/Clusterization.hpp index 3ebfd8d562d..a7b4d853b20 100644 --- a/Core/include/Acts/Clusterization/Clusterization.hpp +++ b/Core/include/Acts/Clusterization/Clusterization.hpp @@ -13,6 +13,26 @@ namespace Acts::Ccl { +template +concept HasRetrievableColumnInfo = requires(Cell cell) { + { getCellColumn(cell) } -> std::same_as; +}; + +template +concept HasRetrievableRowInfo = requires(Cell cell) { + { getCellRow(cell) } -> std::same_as; +}; + +template +concept HasRetrievableLabelInfo = requires(Cell cell) { + { getCellLabel(cell) } -> std::same_as; +}; + +template +concept CanAcceptCell = requires(Cell cell, Cluster cluster) { + { clusterAddCell(cluster, cell) } -> std::same_as; +}; + using Label = int; constexpr Label NO_LABEL = 0; @@ -28,17 +48,21 @@ enum class ConnectResult { // Default connection type for 2-D grids: 4- or 8-cell connectivity template + requires(Acts::Ccl::HasRetrievableColumnInfo && + Acts::Ccl::HasRetrievableRowInfo) struct Connect2D { - bool conn8; - Connect2D() : conn8{true} {} + bool conn8{true}; + Connect2D() = default; explicit Connect2D(bool commonCorner) : conn8{commonCorner} {} - ConnectResult operator()(const Cell& ref, const Cell& iter) const; + virtual ConnectResult operator()(const Cell& ref, const Cell& iter) const; + virtual ~Connect2D() = default; }; // Default connection type for 1-D grids: 2-cell connectivity -template +template struct Connect1D { - ConnectResult operator()(const Cell& ref, const Cell& iter) const; + virtual ConnectResult operator()(const Cell& ref, const Cell& iter) const; + virtual ~Connect1D() = default; }; // Default connection type based on GridDim @@ -49,13 +73,16 @@ struct DefaultConnect { }; template -struct DefaultConnect : public Connect2D { - explicit DefaultConnect(bool commonCorner) : Connect2D(commonCorner) {} - DefaultConnect() : DefaultConnect(true) {} +struct DefaultConnect : public Connect1D { + ~DefaultConnect() override = default; }; template -struct DefaultConnect : public Connect1D {}; +struct DefaultConnect : public Connect2D { + explicit DefaultConnect(bool commonCorner) : Connect2D(commonCorner) {} + DefaultConnect() = default; + ~DefaultConnect() override = default; +}; /// @brief labelClusters /// @@ -70,6 +97,8 @@ struct DefaultConnect : public Connect1D {}; template > + requires( + Acts::Ccl::HasRetrievableLabelInfo) void labelClusters(CellCollection& cells, Connect connect = Connect()); /// @brief mergeClusters @@ -82,6 +111,9 @@ void labelClusters(CellCollection& cells, Connect connect = Connect()); /// @return nothing template + requires(GridDim == 1 || GridDim == 2) && + Acts::Ccl::HasRetrievableLabelInfo< + typename CellCollection::value_type> ClusterCollection mergeClusters(CellCollection& /*cells*/); /// @brief createClusters diff --git a/Core/include/Acts/Clusterization/Clusterization.ipp b/Core/include/Acts/Clusterization/Clusterization.ipp index 0aa79b53981..90a69930ce1 100644 --- a/Core/include/Acts/Clusterization/Clusterization.ipp +++ b/Core/include/Acts/Clusterization/Clusterization.ipp @@ -14,60 +14,6 @@ namespace Acts::Ccl::internal { -// Machinery for validating generic Cell/Cluster types at compile-time - -template -struct cellTypeHasRequiredFunctions : std::false_type {}; - -template -struct cellTypeHasRequiredFunctions< - T, 2, - std::void_t())), - decltype(getCellColumn(std::declval())), - decltype(getCellLabel(std::declval()))>> : std::true_type { -}; - -template -struct cellTypeHasRequiredFunctions< - T, 1, - std::void_t())), - decltype(getCellLabel(std::declval()))>> : std::true_type { -}; - -template -struct clusterTypeHasRequiredFunctions : std::false_type {}; - -template -struct clusterTypeHasRequiredFunctions< - T, U, - std::void_t(), std::declval()))>> - : std::true_type {}; - -template -constexpr void staticCheckGridDim() { - static_assert( - GridDim == 1 || GridDim == 2, - "mergeClusters is only defined for grid dimensions of 1 or 2. "); -} - -template -constexpr void staticCheckCellType() { - constexpr bool hasFns = cellTypeHasRequiredFunctions(); - static_assert(hasFns, - "Cell type should have the following functions: " - "'int getCellRow(const Cell&)', " - "'int getCellColumn(const Cell&)', " - "'Label& getCellLabel(Cell&)'"); -} - -template -constexpr void staticCheckClusterType() { - constexpr bool hasFns = clusterTypeHasRequiredFunctions(); - static_assert(hasFns, - "Cluster type should have the following function: " - "'void clusterAddCell(Cluster&, const Cell&)'"); -} - template struct Compare { static_assert(GridDim != 1 && GridDim != 2, @@ -75,25 +21,27 @@ struct Compare { }; // Comparator function object for cells, column-wise ordering -// Specialization for 2-D grid -template -struct Compare { +// Specialization for 1-D grids +template +struct Compare { bool operator()(const Cell& c0, const Cell& c1) const { - int row0 = getCellRow(c0); - int row1 = getCellRow(c1); int col0 = getCellColumn(c0); int col1 = getCellColumn(c1); - return (col0 == col1) ? row0 < row1 : col0 < col1; + return col0 < col1; } }; -// Specialization for 1-D grids +// Specialization for 2-D grid template -struct Compare { + requires(Acts::Ccl::HasRetrievableColumnInfo && + Acts::Ccl::HasRetrievableRowInfo) +struct Compare { bool operator()(const Cell& c0, const Cell& c1) const { + int row0 = getCellRow(c0); + int row1 = getCellRow(c1); int col0 = getCellColumn(c0); int col1 = getCellColumn(c1); - return col0 < col1; + return (col0 == col1) ? row0 < row1 : col0 < col1; } }; @@ -184,6 +132,10 @@ Connections getConnections(typename std::vector::iterator it, } template + requires( + Acts::Ccl::HasRetrievableLabelInfo && + Acts::Ccl::CanAcceptCell) ClusterCollection mergeClustersImpl(CellCollection& cells) { using Cluster = typename ClusterCollection::value_type; @@ -215,6 +167,8 @@ ClusterCollection mergeClustersImpl(CellCollection& cells) { namespace Acts::Ccl { template + requires(Acts::Ccl::HasRetrievableColumnInfo && + Acts::Ccl::HasRetrievableRowInfo) ConnectResult Connect2D::operator()(const Cell& ref, const Cell& iter) const { int deltaRow = std::abs(getCellRow(ref) - getCellRow(iter)); @@ -237,7 +191,7 @@ ConnectResult Connect2D::operator()(const Cell& ref, return ConnectResult::eNoConn; } -template +template ConnectResult Connect1D::operator()(const Cell& ref, const Cell& iter) const { int deltaCol = std::abs(getCellColumn(ref) - getCellColumn(iter)); @@ -267,9 +221,10 @@ void recordEquivalences(const internal::Connections seen, } template + requires( + Acts::Ccl::HasRetrievableLabelInfo) void labelClusters(CellCollection& cells, Connect connect) { using Cell = typename CellCollection::value_type; - internal::staticCheckCellType(); internal::DisjointSets ds{}; @@ -277,7 +232,8 @@ void labelClusters(CellCollection& cells, Connect connect) { std::ranges::sort(cells, internal::Compare()); // First pass: Allocate labels and record equivalences - for (auto it = cells.begin(); it != cells.end(); ++it) { + for (auto it = std::ranges::begin(cells); it != std::ranges::end(cells); + ++it) { const internal::Connections seen = internal::getConnections(it, cells, connect); if (seen.nconn == 0) { @@ -299,13 +255,11 @@ void labelClusters(CellCollection& cells, Connect connect) { template + requires(GridDim == 1 || GridDim == 2) && + Acts::Ccl::HasRetrievableLabelInfo< + typename CellCollection::value_type> ClusterCollection mergeClusters(CellCollection& cells) { using Cell = typename CellCollection::value_type; - using Cluster = typename ClusterCollection::value_type; - internal::staticCheckGridDim(); - internal::staticCheckCellType(); - internal::staticCheckClusterType(); - if constexpr (GridDim > 1) { // Sort the cells by their cluster label, only needed if more than // one spatial dimension @@ -318,10 +272,6 @@ ClusterCollection mergeClusters(CellCollection& cells) { template ClusterCollection createClusters(CellCollection& cells, Connect connect) { - using Cell = typename CellCollection::value_type; - using Cluster = typename ClusterCollection::value_type; - internal::staticCheckCellType(); - internal::staticCheckClusterType(); labelClusters(cells, connect); return mergeClusters(cells); } diff --git a/Core/include/Acts/Clusterization/TimedClusterization.hpp b/Core/include/Acts/Clusterization/TimedClusterization.hpp new file mode 100644 index 00000000000..e92ebce44e2 --- /dev/null +++ b/Core/include/Acts/Clusterization/TimedClusterization.hpp @@ -0,0 +1,38 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Clusterization/Clusterization.hpp" +#include "Acts/Definitions/Algebra.hpp" + +#include + +namespace Acts::Ccl { + +template +concept HasRetrievableTimeInfo = requires(Cell cell) { + { getCellTime(cell) } -> std::same_as; +}; + +template +struct TimedConnect : public Acts::Ccl::DefaultConnect { + Acts::ActsScalar timeTolerance{std::numeric_limits::max()}; + + TimedConnect() = default; + TimedConnect(Acts::ActsScalar time); + TimedConnect(Acts::ActsScalar time, bool commonCorner) + requires(N == 2); + ~TimedConnect() override = default; + + ConnectResult operator()(const Cell& ref, const Cell& iter) const override; +}; + +} // namespace Acts::Ccl + +#include "Acts/Clusterization/TimedClusterization.ipp" diff --git a/Core/include/Acts/Clusterization/TimedClusterization.ipp b/Core/include/Acts/Clusterization/TimedClusterization.ipp new file mode 100644 index 00000000000..0e7b3e5bda8 --- /dev/null +++ b/Core/include/Acts/Clusterization/TimedClusterization.ipp @@ -0,0 +1,36 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +namespace Acts::Ccl { + +template +TimedConnect::TimedConnect(Acts::ActsScalar time) + : timeTolerance(time) {} + +template +TimedConnect::TimedConnect(Acts::ActsScalar time, bool commonCorner) + requires(N == 2) + : Acts::Ccl::DefaultConnect(commonCorner), timeTolerance(time) {} + +template +Acts::Ccl::ConnectResult TimedConnect::operator()( + const Cell& ref, const Cell& iter) const { + Acts::Ccl::ConnectResult spaceCompatibility = + Acts::Ccl::DefaultConnect::operator()(ref, iter); + if (spaceCompatibility != Acts::Ccl::ConnectResult::eConn) { + return spaceCompatibility; + } + + if (std::abs(getCellTime(ref) - getCellTime(iter)) < timeTolerance) { + return Acts::Ccl::ConnectResult::eConn; + } + + return Acts::Ccl::ConnectResult::eNoConn; +} + +} // namespace Acts::Ccl diff --git a/Core/include/Acts/Detector/LayerStructureBuilder.hpp b/Core/include/Acts/Detector/LayerStructureBuilder.hpp index 9dd0e0a8e8e..02fd726824e 100644 --- a/Core/include/Acts/Detector/LayerStructureBuilder.hpp +++ b/Core/include/Acts/Detector/LayerStructureBuilder.hpp @@ -91,8 +91,9 @@ class LayerStructureBuilder : public IInternalStructureBuilder { /// Minimum number of surfaces to build an internal structure /// - otherwise the tryAll options is used unsigned int nMinimalSurfaces = 4u; - /// Polyhedron approximations - unsigned int nSegments = 1u; + /// Polyhedron approximations: number of segments to be used + /// to approximate a quarter of a circle + unsigned int quarterSegments = 1u; /// Extra information, mainly for screen output std::string auxiliary = ""; }; diff --git a/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp index 27b411a99cc..f624cd88aee 100644 --- a/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp @@ -250,6 +250,17 @@ class GenericBoundTrackParameters { return m_surface->referenceFrame(geoCtx, position(geoCtx), momentum()); } + /// Reflect the parameters in place. + void reflectInPlace() { m_params = reflectBoundParameters(m_params); } + + /// Reflect the parameters. + /// @return Reflected parameters. + GenericBoundTrackParameters reflect() const { + GenericBoundTrackParameters reflected = *this; + reflected.reflectInPlace(); + return reflected; + } + private: BoundVector m_params; std::optional m_cov; diff --git a/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp index a42ac2f116f..6e4ed9bae7a 100644 --- a/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp @@ -111,6 +111,14 @@ class GenericCurvilinearTrackParameters Vector3 position() const { return GenericBoundTrackParameters::position({}); } + + /// Reflect the parameters. + /// @return Reflected parameters. + GenericCurvilinearTrackParameters reflect() const { + GenericCurvilinearTrackParameters reflected = *this; + reflected.reflectInPlace(); + return reflected; + } }; } // namespace Acts diff --git a/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp index ab4218a4e94..214aa2e1551 100644 --- a/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp @@ -12,8 +12,11 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/TrackParametersConcept.hpp" +#include "Acts/EventData/TransformationHelpers.hpp" #include "Acts/EventData/detail/PrintParameters.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include "Acts/Utilities/UnitVectors.hpp" +#include "Acts/Utilities/VectorHelpers.hpp" #include #include @@ -54,6 +57,29 @@ class GenericFreeTrackParameters { m_cov(std::move(cov)), m_particleHypothesis(std::move(particleHypothesis)) {} + /// Construct from four-position, direction, absolute momentum, and charge. + /// + /// @param pos4 Track position/time four-vector + /// @param dir Track direction three-vector; normalization is ignored. + /// @param qOverP Charge over momentum + /// @param cov Free parameters covariance matrix + /// @param particleHypothesis Particle hypothesis + GenericFreeTrackParameters(const Vector4& pos4, const Vector3& dir, + Scalar qOverP, std::optional cov, + ParticleHypothesis particleHypothesis) + : m_params(FreeVector::Zero()), + m_cov(std::move(cov)), + m_particleHypothesis(std::move(particleHypothesis)) { + m_params[eFreePos0] = pos4[ePos0]; + m_params[eFreePos1] = pos4[ePos1]; + m_params[eFreePos2] = pos4[ePos2]; + m_params[eFreeTime] = pos4[eTime]; + m_params[eFreeDir0] = dir[eMom0]; + m_params[eFreeDir1] = dir[eMom1]; + m_params[eFreeDir2] = dir[eMom2]; + m_params[eFreeQOverP] = qOverP; + } + /// Construct from four-position, angles, absolute momentum, and charge. /// /// @param pos4 Track position/time four-vector @@ -134,9 +160,9 @@ class GenericFreeTrackParameters { Scalar time() const { return m_params[eFreeTime]; } /// Phi direction. - Scalar phi() const { return phi(direction()); } + Scalar phi() const { return VectorHelpers::phi(direction()); } /// Theta direction. - Scalar theta() const { return theta(direction()); } + Scalar theta() const { return VectorHelpers::theta(direction()); } /// Charge over momentum. Scalar qOverP() const { return m_params[eFreeQOverP]; } @@ -154,12 +180,12 @@ class GenericFreeTrackParameters { // [f*sin(theta)*cos(phi), f*sin(theta)*sin(phi), f*cos(theta)] // w/ f,sin(theta) positive, the transverse magnitude is then // sqrt(f^2*sin^2(theta)) = f*sin(theta) - Scalar transverseMagnitude = - std::hypot(m_params[eFreeDir0], m_params[eFreeDir1]); + Scalar transverseMagnitude2 = + square(m_params[eFreeDir0]) + square(m_params[eFreeDir1]); // absolute magnitude is f by construction - Scalar magnitude = std::hypot(transverseMagnitude, m_params[eFreeDir2]); + Scalar magnitude2 = transverseMagnitude2 + square(m_params[eFreeDir2]); // such that we can extract sin(theta) = f*sin(theta) / f - return (transverseMagnitude / magnitude) * absoluteMomentum(); + return std::sqrt(transverseMagnitude2 / magnitude2) * absoluteMomentum(); } /// Momentum three-vector. Vector3 momentum() const { return absoluteMomentum() * direction(); } @@ -174,6 +200,17 @@ class GenericFreeTrackParameters { return m_particleHypothesis; } + /// Reflect the parameters in place. + void reflectInPlace() { m_params = reflectFreeParameters(m_params); } + + /// Reflect the parameters. + /// @return Reflected parameters. + GenericFreeTrackParameters reflect() const { + GenericFreeTrackParameters reflected = *this; + reflected.reflectInPlace(); + return reflected; + } + private: FreeVector m_params; std::optional m_cov; diff --git a/Core/include/Acts/EventData/Seed.hpp b/Core/include/Acts/EventData/Seed.hpp index bd159eaa54a..db93b319dcb 100644 --- a/Core/include/Acts/EventData/Seed.hpp +++ b/Core/include/Acts/EventData/Seed.hpp @@ -14,7 +14,7 @@ namespace Acts { template - requires(N >= 3) + requires(N >= 3ul) class Seed { public: using value_type = external_spacepoint_t; diff --git a/Core/include/Acts/EventData/Seed.ipp b/Core/include/Acts/EventData/Seed.ipp index 5f66aa06772..74f2c0e4169 100644 --- a/Core/include/Acts/EventData/Seed.ipp +++ b/Core/include/Acts/EventData/Seed.ipp @@ -9,7 +9,7 @@ namespace Acts { template - requires(N >= 3) + requires(N >= 3ul) template requires(sizeof...(args_t) == N) && (std::same_as && ...) @@ -17,32 +17,32 @@ Seed::Seed(const args_t&... points) : m_spacepoints({&points...}) {} template - requires(N >= 3) + requires(N >= 3ul) void Seed::setVertexZ(float vertex) { m_vertexZ = vertex; } template - requires(N >= 3) + requires(N >= 3ul) void Seed::setQuality(float seedQuality) { m_seedQuality = seedQuality; } template - requires(N >= 3) + requires(N >= 3ul) const std::array& Seed::sp() const { return m_spacepoints; } template - requires(N >= 3) + requires(N >= 3ul) float Seed::z() const { return m_vertexZ; } template - requires(N >= 3) + requires(N >= 3ul) float Seed::seedQuality() const { return m_seedQuality; } diff --git a/Core/include/Acts/EventData/SourceLink.hpp b/Core/include/Acts/EventData/SourceLink.hpp index 5e032d11e4f..ba1515738e6 100644 --- a/Core/include/Acts/EventData/SourceLink.hpp +++ b/Core/include/Acts/EventData/SourceLink.hpp @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/Core/include/Acts/EventData/SpacePointContainer.hpp b/Core/include/Acts/EventData/SpacePointContainer.hpp index 32e43645d82..619a1393ecc 100644 --- a/Core/include/Acts/EventData/SpacePointContainer.hpp +++ b/Core/include/Acts/EventData/SpacePointContainer.hpp @@ -12,15 +12,13 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/SpacePointData.hpp" #include "Acts/EventData/SpacePointProxy.hpp" -#include "Acts/EventData/SpacePointProxyIterator.hpp" #include "Acts/EventData/Utils.hpp" #include "Acts/Utilities/HashedString.hpp" +#include "Acts/Utilities/Iterator.hpp" #include #include -#include - namespace Acts { struct SpacePointContainerConfig { @@ -67,19 +65,22 @@ class SpacePointContainer { public: friend class Acts::SpacePointProxy< Acts::SpacePointContainer>; - friend class Acts::SpacePointProxyIterator< - Acts::SpacePointContainer>; public: - using iterator = Acts::SpacePointProxyIterator< - Acts::SpacePointContainer>; - using const_iterator = iterator; - using SpacePointProxyType = Acts::SpacePointProxy>; + + using iterator = + ContainerIndexIterator, + SpacePointProxyType&, false>; + using const_iterator = + ContainerIndexIterator, + const SpacePointProxyType&, true>; + using ValueType = typename container_t::ValueType; using ProxyType = SpacePointProxyType; using value_type = ProxyType; + using size_type = std::size_t; public: // Constructors @@ -118,9 +119,15 @@ class SpacePointContainer { std::size_t size() const; - iterator begin() const; - iterator end() const; + iterator begin(); + iterator end(); + const_iterator cbegin() const; + const_iterator cend() const; + const_iterator begin() const; + const_iterator end() const; + ProxyType& at(const std::size_t n); + const ProxyType& at(const std::size_t n) const; const ValueType& sp(const std::size_t n) const; private: @@ -128,6 +135,7 @@ class SpacePointContainer { const container_t& container() const; const ProxyType& proxy(const std::size_t n) const; + std::vector& proxies(); const std::vector& proxies() const; float x(const std::size_t n) const; diff --git a/Core/include/Acts/EventData/SpacePointContainer.ipp b/Core/include/Acts/EventData/SpacePointContainer.ipp index abcab8ab5a2..e0c381e4a50 100644 --- a/Core/include/Acts/EventData/SpacePointContainer.ipp +++ b/Core/include/Acts/EventData/SpacePointContainer.ipp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include +#include namespace Acts { @@ -44,7 +44,7 @@ void SpacePointContainer::initialize() { m_data.setZ(i, external_container.z_impl(i)); m_data.setRadius( i, std::sqrt(m_data.x(i) * m_data.x(i) + m_data.y(i) * m_data.y(i))); - m_data.setPhi(i, atan2f(m_data.y(i), m_data.x(i))); + m_data.setPhi(i, std::atan2(m_data.y(i), m_data.x(i))); m_data.setVarianceR(i, external_container.varianceR_impl(i)); m_data.setVarianceZ(i, external_container.varianceZ_impl(i)); @@ -105,22 +105,58 @@ std::size_t SpacePointContainer::size() const { template class holder_t> typename SpacePointContainer::iterator -SpacePointContainer::begin() const { +SpacePointContainer::begin() { return {*this, 0}; } template class holder_t> typename SpacePointContainer::iterator +SpacePointContainer::end() { + return {*this, size()}; +} + +template class holder_t> +typename SpacePointContainer::const_iterator +SpacePointContainer::begin() const { + return {*this, 0}; +} + +template class holder_t> +typename SpacePointContainer::const_iterator SpacePointContainer::end() const { return {*this, size()}; } +template class holder_t> +typename SpacePointContainer::const_iterator +SpacePointContainer::cbegin() const { + return {*this, 0}; +} + +template class holder_t> +typename SpacePointContainer::const_iterator +SpacePointContainer::cend() const { + return {*this, size()}; +} + template class holder_t> const container_t& SpacePointContainer::container() const { return *m_container; } +template class holder_t> +typename SpacePointContainer::ProxyType& +SpacePointContainer::at(const std::size_t n) { + return proxies().at(n); +} + +template class holder_t> +const typename SpacePointContainer::ProxyType& +SpacePointContainer::at(const std::size_t n) const { + return proxies().at(n); +} + template class holder_t> const typename SpacePointContainer::ValueType& SpacePointContainer::sp(const std::size_t n) const { @@ -173,6 +209,12 @@ SpacePointContainer::proxy(const std::size_t n) const { return proxies()[n]; } +template class holder_t> +std::vector::ProxyType>& +SpacePointContainer::proxies() { + return m_proxies; +} + template class holder_t> const std::vector< typename SpacePointContainer::ProxyType>& diff --git a/Core/include/Acts/EventData/SpacePointProxyIterator.hpp b/Core/include/Acts/EventData/SpacePointProxyIterator.hpp deleted file mode 100644 index c8b91d3c052..00000000000 --- a/Core/include/Acts/EventData/SpacePointProxyIterator.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/EventData/SpacePointProxy.hpp" -#include "Acts/Utilities/Holders.hpp" - -namespace Acts { - -template -class SpacePointProxyIterator { - public: - using ContainerType = container_t; - using ProxyType = typename container_t::SpacePointProxyType; - - using iterator_category = std::random_access_iterator_tag; - using value_type = ProxyType; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - - public: - // Constructors - SpacePointProxyIterator(container_t&& container, std::size_t index) = delete; - SpacePointProxyIterator(const container_t& container, std::size_t index); - - SpacePointProxyIterator& operator++(); - SpacePointProxyIterator& operator--(); - SpacePointProxyIterator operator++(int); - SpacePointProxyIterator operator--(int); - - bool operator==(const SpacePointProxyIterator& other) const; - auto operator<=>(const SpacePointProxyIterator& other) const; - - SpacePointProxyIterator& operator+=(const std::size_t offset); - SpacePointProxyIterator& operator-=(const std::size_t offset); - - SpacePointProxyIterator operator+(const std::size_t offset) const; - SpacePointProxyIterator operator-(const std::size_t offset) const; - - difference_type operator-(const SpacePointProxyIterator& other) const; - - const value_type& operator*() const; - - private: - const container_t* m_container{nullptr}; - std::size_t m_index{0ul}; -}; - -} // namespace Acts - -#include "Acts/EventData/SpacePointProxyIterator.ipp" diff --git a/Core/include/Acts/EventData/SpacePointProxyIterator.ipp b/Core/include/Acts/EventData/SpacePointProxyIterator.ipp deleted file mode 100644 index 5759e0416a1..00000000000 --- a/Core/include/Acts/EventData/SpacePointProxyIterator.ipp +++ /dev/null @@ -1,100 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -namespace Acts { - -// Implementation -template -SpacePointProxyIterator::SpacePointProxyIterator( - const container_t& container, std::size_t index) - : m_container(&container), m_index(index) {} - -template -SpacePointProxyIterator& -SpacePointProxyIterator::operator++() { - ++m_index; - return *this; -} - -template -SpacePointProxyIterator& -SpacePointProxyIterator::operator--() { - --m_index; - return *this; -} - -template -SpacePointProxyIterator -SpacePointProxyIterator::operator++(int) { - SpacePointProxyIterator other(*this); - ++m_index; - return other; -} - -template -SpacePointProxyIterator -SpacePointProxyIterator::operator--(int) { - SpacePointProxyIterator other(*this); - --m_index; - return other; -} - -template -bool SpacePointProxyIterator::operator==( - const SpacePointProxyIterator& other) const { - return m_container == other.m_container && m_index == other.m_index; -} - -template -auto SpacePointProxyIterator::operator<=>( - const SpacePointProxyIterator& other) const { - return m_index <=> other.m_index; -} - -template -SpacePointProxyIterator& -SpacePointProxyIterator::operator+=(const std::size_t offset) { - m_index += offset; - return *this; -} - -template -SpacePointProxyIterator& -SpacePointProxyIterator::operator-=(const std::size_t offset) { - m_index -= offset; - return *this; -} - -template -SpacePointProxyIterator -SpacePointProxyIterator::operator+( - const std::size_t offset) const { - return SpacePointProxyIterator(*m_container, m_index + offset); -} - -template -SpacePointProxyIterator -SpacePointProxyIterator::operator-( - const std::size_t offset) const { - return SpacePointProxyIterator(*m_container, m_index - offset); -} - -template -typename SpacePointProxyIterator::difference_type -SpacePointProxyIterator::operator-( - const SpacePointProxyIterator& other) const { - return m_index - other.m_index; -} - -template -const typename SpacePointProxyIterator::value_type& -SpacePointProxyIterator::operator*() const { - return m_container->proxy(m_index); -} - -} // namespace Acts diff --git a/Core/include/Acts/EventData/TrackContainer.hpp b/Core/include/Acts/EventData/TrackContainer.hpp index 9591a66c6a4..f70c430f002 100644 --- a/Core/include/Acts/EventData/TrackContainer.hpp +++ b/Core/include/Acts/EventData/TrackContainer.hpp @@ -19,6 +19,8 @@ #include "Acts/EventData/Utils.hpp" #include "Acts/Utilities/HashedString.hpp" #include "Acts/Utilities/Holders.hpp" +#include "Acts/Utilities/Iterator.hpp" +#include "Acts/Utilities/TypeTraits.hpp" #include "Acts/Utilities/UnitVectors.hpp" #include @@ -78,6 +80,12 @@ class TrackContainer { using ConstTrackStateProxy = typename MultiTrajectory::ConstTrackStateProxy; + using size_type = IndexType; + using iterator = + Acts::ContainerIndexIterator; + using const_iterator = + Acts::ContainerIndexIterator; + #ifndef DOXYGEN friend TrackProxy; friend ConstTrackProxy; @@ -148,6 +156,21 @@ class TrackContainer { return {*this, itrack}; } + /// Get a const track proxy for a track index + /// @param itrack the track index in the container + /// @return A const track proxy for the index + ConstTrackProxy at(IndexType itrack) const { return getTrack(itrack); } + + /// Get a mutable track proxy for a track index + /// @note Only available if the track container is not read-only + /// @param itrack the track index in the container + /// @return A mutable track proxy for the index + TrackProxy at(IndexType itrack) + requires(!ReadOnly) + { + return {*this, itrack}; + } + /// Add a track to the container. Note this only creates the logical track and /// allocates memory. You can combine this with @c getTrack to obtain a track proxy /// @note Only available if the track container is not read-only @@ -184,36 +207,28 @@ class TrackContainer { /// Get a mutable iterator to the first track in the container /// @note Only available if the track container is not read-only /// @return a mutable iterator to the first track - auto begin() + iterator begin() requires(!ReadOnly) { - return detail_tc::TrackProxyIterator, - TrackProxy, false>{*this, 0}; + return iterator{*this, 0}; } /// Get a past-the-end iterator for this container /// @note Only available if the track container is not read-only /// @return a past-the-end iterator - auto end() + iterator end() requires(!ReadOnly) { - return detail_tc::TrackProxyIterator, - TrackProxy, false>{*this, size()}; + return iterator{*this, size()}; } /// Get an const iterator to the first track in the container /// @return a const iterator to the first track - auto begin() const { - return detail_tc::TrackProxyIterator, - ConstTrackProxy, true>{*this, 0}; - } + const_iterator begin() const { return const_iterator{*this, 0}; } /// Get a past-the-end iterator for this container /// @return a past-the-end iterator - auto end() const { - return detail_tc::TrackProxyIterator, - ConstTrackProxy, true>{*this, size()}; - } + const_iterator end() const { return const_iterator{*this, size()}; } /// @} @@ -411,8 +426,8 @@ class TrackContainer { } } - detail_tc::ConstIf, ReadOnly> m_container; - detail_tc::ConstIf, ReadOnly> m_traj; + const_if_t> m_container; + const_if_t> m_traj; }; template diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index f5a5aeca6d2..5939022e2e8 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -17,6 +17,7 @@ #include "Acts/EventData/TrackProxyConcept.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/Utilities/HashedString.hpp" +#include "Acts/Utilities/TypeTraits.hpp" #include "Acts/Utilities/UnitVectors.hpp" #include @@ -29,108 +30,6 @@ template class holder_t> class TrackContainer; -namespace detail_tc { -template -using ConstIf = std::conditional_t; - -/// Helper iterator to allow iteration over tracks via track proxies. -template -class TrackProxyIterator { - using ProxyType = proxy_t; - using IndexType = typename ProxyType::IndexType; - using ContainerType = container_t; - - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = ProxyType; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - - TrackProxyIterator(container_t& container, IndexType itrack) - requires(!ReadOnly) - : m_container(&container), m_itrack(itrack) {} - - TrackProxyIterator(const container_t& container, IndexType itrack) - requires ReadOnly - : m_container(&container), m_itrack(itrack) {} - - TrackProxyIterator& operator++() { - m_itrack++; - return *this; - } - TrackProxyIterator& operator--() { - m_itrack--; - return *this; - } - - bool operator==(const TrackProxyIterator& other) const { - return m_container == other.m_container && m_itrack == other.m_itrack; - } - - auto operator<=>(const TrackProxyIterator& other) const { - return m_itrack <=> other.m_itrack; - } - - ProxyType operator*() const { return m_container->getTrack(m_itrack); } - - ProxyType operator*() - requires(!ReadOnly) - { - return m_container->getTrack(m_itrack); - } - - TrackProxyIterator operator[](difference_type n) const { - TrackProxyIterator copy = *this; - copy += n; - return copy; - }; - - TrackProxyIterator& operator+=(difference_type n) { - m_itrack += n; - return *this; - } - - TrackProxyIterator operator-=(difference_type n) { - m_itrack -= n; - return *this; - } - - friend difference_type operator-(const TrackProxyIterator& lhs, - const TrackProxyIterator& rhs) { - return lhs.m_itrack - rhs.m_itrack; - } - - friend TrackProxyIterator operator+(const TrackProxyIterator& lhs, - difference_type rhs) { - TrackProxyIterator copy = lhs; - copy += rhs; - return copy; - } - - friend TrackProxyIterator operator+(difference_type lhs, - const TrackProxyIterator& rhs) { - return rhs + lhs; - } - - friend TrackProxyIterator operator-(const TrackProxyIterator& lhs, - difference_type rhs) { - return lhs + (-rhs); - } - - friend TrackProxyIterator operator-(difference_type lhs, - const TrackProxyIterator& rhs) { - return rhs + (-lhs); - } - - private: - detail_lt::TransitiveConstPointer> - m_container; - IndexType m_itrack; -}; - -} // namespace detail_tc - /// Proxy class representing a single track. /// This class provides a **view** into an associated @ref TrackContainer, and /// has **reference semantics**. You can think of it as a pointer to a vector @@ -425,7 +324,8 @@ class TrackProxy { return std::distance(tsRange.begin(), tsRange.end()); } - /// Return the number of measurements for the track. Const version + /// Return a mutable reference to the number of measurements for the track. + /// Mutable version /// @note Only available if the track proxy is not read-only /// @return The number of measurements unsigned int& nMeasurements() @@ -434,8 +334,7 @@ class TrackProxy { return component(); } - /// Return a mutable reference to the number of measurements for the track. - /// Mutable version + /// Return the number of measurements for the track. Const version /// @return The number of measurements unsigned int nMeasurements() const { return component(); @@ -876,13 +775,14 @@ class TrackProxy { const auto& container() const { return *m_container; } private: - TrackProxy(detail_tc::ConstIf, - ReadOnly>& container, - IndexType itrack) + TrackProxy( + const_if_t>& + container, + IndexType itrack) : m_container{&container}, m_index{itrack} {} - detail_lt::TransitiveConstPointer, ReadOnly>> + detail_lt::TransitiveConstPointer< + const_if_t>> m_container; IndexType m_index; }; diff --git a/Core/include/Acts/EventData/TrackStateProxy.hpp b/Core/include/Acts/EventData/TrackStateProxy.hpp index bed0061ecbf..34de86cd3d7 100644 --- a/Core/include/Acts/EventData/TrackStateProxy.hpp +++ b/Core/include/Acts/EventData/TrackStateProxy.hpp @@ -1043,8 +1043,9 @@ class TrackStateProxy { jacobian() = other.jacobian(); } + // NOTE: we should not check hasCalibrated on this, since it + // may be not yet allocated if (ACTS_CHECK_BIT(mask, PM::Calibrated) && - has() && other.template has()) { allocateCalibrated(other.calibratedSize()); diff --git a/Core/include/Acts/EventData/TransformationHelpers.hpp b/Core/include/Acts/EventData/TransformationHelpers.hpp index 7fa297f4abc..39240bf469e 100644 --- a/Core/include/Acts/EventData/TransformationHelpers.hpp +++ b/Core/include/Acts/EventData/TransformationHelpers.hpp @@ -13,11 +13,39 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/Result.hpp" +#include "Acts/Utilities/detail/periodic.hpp" namespace Acts { class Surface; +/// Reflect bound track parameters. +/// +/// @param boundParams Bound track parameters vector +/// @return Reflected bound track parameters vector +inline BoundVector reflectBoundParameters(const BoundVector& boundParams) { + BoundVector reflected = boundParams; + auto [phi, theta] = detail::normalizePhiTheta( + boundParams[eBoundPhi] - M_PI, M_PI - boundParams[eBoundTheta]); + reflected[eBoundPhi] = phi; + reflected[eBoundTheta] = theta; + reflected[eBoundQOverP] = -boundParams[eBoundQOverP]; + return reflected; +} + +/// Reflect free track parameters. +/// +/// @param freeParams Free track parameters vector +/// @return Reflected free track parameters vector +inline FreeVector reflectFreeParameters(const FreeVector& freeParams) { + FreeVector reflected = freeParams; + reflected[eFreeDir0] = -freeParams[eFreeDir0]; + reflected[eFreeDir1] = -freeParams[eFreeDir1]; + reflected[eFreeDir2] = -freeParams[eFreeDir2]; + reflected[eFreeQOverP] = -freeParams[eFreeQOverP]; + return reflected; +} + /// Transform bound track parameters into equivalent free track parameters. /// /// @param surface Surface onto which the input parameters are bound diff --git a/Core/include/Acts/Geometry/CompositePortalLink.hpp b/Core/include/Acts/Geometry/CompositePortalLink.hpp index 5ae6d76a106..6586d875b79 100644 --- a/Core/include/Acts/Geometry/CompositePortalLink.hpp +++ b/Core/include/Acts/Geometry/CompositePortalLink.hpp @@ -17,6 +17,9 @@ namespace Acts { +class GridPortalLink; +class Surface; + /// Composite portal links can graft together other portal link instances, for /// example grids that could not be merged due to invalid binnings. /// @@ -49,6 +52,15 @@ class CompositePortalLink final : public PortalLinkBase { std::unique_ptr b, BinningValue direction, bool flatten = true); + /// Construct a composite portal from any number of arbitrary other portal + /// links. The only requirement is that the portal link surfaces are + /// mergeable. + /// @param links The portal links + /// @param direction The binning direction + /// @param flatten If true, the composite will flatten any nested composite + CompositePortalLink(std::vector> links, + BinningValue direction, bool flatten = true); + /// Print the composite portal link /// @param os The output stream void toStream(std::ostream& os) const override; @@ -81,19 +93,22 @@ class CompositePortalLink final : public PortalLinkBase { /// @return The number of children std::size_t size() const; - private: - /// Helper function to construct a merged surface from two portal links along - /// a given direction - /// @param a The first portal link - /// @param b The second portal link - /// @param direction The merging direction - /// @return The merged surface - static std::shared_ptr mergedSurface(const PortalLinkBase* a, - const PortalLinkBase* b, - BinningValue direction); + /// (Potentially) create a grid portal link that represents this composite + /// portal link. + /// @note This only works, if the composite is **flat** and only contains + /// **trivial portal links**. If these preconditions are not met, this + /// function returns a nullptr. + /// @param gctx The geometry context + /// @param logger The logger + /// @return The grid portal link + std::unique_ptr makeGrid(const GeometryContext& gctx, + const Logger& logger) const; + private: boost::container::small_vector, 4> m_children{}; + + BinningValue m_direction; }; } // namespace Acts diff --git a/Core/include/Acts/Geometry/CylinderVolumeBounds.hpp b/Core/include/Acts/Geometry/CylinderVolumeBounds.hpp index c92e001fa9f..002af04cd59 100644 --- a/Core/include/Acts/Geometry/CylinderVolumeBounds.hpp +++ b/Core/include/Acts/Geometry/CylinderVolumeBounds.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/BoundarySurfaceFace.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Geometry/VolumeBounds.hpp" #include "Acts/Utilities/BinningType.hpp" @@ -80,6 +81,18 @@ class CylinderVolumeBounds : public VolumeBounds { eSize }; + /// Enum describing the possible faces of a cylinder volume + /// @note These values are synchronized with the BoundarySurfaceFace enum. + /// Once Gen1 is removed, this can be changed. + enum class Face : unsigned int { + PositiveDisc = BoundarySurfaceFace::positiveFaceXY, + NegativeDisc = BoundarySurfaceFace::negativeFaceXY, + OuterCylinder = BoundarySurfaceFace::tubeOuterCover, + InnerCylinder = BoundarySurfaceFace::tubeInnerCover, + NegativePhiPlane = BoundarySurfaceFace::tubeSectorNegativePhi, + PositivePhiPlane = BoundarySurfaceFace::tubeSectorPositivePhi + }; + CylinderVolumeBounds() = delete; /// Constructor @@ -115,7 +128,7 @@ class CylinderVolumeBounds : public VolumeBounds { /// Copy Constructor /// /// @param cylbo is the source cylinder volume bounds for the copy - CylinderVolumeBounds(const CylinderVolumeBounds& cylbo) = default; + CylinderVolumeBounds(const CylinderVolumeBounds& cylbo); ~CylinderVolumeBounds() override = default; CylinderVolumeBounds& operator=(const CylinderVolumeBounds& cylbo) = default; diff --git a/Core/include/Acts/Geometry/CylinderVolumeStack.hpp b/Core/include/Acts/Geometry/CylinderVolumeStack.hpp index 5e708a5c3c9..3fcb8f1039b 100644 --- a/Core/include/Acts/Geometry/CylinderVolumeStack.hpp +++ b/Core/include/Acts/Geometry/CylinderVolumeStack.hpp @@ -85,23 +85,12 @@ class CylinderVolumeStack : public Volume { /// construction. /// @param volbounds is the new bounds /// @param transform is the new transform - /// @pre The volume bounds need to be of type - /// @c CylinderVolumeBounds. - void update(std::shared_ptr volbounds, - std::optional transform = std::nullopt) override; - - /// Update the volume bounds and transform. This - /// will update the bounds of all volumes in the stack - /// to accommodate the new bounds and optionally create - /// gap volumes according to the resize strategy set during - /// construction. - /// @param newBounds is the new bounds - /// @param transform is the new transform /// @param logger is the logger /// @pre The volume bounds need to be of type /// @c CylinderVolumeBounds. - void update(std::shared_ptr newBounds, - std::optional transform, const Logger& logger); + void update(std::shared_ptr volbounds, + std::optional transform = std::nullopt, + const Logger& logger = getDummyLogger()) override; /// Access the gap volume that were created during attachment or resizing. /// @return the vector of gap volumes @@ -133,11 +122,12 @@ class CylinderVolumeStack : public Volume { Acts::Logging::Level lvl); /// Helper function that prints output helping in debugging overlaps + /// @param direction is the overlap check direction /// @param a is the first volume /// @param b is the second volume /// @param logger is the logger - static void overlapPrint(const VolumeTuple& a, const VolumeTuple& b, - const Logger& logger); + static void overlapPrint(BinningValue direction, const VolumeTuple& a, + const VolumeTuple& b, const Logger& logger); /// Helper function that checks if volumes are properly aligned /// for attachment. diff --git a/Core/include/Acts/Geometry/Extent.hpp b/Core/include/Acts/Geometry/Extent.hpp index 698477744c0..8728728996b 100644 --- a/Core/include/Acts/Geometry/Extent.hpp +++ b/Core/include/Acts/Geometry/Extent.hpp @@ -52,12 +52,6 @@ struct ExtentEnvelope { } } - /// Constructor from an array of envelopes - /// @param values the array of envelopes - constexpr explicit ExtentEnvelope( - const std::array& values) - : m_values(values) {} - /// Static factory for a zero envelope /// @return the zero envelope constexpr static ExtentEnvelope Zero() { @@ -74,6 +68,33 @@ struct ExtentEnvelope { }}; } + /// Helper struct for designated initializer construction + struct Arguments { + Envelope x = zeroEnvelope; + Envelope y = zeroEnvelope; + Envelope z = zeroEnvelope; + Envelope r = zeroEnvelope; + Envelope phi = zeroEnvelope; + Envelope rPhi = zeroEnvelope; + Envelope h = zeroEnvelope; + Envelope eta = zeroEnvelope; + Envelope mag = zeroEnvelope; + }; + + /// Constructor using a helper struct for designated initializaion + /// @param args the arguments + constexpr explicit ExtentEnvelope(Arguments&& args) { + using enum BinningValue; + m_values[toUnderlying(binX)] = args.x; + m_values[toUnderlying(binY)] = args.y; + m_values[toUnderlying(binZ)] = args.z; + m_values[toUnderlying(binR)] = args.r; + m_values[toUnderlying(binPhi)] = args.phi; + m_values[toUnderlying(binH)] = args.h; + m_values[toUnderlying(binEta)] = args.eta; + m_values[toUnderlying(binMag)] = args.mag; + } + /// Comparison operator between envelope sets /// @param lhs the left hand side /// @param rhs the right hand side diff --git a/Core/include/Acts/Geometry/GridPortalLink.hpp b/Core/include/Acts/Geometry/GridPortalLink.hpp index 0f84d496920..411e6bb9962 100644 --- a/Core/include/Acts/Geometry/GridPortalLink.hpp +++ b/Core/include/Acts/Geometry/GridPortalLink.hpp @@ -381,12 +381,12 @@ class GridPortalLink : public PortalLinkBase { /// Helper function to get grid bin content in type-eraased way. /// @param indices The bin indices /// @return The tracking volume at the bin - virtual TrackingVolume*& atLocalBins(IndexType indices) = 0; + virtual const TrackingVolume*& atLocalBins(IndexType indices) = 0; /// Helper function to get grid bin content in type-eraased way. /// @param indices The bin indices /// @return The tracking volume at the bin - virtual TrackingVolume* atLocalBins(IndexType indices) const = 0; + virtual const TrackingVolume* atLocalBins(IndexType indices) const = 0; private: BinningValue m_direction; @@ -399,7 +399,7 @@ template class GridPortalLinkT final : public GridPortalLink { public: /// The internal grid type - using GridType = Grid; + using GridType = Grid; /// The dimension of the grid static constexpr std::size_t DIM = sizeof...(Axes); @@ -530,7 +530,6 @@ class GridPortalLinkT final : public GridPortalLink { return m_grid.atPosition(m_projection(position)); } - protected: /// Type erased access to the number of bins /// @return The number of bins in each direction IndexType numLocalBins() const override { @@ -545,7 +544,7 @@ class GridPortalLinkT final : public GridPortalLink { /// Type erased local bin access /// @param indices The bin indices /// @return The tracking volume at the bin - TrackingVolume*& atLocalBins(IndexType indices) override { + const TrackingVolume*& atLocalBins(IndexType indices) override { throw_assert(indices.size() == DIM, "Invalid number of indices"); typename GridType::index_t idx; for (std::size_t i = 0; i < DIM; i++) { @@ -557,7 +556,7 @@ class GridPortalLinkT final : public GridPortalLink { /// Type erased local bin access /// @param indices The bin indices /// @return The tracking volume at the bin - TrackingVolume* atLocalBins(IndexType indices) const override { + const TrackingVolume* atLocalBins(IndexType indices) const override { throw_assert(indices.size() == DIM, "Invalid number of indices"); typename GridType::index_t idx; for (std::size_t i = 0; i < DIM; i++) { diff --git a/Core/include/Acts/Geometry/Polyhedron.hpp b/Core/include/Acts/Geometry/Polyhedron.hpp index 2a6eb47d765..00ccaaab4c5 100644 --- a/Core/include/Acts/Geometry/Polyhedron.hpp +++ b/Core/include/Acts/Geometry/Polyhedron.hpp @@ -39,6 +39,7 @@ struct Polyhedron { /// @param facesIn List of lists of indices for faces. /// @param triangularMeshIn List of lists of indices for a triangular mesh /// @param isExact A dedicated flag if this is exact or not + /// /// @note This creates copies of the input vectors Polyhedron(const std::vector& verticesIn, const std::vector& facesIn, diff --git a/Core/include/Acts/Geometry/PortalShell.hpp b/Core/include/Acts/Geometry/PortalShell.hpp new file mode 100644 index 00000000000..7405a64ad0a --- /dev/null +++ b/Core/include/Acts/Geometry/PortalShell.hpp @@ -0,0 +1,215 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +namespace Acts { + +class Portal; +class TrackingVolume; +class GeometryContext; + +/// @class PortalShellBase +/// The portal shell of a volume is the set of portals that describes +/// connections "outside" of the volume. Which boundary surfaces of a volume are +/// included in the shell depends on the volume. Portal shells are only used +/// during geometry construction, and are not part of the final geometry +/// description. +/// +/// This class is the base class for all portal shells +class PortalShellBase { + public: + /// Virtusl destructor + virtual ~PortalShellBase() = default; + + /// Connect a volume to the outer side of all portal shells. Which "side" is + /// "outer" depends on the volume type. + /// This method essentially creates a @c TrivialPortalLink on the unconnected + /// side of each portal that is part of the chell + /// @param volume The volume to connect + virtual void connectOuter(TrackingVolume& volume) = 0; + + /// Get the number of portals in the shell. This number depends on the volume + /// type + /// @return The number of portals in the shell + virtual std::size_t size() const = 0; + + /// Instruct the shell to register the portals with the volume, handing over + /// shared ownership in the process. + /// @note The target volume depends on the shell type, e.g. composite shells + /// like the @c CylinerStackPortalShell register portals to the *correct* + /// volumes. + virtual void applyToVolume() = 0; + + /// Check if a portal is *valid*, e.g. if non of the portals has two + /// unconnected sides. + /// @return True if the shell is valid, false otherwise + virtual bool isValid() const = 0; + + /// Get a label for the portal shell for debugging purposes + /// @return A label for the portal shell + virtual std::string label() const = 0; +}; + +/// @class CylinderPortalShell +/// Base class for cylinder shaped portal shells, e.g. shells for cylinder +/// volumes +class CylinderPortalShell : public PortalShellBase { + public: + using Face = CylinderVolumeBounds::Face; + + using enum CylinderVolumeBounds::Face; + + /// Retrieve the portal associated to the given face. Can be nullptr if unset. + /// @param face The face to retrieve the portal for + /// @return The portal associated to the face + virtual Portal* portal(Face face) = 0; + + /// Retrieve a shared_ptr for the portal associated to the given face. Can be + /// nullptr if unset. + /// @param face The face to retrieve the portal for + /// @return The portal associated to the face + virtual std::shared_ptr portalPtr(Face face) = 0; + + /// Set the portal associated to the given face. + /// @param portal The portal to set + /// @param face The face to set the portal + virtual void setPortal(std::shared_ptr portal, Face face) = 0; + + /// @copydoc PortalShellBase::connectOuter + void connectOuter(TrackingVolume& volume) override; +}; + +/// Output stream operator for the CylinderPortalShell::Face enum +/// @param os The output stream +/// @param face The face to output +/// @return The output stream +std::ostream& operator<<(std::ostream& os, CylinderPortalShell::Face face); + +/// @class SingleCylinderPortalShell +/// This class describes a cylinder shell containing a single volume. The +/// available faces depend on the configuration of the cylinder volume bounds. +/// If a phi sector is configured, the shell will have corresponding portal +/// slots. If the inner radius is non-zero, the shell will have an inner +/// cylinder portal slot. +class SingleCylinderPortalShell : public CylinderPortalShell { + public: + /// Construct a single cylinder portal shell for the given volume + /// @param volume The volume to create the shell for + explicit SingleCylinderPortalShell(TrackingVolume& volume); + + /// @copydoc PortalShellBase::size + std::size_t size() const final; + + /// @copydoc CylinderPortalShell::portal + Portal* portal(Face face) final; + + /// @copydoc CylinderPortalShell::portalPtr + std::shared_ptr portalPtr(Face face) final; + + /// @copydoc CylinderPortalShell::setPortal + void setPortal(std::shared_ptr portal, Face face) final; + + /// @copydoc PortalShellBase::applyToVolume + void applyToVolume() override; + + /// @copydoc PortalShellBase::isValid + bool isValid() const override; + + /// @copydoc PortalShellBase::label + std::string label() const override; + + private: + std::array, 6> m_portals{}; + + TrackingVolume* m_volume; +}; + +/// @class CylinderStackPortalShell +/// This class describes a cylinder shell containing multiple volumes. The +/// available faces depend on the configuration of the cylinder volume bounds. +/// @note The stack shell currently does not support phi sectors +/// The stack can be oriented along the (local) z or r direction, which drives +/// the stacking. Depending on the direction, portals on the shells of children +/// are merged or fused. Subsequently, portal access respects shared portals +/// between shells. Below is an illustration of a stack in the r direction: +/// +/// Fused +-----------------+ +/// portals ----+ | | +/// | | v OuterCylinder +/// | +------+------+ +/// | | | | +/// | | | |<--+ +/// +--+---+ v | | +/// +---+---------+ | +/// | | | | Shared portal +/// | | |<--+--- (grid) +/// | v | | PositiveDisc +/// +-------------+ | +/// r ^ | | | +/// | | |<--+ +/// | | | +/// | +-------------+ InnerCylinder +/// +-----> ^ (if rMin>0) +/// z | | +/// +-----------------+ +/// +/// @note The shells must be ordered in the given direction +/// Depending on the stack direction, the portal lookup will return different +/// portals. In the illustration above, the `PositiveDisc` portal is shared +/// among all shells, while the `OuterCylinder` and `InnerCylinder` portals are +/// looked up from the innermost and outermost shell in the r direction. +class CylinderStackPortalShell : public CylinderPortalShell { + public: + /// Construct the portal shell stack from the given shells + /// @param gctx The geometry context + /// @param shells The shells to stack + /// @note The shells must be ordered in the given direction + /// @param direction The stacking direction + /// @param logger A logging instance for debugging + CylinderStackPortalShell(const GeometryContext& gctx, + std::vector shells, + BinningValue direction, + const Logger& logger = getDummyLogger()); + + /// @copydoc PortalShellBase::size + std::size_t size() const final; + + /// @copydoc CylinderPortalShell::portal + Portal* portal(Face face) final; + + /// @copydoc CylinderPortalShell::portalPtr + std::shared_ptr portalPtr(Face face) final; + + /// @copydoc CylinderPortalShell::setPortal + void setPortal(std::shared_ptr portal, Face face) final; + + void applyToVolume() override { + // No-op, because it's a composite portal shell + } + + /// @copydoc PortalShellBase::isValid + bool isValid() const override; + + /// @copydoc PortalShellBase::label + std::string label() const override; + + private: + BinningValue m_direction; + std::vector m_shells; + bool m_hasInnerCylinder{true}; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/ProtoLayer.hpp b/Core/include/Acts/Geometry/ProtoLayer.hpp index d12e674918d..1e5d95dc025 100644 --- a/Core/include/Acts/Geometry/ProtoLayer.hpp +++ b/Core/include/Acts/Geometry/ProtoLayer.hpp @@ -33,6 +33,9 @@ struct ProtoLayer { /// The envelope parameters ExtentEnvelope envelope = ExtentEnvelope::Zero(); + /// The local transform + Transform3 transform = Transform3::Identity(); + /// Constructor /// /// Loops over a provided vector of surface and calculates the various @@ -41,8 +44,10 @@ struct ProtoLayer { /// /// @param gctx The current geometry context object, e.g. alignment /// @param surfaces The vector of surfaces to consider + /// @param transformIn The local transform to evaluate the sizing in ProtoLayer(const GeometryContext& gctx, - const std::vector& surfaces); + const std::vector& surfaces, + const Transform3& transformIn = Transform3::Identity()); /// Constructor /// @@ -52,8 +57,23 @@ struct ProtoLayer { /// /// @param gctx The current geometry context object, e.g. alignment /// @param surfaces The vector of surfaces to consider + /// @param transformIn The local transform to evaluate the sizing in ProtoLayer(const GeometryContext& gctx, - const std::vector>& surfaces); + const std::vector>& surfaces, + const Transform3& transformIn = Transform3::Identity()); + + /// Constructor + /// + /// Loops over a provided vector of surface and calculates the various + /// min/max values in one go. Also takes into account the thickness + /// of an associated DetectorElement, if it exists. + /// + /// @param gctx The current geometry context object, e.g. alignment + /// @param surfaces The vector of surfaces to consider + /// @param transformIn The local transform to evaluate the sizing in + ProtoLayer(const GeometryContext& gctx, + const std::vector>& surfaces, + const Transform3& transformIn = Transform3::Identity()); ProtoLayer() = default; @@ -81,6 +101,14 @@ struct ProtoLayer { /// @param sl the input ostream std::ostream& toStream(std::ostream& sl) const; + /// Output stream operator + /// @param sl the input ostream + /// @param pl the ProtoLayer to be printed + /// @return the output ostream + friend std::ostream& operator<<(std::ostream& sl, const ProtoLayer& pl) { + return pl.toStream(sl); + } + /// Give access to the surfaces used/assigned to the ProtoLayer const std::vector& surfaces() const; diff --git a/Core/include/Acts/Geometry/TrackingGeometry.hpp b/Core/include/Acts/Geometry/TrackingGeometry.hpp index b0ac658eecd..e8d48c5ae5e 100644 --- a/Core/include/Acts/Geometry/TrackingGeometry.hpp +++ b/Core/include/Acts/Geometry/TrackingGeometry.hpp @@ -145,6 +145,17 @@ class TrackingGeometry { const std::unordered_map& geoIdSurfaceMap() const; + /// Visualize a tracking geometry including substructure + /// @param helper The visualization helper that implement the output + /// @param gctx The geometry context + /// @param viewConfig Global view config + /// @param portalViewConfig View config for portals + /// @param sensitiveViewConfig View configuration for sensitive surfaces + void visualize(IVisualization3D& helper, const GeometryContext& gctx, + const ViewConfig& viewConfig = s_viewVolume, + const ViewConfig& portalViewConfig = s_viewPortal, + const ViewConfig& sensitiveViewConfig = s_viewSensitive) const; + private: // the known world std::shared_ptr m_world; diff --git a/Core/include/Acts/Geometry/TrackingVolume.hpp b/Core/include/Acts/Geometry/TrackingVolume.hpp index b208eefa54b..c980314fa6d 100644 --- a/Core/include/Acts/Geometry/TrackingVolume.hpp +++ b/Core/include/Acts/Geometry/TrackingVolume.hpp @@ -15,6 +15,7 @@ #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Geometry/GlueVolumesDescriptor.hpp" #include "Acts/Geometry/Layer.hpp" +#include "Acts/Geometry/Portal.hpp" #include "Acts/Geometry/TrackingVolumeVisitorConcept.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Material/IVolumeMaterial.hpp" @@ -25,6 +26,7 @@ #include "Acts/Utilities/BinnedArray.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/TransformRange.hpp" +#include "Acts/Visualization/ViewConfig.hpp" #include #include @@ -181,6 +183,10 @@ class TrackingVolume : public Volume { for (const auto& bs : m_boundarySurfaces) { visitor(&(bs->surfaceRepresentation())); } + + for (const auto& portal : portals()) { + visitor(&portal.surface()); + } } // Internal structure @@ -214,6 +220,14 @@ class TrackingVolume : public Volume { volume->visitSurfaces(visitor, restrictToSensitives); } } + + for (const auto& surface : surfaces()) { + visitor(&surface); + } + + for (const auto& volume : volumes()) { + volume.visitSurfaces(visitor, restrictToSensitives); + } } /// @brief Visit all sensitive surfaces @@ -473,6 +487,17 @@ class TrackingVolume : public Volume { /// - positiveFaceXY GlueVolumesDescriptor& glueVolumesDescriptor(); + /// Produces a 3D visualization of this tracking volume + /// @param helper The visualization helper describing the output format + /// @param gctx The geometry context + /// @param viewConfig The view configuration + /// @param portalViewConfig View configuration for portals + /// @param sensitiveViewConfig View configuration for sensitive surfaces + void visualize(IVisualization3D& helper, const GeometryContext& gctx, + const ViewConfig& viewConfig, + const ViewConfig& portalViewConfig, + const ViewConfig& sensitiveViewConfig) const; + private: void connectDenseBoundarySurfaces( MutableTrackingVolumeVector& confinedDenseVolumes); diff --git a/Core/include/Acts/Geometry/TrivialPortalLink.hpp b/Core/include/Acts/Geometry/TrivialPortalLink.hpp index 9f99881fdc7..f68a7e326c6 100644 --- a/Core/include/Acts/Geometry/TrivialPortalLink.hpp +++ b/Core/include/Acts/Geometry/TrivialPortalLink.hpp @@ -58,6 +58,10 @@ class TrivialPortalLink final : public PortalLinkBase { const GeometryContext& gctx, const Vector3& position, double tolerance = s_onSurfaceTolerance) const override; + /// Get the single volume that this trivial portal link is associated with + /// @return The target volume + const TrackingVolume& volume() const; + private: TrackingVolume* m_volume; }; diff --git a/Core/include/Acts/Geometry/Volume.hpp b/Core/include/Acts/Geometry/Volume.hpp index a59451efe09..da8932b1eab 100644 --- a/Core/include/Acts/Geometry/Volume.hpp +++ b/Core/include/Acts/Geometry/Volume.hpp @@ -13,6 +13,7 @@ #include "Acts/Geometry/GeometryObject.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/BoundingBox.hpp" +#include "Acts/Utilities/Logger.hpp" #include #include @@ -83,8 +84,10 @@ class Volume : public GeometryObject { /// Set the volume bounds and optionally also update the volume transform /// @param volbounds The volume bounds to be assigned /// @param transform The transform to be assigned, can be optional + /// @param logger A logger object to log messages virtual void update(std::shared_ptr volbounds, - std::optional transform = std::nullopt); + std::optional transform = std::nullopt, + const Logger& logger = Acts::getDummyLogger()); /// Construct bounding box for this shape /// @param envelope Optional envelope to add / subtract from min/max @@ -122,7 +125,7 @@ class Volume : public GeometryObject { /// @param gctx The geometry context /// @param viewConfig The view configuration void visualize(IVisualization3D& helper, const GeometryContext& gctx, - const ViewConfig& viewConfig = {}) const; + const ViewConfig& viewConfig) const; protected: Transform3 m_transform; diff --git a/Core/include/Acts/Material/ISurfaceMaterial.hpp b/Core/include/Acts/Material/ISurfaceMaterial.hpp index d26563340cf..a379e18e715 100644 --- a/Core/include/Acts/Material/ISurfaceMaterial.hpp +++ b/Core/include/Acts/Material/ISurfaceMaterial.hpp @@ -15,6 +15,7 @@ #include "Acts/Material/MaterialSlab.hpp" #include +#include #include namespace Acts { @@ -117,6 +118,15 @@ class ISurfaceMaterial { /// Output Method for std::ostream, to be overloaded by child classes virtual std::ostream& toStream(std::ostream& sl) const = 0; + /// @brief output into a string + /// + /// @return the string representation + std::string toString() const { + std::stringstream sstrm; + toStream(sstrm); + return sstrm.str(); + } + protected: double m_splitFactor{1.}; //!< the split factor in favour of oppositePre MappingType m_mappingType{ diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp index 8df2afdfe8e..eb9db91e71f 100644 --- a/Core/include/Acts/Propagator/DirectNavigator.hpp +++ b/Core/include/Acts/Propagator/DirectNavigator.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/BoundarySurfaceT.hpp" #include "Acts/Geometry/Layer.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" @@ -17,6 +18,7 @@ #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Intersection.hpp" +#include "Acts/Utilities/Logger.hpp" #include #include @@ -63,7 +65,7 @@ class DirectNavigator { Options options; /// Index of the next surface to try - std::size_t surfaceIndex = 0; + int surfaceIndex = 0; /// Navigation state - external interface: the current surface const Surface* currentSurface = nullptr; @@ -72,6 +74,35 @@ class DirectNavigator { bool targetReached = false; /// Navigation state - external interface: a break has been detected bool navigationBreak = false; + + const Surface* navSurface() const { + return options.surfaces.at(surfaceIndex); + } + + void nextSurface(Direction direction) { + if (direction == Direction::Forward) { + ++surfaceIndex; + } else { + --surfaceIndex; + } + } + + bool endOfSurfaces() const { + return surfaceIndex < 0 || + surfaceIndex >= static_cast(options.surfaces.size()); + } + + int remainingSurfaces(Direction direction) const { + if (direction == Direction::Forward) { + return options.surfaces.size() - surfaceIndex; + } + return surfaceIndex + 1; + } + + void resetSurfaceIndex(Direction direction) { + surfaceIndex = + direction == Direction::Forward ? 0 : options.surfaces.size() - 1; + } }; explicit DirectNavigator(std::unique_ptr _logger = @@ -135,17 +166,41 @@ class DirectNavigator { void initialize(propagator_state_t& state, const stepper_t& /*stepper*/) const { ACTS_VERBOSE("Initialize. Surface sequence for navigation:"); - for (auto surface : state.navigation.options.surfaces) { + for (const Surface* surface : state.navigation.options.surfaces) { ACTS_VERBOSE(surface->geometryId() << " - " << surface->center(state.geoContext).transpose()); } // We set the current surface to the start surface state.navigation.currentSurface = state.navigation.options.startSurface; - if (state.navigation.currentSurface) { + if (state.navigation.currentSurface != nullptr) { ACTS_VERBOSE("Current surface set to start surface " << state.navigation.currentSurface->geometryId()); + } else { + ACTS_VERBOSE("Current surface set to nullptr"); + } + + // Reset the surface index + state.navigation.resetSurfaceIndex(state.options.direction); + for (const Surface* surface : state.navigation.options.surfaces) { + // make sure we skip over the start surface + state.navigation.nextSurface(state.options.direction); + if (surface == state.navigation.currentSurface) { + break; + } + } + ACTS_VERBOSE("Start surface index set to " + << state.navigation.surfaceIndex); + if (state.navigation.endOfSurfaces()) { + ACTS_DEBUG( + "Did not find the start surface in the sequence. Assuming it is not " + "part of the sequence. Trusting the correctness of the input " + "sequence. Resetting the surface index."); + state.navigation.resetSurfaceIndex(state.options.direction); } + + state.navigation.navigationBreak = false; + state.navigation.targetReached = false; } /// @brief Navigator pre step call @@ -157,21 +212,25 @@ class DirectNavigator { /// @param [in] stepper Stepper in use template void preStep(propagator_state_t& state, const stepper_t& stepper) const { + if (state.navigation.navigationBreak) { + return; + } + ACTS_VERBOSE("pre step"); // Navigator target always resets the current surface state.navigation.currentSurface = nullptr; // Output the position in the sequence - ACTS_VERBOSE((state.navigation.options.surfaces.size() - - state.navigation.surfaceIndex) + ACTS_VERBOSE(state.navigation.remainingSurfaces(state.options.direction) << " out of " << state.navigation.options.surfaces.size() << " surfaces remain to try."); - if (state.navigation.surfaceIndex >= - state.navigation.options.surfaces.size()) { + if (state.navigation.endOfSurfaces()) { // Set the navigation break + ACTS_VERBOSE("End of surfaces reached, navigation break."); state.navigation.navigationBreak = true; + stepper.releaseStepSize(state.stepping, ConstrainedStep::actor); // If no externally provided target is given, the target is reached if (state.navigation.options.targetSurface == nullptr) { state.navigation.targetReached = true; @@ -183,8 +242,7 @@ class DirectNavigator { // Establish & update the surface status // TODO we do not know the intersection index - passing the closer one - const auto& surface = - *state.navigation.options.surfaces.at(state.navigation.surfaceIndex); + const auto& surface = *state.navigation.navSurface(); const double farLimit = std::numeric_limits::max(); const auto index = chooseIntersection( @@ -202,7 +260,7 @@ class DirectNavigator { "Surface not reachable anymore, switching to next one in " "sequence"); // Move the sequence to the next surface - ++state.navigation.surfaceIndex; + state.navigation.nextSurface(state.options.direction); } else { ACTS_VERBOSE("Navigation stepSize set to " << stepper.outputStepSize(state.stepping)); @@ -218,26 +276,27 @@ class DirectNavigator { /// @param [in] stepper Stepper in use template void postStep(propagator_state_t& state, const stepper_t& stepper) const { + if (state.navigation.navigationBreak) { + return; + } + ACTS_VERBOSE("post step"); // Navigator post step always resets the current surface state.navigation.currentSurface = nullptr; // Output the position in the sequence - ACTS_VERBOSE((state.navigation.options.surfaces.size() - - state.navigation.surfaceIndex) + ACTS_VERBOSE(state.navigation.remainingSurfaces(state.options.direction) << " out of " << state.navigation.options.surfaces.size() << " surfaces remain to try."); - if (state.navigation.surfaceIndex >= - state.navigation.options.surfaces.size()) { + if (state.navigation.endOfSurfaces()) { return; } // Establish the surface status // TODO we do not know the intersection index - passing the closer one - const auto& surface = - *state.navigation.options.surfaces.at(state.navigation.surfaceIndex); + const auto& surface = *state.navigation.navSurface(); const double farLimit = std::numeric_limits::max(); const auto index = chooseIntersection( @@ -252,14 +311,12 @@ class DirectNavigator { *m_logger); if (surfaceStatus == Intersection3D::Status::onSurface) { // Set the current surface - state.navigation.currentSurface = - state.navigation.options.surfaces.at(state.navigation.surfaceIndex); + state.navigation.currentSurface = state.navigation.navSurface(); ACTS_VERBOSE("Current surface set to " << state.navigation.currentSurface->geometryId()); // Move the sequence to the next surface - ++state.navigation.surfaceIndex; - if (state.navigation.surfaceIndex < - state.navigation.options.surfaces.size()) { + state.navigation.nextSurface(state.options.direction); + if (!state.navigation.endOfSurfaces()) { ACTS_VERBOSE("Next surface candidate is " << state.navigation.options.surfaces .at(state.navigation.surfaceIndex) diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index cd08613f722..84b305ae431 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -10,7 +10,6 @@ #include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Propagator/EigenStepperError.hpp" #include "Acts/Propagator/detail/CovarianceEngine.hpp" -#include "Acts/Utilities/QuickMath.hpp" #include @@ -177,17 +176,11 @@ Acts::Result Acts::EigenStepper::step( // This is given by the order of the Runge-Kutta method constexpr double exponent = 0.25; - // Whether to use fast power function if available - constexpr bool tryUseFastPow{false}; - double x = state.options.stepping.stepTolerance / errorEstimate_; - if constexpr (exponent == 0.25 && !tryUseFastPow) { + if constexpr (exponent == 0.25) { // This is 3x faster than std::pow x = std::sqrt(std::sqrt(x)); - } else if constexpr (std::numeric_limits::is_iec559 && - tryUseFastPow) { - x = fastPow(x, exponent); } else { x = std::pow(x, exponent); } diff --git a/Core/include/Acts/Propagator/EigenStepperDenseExtension.hpp b/Core/include/Acts/Propagator/EigenStepperDenseExtension.hpp index 822e7f7d6ea..70dbb1cb820 100644 --- a/Core/include/Acts/Propagator/EigenStepperDenseExtension.hpp +++ b/Core/include/Acts/Propagator/EigenStepperDenseExtension.hpp @@ -17,6 +17,7 @@ #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Propagator/EigenStepperDefaultExtension.hpp" #include "Acts/Propagator/Propagator.hpp" +#include "Acts/Utilities/MathHelpers.hpp" namespace Acts { @@ -108,7 +109,7 @@ struct EigenStepperDenseExtension { // Evaluate k for the time propagation Lambdappi[0] = -qop[0] * qop[0] * qop[0] * g * energy[0] / (q * q); //~ tKi[0] = std::hypot(1, mass / initialMomentum); - tKi[0] = std::hypot(1, mass * qop[0]); + tKi[0] = fastHypot(1, mass * qop[0]); kQoP[0] = Lambdappi[0]; } else { // Update parameters and check for momentum condition @@ -122,7 +123,7 @@ struct EigenStepperDenseExtension { // Evaluate k_i for the time propagation auto qopNew = qop[0] + h * Lambdappi[i - 1]; Lambdappi[i] = -qopNew * qopNew * qopNew * g * energy[i] / (q * q); - tKi[i] = std::hypot(1, mass * qopNew); + tKi[i] = fastHypot(1, mass * qopNew); kQoP[i] = Lambdappi[i]; } return true; @@ -167,14 +168,14 @@ struct EigenStepperDenseExtension { } // Add derivative dlambda/ds = Lambda'' - state.stepping.derivative(7) = -std::hypot(mass, newMomentum) * g / + state.stepping.derivative(7) = -fastHypot(mass, newMomentum) * g / (newMomentum * newMomentum * newMomentum); // Update momentum state.stepping.pars[eFreeQOverP] = stepper.charge(state.stepping) / newMomentum; // Add derivative dt/ds = 1/(beta * c) = sqrt(m^2 * p^{-2} + c^{-2}) - state.stepping.derivative(3) = std::hypot(1, mass / newMomentum); + state.stepping.derivative(3) = fastHypot(1, mass / newMomentum); // Update time state.stepping.pars[eFreeTime] += (h / 6.) * (tKi[0] + 2. * (tKi[1] + tKi[2]) + tKi[3]); @@ -332,7 +333,7 @@ struct EigenStepperDenseExtension { //~ (3. * g + qop[0] * dgdqop(energy[0], .mass, //~ absPdg, meanEnergyLoss)); - double dtp1dl = qop[0] * mass * mass / std::hypot(1, qop[0] * mass); + double dtp1dl = qop[0] * mass * mass / fastHypot(1, qop[0] * mass); double qopNew = qop[0] + half_h * Lambdappi[0]; //~ double dtpp2dl = -mass * mass * qopNew * @@ -340,7 +341,7 @@ struct EigenStepperDenseExtension { //~ (3. * g * (1. + half_h * jdL[0]) + //~ qopNew * dgdqop(energy[1], mass, absPdgCode, meanEnergyLoss)); - double dtp2dl = qopNew * mass * mass / std::hypot(1, qopNew * mass); + double dtp2dl = qopNew * mass * mass / fastHypot(1, qopNew * mass); qopNew = qop[0] + half_h * Lambdappi[1]; //~ double dtpp3dl = -mass * mass * qopNew * @@ -348,9 +349,9 @@ struct EigenStepperDenseExtension { //~ (3. * g * (1. + half_h * jdL[1]) + //~ qopNew * dgdqop(energy[2], mass, absPdg, meanEnergyLoss)); - double dtp3dl = qopNew * mass * mass / std::hypot(1, qopNew * mass); + double dtp3dl = qopNew * mass * mass / fastHypot(1, qopNew * mass); qopNew = qop[0] + half_h * Lambdappi[2]; - double dtp4dl = qopNew * mass * mass / std::hypot(1, qopNew * mass); + double dtp4dl = qopNew * mass * mass / fastHypot(1, qopNew * mass); //~ D(3, 7) = h * mass * mass * qop[0] / //~ std::hypot(1., mass * qop[0]) @@ -376,7 +377,7 @@ struct EigenStepperDenseExtension { PdgParticle absPdg = particleHypothesis.absolutePdg(); float absQ = particleHypothesis.absoluteCharge(); - energy[0] = std::hypot(initialMomentum, mass); + energy[0] = fastHypot(initialMomentum, mass); // use unit length as thickness to compute the energy loss per unit length MaterialSlab slab(material, 1); // Use the same energy loss throughout the step. @@ -430,7 +431,7 @@ struct EigenStepperDenseExtension { const stepper_t& stepper, const int i) { // Update parameters related to a changed momentum currentMomentum = initialMomentum + h * dPds[i - 1]; - energy[i] = std::hypot(currentMomentum, mass); + energy[i] = fastHypot(currentMomentum, mass); dPds[i] = g * energy[i] / currentMomentum; qop[i] = stepper.charge(state.stepping) / currentMomentum; // Calculate term for later error propagation diff --git a/Core/include/Acts/Propagator/MultiStepperAborters.hpp b/Core/include/Acts/Propagator/MultiStepperAborters.hpp index f675200975e..0e28375df94 100644 --- a/Core/include/Acts/Propagator/MultiStepperAborters.hpp +++ b/Core/include/Acts/Propagator/MultiStepperAborters.hpp @@ -71,8 +71,8 @@ struct MultiStepperSurfaceReached : public SurfaceReached { } ACTS_VERBOSE( - "MultiStepperSurfaceReached aborter | " - "Target intersection not found. Maybe next time?"); + "MultiStepperSurfaceReached aborter | Average distance to target: " + << sIntersection.pathLength()); } bool reached = true; @@ -84,6 +84,7 @@ struct MultiStepperSurfaceReached : public SurfaceReached { if (!SurfaceReached::checkAbort(singleState, singleStepper, navigator, logger)) { + cmp.status() = Acts::Intersection3D::Status::reachable; reached = false; } else { cmp.status() = Acts::Intersection3D::Status::onSurface; diff --git a/Core/include/Acts/Propagator/Navigator.hpp b/Core/include/Acts/Propagator/Navigator.hpp index b2bea757749..c2a9427257c 100644 --- a/Core/include/Acts/Propagator/Navigator.hpp +++ b/Core/include/Acts/Propagator/Navigator.hpp @@ -205,8 +205,6 @@ class Navigator { : m_cfg{std::move(cfg)}, m_logger{std::move(_logger)} {} State makeState(const Options& options) const { - assert(options.startSurface != nullptr && "Start surface must be set"); - State state; state.options = options; state.startSurface = options.startSurface; diff --git a/Core/include/Acts/Propagator/PropagatorOptions.hpp b/Core/include/Acts/Propagator/PropagatorOptions.hpp index 039aed013a5..78e7edab2fe 100644 --- a/Core/include/Acts/Propagator/PropagatorOptions.hpp +++ b/Core/include/Acts/Propagator/PropagatorOptions.hpp @@ -33,13 +33,22 @@ struct PurePropagatorPlainOptions { /// Absolute maximum path length double pathLimit = std::numeric_limits::max(); - /// Required tolerance to reach surface - double surfaceTolerance = s_onSurfaceTolerance; - /// Loop protection step, it adapts the pathLimit bool loopProtection = true; /// Allowed loop fraction, 1 is a full loop double loopFraction = 0.5; + + /// Required tolerance to reach surface + double surfaceTolerance = s_onSurfaceTolerance; + + /// Constrain the propagation to selected volumes + /// @note ignored if empty + /// @note requires `VolumeConstraintAborter` aborter + std::vector constrainToVolumeIds; + /// Additional volumes to be considered as end of world + /// @note ignored if empty + /// @note requires `VolumeConstraintAborter` aborter + std::vector endOfWorldVolumeIds; }; } // namespace detail diff --git a/Core/include/Acts/Propagator/StandardAborters.hpp b/Core/include/Acts/Propagator/StandardAborters.hpp index ce460a945d7..706b0ce0697 100644 --- a/Core/include/Acts/Propagator/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/StandardAborters.hpp @@ -161,7 +161,7 @@ struct ForcedSurfaceReached : SurfaceReached { : SurfaceReached(std::numeric_limits::lowest()) {} }; -/// This is the condition that the end of World has been reached +/// This is the condition that the end of world has been reached /// it then triggers an propagation abort struct EndOfWorldReached { /// boolean operator for abort condition without using the result @@ -181,6 +181,60 @@ struct EndOfWorldReached { } }; +/// This is the condition that the end of world has been reached +/// it then triggers a propagation abort +struct VolumeConstraintAborter { + /// boolean operator for abort condition without using the result + /// + /// @tparam propagator_state_t Type of the propagator state + /// @tparam navigator_t Type of the navigator + /// + /// @param [in,out] state The propagation state object + /// @param [in] navigator The navigator object + /// @param logger a logger instance + template + bool checkAbort(propagator_state_t& state, const stepper_t& /*stepper*/, + const navigator_t& navigator, const Logger& logger) const { + const auto& constrainToVolumeIds = state.options.constrainToVolumeIds; + const auto& endOfWorldVolumeIds = state.options.endOfWorldVolumeIds; + + if (constrainToVolumeIds.empty() && endOfWorldVolumeIds.empty()) { + return false; + } + const auto* currentVolume = navigator.currentVolume(state.navigation); + + // We need a volume to check its ID + if (currentVolume == nullptr) { + return false; + } + + const auto currentVolumeId = + static_cast(currentVolume->geometryId().volume()); + + if (!constrainToVolumeIds.empty() && + std::find(constrainToVolumeIds.begin(), constrainToVolumeIds.end(), + currentVolumeId) == constrainToVolumeIds.end()) { + ACTS_VERBOSE( + "VolumeConstraintAborter aborter | Abort with volume constrain " + << currentVolumeId); + return true; + } + + if (!endOfWorldVolumeIds.empty() && + std::find(endOfWorldVolumeIds.begin(), endOfWorldVolumeIds.end(), + currentVolumeId) != endOfWorldVolumeIds.end()) { + ACTS_VERBOSE( + "VolumeConstraintAborter aborter | Abort with additional end of " + "world volume " + << currentVolumeId); + return true; + } + + return false; + } +}; + /// Aborter that checks if the propagation has reached any surface struct AnySurfaceReached { template @@ -336,8 +337,8 @@ class StraightLineStepper { direction(prop_state.stepping); // dt / ds prop_state.stepping.derivative(eFreeTime) = - std::hypot(1., prop_state.stepping.particleHypothesis.mass() / - absoluteMomentum(prop_state.stepping)); + fastHypot(1., prop_state.stepping.particleHypothesis.mass() / + absoluteMomentum(prop_state.stepping)); // d (dr/ds) / ds : == 0 prop_state.stepping.derivative.template segment<3>(4) = Acts::Vector3::Zero().transpose(); @@ -424,7 +425,7 @@ class StraightLineStepper { const auto m = state.stepping.particleHypothesis.mass(); const auto p = absoluteMomentum(state.stepping); // time propagates along distance as 1/b = sqrt(1 + m²/p²) - const auto dtds = std::hypot(1., m / p); + const auto dtds = fastHypot(1., m / p); // Update the track parameters according to the equations of motion Vector3 dir = direction(state.stepping); state.stepping.pars.template segment<3>(eFreePos0) += h * dir; diff --git a/Core/include/Acts/Propagator/TryAllNavigator.hpp b/Core/include/Acts/Propagator/TryAllNavigator.hpp index 9a3a2d78475..83d7cbc64a4 100644 --- a/Core/include/Acts/Propagator/TryAllNavigator.hpp +++ b/Core/include/Acts/Propagator/TryAllNavigator.hpp @@ -96,8 +96,6 @@ class TryAllNavigatorBase { : m_cfg(std::move(cfg)), m_logger{std::move(_logger)} {} State makeState(const Options& options) const { - assert(options.startSurface != nullptr && "Start surface must be set"); - State state; state.options = options; state.startSurface = options.startSurface; @@ -587,8 +585,6 @@ class TryAllOverstepNavigator : public TryAllNavigatorBase { : TryAllNavigatorBase(std::move(cfg), std::move(logger)) {} State makeState(const Options& options) const { - assert(options.startSurface != nullptr && "Start surface must be set"); - State state; state.options = options; state.startSurface = options.startSurface; diff --git a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp index e55f60a3dbd..8cf1d6b25f9 100644 --- a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp @@ -17,6 +17,7 @@ #include "Acts/Material/ISurfaceMaterial.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include #include @@ -146,7 +147,7 @@ struct PointwiseMaterialInteraction { const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); // in forward(backward) propagation, energy decreases(increases) and // variances increase(decrease) - const auto nextE = std::hypot(mass, momentum) - Eloss * navDir; + const auto nextE = fastHypot(mass, momentum) - Eloss * navDir; // put particle at rest if energy loss is too large nextP = (mass < nextE) ? std::sqrt(nextE * nextE - mass * mass) : 0; // minimum momentum below which we will not push particles via material diff --git a/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp b/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp index 4b491c6ddc5..f3850e102ce 100644 --- a/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp +++ b/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp @@ -10,17 +10,16 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/Seed.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include #include #include #include #include -#include namespace Acts { /// @todo: @@ -242,12 +241,11 @@ std::optional estimateTrackParamsFromSeed( int sign = ia > 0 ? -1 : 1; const ActsScalar R = circleCenter.norm(); ActsScalar invTanTheta = - local2.z() / - (2.f * R * std::asin(std::hypot(local2.x(), local2.y()) / (2.f * R))); + local2.z() / (2.f * R * std::asin(local2.head<2>().norm() / (2.f * R))); // The momentum direction in the new frame (the center of the circle has the // coordinate (-1.*A/(2*B), 1./(2*B))) ActsScalar A = -circleCenter(0) / circleCenter(1); - Vector3 transDirection(1., A, std::hypot(1, A) * invTanTheta); + Vector3 transDirection(1., A, fastHypot(1, A) * invTanTheta); // Transform it back to the original frame Vector3 direction = rotation * transDirection.normalized(); @@ -277,7 +275,7 @@ std::optional estimateTrackParamsFromSeed( // momentum on the transverse plane of the new frame) ActsScalar qOverPt = sign * (UnitConstants::m) / (0.3 * bFieldInTesla * R); // The estimated q/p in [GeV/c]^-1 - params[eBoundQOverP] = qOverPt / std::hypot(1., invTanTheta); + params[eBoundQOverP] = qOverPt / fastHypot(1., invTanTheta); if (params.hasNaN()) { ACTS_ERROR( @@ -289,4 +287,41 @@ std::optional estimateTrackParamsFromSeed( return params; } +/// Configuration for the estimation of the covariance matrix of the track +/// parameters with `estimateTrackParamCovariance`. +struct EstimateTrackParamCovarianceConfig { + /// The initial sigmas for the track parameters + BoundVector initialSigmas = {1. * UnitConstants::mm, + 1. * UnitConstants::mm, + 1. * UnitConstants::degree, + 1. * UnitConstants::degree, + 1. * UnitConstants::e / UnitConstants::GeV, + 1. * UnitConstants::ns}; + + /// The initial relative uncertainty of the q/pt + double initialSigmaPtRel = 0.1; + + /// The inflation factors for the variances of the track parameters + BoundVector initialVarInflation = {1., 1., 1., 1., 1., 1.}; + /// The inflation factor for time uncertainty if the time parameter was not + /// estimated + double noTimeVarInflation = 100.; +}; + +/// Estimate the covariance matrix of the given track parameters based on the +/// provided configuration. The assumption is that we can model the uncertainty +/// of the track parameters as a diagonal matrix with the provided initial +/// sigmas. The inflation factors are used to inflate the initial variances +/// based on the provided configuration. The uncertainty of q/p is estimated +/// based on the relative uncertainty of the q/pt and the theta uncertainty. +/// +/// @param config is the configuration for the estimation +/// @param params is the track parameters +/// @param hasTime is true if the track parameters have time +/// +/// @return the covariance matrix of the track parameters +BoundMatrix estimateTrackParamCovariance( + const EstimateTrackParamCovarianceConfig& config, const BoundVector& params, + bool hasTime); + } // namespace Acts diff --git a/Core/include/Acts/Seeding/HoughTransformUtils.hpp b/Core/include/Acts/Seeding/HoughTransformUtils.hpp index a7a84d8908c..4fcece48034 100644 --- a/Core/include/Acts/Seeding/HoughTransformUtils.hpp +++ b/Core/include/Acts/Seeding/HoughTransformUtils.hpp @@ -14,6 +14,7 @@ #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" +#include #include #include #include diff --git a/Core/include/Acts/Seeding/SeedFinder.hpp b/Core/include/Acts/Seeding/SeedFinder.hpp index ecad26c540f..cfe9908574c 100644 --- a/Core/include/Acts/Seeding/SeedFinder.hpp +++ b/Core/include/Acts/Seeding/SeedFinder.hpp @@ -17,12 +17,14 @@ #include "Acts/Seeding/SeedFinderConfig.hpp" #include "Acts/Seeding/SeedFinderUtils.hpp" #include "Acts/Seeding/SpacePointGrid.hpp" +#include "Acts/Seeding/detail/UtilityFunctions.hpp" #include #include #include #include #include +#include #include #include #include @@ -30,6 +32,17 @@ namespace Acts { +template +concept GridBinCollection = + std::ranges::random_access_range && + std::same_as; + +template +concept CollectionStoresSeedsTo = requires(Coll coll, external_t sp) { + Acts::detail::pushBackOrInsertAtEnd(coll, + Acts::Seed(sp, sp, sp)); +}; + enum class SpacePointCandidateType : short { eBottom, eTop }; enum class DetectorMeasurementInfo : short { eDefault, eDetailed }; @@ -44,32 +57,32 @@ class SeedFinder { public: struct SeedingState { // bottom space point - std::vector compatBottomSP; - std::vector compatTopSP; + std::vector compatBottomSP{}; + std::vector compatTopSP{}; // contains parameters required to calculate circle with linear equation // ...for bottom-middle - std::vector linCircleBottom; + std::vector linCircleBottom{}; // ...for middle-top - std::vector linCircleTop; + std::vector linCircleTop{}; // create vectors here to avoid reallocation in each loop - std::vector topSpVec; - std::vector curvatures; - std::vector impactParameters; + std::vector topSpVec{}; + std::vector curvatures{}; + std::vector impactParameters{}; // managing seed candidates for SpM - CandidatesForMiddleSp candidates_collector; + CandidatesForMiddleSp candidates_collector{}; // managing doublet candidates boost::container::small_vector, Acts::detail::ipow(3, grid_t::DIM)> - bottomNeighbours; + bottomNeighbours{}; boost::container::small_vector, Acts::detail::ipow(3, grid_t::DIM)> - topNeighbours; + topNeighbours{}; // Mutable variables for Space points used in the seeding - Acts::SpacePointMutableData spacePointMutableData; + Acts::SpacePointMutableData spacePointMutableData{}; }; /// The only constructor. Requires a config object. @@ -97,7 +110,9 @@ class SeedFinder { /// @param rMiddleSPRange range object containing the minimum and maximum r for middle SP for a certain z bin. /// @note Ranges must return pointers. /// @note Ranges must be separate objects for each parallel call. - template + template + requires Acts::CollectionStoresSeedsTo void createSeedsForGroup(const Acts::SeedFinderOptions& options, SeedingState& state, const grid_t& grid, container_t& outputCollection, @@ -107,6 +122,16 @@ class SeedFinder { const Acts::Range1D& rMiddleSPRange) const; private: + /// Given a middle space point candidate, get the proper radius validity range + /// In case the radius range changes according to the z-bin we need to + /// retrieve the proper range. We can do this computation only once, since + /// all the middle space point candidates belong to the same z-bin + /// @param spM space point candidate to be used as middle SP in a seed + /// @param rMiddleSPRange range object containing the minimum and maximum r for middle SP for a certain z bin. + std::pair retrieveRadiusRangeForMiddle( + const external_spacepoint_t& spM, + const Acts::Range1D& rMiddleSPRange) const; + /// Iterates over dublets and tests the compatibility between them by applying /// a series of cuts that can be tested with only two SPs /// @param options frequently changing configuration (like beam position) diff --git a/Core/include/Acts/Seeding/SeedFinder.ipp b/Core/include/Acts/Seeding/SeedFinder.ipp index 9f10c356259..45b95ec05d5 100644 --- a/Core/include/Acts/Seeding/SeedFinder.ipp +++ b/Core/include/Acts/Seeding/SeedFinder.ipp @@ -36,7 +36,9 @@ SeedFinder::SeedFinder( } template -template +template + requires Acts::CollectionStoresSeedsTo void SeedFinder::createSeedsForGroup( const Acts::SeedFinderOptions& options, SeedingState& state, const grid_t& grid, container_t& outputCollection, @@ -104,40 +106,20 @@ void SeedFinder::createSeedsForGroup( return; } + // we compute this here since all middle space point candidates belong to the + // same z-bin + auto [minRadiusRangeForMiddle, maxRadiusRangeForMiddle] = + retrieveRadiusRangeForMiddle(*middleSPs.front(), rMiddleSPRange); for (const external_spacepoint_t* spM : middleSPs) { const float rM = spM->radius(); // check if spM is outside our radial region of interest - if (m_config.useVariableMiddleSPRange) { - if (rM < rMiddleSPRange.min()) { - continue; - } - if (rM > rMiddleSPRange.max()) { - // break because SPs are sorted in r - break; - } - } else if (!m_config.rRangeMiddleSP.empty()) { - /// get zBin position of the middle SP - auto pVal = std::lower_bound(m_config.zBinEdges.begin(), - m_config.zBinEdges.end(), spM->z()); - int zBin = std::distance(m_config.zBinEdges.begin(), pVal); - /// protects against zM at the limit of zBinEdges - zBin == 0 ? zBin : --zBin; - if (rM < m_config.rRangeMiddleSP[zBin][0]) { - continue; - } - if (rM > m_config.rRangeMiddleSP[zBin][1]) { - // break because SPs are sorted in r - break; - } - } else { - if (rM < m_config.rMinMiddle) { - continue; - } - if (rM > m_config.rMaxMiddle) { - // break because SPs are sorted in r - break; - } + if (rM < minRadiusRangeForMiddle) { + continue; + } + if (rM > maxRadiusRangeForMiddle) { + // break because SPs are sorted in r + break; } const float zM = spM->z(); @@ -386,8 +368,15 @@ SeedFinder::getCompatibleDoublets( const float uT = xNewFrame * iDeltaR2; const float vT = yNewFrame * iDeltaR2; - // interactionPointCut == true we apply this cut first cuts before - // coordinate transformation to avoid unnecessary calculations + // We check the interaction point by evaluating the minimal distance + // between the origin and the straight line connecting the two points in + // the doublets. Using a geometric similarity, the Im is given by + // yNewFrame * rM / deltaR <= m_config.impactMax + // However, we make here an approximation of the impact parameter + // which is valid under the assumption yNewFrame / xNewFrame is small + // The correct computation would be: + // yNewFrame * yNewFrame * rM * rM <= m_config.impactMax * + // m_config.impactMax * deltaR2 if (std::abs(rM * yNewFrame) <= impactMax * xNewFrame) { // check if duplet cotTheta is within the region of interest // cotTheta is defined as (deltaZ / deltaR) but instead we multiply @@ -827,4 +816,25 @@ SeedFinder::filterCandidates( } // loop on bottoms } +template +std::pair SeedFinder:: + retrieveRadiusRangeForMiddle( + const external_spacepoint_t& spM, + const Acts::Range1D& rMiddleSPRange) const { + if (m_config.useVariableMiddleSPRange) { + return std::make_pair(rMiddleSPRange.min(), rMiddleSPRange.max()); + } + if (!m_config.rRangeMiddleSP.empty()) { + /// get zBin position of the middle SP + auto pVal = std::lower_bound(m_config.zBinEdges.begin(), + m_config.zBinEdges.end(), spM.z()); + int zBin = std::distance(m_config.zBinEdges.begin(), pVal); + /// protects against zM at the limit of zBinEdges + zBin == 0 ? zBin : --zBin; + return std::make_pair(m_config.rRangeMiddleSP[zBin][0], + m_config.rRangeMiddleSP[zBin][1]); + } + return std::make_pair(m_config.rMinMiddle, m_config.rMaxMiddle); +} + } // namespace Acts diff --git a/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.hpp b/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.hpp index b6902ecd35a..4410f1c125b 100644 --- a/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.hpp +++ b/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.hpp @@ -8,22 +8,32 @@ #pragma once -#include "Acts/Geometry/Extent.hpp" #include "Acts/Seeding/BinnedGroup.hpp" #include "Acts/Seeding/SeedFinderConfig.hpp" #include "Acts/Utilities/Grid.hpp" +#include #include namespace Acts { +/// Concept to check the provided external space point type +/// can be used to fill the space point grid +template +concept CylindricalGridElement = requires(external_spacepoint_t sp) { + { sp.phi() } -> std::same_as; + { sp.z() } -> std::same_as; + { sp.radius() } -> std::same_as; +}; + /// Cylindrical Space Point bin is a 2D grid with (phi, z) bins /// It stores a vector of internal space points to external space points -template +template using CylindricalSpacePointGrid = Acts::Grid< std::vector, Acts::Axis, - Acts::Axis>; + Acts::Axis, + Acts::Axis>; /// Cylindrical Binned Group template @@ -36,26 +46,29 @@ using CylindricalBinnedGroupIterator = Acts::BinnedGroupIterator< struct CylindricalSpacePointGridConfig { // minimum pT to be found by seedFinder - float minPt = 0; + float minPt = 0 * Acts::UnitConstants::MeV; + // maximum extension of sensitive detector layer relevant for seeding as + // distance from x=y=0 (i.e. in r) + float rMax = 320 * Acts::UnitConstants::mm; // maximum extension of sensitive detector layer relevant for seeding as // distance from x=y=0 (i.e. in r) - float rMax = 0; + float rMin = 0 * Acts::UnitConstants::mm; // maximum extension of sensitive detector layer relevant for seeding in // positive direction in z - float zMax = 0; + float zMax = 0 * Acts::UnitConstants::mm; // maximum extension of sensitive detector layer relevant for seeding in // negative direction in z - float zMin = 0; + float zMin = 0 * Acts::UnitConstants::mm; // maximum distance in r from middle space point to bottom or top spacepoint - float deltaRMax = 0; + float deltaRMax = 0 * Acts::UnitConstants::mm; // maximum forward direction expressed as cot(theta) float cotThetaMax = 0; // maximum impact parameter in mm - float impactMax = 0; + float impactMax = 0 * Acts::UnitConstants::mm; // minimum phi value for phiAxis construction - float phiMin = -M_PI; + float phiMin = -std::numbers::pi_v; // maximum phi value for phiAxis construction - float phiMax = M_PI; + float phiMax = std::numbers::pi_v; // Multiplicator for the number of phi-bins. The minimum number of phi-bins // depends on min_pt, magnetic field: 2*M_PI/(minPT particle phi-deflection). // phiBinDeflectionCoverage is a multiplier for this number. If @@ -66,7 +79,8 @@ struct CylindricalSpacePointGridConfig { // maximum number of phi bins int maxPhiBins = 10000; // enable non equidistant binning in z - std::vector zBinEdges; + std::vector zBinEdges{}; + std::vector rBinEdges{}; bool isInInternalUnits = false; CylindricalSpacePointGridConfig toInternalUnits() const { if (isInInternalUnits) { @@ -74,22 +88,54 @@ struct CylindricalSpacePointGridConfig { "Repeated conversion to internal units for " "CylindricalSpacePointGridConfig"); } + using namespace Acts::UnitLiterals; CylindricalSpacePointGridConfig config = *this; config.isInInternalUnits = true; config.minPt /= 1_MeV; + config.rMin /= 1_mm; config.rMax /= 1_mm; config.zMax /= 1_mm; config.zMin /= 1_mm; config.deltaRMax /= 1_mm; + for (float& val : config.zBinEdges) { + val /= 1_mm; + } + for (float& val : config.rBinEdges) { + val /= 1_mm; + } + + if (config.phiMin < -std::numbers::pi_v || + config.phiMax > std::numbers::pi_v) { + throw std::runtime_error( + "CylindricalSpacePointGridConfig: phiMin (" + + std::to_string(config.phiMin) + ") and/or phiMax (" + + std::to_string(config.phiMax) + + ") are outside " + "the allowed phi range, defined as " + "[-std::numbers::pi_v, std::numbers::pi_v]"); + } + if (config.phiMin > config.phiMax) { + throw std::runtime_error( + "CylindricalSpacePointGridConfig: phiMin is bigger then phiMax"); + } + if (config.rMin > config.rMax) { + throw std::runtime_error( + "CylindricalSpacePointGridConfig: rMin is bigger then rMax"); + } + if (config.zMin > config.zMax) { + throw std::runtime_error( + "CylindricalSpacePointGridConfig: zMin is bigger than zMax"); + } + return config; } }; struct CylindricalSpacePointGridOptions { // magnetic field - float bFieldInZ = 0; + float bFieldInZ = 0. * Acts::UnitConstants::T; bool isInInternalUnits = false; CylindricalSpacePointGridOptions toInternalUnits() const { if (isInInternalUnits) { @@ -121,7 +167,17 @@ class CylindricalSpacePointGridCreator { const Acts::SeedFinderOptions& options, Acts::CylindricalSpacePointGrid& grid, external_spacepoint_iterator_t spBegin, - external_spacepoint_iterator_t spEnd, Acts::Extent& rRangeSPExtent); + external_spacepoint_iterator_t spEnd); + + template + requires std::ranges::range && + std::same_as + static void fillGrid( + const Acts::SeedFinderConfig& config, + const Acts::SeedFinderOptions& options, + Acts::CylindricalSpacePointGrid& grid, + const external_collection_t& collection); }; } // namespace Acts diff --git a/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.ipp b/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.ipp index ad23beeaf77..523d470eae5 100644 --- a/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.ipp +++ b/Core/include/Acts/Seeding/detail/CylindricalSpacePointGrid.ipp @@ -103,7 +103,7 @@ Acts::CylindricalSpacePointGridCreator::createGrid( config.phiMin, config.phiMax, phiBins); // vector that will store the edges of the bins of z - std::vector zValues; + std::vector zValues{}; // If zBinEdges is not defined, calculate the edges as zMin + bin * zBinSize if (config.zBinEdges.empty()) { @@ -130,9 +130,19 @@ Acts::CylindricalSpacePointGridCreator::createGrid( } } - Axis zAxis(std::move(zValues)); + std::vector rValues{}; + rValues.reserve(std::max(2ul, config.rBinEdges.size())); + if (config.rBinEdges.empty()) { + rValues = {config.rMin, config.rMax}; + } else { + rValues.insert(rValues.end(), config.rBinEdges.begin(), + config.rBinEdges.end()); + } + + Axis zAxis(std::move(zValues)); + Axis rAxis(std::move(rValues)); return Acts::CylindricalSpacePointGrid( - std::make_tuple(std::move(phiAxis), std::move(zAxis))); + std::make_tuple(std::move(phiAxis), std::move(zAxis), std::move(rAxis))); } template & grid, external_spacepoint_iterator_t spBegin, - external_spacepoint_iterator_t spEnd, Acts::Extent& rRangeSPExtent) { - using iterated_value_t = - typename std::iter_value_t; - using iterated_t = typename std::remove_const_t< - typename std::remove_pointer_t>; - static_assert(!std::is_pointer_v, - "Iterator must contain pointers to space points"); - static_assert(std::same_as, - "Iterator does not contain type this class was templated with"); - + external_spacepoint_iterator_t spEnd) { if (!config.isInInternalUnits) { throw std::runtime_error( "SeedFinderConfig not in ACTS internal units in BinnedSPGroup"); @@ -164,13 +165,15 @@ void Acts::CylindricalSpacePointGridCreator::fillGrid( "SeedFinderOptions not in ACTS internal units in BinnedSPGroup"); } - // sort by radius - // add magnitude of beamPos to rMax to avoid excluding measurements - // create number of bins equal to number of millimeters rMax - // (worst case minR: configured minR + 1mm) - // binSizeR allows to increase or reduce numRBins if needed - std::size_t numRBins = static_cast( - (config.rMax + options.beamPos.norm()) / config.binSizeR); + // Space points are assumed to be ALREADY CORRECTED for beamspot position + // phi, z and r space point selection comes naturally from the + // grid axis definition. Calling `isInside` will let us know if we are + // inside the grid range. + // If a space point is outside the validity range of these quantities + // it goes in an over- or under-flow bin. We want to avoid to consider those + // and skip some computations. + // Additional cuts can be applied by customizing the space point selector + // in the config object. // keep track of changed bins while sorting std::vector usedBinIndex(grid.size(), false); @@ -181,40 +184,19 @@ void Acts::CylindricalSpacePointGridCreator::fillGrid( for (external_spacepoint_iterator_t it = spBegin; it != spEnd; it++, ++counter) { const external_spacepoint_t& sp = *it; - float spX = sp.x(); - float spY = sp.y(); - float spZ = sp.z(); - - // store x,y,z values in extent - rRangeSPExtent.extend({spX, spY, spZ}); // remove SPs according to experiment specific cuts if (!config.spacePointSelector(sp)) { continue; } - // remove SPs outside z and phi region - if (spZ > config.zMax || spZ < config.zMin) { - continue; - } - - float spPhi = std::atan2(spY, spX); - if (spPhi > config.phiMax || spPhi < config.phiMin) { - continue; - } - - // calculate r-Bin index and protect against overflow (underflow not - // possible) - std::size_t rIndex = - static_cast(sp.radius() / config.binSizeR); - // if index out of bounds, the SP is outside the region of interest - if (rIndex >= numRBins) { + // fill rbins into grid + Acts::Vector3 position(sp.phi(), sp.z(), sp.radius()); + if (!grid.isInside(position)) { continue; } - // fill rbins into grid - std::size_t globIndex = - grid.globalBinFromPosition(Acts::Vector2{sp.phi(), sp.z()}); + std::size_t globIndex = grid.globalBinFromPosition(position); auto& rbin = grid.at(globIndex); rbin.push_back(&sp); @@ -232,3 +214,17 @@ void Acts::CylindricalSpacePointGridCreator::fillGrid( std::ranges::sort(rbin, {}, [](const auto& rb) { return rb->radius(); }); } } + +template + requires std::ranges::range && + std::same_as +void Acts::CylindricalSpacePointGridCreator::fillGrid( + const Acts::SeedFinderConfig& config, + const Acts::SeedFinderOptions& options, + Acts::CylindricalSpacePointGrid& grid, + const external_collection_t& collection) { + Acts::CylindricalSpacePointGridCreator::fillGrid( + config, options, grid, std::ranges::begin(collection), + std::ranges::end(collection)); +} diff --git a/Core/include/Acts/Seeding/detail/UtilityFunctions.hpp b/Core/include/Acts/Seeding/detail/UtilityFunctions.hpp index 369906d7f46..9cc61c4fe3f 100644 --- a/Core/include/Acts/Seeding/detail/UtilityFunctions.hpp +++ b/Core/include/Acts/Seeding/detail/UtilityFunctions.hpp @@ -30,16 +30,23 @@ concept isCollectionThatSupportsInsert = }; // Define some functions -template -void pushBackOrInsertAtEnd( - Acts::detail::isCollectionThatSupportsPushBack auto& storage, - value_t&& value) { +template + requires requires(storage_t coll, value_t value) { + coll.push_back(value); + coll.push_back(std::move(value)); + } +void pushBackOrInsertAtEnd(storage_t& storage, value_t&& value) { storage.push_back(std::forward(value)); } template requires(!Acts::detail::isCollectionThatSupportsPushBack && - Acts::detail::isCollectionThatSupportsInsert) + Acts::detail::isCollectionThatSupportsInsert && + requires(storage_t coll, value_t value) { + coll.insert(std::ranges::end(coll), value); + coll.insert(std::ranges::end(coll), std::move(value)); + }) void pushBackOrInsertAtEnd(storage_t& storage, value_t&& value) { storage.insert(std::ranges::end(storage), std::forward(value)); } diff --git a/Core/include/Acts/Surfaces/AnnulusBounds.hpp b/Core/include/Acts/Surfaces/AnnulusBounds.hpp index 17e64f2a505..f1283ec63b8 100644 --- a/Core/include/Acts/Surfaces/AnnulusBounds.hpp +++ b/Core/include/Acts/Surfaces/AnnulusBounds.hpp @@ -133,19 +133,20 @@ class AnnulusBounds : public DiscBounds { std::vector corners() const; /// This method returns the xy coordinates of the four corners of the - /// bounds in module coordinates (in x/y) + /// bounds in module coordinates (in x/y), and if quarterSegments is bigger or + /// equal to 0, the curved part of the segment is included and approximated + /// by the corresponding number of segments. + /// /// Starting from the upper right (max R, pos locX) and proceeding clock-wise /// i.e. (max R; pos locX), (min R; pos locX), (min R; neg loc X), (max R: neg /// locX) /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line - /// - /// @note that that if @c lseg > 0, the extrema points are given, - /// which may slightly alter the number of segments returned + /// @param quarterSegments the number of segments used to approximate + /// a quarter of a circle /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg) const override; + std::vector vertices( + unsigned int quarterSegments = 2u) const override; /// This method returns inner radius double rMin() const final; diff --git a/Core/include/Acts/Surfaces/ConeSurface.hpp b/Core/include/Acts/Surfaces/ConeSurface.hpp index c15d84d1c23..a2b2572bba8 100644 --- a/Core/include/Acts/Surfaces/ConeSurface.hpp +++ b/Core/include/Acts/Surfaces/ConeSurface.hpp @@ -200,15 +200,16 @@ class ConeSurface : public RegularSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, it represents - /// the full 2*M_PI coverange, if lseg is set to 1 only the extrema - /// are given - /// @note that a surface transform can invalidate the extrema - /// in the transformed space + /// @param quarterSegments Number of segments used to approximate a quarter + /// + /// @note The phi extrema points at (-pi, -1/2 pi, 0, 1/2 pi) that fall within + /// the surface will be inserted to guarantee an appropriate extent + /// measurement in x and y /// /// @return A list of vertices and a face/facett description of it - Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const override; + Polyhedron polyhedronRepresentation( + const GeometryContext& gctx, + unsigned int quarterSegments = 2u) const override; /// Return properly formatted class name for screen output std::string name() const override; diff --git a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp index 2f116ddcee5..e802056e7fd 100644 --- a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp +++ b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp @@ -114,13 +114,13 @@ class ConvexPolygonBounds : public ConvexPolygonBoundsBase { /// Return the vertices /// - /// @param lseg the number of segments used to approximate + /// @param ignoredSegments the number of segments used to approximate /// and eventually curved line /// /// @note the number of segments is ignored in this representation /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg = 1) const final; + std::vector vertices(unsigned int ignoredSegments = 0u) const final; /// Return a rectangle bounds object that encloses this polygon. /// @return The rectangular bounds diff --git a/Core/include/Acts/Surfaces/ConvexPolygonBounds.ipp b/Core/include/Acts/Surfaces/ConvexPolygonBounds.ipp index 505d9f95d82..9f1a61955ae 100644 --- a/Core/include/Acts/Surfaces/ConvexPolygonBounds.ipp +++ b/Core/include/Acts/Surfaces/ConvexPolygonBounds.ipp @@ -111,7 +111,7 @@ bool Acts::ConvexPolygonBounds::inside( template std::vector Acts::ConvexPolygonBounds::vertices( - unsigned int /*lseg*/) const { + unsigned int /*ignoredSegments*/) const { return {m_vertices.begin(), m_vertices.end()}; } diff --git a/Core/include/Acts/Surfaces/CylinderBounds.hpp b/Core/include/Acts/Surfaces/CylinderBounds.hpp index 7453029c213..51508055e06 100644 --- a/Core/include/Acts/Surfaces/CylinderBounds.hpp +++ b/Core/include/Acts/Surfaces/CylinderBounds.hpp @@ -112,12 +112,18 @@ class CylinderBounds : public SurfaceBounds { /// Returns true for full phi coverage bool coversFullAzimuth() const; - /// Create the bows/circles on either side of the cylinder + /// Create the bow/circle vertices on either side of the cylinder /// - /// @param trans is the global transform - /// @param lseg are the numbero if phi segments - std::vector createCircles(const Transform3 trans, - std::size_t lseg) const; + /// @param transform is the global transform + /// @param quarterSegments is the number of segments to approximate a quarter + /// of a circle. In order to symmetrize fully closed and sectoral cylinders, + /// also in the first case the two end points are given (albeit they overlap) + /// in -pi / pi + /// + /// @return a singlevector containing the vertices from one side and then + /// from the other side consecutively + std::vector circleVertices(const Transform3 transform, + unsigned int quarterSegments) const; /// Output Method for std::ostream std::ostream& toStream(std::ostream& sl) const final; diff --git a/Core/include/Acts/Surfaces/CylinderSurface.hpp b/Core/include/Acts/Surfaces/CylinderSurface.hpp index 82bc1c68bc2..09b953afce1 100644 --- a/Core/include/Acts/Surfaces/CylinderSurface.hpp +++ b/Core/include/Acts/Surfaces/CylinderSurface.hpp @@ -214,14 +214,20 @@ class CylinderSurface : public RegularSurface { /// Return a Polyhedron for a cylinder /// + /// This method represents the cylinder as a polyhedron with a given number + /// of segments to represent a quarter of a full circle. The polyedron will + /// consist of the vertices of the cylinder on both sides, and faces between + /// them, both as rectangular faces and as triangular faces. + /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, it represents - /// the full 2*M_PI coverange, if lseg is set to 1 only the extrema - /// are given + /// @param quarterSegments The number of segments to approximate a quarter of the + /// full circle; it's chosen to be 1, only the extrema points (-pi, -0.5pi, + /// 0., 0.5pi) are inserted to capture the correct extent in the x-y plane /// /// @return A list of vertices and a face/facett description of it - Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const override; + Polyhedron polyhedronRepresentation( + const GeometryContext& gctx, + unsigned int quarterSegments = 2u) const override; /// Calculate the derivative of path length at the geometry constraint or /// point-of-closest-approach w.r.t. alignment parameters of the surface (i.e. diff --git a/Core/include/Acts/Surfaces/DiamondBounds.hpp b/Core/include/Acts/Surfaces/DiamondBounds.hpp index ddaf727a3e9..7497bd22309 100644 --- a/Core/include/Acts/Surfaces/DiamondBounds.hpp +++ b/Core/include/Acts/Surfaces/DiamondBounds.hpp @@ -90,15 +90,13 @@ class DiamondBounds : public PlanarBounds { bool inside(const Vector2& lposition, const BoundaryTolerance& boundaryTolerance) const final; - /// Return the vertices + /// Return the vertices that describe this shape /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line - /// - /// @note the number of segments is ignored for this representation + /// @param ignoredSegments is an ignored parameter only used for + /// curved bound segments /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg = 1) const final; + std::vector vertices(unsigned int ignoredSegments = 0u) const final; // Bounding box representation const RectangleBounds& boundingBox() const final; @@ -128,8 +126,7 @@ inline std::vector DiamondBounds::values() const { } inline void DiamondBounds::checkConsistency() noexcept(false) { - if (std::any_of(m_values.begin(), m_values.end(), - [](auto v) { return v <= 0.; })) { + if (std::ranges::any_of(m_values, [](auto v) { return v <= 0.; })) { throw std::invalid_argument("DiamondBounds: negative half length."); } if (get(eHalfLengthXnegY) > get(eHalfLengthXzeroY) || diff --git a/Core/include/Acts/Surfaces/DiscBounds.hpp b/Core/include/Acts/Surfaces/DiscBounds.hpp index 22bcce3f960..626f8e2405c 100644 --- a/Core/include/Acts/Surfaces/DiscBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscBounds.hpp @@ -29,14 +29,13 @@ class DiscBounds : public SurfaceBounds { /// Return the vertices /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line, the number refers to full 2*PI - /// - /// @note that the extremas are given, which may slightly alter the - /// number of segments returned + /// @param quarterSegments The number of segments used to describe a quarter + /// of a circle, if it is 1, then only the extrema points in phi are inserted + /// next to the segment corners /// /// @return vector for vertices in 2D - virtual std::vector vertices(unsigned int lseg) const = 0; + virtual std::vector vertices( + unsigned int quarterSegments = 2u) const = 0; /// Returns a reference radius for binning virtual double binningValueR() const = 0; diff --git a/Core/include/Acts/Surfaces/DiscSurface.hpp b/Core/include/Acts/Surfaces/DiscSurface.hpp index 112ec0b30d2..e2c10df4023 100644 --- a/Core/include/Acts/Surfaces/DiscSurface.hpp +++ b/Core/include/Acts/Surfaces/DiscSurface.hpp @@ -312,13 +312,12 @@ class DiscSurface : public RegularSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, it represents - /// the full 2*M_PI coverange, if lseg is set to 1 only the extrema - /// are given + /// @param quarterSegments Number of segments used to describe the + /// quarter of a full circle /// /// @return A list of vertices and a face/facett description of it - Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const override; + Polyhedron polyhedronRepresentation( + const GeometryContext& gctx, unsigned int quarterSegments) const override; /// Calculate the derivative of bound track parameters local position w.r.t. /// position in local 3D Cartesian coordinates diff --git a/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp b/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp index 1e4d9d7706a..a3e4e5ea28f 100644 --- a/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp @@ -123,13 +123,11 @@ class DiscTrapezoidBounds : public DiscBounds { /// This method returns the xy coordinates of the four corners of the /// bounds in module coorindates (in xy) /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line - /// - /// @note that the number of segments are ignored for this surface + /// @param ignoredSegments is an ignored parameter only used for + /// curved bound segments /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg) const final; + std::vector vertices(unsigned int ignoredSegments = 0u) const final; private: std::array m_values; diff --git a/Core/include/Acts/Surfaces/EllipseBounds.hpp b/Core/include/Acts/Surfaces/EllipseBounds.hpp index dc6983b86bb..aec08d10381 100644 --- a/Core/include/Acts/Surfaces/EllipseBounds.hpp +++ b/Core/include/Acts/Surfaces/EllipseBounds.hpp @@ -92,14 +92,13 @@ class EllipseBounds : public PlanarBounds { /// Return the vertices /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line, here it refers to the full 2PI Ellipse - /// - /// @note the number of segments to may be altered by also providing - /// the extremas in all direction + /// @param quarterSegments is the number of segments to approximate a quarter + /// of a circle. In order to symmetrize fully closed and sectoral cylinders, + /// also in the first case the two end points are given (albeit they overlap) + /// in -pi / pi /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg) const final; + std::vector vertices(unsigned int quarterSegments) const final; // Bounding box representation const RectangleBounds& boundingBox() const final; diff --git a/Core/include/Acts/Surfaces/PerigeeSurface.hpp b/Core/include/Acts/Surfaces/PerigeeSurface.hpp index 37b1044f0ce..1d913ecc43e 100644 --- a/Core/include/Acts/Surfaces/PerigeeSurface.hpp +++ b/Core/include/Acts/Surfaces/PerigeeSurface.hpp @@ -76,11 +76,11 @@ class PerigeeSurface : public LineSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg is ignored for a perigee @note ignored + /// @param ingoreSegments is an ignored parameter /// /// @return A list of vertices and a face/facett description of it Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const final; + unsigned int ingoreSegments) const final; protected: /// Output Method for std::ostream diff --git a/Core/include/Acts/Surfaces/PlanarBounds.hpp b/Core/include/Acts/Surfaces/PlanarBounds.hpp index 8c4363dbbc3..28bc0e42b60 100644 --- a/Core/include/Acts/Surfaces/PlanarBounds.hpp +++ b/Core/include/Acts/Surfaces/PlanarBounds.hpp @@ -26,14 +26,15 @@ class PlanarBounds : public SurfaceBounds { public: /// Return the vertices /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line + /// @param quarterSegments is the number of segments used to describe curved + /// segments in a quarter of the phi range. If it is 1, then only the extrema + /// points in phi are inserted next to the segment corners. /// - /// @note that the extremas are given, which may slightly alter the - /// number of segments returned + /// @note for planar bounds without curved segments @c quarterSegments is ignored /// /// @return vector for vertices in 2D - virtual std::vector vertices(unsigned int lseg = 1) const = 0; + virtual std::vector vertices( + unsigned int quarterSegments = 2u) const = 0; /// Bounding box parameters /// diff --git a/Core/include/Acts/Surfaces/PlaneSurface.hpp b/Core/include/Acts/Surfaces/PlaneSurface.hpp index 866cb4d4585..2bc65398f8d 100644 --- a/Core/include/Acts/Surfaces/PlaneSurface.hpp +++ b/Core/include/Acts/Surfaces/PlaneSurface.hpp @@ -198,13 +198,15 @@ class PlaneSurface : public RegularSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, it represents - /// the full 2*M_PI coverange, if lseg is set to 1 only the extrema - /// are given + /// @param quarterSegments is the number of segments used to describe curved + /// segments in a quarter of the phi range. If it is 1, then only the extrema + /// points in phi are inserted next to the segment corners. + /// + /// @note for planar surfaces without curved segments @c quarterSegments is ignored /// /// @return A list of vertices and a face/facett description of it - Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const override; + Polyhedron polyhedronRepresentation( + const GeometryContext& gctx, unsigned int quarterSegments) const override; /// Return properly formatted class name for screen output std::string name() const override; diff --git a/Core/include/Acts/Surfaces/RectangleBounds.hpp b/Core/include/Acts/Surfaces/RectangleBounds.hpp index 7ad67f3aafb..aba1743fa74 100644 --- a/Core/include/Acts/Surfaces/RectangleBounds.hpp +++ b/Core/include/Acts/Surfaces/RectangleBounds.hpp @@ -86,13 +86,12 @@ class RectangleBounds : public PlanarBounds { /// Return the vertices /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line - /// + /// @param quarterSegments is the number of segments used to describe curved + /// segments in a quarter of the phi range. /// @note the number of segments is ignored in this representation /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg = 1) const final; + std::vector vertices(unsigned int quarterSegments = 0u) const final; // Bounding box representation const RectangleBounds& boundingBox() const final; diff --git a/Core/include/Acts/Surfaces/StrawSurface.hpp b/Core/include/Acts/Surfaces/StrawSurface.hpp index 88fd8423525..706bb7d9823 100644 --- a/Core/include/Acts/Surfaces/StrawSurface.hpp +++ b/Core/include/Acts/Surfaces/StrawSurface.hpp @@ -92,13 +92,13 @@ class StrawSurface : public LineSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, it represents - /// the full 2*M_PI coverange, if lseg is set to 1 only the extrema - /// are given @note if lseg is set to 1 then only the straw is created + /// @param quarterSegments is the number of segments used to describe curved + /// segments in a quarter of the phi range. If it is 1, then only the extrema + /// points in phi are inserted next to the segment corners. /// /// @return A list of vertices and a face/facett description of it Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const final; + unsigned int quarterSegments) const final; }; inline Surface::SurfaceType StrawSurface::type() const { diff --git a/Core/include/Acts/Surfaces/Surface.hpp b/Core/include/Acts/Surfaces/Surface.hpp index c93e7d3c9aa..79153e8c98e 100644 --- a/Core/include/Acts/Surfaces/Surface.hpp +++ b/Core/include/Acts/Surfaces/Surface.hpp @@ -418,20 +418,21 @@ class Surface : public virtual GeometryObject, /// Return properly formatted class name virtual std::string name() const = 0; - /// Return a Polyhedron for this object + /// Return a Polyhedron for surface objects /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg Number of segments along curved lines, if the lseg - /// is set to one, only the corners and the extrema are given, - /// otherwise it represents the number of segments for a full 2*M_PI - /// circle and is scaled to the relevant sector + /// @param quarterSegments The number of segemtns to approximate a 0.5*pi sector, + /// which represents a quarter of the full circle + /// + /// @note In order to symmetrize the code between sectoral and closed cylinders + /// in case of closed cylinders, both (-pi, pi) are given as separate vertices /// /// @note An internal surface transform can invalidate the extrema /// in the transformed space /// /// @return A list of vertices and a face/facett description of it - virtual Polyhedron polyhedronRepresentation(const GeometryContext& gctx, - std::size_t lseg) const = 0; + virtual Polyhedron polyhedronRepresentation( + const GeometryContext& gctx, unsigned int quarterSegments = 2u) const = 0; /// The derivative of bound track parameters w.r.t. alignment /// parameters of its reference surface (i.e. local frame origin in @@ -481,7 +482,7 @@ class Surface : public virtual GeometryObject, const GeometryContext& gctx, const Vector3& position) const = 0; void visualize(IVisualization3D& helper, const GeometryContext& gctx, - const ViewConfig& viewConfig = {}) const; + const ViewConfig& viewConfig = s_viewSurface) const; protected: /// Output Method for std::ostream, to be overloaded by child classes diff --git a/Core/include/Acts/Surfaces/SurfaceConcept.hpp b/Core/include/Acts/Surfaces/SurfaceConcept.hpp index e09b420ee9f..2abcda1d5d7 100644 --- a/Core/include/Acts/Surfaces/SurfaceConcept.hpp +++ b/Core/include/Acts/Surfaces/SurfaceConcept.hpp @@ -87,7 +87,7 @@ concept SurfaceConcept = requires(S s, const S cs, S s2, const S cs2, { cs.name() } -> std::same_as; { - cs.polyhedronRepresentation(gctx, std::declval()) + cs.polyhedronRepresentation(gctx, std::declval()) } -> std::same_as; { diff --git a/Core/include/Acts/Surfaces/TrapezoidBounds.hpp b/Core/include/Acts/Surfaces/TrapezoidBounds.hpp index 3f116dc4110..8ccd3a5c928 100644 --- a/Core/include/Acts/Surfaces/TrapezoidBounds.hpp +++ b/Core/include/Acts/Surfaces/TrapezoidBounds.hpp @@ -108,13 +108,13 @@ class TrapezoidBounds : public PlanarBounds { /// Return the vertices /// - /// @param lseg the number of segments used to approximate - /// and eventually curved line + /// @param ignoredSegments is and ignored parameter used to describe + /// the number of segments to approximate curved sectors. /// /// @note the number of segments is ignored in this representation /// /// @return vector for vertices in 2D - std::vector vertices(unsigned int lseg = 1) const final; + std::vector vertices(unsigned int ignoredSegments = 0u) const final; // Bounding box representation const RectangleBounds& boundingBox() const final; diff --git a/Core/include/Acts/Surfaces/detail/BoundaryCheckHelper.hpp b/Core/include/Acts/Surfaces/detail/BoundaryCheckHelper.hpp index eefdde58bb8..898fe5541ae 100644 --- a/Core/include/Acts/Surfaces/detail/BoundaryCheckHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/BoundaryCheckHelper.hpp @@ -10,6 +10,8 @@ #include "Acts/Surfaces/detail/VerticesHelper.hpp" +#include + namespace Acts::detail { /// Check if a point is inside a box. @@ -72,8 +74,7 @@ inline bool insideAlignedBox(const Vector2& lowerLeft, /// @param jacobianOpt The Jacobian to transform the distance to Cartesian /// /// @return True if the point is inside the polygon. -template -inline bool insidePolygon(const Vector2Container& vertices, +inline bool insidePolygon(std::span vertices, const BoundaryTolerance& tolerance, const Vector2& point, const std::optional& jacobianOpt) { diff --git a/Core/include/Acts/Surfaces/detail/FacesHelper.hpp b/Core/include/Acts/Surfaces/detail/FacesHelper.hpp index 260f7ce3b01..ca2922c325e 100644 --- a/Core/include/Acts/Surfaces/detail/FacesHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/FacesHelper.hpp @@ -21,7 +21,7 @@ namespace Acts::detail { struct FacesHelper { using FaceVector = std::vector; - /// @brief This method words for all convex type surface setups + /// @brief This method works for all convex type surface setups /// It includes: /// /// Rectangle / Triangle / Polygon @@ -60,14 +60,12 @@ struct FacesHelper { /// vector is splittable in half into the two bows. /// /// @param vertices The vector of vertices - /// @param fullTwoPi The indicator if the concentric face is closed static std::pair cylindricalFaceMesh( - const std::vector& vertices, bool fullTwoPi = true) { + const std::vector& vertices) { FaceVector faces; FaceVector triangularMesh; std::size_t nqfaces = static_cast(0.5 * vertices.size()); - std::size_t reduce = (!fullTwoPi) ? 1 : 0; - for (std::size_t iface = 0; iface < nqfaces - reduce; ++iface) { + for (std::size_t iface = 0; iface < nqfaces - 1; ++iface) { std::size_t p2 = (iface + 1 == nqfaces) ? 0 : iface + 1; std::vector face = {iface, p2, p2 + nqfaces, nqfaces + iface}; diff --git a/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp b/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp index 71938626eaf..dd18f3e800e 100644 --- a/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -22,51 +23,53 @@ namespace Acts::detail::VerticesHelper { /// A method that inserts the cartesian extrema points and segments /// a curved segment into sub segments /// -/// @param phiMin the minimum Phi of the bounds object -/// @param phiMax the maximum Phi of the bounds object +/// @param phiMin the minimum phi value +/// @param phiMax The second phi value /// @param phiRef is a vector of reference phi values to be included as well -/// @param phiTolerance is the tolerance for reference phi insertion -/// @return a vector +/// @param quarterSegments number of segments used to approximate a segment quarter +/// +/// @return a vector of generated phi values std::vector phiSegments(ActsScalar phiMin = -M_PI, ActsScalar phiMax = M_PI, const std::vector& phiRefs = {}, - ActsScalar phiTolerance = 1e-6); + unsigned int quarterSegments = 2u); /// Helper method to create a regular 2 or 3 D segment -/// between two phi values +/// between two phi values with a given number of segments +/// +/// It will insert the phi at extrema points and reference points, it uses +/// a minimum approximation of a circle with 8 segments /// /// @tparam vertex_t Type of vertex to be applied /// @tparam transform_t Optional transform /// -/// @param vertices [in,out] The 3D vertices to be filled -/// @param rxy The radius description if first +/= second: ellipse -/// @param phi1 The first phi value -/// @param phi2 The second phi value -/// @param lseg The number of segments for full 2*PI -/// @param addon The additional segments to be built +/// @param rXY The radius description if first +/= second: ellipse +/// @param phiMin the minimum phi value +/// @param phiMax the second phi value +/// @param phiRef is a vector of reference phi values to be included as well +/// @param quarterSegments number of segments used to approximate a segment quarter /// @param offset The out of plane offset position of the bow /// @param transform The transform applied (optional) +/// +/// @return a vector of vertices template -void createSegment(std::vector& vertices, - std::pair rxy, ActsScalar phi1, - ActsScalar phi2, unsigned int lseg, int addon = 0, - const vertex_t& offset = vertex_t::Zero(), - const transform_t& transform = transform_t::Identity()) { - // Calculate the number of segments - 1 is the minimum - unsigned int segs = - static_cast(std::abs(phi2 - phi1) / (2 * M_PI) * lseg); - segs = segs > 0 ? segs : 1; - ActsScalar phistep = (phi2 - phi1) / segs; - // Create the segments - for (unsigned int iphi = 0; iphi < segs + addon; ++iphi) { - ActsScalar phi = phi1 + iphi * phistep; +std::vector segmentVertices( + std::pair rXY, ActsScalar phiMin, ActsScalar phiMax, + const std::vector& phiRefs = {}, + unsigned int quarterSegments = 2u, + const vertex_t& offset = vertex_t::Zero(), + const transform_t& transform = transform_t::Identity()) { + std::vector vertices; + std::vector phis = + phiSegments(phiMin, phiMax, phiRefs, quarterSegments); + for (ActsScalar phi : phis) { vertex_t vertex = vertex_t::Zero(); - vertex(0) = rxy.first * std::cos(phi); - vertex(1) = rxy.second * std::sin(phi); - + vertex(0) = rXY.first * std::cos(phi); + vertex(1) = rXY.second * std::sin(phi); vertex = vertex + offset; vertices.push_back(transform * vertex); } + return vertices; } /// Construct vertices on an ellipse-like bound object. @@ -76,14 +79,15 @@ void createSegment(std::vector& vertices, /// @param outerRx The radius of the outer ellipse (in x) /// @param outerRy The radius of the outer ellipse (in y) /// @param avgPhi The phi direction of the center if sector -/// @param halfPhi The half phi sector if sector -/// @param lseg The number of segments for for a full 2*pi segment +/// @param halfPhi The half phi sector of the ellipse +/// @param quarterSegments number of segments used to approximate a segment quarter +/// /// @return a vector of 2d-vectors std::vector ellipsoidVertices(ActsScalar innerRx, ActsScalar innerRy, ActsScalar outerRx, ActsScalar outerRy, ActsScalar avgPhi = 0., ActsScalar halfPhi = M_PI, - unsigned int lseg = 1); + unsigned int quarterSegments = 2u); /// Construct vertices on an disc/wheel-like bound object. /// @@ -91,12 +95,14 @@ std::vector ellipsoidVertices(ActsScalar innerRx, ActsScalar innerRy, /// @param outerR The radius of the outer circle (sector) /// @param avgPhi The phi direction of the center if sector /// @param halfPhi The half phi sector if sector -/// @param lseg The number of segments for for a full 2*pi segment +/// @param quarterSegments number of segments used to approximate a segment quarter +/// /// @return a vector of 2d-vectors std::vector circularVertices(ActsScalar innerR, ActsScalar outerR, ActsScalar avgPhi = 0., ActsScalar halfPhi = M_PI, - unsigned int lseg = 1); + unsigned int quarterSegments = 2u); + /// Check if the point is inside the polygon w/o any tolerances. /// /// @tparam vertex_container_t is an iterable container @@ -169,9 +175,8 @@ bool onHyperPlane(const std::vector& vertices, ActsScalar tolerance = s_onSurfaceTolerance); /// Calculate the closest point on the polygon. -template inline Vector2 computeClosestPointOnPolygon(const Vector2& point, - const Vector2Container& vertices, + std::span vertices, const SquareMatrix2& metric) { auto squaredNorm = [&](const Vector2& x) { return (x.transpose() * metric * x).value(); diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 4878724f99e..05e236e1e29 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -11,43 +11,31 @@ // Workaround for building on clang+libstdc++ #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp" -#include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" -#include "Acts/EventData/MeasurementHelpers.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" -#include "Acts/EventData/ProxyAccessor.hpp" -#include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/EventData/Types.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" -#include "Acts/Material/MaterialSlab.hpp" #include "Acts/Propagator/ActorList.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" -#include "Acts/Propagator/Propagator.hpp" #include "Acts/Propagator/StandardAborters.hpp" #include "Acts/Propagator/detail/LoopProtection.hpp" #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp" -#include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp" #include "Acts/TrackFitting/KalmanFitter.hpp" #include "Acts/TrackFitting/detail/VoidFitterComponents.hpp" #include "Acts/Utilities/CalibrationContext.hpp" -#include "Acts/Utilities/HashedString.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" -#include "Acts/Utilities/TrackHelpers.hpp" -#include "Acts/Utilities/Zip.hpp" #include #include #include -#include -#include +#include #include -#include namespace Acts { @@ -592,19 +580,28 @@ class CombinatorialKalmanFilter { ACTS_ERROR("Error in filter: " << res.error()); result.lastError = res.error(); } + + if (result.finished) { + return; + } } + assert(!result.activeBranches.empty() && "No active branches"); + const bool isEndOfWorldReached = endOfWorldReached.checkAbort(state, stepper, navigator, logger()); + const bool isVolumeConstraintReached = volumeConstraintAborter.checkAbort( + state, stepper, navigator, logger()); const bool isPathLimitReached = result.pathLimitReached.checkAbort( state, stepper, navigator, logger()); const bool isTargetReached = targetReached.checkAbort(state, stepper, navigator, logger()); - const bool allBranchesStopped = result.activeBranches.empty(); - if (isEndOfWorldReached || isPathLimitReached || isTargetReached || - allBranchesStopped) { + if (isEndOfWorldReached || isVolumeConstraintReached || + isPathLimitReached || isTargetReached) { if (isEndOfWorldReached) { ACTS_VERBOSE("End of world reached"); + } else if (isVolumeConstraintReached) { + ACTS_VERBOSE("Volume constraint reached"); } else if (isPathLimitReached) { ACTS_VERBOSE("Path limit reached"); } else if (isTargetReached) { @@ -629,26 +626,12 @@ class CombinatorialKalmanFilter { stepper.releaseStepSize(state.stepping, ConstrainedStep::actor); } - if (!allBranchesStopped) { - // Record the active branch and remove it from the list - storeLastActiveBranch(result); - result.activeBranches.pop_back(); - } else { - // This can happen if we stopped all branches in the filter step - ACTS_VERBOSE("All branches stopped"); - } + // Record the active branch and remove it from the list + storeLastActiveBranch(result); + result.activeBranches.pop_back(); - // If no more active branches, done with filtering; Otherwise, reset - // propagation state to track state at next active branch - if (!result.activeBranches.empty()) { - ACTS_VERBOSE("Propagation jumps to branch with tip = " - << result.activeBranches.back().tipIndex()); - reset(state, stepper, navigator, result); - } else { - ACTS_VERBOSE("Stop Kalman filtering with " - << result.collectedTracks.size() << " found tracks"); - result.finished = true; - } + // Reset propagation state to track state at next active branch + reset(state, stepper, navigator, result); } } @@ -674,9 +657,20 @@ class CombinatorialKalmanFilter { typename navigator_t> void reset(propagator_state_t& state, const stepper_t& stepper, const navigator_t& navigator, result_type& result) const { + if (result.activeBranches.empty()) { + ACTS_VERBOSE("Stop CKF with " << result.collectedTracks.size() + << " found tracks"); + result.finished = true; + + return; + } + auto currentBranch = result.activeBranches.back(); auto currentState = currentBranch.outermostTrackState(); + ACTS_VERBOSE("Propagation jumps to branch with tip = " + << currentBranch.tipIndex()); + // Reset the stepping state stepper.resetState(state.stepping, currentState.filtered(), currentState.filteredCovariance(), @@ -722,40 +716,67 @@ class CombinatorialKalmanFilter { using PM = TrackStatePropMask; bool isSensitive = surface->associatedDetectorElement() != nullptr; - bool isMaterial = surface->surfaceMaterial() != nullptr; + bool hasMaterial = surface->surfaceMaterial() != nullptr; + bool isMaterialOnly = hasMaterial && !isSensitive; + bool expectMeasurements = isSensitive; - std::size_t nBranchesOnSurface = 0; - - if (auto [slBegin, slEnd] = m_sourceLinkAccessor(*surface); - slBegin != slEnd) { - // Screen output message + if (isSensitive) { ACTS_VERBOSE("Measurement surface " << surface->geometryId() << " detected."); + } else if (isMaterialOnly) { + ACTS_VERBOSE("Material surface " << surface->geometryId() + << " detected."); + } else { + ACTS_VERBOSE("Passive surface " << surface->geometryId() + << " detected."); + return Result::success(); + } + + using SourceLinkRange = decltype(m_sourceLinkAccessor(*surface)); + std::optional slRange = std::nullopt; + bool hasMeasurements = false; + if (isSensitive) { + slRange = m_sourceLinkAccessor(*surface); + hasMeasurements = slRange->first != slRange->second; + } + bool isHole = isSensitive && !hasMeasurements; - // Transport the covariance to the surface + if (isHole) { + ACTS_VERBOSE("Detected hole before measurement selection on surface " + << surface->geometryId()); + } + + // Transport the covariance to the surface + if (isHole || isMaterialOnly) { + stepper.transportCovarianceToCurvilinear(state.stepping); + } else { stepper.transportCovarianceToBound(state.stepping, *surface); + } - // Update state and stepper with pre material effects - materialInteractor(surface, state, stepper, navigator, - MaterialUpdateStage::PreUpdate); + // Update state and stepper with pre material effects + materialInteractor(surface, state, stepper, navigator, + MaterialUpdateStage::PreUpdate); - // Bind the transported state to the current surface - auto boundStateRes = - stepper.boundState(state.stepping, *surface, false); - if (!boundStateRes.ok()) { - return boundStateRes.error(); - } - auto& boundState = *boundStateRes; - auto& [boundParams, jacobian, pathLength] = boundState; - boundParams.covariance() = state.stepping.cov; + // Bind the transported state to the current surface + auto boundStateRes = stepper.boundState(state.stepping, *surface, false); + if (!boundStateRes.ok()) { + return boundStateRes.error(); + } + auto& boundState = *boundStateRes; + auto& [boundParams, jacobian, pathLength] = boundState; + boundParams.covariance() = state.stepping.cov; - auto currentBranch = result.activeBranches.back(); - TrackIndexType prevTip = currentBranch.tipIndex(); + auto currentBranch = result.activeBranches.back(); + TrackIndexType prevTip = currentBranch.tipIndex(); - // Create trackstates for all source links (will be filtered later) - using TrackStatesResult = - Acts::Result>; - TrackStatesResult tsRes = trackStateCandidateCreator( + // Create trackstates for all source links (will be filtered later) + using TrackStatesResult = + Acts::Result>; + TrackStatesResult tsRes = TrackStatesResult::success({}); + if (hasMeasurements) { + auto [slBegin, slEnd] = *slRange; + + tsRes = trackStateCandidateCreator( state.geoContext, *calibrationContextPtr, *surface, boundState, slBegin, slEnd, prevTip, *result.trackStates, result.trackStateCandidates, *result.trackStates, logger()); @@ -764,175 +785,98 @@ class CombinatorialKalmanFilter { "Processing of selected track states failed: " << tsRes.error()); return tsRes.error(); } - const CkfTypes::BranchVector& newTrackStateList = - *tsRes; - - if (newTrackStateList.empty()) { - ACTS_VERBOSE("Detected hole after measurement selection on surface " - << surface->geometryId()); - - // Setting the number of branches on the surface to 1 as the hole - // still counts as a branch - nBranchesOnSurface = 1; - - auto stateMask = PM::Predicted | PM::Jacobian; - - // Add a hole track state to the multitrajectory - TrackIndexType currentTip = addNonSourcelinkState( - stateMask, boundState, result, true, prevTip); - auto nonSourcelinkState = - result.trackStates->getTrackState(currentTip); - currentBranch.tipIndex() = currentTip; - currentBranch.nHoles()++; + } + const CkfTypes::BranchVector& newTrackStateList = *tsRes; + + if (!newTrackStateList.empty()) { + Result procRes = + processNewTrackStates(state.geoContext, newTrackStateList, result); + if (!procRes.ok()) { + ACTS_ERROR("Processing of selected track states failed: " + << procRes.error()); + return procRes.error(); + } + unsigned int nBranchesOnSurface = *procRes; - BranchStopperResult branchStopperResult = - m_extensions.branchStopper(currentBranch, nonSourcelinkState); + if (nBranchesOnSurface == 0) { + ACTS_VERBOSE("All branches on surface " << surface->geometryId() + << " have been stopped"); - // Check the branch - if (branchStopperResult == BranchStopperResult::Continue) { - // Remembered the active branch and its state - } else { - // No branch on this surface - nBranchesOnSurface = 0; - if (branchStopperResult == BranchStopperResult::StopAndKeep) { - storeLastActiveBranch(result); - } - // Remove the branch from list - result.activeBranches.pop_back(); - } - } else { - Result procRes = processNewTrackStates( - state.geoContext, newTrackStateList, result); - if (!procRes.ok()) { - ACTS_ERROR("Processing of selected track states failed: " - << procRes.error()); - return procRes.error(); - } - nBranchesOnSurface = *procRes; + reset(state, stepper, navigator, result); - if (nBranchesOnSurface == 0) { - // All branches on the surface have been stopped. Reset happens at - // the end of the function - ACTS_VERBOSE("All branches on surface " << surface->geometryId() - << " have been stopped"); - } else { - // `currentBranch` is invalidated after `processNewTrackStates` - currentBranch = result.activeBranches.back(); - prevTip = currentBranch.tipIndex(); - - auto currentState = currentBranch.outermostTrackState(); - - if (currentState.typeFlags().test(TrackStateFlag::OutlierFlag)) { - // We don't need to update the stepper given an outlier state - ACTS_VERBOSE("Outlier state detected on surface " - << surface->geometryId()); - } else { - // If there are measurement track states on this surface - ACTS_VERBOSE("Filtering step successful with " - << nBranchesOnSurface << " branches"); - // Update stepping state using filtered parameters of last track - // state on this surface - stepper.update(state.stepping, - MultiTrajectoryHelpers::freeFiltered( - state.options.geoContext, currentState), - currentState.filtered(), - currentState.filteredCovariance(), *surface); - ACTS_VERBOSE( - "Stepping state is updated with filtered parameter:"); - ACTS_VERBOSE("-> " << currentState.filtered().transpose() - << " of track state with tip = " - << currentState.index()); - } - } + return Result::success(); } - // Update state and stepper with post material effects - materialInteractor(surface, state, stepper, navigator, - MaterialUpdateStage::PostUpdate); - } else if (isSensitive || isMaterial) { - ACTS_VERBOSE("Handle " << (isSensitive ? "sensitive" : "passive") - << " surface: " << surface->geometryId() - << " without measurements"); - - // No splitting on the surface without source links. Set it to one - // first, but could be changed later - nBranchesOnSurface = 1; - - auto currentBranch = result.activeBranches.back(); - TrackIndexType prevTip = currentBranch.tipIndex(); + // `currentBranch` is invalidated after `processNewTrackStates` + currentBranch = result.activeBranches.back(); + prevTip = currentBranch.tipIndex(); + } else { + if (expectMeasurements) { + ACTS_VERBOSE("Detected hole after measurement selection on surface " + << surface->geometryId()); + } - // No source links on surface, add either hole or passive material - // TrackState. No storage allocation for uncalibrated/calibrated - // measurement and filtered parameter auto stateMask = PM::Predicted | PM::Jacobian; - // Transport the covariance to a curvilinear surface - stepper.transportCovarianceToCurvilinear(state.stepping); - - // Update state and stepper with pre material effects - materialInteractor(surface, state, stepper, navigator, - MaterialUpdateStage::PreUpdate); - - // Transport & bind the state to the current surface - auto boundStateRes = - stepper.boundState(state.stepping, *surface, false); - if (!boundStateRes.ok()) { - return boundStateRes.error(); - } - auto& boundState = *boundStateRes; - auto& [boundParams, jacobian, pathLength] = boundState; - boundParams.covariance() = state.stepping.cov; - // Add a hole or material track state to the multitrajectory TrackIndexType currentTip = addNonSourcelinkState( - stateMask, boundState, result, isSensitive, prevTip); - auto nonSourcelinkState = result.trackStates->getTrackState(currentTip); + stateMask, boundState, result, expectMeasurements, prevTip); currentBranch.tipIndex() = currentTip; - - if (isSensitive) { + auto currentState = currentBranch.outermostTrackState(); + if (expectMeasurements) { currentBranch.nHoles()++; } BranchStopperResult branchStopperResult = - m_extensions.branchStopper(currentBranch, nonSourcelinkState); + m_extensions.branchStopper(currentBranch, currentState); // Check the branch if (branchStopperResult == BranchStopperResult::Continue) { // Remembered the active branch and its state } else { // No branch on this surface - nBranchesOnSurface = 0; if (branchStopperResult == BranchStopperResult::StopAndKeep) { storeLastActiveBranch(result); } // Remove the branch from list result.activeBranches.pop_back(); - } - // Update state and stepper with post material effects - materialInteractor(surface, state, stepper, navigator, - MaterialUpdateStage::PostUpdate); - } else { - // Neither measurement nor material on surface, this branch is still - // valid. Count the branch on current surface - nBranchesOnSurface = 1; - } + // Branch on the surface has been stopped - reset + ACTS_VERBOSE("Branch on surface " << surface->geometryId() + << " has been stopped"); - // Reset current branch if there is no branch on current surface - if (nBranchesOnSurface == 0) { - ACTS_DEBUG("Branch on surface " << surface->geometryId() - << " is stopped"); - if (!result.activeBranches.empty()) { - ACTS_VERBOSE("Propagation jumps to branch with tip = " - << result.activeBranches.back().tipIndex()); reset(state, stepper, navigator, result); - } else { - ACTS_VERBOSE("Stop Kalman filtering with " - << result.collectedTracks.size() << " found tracks"); - result.finished = true; + + return Result::success(); } } + auto currentState = currentBranch.outermostTrackState(); + + if (currentState.typeFlags().test(TrackStateFlag::OutlierFlag)) { + // We don't need to update the stepper given an outlier state + ACTS_VERBOSE("Outlier state detected on surface " + << surface->geometryId()); + } else if (currentState.typeFlags().test( + TrackStateFlag::MeasurementFlag)) { + // If there are measurement track states on this surface + // Update stepping state using filtered parameters of last track + // state on this surface + stepper.update(state.stepping, + MultiTrajectoryHelpers::freeFiltered( + state.options.geoContext, currentState), + currentState.filtered(), + currentState.filteredCovariance(), *surface); + ACTS_VERBOSE("Stepping state is updated with filtered parameter:"); + ACTS_VERBOSE("-> " << currentState.filtered().transpose() + << " of track state with tip = " + << currentState.index()); + } + + // Update state and stepper with post material effects + materialInteractor(surface, state, stepper, navigator, + MaterialUpdateStage::PostUpdate); + return Result::success(); } @@ -956,12 +900,13 @@ class CombinatorialKalmanFilter { auto rootBranch = result.activeBranches.back(); - // Build the new branches by forking the root branch. Reverse the order to - // process the best candidate first + // Build the new branches by forking the root branch. Reverse the order + // to process the best candidate first CkfTypes::BranchVector newBranches; for (auto it = newTrackStateList.rbegin(); it != newTrackStateList.rend(); ++it) { - // Keep the root branch as the first branch, make a copy for the others + // Keep the root branch as the first branch, make a copy for the + // others auto newBranch = (it == newTrackStateList.rbegin()) ? rootBranch : rootBranch.shallowCopy(); @@ -1173,6 +1118,9 @@ class CombinatorialKalmanFilter { /// End of world aborter EndOfWorldReached endOfWorldReached; + /// Volume constraint aborter + VolumeConstraintAborter volumeConstraintAborter; + /// Actor logger instance const Logger* actorLogger{nullptr}; /// Updater logger instance diff --git a/Core/include/Acts/TrackFinding/MeasurementSelector.ipp b/Core/include/Acts/TrackFinding/MeasurementSelector.ipp index 3e32c884585..30d09c002be 100644 --- a/Core/include/Acts/TrackFinding/MeasurementSelector.ipp +++ b/Core/include/Acts/TrackFinding/MeasurementSelector.ipp @@ -21,9 +21,9 @@ MeasurementSelector::select( ACTS_VERBOSE("Invoked MeasurementSelector"); - // Return error if no measurement + // Return if no measurement if (candidates.empty()) { - return CombinatorialKalmanFilterError::MeasurementSelectionFailed; + return Result::success(std::pair(candidates.begin(), candidates.end())); } // Get geoID of this surface @@ -92,20 +92,19 @@ MeasurementSelector::select( "No measurement candidate. Return an outlier measurement chi2=" << minChi2); isOutlier = true; - return Result::success(std::make_pair(candidates.begin() + minIndex, - candidates.begin() + minIndex + 1)); + return Result::success(std::pair(candidates.begin() + minIndex, + candidates.begin() + minIndex + 1)); } else { ACTS_VERBOSE("No measurement candidate. Return empty chi2=" << minChi2); - return Result::success( - std::make_pair(candidates.begin(), candidates.begin())); + return Result::success(std::pair(candidates.begin(), candidates.begin())); } } if (passedCandidates <= 1ul) { // return single item range, no sorting necessary ACTS_VERBOSE("Returning only 1 element chi2=" << minChi2); - return Result::success(std::make_pair(candidates.begin() + minIndex, - candidates.begin() + minIndex + 1)); + return Result::success(std::pair(candidates.begin() + minIndex, + candidates.begin() + minIndex + 1)); } std::sort( @@ -115,7 +114,7 @@ MeasurementSelector::select( ACTS_VERBOSE("Number of selected measurements: " << passedCandidates << ", max: " << cuts.numMeasurements); - return Result::success(std::make_pair( + return Result::success(std::pair( candidates.begin(), candidates.begin() + std::min(cuts.numMeasurements, passedCandidates))); } diff --git a/Core/include/Acts/TrackFinding/TrackSelector.hpp b/Core/include/Acts/TrackFinding/TrackSelector.hpp index d0e8526dc35..e782b7feb9d 100644 --- a/Core/include/Acts/TrackFinding/TrackSelector.hpp +++ b/Core/include/Acts/TrackFinding/TrackSelector.hpp @@ -142,7 +142,7 @@ class TrackSelector { std::vector cutSets = {}; /// Eta bin edges for varying cuts by eta - std::vector absEtaEdges = {}; + std::vector absEtaEdges = {0, inf}; /// Get the number of eta bins /// @return Number of eta bins @@ -150,7 +150,7 @@ class TrackSelector { /// Construct an empty (accepts everything) configuration. /// Results in a single cut set and one abs eta bin from 0 to infinity. - EtaBinnedConfig() : cutSets{{}}, absEtaEdges{{0, inf}} {}; + EtaBinnedConfig() : cutSets{{}} {}; /// Constructor to create a config object that is not upper-bounded. /// This is useful to use the "fluent" API to populate the configuration. @@ -163,13 +163,12 @@ class TrackSelector { /// @param absEtaEdgesIn is the vector of eta bin edges EtaBinnedConfig(std::vector absEtaEdgesIn) : absEtaEdges{std::move(absEtaEdgesIn)} { - cutSets.resize(absEtaEdges.size() - 1); + cutSets.resize(nEtaBins()); } /// Auto-converting constructor from a single cut configuration. /// Results in a single absolute eta bin from 0 to infinity. - EtaBinnedConfig(Config cutSet) - : cutSets{std::move(cutSet)}, absEtaEdges{{0, inf}} {} + EtaBinnedConfig(Config cutSet) : cutSets{std::move(cutSet)} {} /// Add a new eta bin with the given upper bound. /// @param etaMax Upper bound of the new eta bin @@ -195,11 +194,17 @@ class TrackSelector { /// @return True if the configuration has a bin for the given eta bool hasCuts(double eta) const; - /// Get the index of the eta bin for a given eta + /// Get the index of the eta bin for a given eta. + /// throws an exception if Eta is outside the abs eta bin edges. /// @param eta Eta value /// @return Index of the eta bin std::size_t binIndex(double eta) const; + /// Get the index of the eta bin for a given eta + /// @param eta Eta value + /// @return Index of the eta bin, or >= nEtaBins() if Eta is outside the abs eta bin edges. + std::size_t binIndexNoCheck(double eta) const; + /// Get the cuts for a given eta /// @param eta Eta value /// @return Cuts for the given eta @@ -237,8 +242,7 @@ class TrackSelector { private: EtaBinnedConfig m_cfg; - bool m_isUnbinned; - bool m_noEtaCuts; + bool m_isUnbinned = false; }; inline TrackSelector::Config& TrackSelector::Config::loc0(double min, @@ -350,14 +354,22 @@ inline bool TrackSelector::EtaBinnedConfig::hasCuts(double eta) const { } inline std::size_t TrackSelector::EtaBinnedConfig::binIndex(double eta) const { - if (!hasCuts(eta)) { + std::size_t index = binIndexNoCheck(eta); + if (!(index < nEtaBins())) { throw std::invalid_argument{"Eta is outside the abs eta bin edges"}; } + return index; +} +inline std::size_t TrackSelector::EtaBinnedConfig::binIndexNoCheck( + double eta) const { auto binIt = std::upper_bound(absEtaEdges.begin(), absEtaEdges.end(), std::abs(eta)); - std::size_t index = std::distance(absEtaEdges.begin(), binIt) - 1; - return index; + std::size_t index = std::distance(absEtaEdges.begin(), binIt); + if (index == 0) { + index = absEtaEdges.size() + 1; // positive value to check for underflow + } + return index - 1; } inline const TrackSelector::Config& TrackSelector::EtaBinnedConfig::getCuts( @@ -428,8 +440,8 @@ bool TrackSelector::isValidTrack(const track_proxy_t& track) const { return track.hasReferenceSurface() && within(track.transverseMomentum(), cuts.ptMin, cuts.ptMax) && - (m_noEtaCuts || (within(absEta(), cuts.absEtaMin, cuts.absEtaMax) && - within(_eta, cuts.etaMin, cuts.etaMax))) && + (!m_isUnbinned || (within(absEta(), cuts.absEtaMin, cuts.absEtaMax) && + within(_eta, cuts.etaMin, cuts.etaMax))) && within(track.phi(), cuts.phiMin, cuts.phiMax) && within(track.loc0(), cuts.loc0Min, cuts.loc0Max) && within(track.loc1(), cuts.loc1Min, cuts.loc1Max) && @@ -452,26 +464,19 @@ inline TrackSelector::TrackSelector( "TrackSelector cut / eta bin configuration is inconsistent"}; } - m_isUnbinned = false; if (m_cfg.nEtaBins() == 1) { static const std::vector infVec = {0, inf}; - bool limitEta = m_cfg.absEtaEdges != infVec; - m_isUnbinned = !limitEta; // single bin, no eta edges given - - const Config& cuts = m_cfg.cutSets[0]; - - if (limitEta && (cuts.etaMin != -inf || cuts.etaMax != inf || - cuts.absEtaMin != 0.0 || cuts.absEtaMax != inf)) { - throw std::invalid_argument{ - "Explicit eta cuts are only valid for single eta bin"}; - } + m_isUnbinned = + m_cfg.absEtaEdges == infVec; // single bin, no eta edges given } - m_noEtaCuts = m_isUnbinned; - for (const auto& cuts : m_cfg.cutSets) { - if (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 || - cuts.absEtaMax != inf) { - m_noEtaCuts = false; + if (!m_isUnbinned) { + for (const auto& cuts : m_cfg.cutSets) { + if (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 || + cuts.absEtaMax != inf) { + throw std::invalid_argument{ + "Explicit eta cuts are only valid for single eta bin"}; + } } } } diff --git a/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp b/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp index 653bef7e899..666ebf6aa23 100644 --- a/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp +++ b/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp @@ -125,6 +125,7 @@ class AtlasBetheHeitlerApprox { constexpr static double m_singleGaussianLimit = 0.002; double m_lowLimit = 0.10; double m_highLimit = 0.20; + bool m_clampToRange = false; public: /// Construct the Bethe-Heitler approximation description with two @@ -138,16 +139,19 @@ class AtlasBetheHeitlerApprox { /// @param highTransform whether the high data need to be transformed /// @param lowLimit the upper limit for the low data /// @param highLimit the upper limit for the high data + /// @param clampToRange whether to clamp the input x/x0 to the allowed range constexpr AtlasBetheHeitlerApprox(const Data &lowData, const Data &highData, bool lowTransform, bool highTransform, double lowLimit = 0.1, - double highLimit = 0.2) + double highLimit = 0.2, + bool clampToRange = false) : m_lowData(lowData), m_highData(highData), m_lowTransform(lowTransform), m_highTransform(highTransform), m_lowLimit(lowLimit), - m_highLimit(highLimit) {} + m_highLimit(highLimit), + m_clampToRange(clampToRange) {} /// Returns the number of components the returned mixture will have constexpr auto numComponents() const { return NComponents; } @@ -155,7 +159,13 @@ class AtlasBetheHeitlerApprox { /// Checks if an input is valid for the parameterization /// /// @param x pathlength in terms of the radiation length - constexpr bool validXOverX0(ActsScalar x) const { return x < m_highLimit; } + constexpr bool validXOverX0(ActsScalar x) const { + if (m_clampToRange) { + return true; + } else { + return x < m_highLimit; + } + } /// Generates the mixture from the polynomials and reweights them, so /// that the sum of all weights is 1 @@ -164,6 +174,11 @@ class AtlasBetheHeitlerApprox { auto mixture(ActsScalar x) const { using Array = boost::container::static_vector; + + if (m_clampToRange) { + x = std::clamp(x, 0.0, m_highLimit); + } + // Build a polynom auto poly = [](ActsScalar xx, const std::array &coeffs) { @@ -238,9 +253,11 @@ class AtlasBetheHeitlerApprox { /// the parameterization for high x/x0 /// @param lowLimit the upper limit for the low x/x0-data /// @param highLimit the upper limit for the high x/x0-data + /// @param clampToRange forwarded to constructor static auto loadFromFiles(const std::string &low_parameters_path, const std::string &high_parameters_path, - double lowLimit = 0.1, double highLimit = 0.2) { + double lowLimit = 0.1, double highLimit = 0.2, + bool clampToRange = false) { auto read_file = [](const std::string &filepath) { std::ifstream file(filepath); @@ -284,7 +301,8 @@ class AtlasBetheHeitlerApprox { const auto [highData, highTransform] = read_file(high_parameters_path); return AtlasBetheHeitlerApprox(lowData, highData, lowTransform, - highTransform, lowLimit, highLimit); + highTransform, lowLimit, highLimit, + clampToRange); } }; @@ -292,6 +310,7 @@ class AtlasBetheHeitlerApprox { /// configuration, that are stored as static data in the source code. /// This may not be an optimal configuration, but should allow to run /// the GSF without the need to load files -AtlasBetheHeitlerApprox<6, 5> makeDefaultBetheHeitlerApprox(); +AtlasBetheHeitlerApprox<6, 5> makeDefaultBetheHeitlerApprox( + bool clampToRange = false); } // namespace Acts diff --git a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp index efe21b58a60..f6c990a696d 100644 --- a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp +++ b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp @@ -123,15 +123,11 @@ struct GaussianSumFitter { using Actors = ActorList; using PropagatorOptions = typename propagator_t::template Options; - std::vector backwardSequence( - std::next(sSequence.rbegin()), sSequence.rend()); - backwardSequence.push_back(opts.referenceSurface); - PropagatorOptions propOptions(opts.geoContext, opts.magFieldContext); propOptions.setPlainOptions(opts.propagatorPlainOptions); - propOptions.navigation.surfaces = backwardSequence; + propOptions.navigation.surfaces = sSequence; propOptions.actorList.template get() .m_cfg.bethe_heitler_approx = &m_betheHeitlerApproximation; diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index f55b0fac1cf..b0fe848c17b 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -484,11 +484,8 @@ class KalmanFitter { result.fittedStates->applyBackwards( result.lastMeasurementIndex, [&](auto trackState) { auto fSurface = &trackState.referenceSurface(); - auto surface_it = std::find_if( - result.passedAgainSurfaces.begin(), - result.passedAgainSurfaces.end(), - [=](const Surface* s) { return s == fSurface; }); - if (surface_it == result.passedAgainSurfaces.end()) { + if (!rangeContainsValue(result.passedAgainSurfaces, + fSurface)) { // If reversed filtering missed this surface, then there is // no smoothed parameter trackState.unset(TrackStatePropMask::Smoothed); diff --git a/Core/include/Acts/Utilities/BinUtility.hpp b/Core/include/Acts/Utilities/BinUtility.hpp index b01c8d27158..a1213ddd8ec 100644 --- a/Core/include/Acts/Utilities/BinUtility.hpp +++ b/Core/include/Acts/Utilities/BinUtility.hpp @@ -47,7 +47,7 @@ class BinUtility { /// Constructor with only a Transform3 /// /// @param tForm is the local to global transform - BinUtility(const Transform3& tForm) + explicit BinUtility(const Transform3& tForm) : m_binningData(), m_transform(tForm), m_itransform(tForm.inverse()) { m_binningData.reserve(3); } @@ -317,13 +317,15 @@ class BinUtility { return ss.str(); } + /// Overload of << operator for std::ostream for debug output + friend std::ostream& operator<<(std::ostream& sl, const BinUtility& bgen) { + return bgen.toStream(sl); + } + private: std::vector m_binningData; /// vector of BinningData Transform3 m_transform; /// shared transform Transform3 m_itransform; /// unique inverse transform }; -/// Overload of << operator for std::ostream for debug output -std::ostream& operator<<(std::ostream& sl, const BinUtility& bgen); - } // namespace Acts diff --git a/Core/include/Acts/Utilities/Delegate.hpp b/Core/include/Acts/Utilities/Delegate.hpp index 8817411d90d..a0ef2cf7252 100644 --- a/Core/include/Acts/Utilities/Delegate.hpp +++ b/Core/include/Acts/Utilities/Delegate.hpp @@ -44,6 +44,7 @@ class Delegate; /// template class Delegate { + public: static constexpr DelegateType kOwnership = O; /// Alias of the return type @@ -51,11 +52,12 @@ class Delegate { using holder_type = H; /// Alias to the function pointer type this class will store using function_type = return_type (*)(const holder_type *, Args...); - using function_ptr_type = return_type (*)(Args...); + using signature_type = R(Args...); using deleter_type = void (*)(const holder_type *); + private: template using isSignatureCompatible = decltype(std::declval() = std::declval()); @@ -157,7 +159,10 @@ class Delegate { /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it /// @tparam Callable The compile-time free function pointer template - void connect() { + void connect() + requires( + Concepts::invocable_and_returns) + { m_payload.payload = nullptr; static_assert( @@ -202,6 +207,14 @@ class Delegate { m_function = callable; } + template + void connect(function_type callable, const Type *instance) + requires(kOwnership == DelegateType::NonOwning) + { + m_payload.payload = instance; + m_function = callable; + } + /// Connect a member function to be called on an instance /// @tparam Callable The compile-time member function pointer /// @tparam Type The type of the instance the member function should be called on @@ -210,13 +223,11 @@ class Delegate { /// it's lifetime is longer than that of @c Delegate. template void connect(const Type *instance) - requires(kOwnership == DelegateType::NonOwning) - { - static_assert(Concepts::invocable_and_returns, - "Callable given does not correspond exactly to required call " - "signature"); + requires(kOwnership == DelegateType::NonOwning && + Concepts::invocable_and_returns) + { m_payload.payload = instance; m_function = [](const holder_type *payload, Args... args) -> return_type { @@ -234,13 +245,10 @@ class Delegate { /// @note @c Delegate assumes owner ship over @p instance. template void connect(std::unique_ptr instance) - requires(kOwnership == DelegateType::Owning) + requires(kOwnership == DelegateType::Owning && + Concepts::invocable_and_returns) { - static_assert(Concepts::invocable_and_returns, - "Callable given does not correspond exactly to required call " - "signature"); - m_payload.payload = std::unique_ptr( instance.release(), [](const holder_type *payload) { const auto *concretePayload = static_cast(payload); @@ -259,7 +267,9 @@ class Delegate { /// @param args The arguments to call the contained function with /// @return Return value of the contained function template - return_type operator()(Ts &&...args) const { + return_type operator()(Ts &&...args) const + requires(std::is_invocable_v) + { assert(connected() && "Delegate is not connected"); return std::invoke(m_function, m_payload.ptr(), std::forward(args)...); } @@ -328,6 +338,11 @@ class OwningDelegate; /// Alias for an owning delegate template class OwningDelegate - : public Delegate {}; + : public Delegate { + public: + OwningDelegate() = default; + OwningDelegate(Delegate &&delegate) + : Delegate(std::move(delegate)) {} +}; } // namespace Acts diff --git a/Core/include/Acts/Utilities/DelegateChainBuilder.hpp b/Core/include/Acts/Utilities/DelegateChainBuilder.hpp new file mode 100644 index 00000000000..6e4f2c89650 --- /dev/null +++ b/Core/include/Acts/Utilities/DelegateChainBuilder.hpp @@ -0,0 +1,159 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/TypeList.hpp" + +#include +#include +#include + +namespace Acts { + +template , auto... Callables> +class DelegateChainBuilder; + +template +class DelegateChainBuilder, + callables...> { + using return_type = + std::conditional_t, void, + std::array>; + using delegate_type = + Delegate; + using tuple_type = std::tuple; + + public: + template + friend class DelegateChainBuilder; + + DelegateChainBuilder() = default; + + template + DelegateChainBuilder(const D& /*unused*/) {} + + template + constexpr auto add(payload_type payload) + requires(std::is_pointer_v) + { + std::tuple payloads = + std::tuple_cat(m_payloads, std::make_tuple(payload)); + + return DelegateChainBuilder, + callables..., Callable>{payloads}; + } + + template + constexpr auto add() { + std::tuple payloads = + std::tuple_cat(m_payloads, std::make_tuple(std::nullptr_t{})); + + return DelegateChainBuilder, + callables..., Callable>{payloads}; + } + + delegate_type build() + requires(sizeof...(callables) > 0) + { + auto block = std::make_unique(m_payloads); + delegate_type delegate; + delegate.template connect<&DispatchBlock::dispatch>(std::move(block)); + return delegate; + } + + void store(delegate_type& delegate) + requires(sizeof...(callables) > 0) + { + auto block = std::make_unique(m_payloads); + delegate.template connect<&DispatchBlock::dispatch>(std::move(block)); + } + + void store(Delegate& delegate) + requires(sizeof...(callables) == 1) + { + constexpr auto callable = + DispatchBlock::template findCallable<0, 0, callables...>(); + delegate.template connect(std::get<0>(m_payloads)); + } + + private: + DelegateChainBuilder(std::tuple payloads) + : m_payloads(payloads) {} + + struct DispatchBlock { + template + static constexpr auto findCallable() { + if constexpr (I == J) { + return head; + } else { + return findCallable(); + } + } + + template + static constexpr auto invoke(result_ptr result, const tuple_type* payloads, + callable_args... args) { + const auto& callable = findCallable(); + + if constexpr (!std::is_same_v, + std::nullptr_t>) { + auto payload = std::get(*payloads); + + if constexpr (!std::is_same_v) { + std::get(*result) = std::invoke( + callable, payload, std::forward(args)...); + } else { + std::invoke(callable, payload, std::forward(args)...); + } + + } else { + if constexpr (!std::is_same_v) { + std::get(*result) = + std::invoke(callable, std::forward(args)...); + } else { + std::invoke(callable, std::forward(args)...); + } + } + + if constexpr (I < sizeof...(payload_types) - 1) { + invoke(result, payloads, std::forward(args)...); + } + } + + DispatchBlock(tuple_type payloads) : m_payloads(std::move(payloads)) {} + + tuple_type m_payloads{}; + + auto dispatch(callable_args... args) const { + if constexpr (std::is_same_v) { + invoke(nullptr, &m_payloads, std::forward(args)...); + } else { + static_assert( + std::is_same_v || std::is_default_constructible_v, + "Delegate chain return type must be void or default constructible"); + return_type result{}; + invoke(&result, &m_payloads, std::forward(args)...); + return result; + } + } + }; + + private: + tuple_type m_payloads{}; +}; + +template +DelegateChainBuilder(const D& /*unused*/) + -> DelegateChainBuilder; + +} // namespace Acts diff --git a/Core/include/Acts/Utilities/Iterator.hpp b/Core/include/Acts/Utilities/Iterator.hpp new file mode 100644 index 00000000000..a69e6c77faa --- /dev/null +++ b/Core/include/Acts/Utilities/Iterator.hpp @@ -0,0 +1,132 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include +#include + +namespace Acts { +template +class ContainerIndexIterator { + public: + using value_type = Value; + using iterator_category = std::random_access_iterator_tag; + using container_type = + std::conditional_t; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + + ContainerIndexIterator() : m_container(nullptr), m_index(0) {} + + ContainerIndexIterator(container_type& container, std::size_t index) + : m_container(&container), m_index(index) {} + + template + explicit ContainerIndexIterator( + const ContainerIndexIterator<_Container, OtherValue, OtherConst>& o) + requires(!OtherConst || Const) + : m_container(o.m_container), m_index(o.m_index) {} + + value_type operator*() const { + assert(m_container != nullptr); + return m_container->at(m_index); + } + + template + ContainerIndexIterator& operator=( + const ContainerIndexIterator<_Container, OtherValue, OtherConst>& o) + requires(!OtherConst || Const) + { + m_container = o.m_container; + m_index = o.m_index; + return *this; + } + + ContainerIndexIterator& operator++() { + ++m_index; + return *this; + } + + ContainerIndexIterator operator++(int) { + auto copy = *this; + ++*this; + return copy; + } + + ContainerIndexIterator& operator+=(const difference_type& i) { + m_index += i; + return *this; + } + + friend ContainerIndexIterator operator+(const ContainerIndexIterator& t, + const difference_type& i) { + return ContainerIndexIterator(*t.m_container, t.m_index + i); + } + + friend ContainerIndexIterator operator+(const difference_type& i, + const ContainerIndexIterator& t) { + return t + i; + } + + ContainerIndexIterator& operator--() { + --m_index; + return *this; + } + + ContainerIndexIterator operator--(int) { + auto copy = *this; + --*this; + return copy; + } + + ContainerIndexIterator& operator-=(const difference_type& i) { + m_index -= i; + return *this; + } + + friend ContainerIndexIterator operator-(const ContainerIndexIterator& t, + const difference_type& i) { + return ContainerIndexIterator(*t.m_container, t.m_index - i); + } + + template + friend difference_type operator-( + const ContainerIndexIterator& t, + const ContainerIndexIterator<_Container, OtherValue, OtherConst>& o) { + assert(t.m_container == o.m_container); + return t.m_index - o.m_index; + } + + value_type operator[](const difference_type& i) const { return *(*this + i); } + + template + std::strong_ordering operator<=>( + const ContainerIndexIterator<_Container, OtherValue, OtherConst>& o) + const { + if (m_container == o.m_container) { + return m_index <=> o.m_index; + } else { + return m_container <=> o.m_container; + } + } + + template + bool operator==(const ContainerIndexIterator<_Container, OtherValue, + OtherConst>& other) const { + return m_container == other.m_container && m_index == other.m_index; + } + + private: + container_type* m_container; + typename container_type::size_type m_index; +}; +} // namespace Acts diff --git a/Core/include/Acts/Utilities/MathHelpers.hpp b/Core/include/Acts/Utilities/MathHelpers.hpp new file mode 100644 index 00000000000..d4489935d32 --- /dev/null +++ b/Core/include/Acts/Utilities/MathHelpers.hpp @@ -0,0 +1,30 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +namespace Acts { + +template +constexpr auto square(T x) { + return x * x; +} + +template +constexpr auto hypotSquare(T... args) { + return (square(args) + ...); +} + +template +constexpr auto fastHypot(T... args) { + return std::sqrt(hypotSquare(args...)); +} + +} // namespace Acts diff --git a/Core/include/Acts/Utilities/QuickMath.hpp b/Core/include/Acts/Utilities/QuickMath.hpp deleted file mode 100644 index 2f03ebb5e50..00000000000 --- a/Core/include/Acts/Utilities/QuickMath.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -#pragma once - -#include -#include -#include -#include - -namespace Acts { - -/// @brief Fast inverse square root function -/// Taken from https://en.wikipedia.org/wiki/Fast_inverse_square_root -/// @param x the number to calculate the inverse square root of -/// @param iterations the number of newton iterations to perform -template -constexpr T fastInverseSqrt(T x, int iterations = 1) noexcept { - static_assert(std::numeric_limits::is_iec559 && - "Only IEEE 754 is supported"); - static_assert(std::is_same_v || std::is_same_v, - "Only float and double are supported"); - using I = std::conditional_t, std::uint32_t, - std::uint64_t>; - - constexpr I magic = - std::is_same_v ? 0x5f3759df : 0x5fe6eb50c7b537a9; - - union { - T f; - I i; - } u = {x}; - - u.i = magic - (u.i >> 1); - - for (int i = 0; i < iterations; ++i) { - u.f *= 1.5f - 0.5f * x * u.f * u.f; - } - - return u.f; -} - -/// @brief Fast power function @see `std::pow` -/// Taken from -/// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp -/// @param a the base -/// @param b the exponent -constexpr double fastPow(double a, double b) { - // enable only on IEEE 754 - static_assert(std::numeric_limits::is_iec559); - - constexpr std::int64_t magic = 0x3FEF127F00000000; - - union { - double f; - std::int64_t i; - } u = {a}; - - u.i = static_cast(b * (u.i - magic) + magic); - - return u.f; -} - -/// @brief Fast power function more precise than @see `fastPow` -/// Taken from -/// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp -/// @param a the base -/// @param b the exponent -constexpr double fastPowMorePrecise(double a, double b) { - // binary exponentiation - double r = 1.0; - int exp = std::abs(static_cast(b)); - double base = b > 0 ? a : 1 / a; - while (exp != 0) { - if ((exp & 1) != 0) { - r *= base; - } - base *= base; - exp >>= 1; - } - - return r * fastPow(a, b - static_cast(b)); -} - -} // namespace Acts diff --git a/Core/src/Utilities/BinUtility.cpp b/Core/include/Acts/Utilities/TypeTraits.hpp similarity index 65% rename from Core/src/Utilities/BinUtility.cpp rename to Core/include/Acts/Utilities/TypeTraits.hpp index 707f6dce1a0..a3c4aaccdcb 100644 --- a/Core/src/Utilities/BinUtility.cpp +++ b/Core/include/Acts/Utilities/TypeTraits.hpp @@ -6,10 +6,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Utilities/BinUtility.hpp" +#pragma once -#include +#include -std::ostream& Acts::operator<<(std::ostream& sl, const BinUtility& bgen) { - return bgen.toStream(sl); -} +namespace Acts { +template +using const_if_t = std::conditional_t; +} // namespace Acts diff --git a/Core/include/Acts/Utilities/VectorHelpers.hpp b/Core/include/Acts/Utilities/VectorHelpers.hpp index e00bfb5ddc7..8f09a6d0591 100644 --- a/Core/include/Acts/Utilities/VectorHelpers.hpp +++ b/Core/include/Acts/Utilities/VectorHelpers.hpp @@ -73,7 +73,7 @@ double perp(const Eigen::MatrixBase& v) noexcept { assert(v.rows() >= 2 && "Perp function not valid for vectors not at least 2D"); } - return std::hypot(v[0], v[1]); + return v.template head<2>().norm(); } /// Calculate the theta angle (longitudinal w.r.t. z axis) of a vector diff --git a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.hpp b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.hpp index 73884fd31f3..91f18618673 100644 --- a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.hpp +++ b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.hpp @@ -22,6 +22,7 @@ #include "Acts/Vertexing/VertexingError.hpp" #include "Acts/Vertexing/VertexingOptions.hpp" +#include #include namespace Acts { @@ -81,8 +82,7 @@ class AdaptiveMultiVertexFitter { Result removeVertexFromCollection(Vertex& vtxToRemove, const Logger& logger) { - auto it = std::find(vertexCollection.begin(), vertexCollection.end(), - &vtxToRemove); + auto it = std::ranges::find(vertexCollection, &vtxToRemove); // Check if the value was found before erasing if (it == vertexCollection.end()) { ACTS_ERROR("vtxToRemove is not part of vertexCollection."); diff --git a/Core/include/Acts/Visualization/GeometryView3D.hpp b/Core/include/Acts/Visualization/GeometryView3D.hpp index 05053de31ef..fed07697a87 100644 --- a/Core/include/Acts/Visualization/GeometryView3D.hpp +++ b/Core/include/Acts/Visualization/GeometryView3D.hpp @@ -31,12 +31,6 @@ class DetectorVolume; class Portal; } // namespace Experimental -static const ViewConfig s_viewSensitive; -static const ViewConfig s_viewPassive; -static const ViewConfig s_viewVolume; -static const ViewConfig s_viewGrid; -static const ViewConfig s_viewLine; - struct GeometryView3D { /// Helper method to draw Polyhedron objects /// diff --git a/Core/include/Acts/Visualization/IVisualization3D.hpp b/Core/include/Acts/Visualization/IVisualization3D.hpp index 734ab3fe129..be8c5b96226 100644 --- a/Core/include/Acts/Visualization/IVisualization3D.hpp +++ b/Core/include/Acts/Visualization/IVisualization3D.hpp @@ -72,6 +72,12 @@ class IVisualization3D { /// Remove all contents of this helper /// virtual void clear() = 0; + + virtual ~IVisualization3D() = default; + + /// Start a new object context + /// @param name The name of the object + virtual void object(const std::string& name) = 0; }; /// Overload of the << operator to facilitate writing to streams. diff --git a/Core/include/Acts/Visualization/ObjVisualization3D.hpp b/Core/include/Acts/Visualization/ObjVisualization3D.hpp index 941e719f870..a9f496ce304 100644 --- a/Core/include/Acts/Visualization/ObjVisualization3D.hpp +++ b/Core/include/Acts/Visualization/ObjVisualization3D.hpp @@ -12,12 +12,8 @@ #include "Acts/Visualization/IVisualization3D.hpp" #include "Acts/Visualization/ViewConfig.hpp" -#include #include -#include -#include #include -#include #include #include @@ -26,14 +22,10 @@ namespace Acts { /// This helper produces output in the OBJ format. Note that colors are not /// supported in this implementation. /// -template class ObjVisualization3D : public IVisualization3D { public: - static_assert(std::is_same_v || std::is_same_v, - "Use either double or float"); - /// Stored value type, should be double or float - using ValueType = T; + using ValueType = double; /// Type of a vertex based on the value type using VertexType = Eigen::Matrix; @@ -77,22 +69,32 @@ class ObjVisualization3D : public IVisualization3D { /// @copydoc Acts::IVisualization3D::clear() void clear() final; + /// Start a new object context with a name + /// @param name The name of the object + void object(const std::string& name) final; + private: + struct Object { + std::string name; + std::vector vertices{}; + std::vector faces{}; + std::vector lines{}; + + /// The object data to be written + /// Map of colors to be written at given index position + std::map lineColors{}; + std::map vertexColors{}; + std::map faceColors{}; + }; + + Object& object(); + const Object& object() const; + /// The output parameters unsigned int m_outputPrecision = 4; double m_outputScalor = 1.; - /// The object data to be written - std::vector m_vertices; - std::vector m_faces; - std::vector m_lines; - /// Map of colors to be written at given index position - std::map m_lineColors; - std::map m_vertexColors; - std::map m_faceColors; -}; -#ifndef DOXYGEN -#include "detail/ObjVisualization3D.ipp" -#endif + std::vector m_objects; +}; } // namespace Acts diff --git a/Core/include/Acts/Visualization/PlyVisualization3D.hpp b/Core/include/Acts/Visualization/PlyVisualization3D.hpp index e2c94481b9c..a4367dc7e02 100644 --- a/Core/include/Acts/Visualization/PlyVisualization3D.hpp +++ b/Core/include/Acts/Visualization/PlyVisualization3D.hpp @@ -60,6 +60,10 @@ class PlyVisualization3D : public IVisualization3D { /// @copydoc Acts::IVisualization3D::clear() void clear() final; + void object(const std::string& /*name*/) final { + // Unimplemented + } + private: std::vector> m_vertices; std::vector m_faces; diff --git a/Core/include/Acts/Visualization/ViewConfig.hpp b/Core/include/Acts/Visualization/ViewConfig.hpp index 6fcf1b47984..e2cf7b80618 100644 --- a/Core/include/Acts/Visualization/ViewConfig.hpp +++ b/Core/include/Acts/Visualization/ViewConfig.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include namespace Acts { @@ -49,17 +49,31 @@ struct Color { constexpr Color(double r, double g, double b) : Color{std::array{r, g, b}} {} + private: + constexpr static int hexToInt(std::string_view hex) { + constexpr auto hexCharToInt = [](char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } else { + throw std::invalid_argument("Invalid hex character"); + } + }; + + int value = 0; + for (char c : hex) { + value = (value << 4) + hexCharToInt(c); + } + return value; + }; + + public: /// Constructor from hex string. The expected format is `#RRGGBB` /// @param hex The hex string constexpr explicit Color(std::string_view hex) { - auto hexToInt = [](std::string_view hexStr) { - int value = 0; - std::stringstream ss; - ss << std::hex << hexStr; - ss >> value; - return value; - }; - if (hex[0] == '#' && hex.size() == 7) { rgb[0] = hexToInt(hex.substr(1, 2)); // Extract R component rgb[1] = hexToInt(hex.substr(3, 2)); // Extract G component @@ -85,7 +99,6 @@ struct Color { /// @param rhs The second color /// @return True if the colors are equal friend bool operator==(const Color& lhs, const Color& rhs) = default; - /// Output stream operator /// @param os The output stream /// @param color The color to be printed @@ -99,6 +112,10 @@ struct Color { std::array rgb{}; }; +constexpr Color s_defaultSurfaceColor{"#0000aa"}; +constexpr Color s_defaultPortalColor{"#308c48"}; +constexpr Color s_defaultVolumColor{"#ffaa00"}; + /// @brief Struct to concentrate all visualization configurations /// in order to harmonize visualization interfaces struct ViewConfig { @@ -112,12 +129,20 @@ struct ViewConfig { double lineThickness = 0.15; /// The visual surface thickness for this object double surfaceThickness = 0.15; - /// The number of segments to approximate full 2pi - unsigned int nSegments = 72; + /// The number of segments to approximate a quarter of the circle + unsigned int quarterSegments = 72; /// Whether to triangulate or not bool triangulate = false; /// Write name - non-empty string indicates writing std::filesystem::path outputName = std::filesystem::path(""); }; +static const ViewConfig s_viewSurface = {.color = {170, 170, 170}}; +static const ViewConfig s_viewPortal = {.color = Color{"#308c48"}}; +static const ViewConfig s_viewSensitive = {.color = {0, 180, 240}}; +static const ViewConfig s_viewPassive = {.color = {240, 280, 0}}; +static const ViewConfig s_viewVolume = {.color = {220, 220, 0}}; +static const ViewConfig s_viewGrid = {.color = {220, 0, 0}}; +static const ViewConfig s_viewLine = {.color = {0, 0, 220}}; + } // namespace Acts diff --git a/Core/include/Acts/Visualization/detail/ObjVisualization3D.ipp b/Core/include/Acts/Visualization/detail/ObjVisualization3D.ipp deleted file mode 100644 index 2419ef8c546..00000000000 --- a/Core/include/Acts/Visualization/detail/ObjVisualization3D.ipp +++ /dev/null @@ -1,179 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -template -void ObjVisualization3D::vertex(const Vector3& vtx, Color color) { - m_vertexColors[m_vertices.size()] = color; - m_vertices.push_back(vtx.template cast()); -} - -template -void ObjVisualization3D::line(const Vector3& a, const Vector3& b, - Color color) { - if (color != Color{0, 0, 0}) { - m_lineColors[m_lines.size()] = color; - } - // not implemented - vertex(a, color); - vertex(b, color); - m_lines.push_back({m_vertices.size() - 2, m_vertices.size() - 1}); -} - -template -void ObjVisualization3D::face(const std::vector& vtxs, - Color color) { - if (color != Color{0, 0, 0}) { - m_faceColors[m_faces.size()] = color; - } - FaceType idxs; - idxs.reserve(vtxs.size()); - for (const auto& vtx : vtxs) { - vertex(vtx, color); - idxs.push_back(m_vertices.size() - 1); - } - m_faces.push_back(std::move(idxs)); -} - -template -void ObjVisualization3D::faces(const std::vector& vtxs, - const std::vector& faces, - Color color) { - // No faces given - call the face() method - if (faces.empty()) { - face(vtxs, color); - } else { - if (color != Color{0, 0, 0}) { - m_faceColors[m_faces.size()] = color; - } - auto vtxoffs = m_vertices.size(); - if (color != Color{0, 0, 0}) { - m_vertexColors[m_vertices.size()] = color; - } - m_vertices.insert(m_vertices.end(), vtxs.begin(), vtxs.end()); - for (const auto& face : faces) { - if (face.size() == 2) { - m_lines.push_back({face[0] + vtxoffs, face[2] + vtxoffs}); - } else { - FaceType rawFace = face; - std::transform(rawFace.begin(), rawFace.end(), rawFace.begin(), - [&](std::size_t& iv) { return (iv + vtxoffs); }); - m_faces.push_back(rawFace); - } - } - } -} - -template -void ObjVisualization3D::write(const std::filesystem::path& path) const { - std::ofstream os; - std::filesystem::path objectpath = path; - if (!objectpath.has_extension()) { - objectpath.replace_extension(std::filesystem::path("obj")); - } - os.open(std::filesystem::absolute(objectpath).string()); - std::filesystem::path mtlpath = objectpath; - mtlpath.replace_extension(std::filesystem::path("mtl")); - - const std::string mtlpathString = std::filesystem::absolute(mtlpath).string(); - os << "mtllib " << mtlpathString << "\n"; - std::ofstream mtlos; - mtlos.open(mtlpathString); - - write(os, mtlos); - os.close(); - mtlos.close(); -} - -template -void ObjVisualization3D::write(std::ostream& os) const { - std::stringstream sterile; - write(os, sterile); -} - -template -void ObjVisualization3D::write(std::ostream& os, std::ostream& mos) const { - std::map materials; - - auto mixColor = [&](const Color& color) -> std::string { - std::string materialName; - materialName = "material_"; - materialName += std::to_string(color[0]) + std::string("_"); - materialName += std::to_string(color[1]) + std::string("_"); - materialName += std::to_string(color[2]); - - if (!materials.contains(materialName)) { - mos << "newmtl " << materialName << "\n"; - std::vector shadings = {"Ka", "Kd", "Ks"}; - for (const auto& shd : shadings) { - mos << shd << " " << std::to_string(color[0] / 256.) << " "; - mos << std::to_string(color[1] / 256.) << " "; - mos << std::to_string(color[2] / 256.) << " " - << "\n"; - } - mos << "\n"; - } - return std::string("usemtl ") + materialName; - }; - - std::size_t iv = 0; - Color lastVertexColor = {0, 0, 0}; - for (const VertexType& vtx : m_vertices) { - if (m_vertexColors.contains(iv)) { - auto color = m_vertexColors.find(iv)->second; - if (color != lastVertexColor) { - os << mixColor(color) << "\n"; - lastVertexColor = color; - } - } - - os << "v " << std::setprecision(m_outputPrecision) - << m_outputScalor * vtx.x() << " " << m_outputScalor * vtx.y() << " " - << m_outputScalor * vtx.z() << "\n"; - ++iv; - } - std::size_t il = 0; - Color lastLineColor = {0, 0, 0}; - for (const LineType& ln : m_lines) { - if (m_lineColors.contains(il)) { - auto color = m_lineColors.find(il)->second; - if (color != lastLineColor) { - os << mixColor(color) << "\n"; - lastLineColor = color; - } - } - os << "l " << ln.first + 1 << " " << ln.second + 1 << "\n"; - ++il; - } - std::size_t is = 0; - Color lastFaceColor = {0, 0, 0}; - for (const FaceType& fc : m_faces) { - if (m_faceColors.contains(is)) { - auto color = m_faceColors.find(is)->second; - if (color != lastFaceColor) { - os << mixColor(color) << "\n"; - lastFaceColor = color; - } - } - os << "f"; - for (std::size_t i = 0; i < fc.size(); i++) { - os << " " << fc[i] + 1; - } - os << "\n"; - ++is; - } -} - -template -void ObjVisualization3D::clear() { - m_vertices.clear(); - m_faces.clear(); - m_lines.clear(); - m_lineColors.clear(); - m_vertexColors.clear(); - m_faceColors.clear(); -} diff --git a/Core/src/Detector/LayerStructureBuilder.cpp b/Core/src/Detector/LayerStructureBuilder.cpp index d936b27398a..94766a80d0d 100644 --- a/Core/src/Detector/LayerStructureBuilder.cpp +++ b/Core/src/Detector/LayerStructureBuilder.cpp @@ -256,7 +256,8 @@ Acts::Experimental::LayerStructureBuilder::construct( if (!support.internalConstraints.empty()) { // Estimate the extent from the surfaces for (const auto& s : internalSurfaces) { - auto sPolyhedron = s->polyhedronRepresentation(gctx, m_cfg.nSegments); + auto sPolyhedron = + s->polyhedronRepresentation(gctx, m_cfg.quarterSegments); supportExtent.extend(sPolyhedron.extent(), support.internalConstraints); } diff --git a/Core/src/Detector/detail/BlueprintDrawer.cpp b/Core/src/Detector/detail/BlueprintDrawer.cpp index bc17c4c5922..b518266dc4d 100644 --- a/Core/src/Detector/detail/BlueprintDrawer.cpp +++ b/Core/src/Detector/detail/BlueprintDrawer.cpp @@ -57,28 +57,35 @@ std::string labelStr( void Acts::Experimental::detail::BlueprintDrawer::dotStream( std::ostream& ss, const Acts::Experimental::Blueprint::Node& node, const Options& options) { + // Replace the "/" in node names + std::string nodeName = node.name; + std::replace(nodeName.begin(), nodeName.end(), '/', '_'); + // Root / leaf or branch if (node.isRoot()) { ss << "digraph " << options.graphName << " {" << '\n'; - ss << node.name << " " << labelStr(options.root, node.name, node.auxiliary) + ss << nodeName << " " << labelStr(options.root, nodeName, node.auxiliary) << '\n'; - ss << node.name << " " << shapeStr(options.root) << '\n'; + ss << nodeName << " " << shapeStr(options.root) << '\n'; } else if (node.isLeaf()) { - ss << node.name << " " << labelStr(options.leaf, node.name, node.auxiliary) + ss << nodeName << " " << labelStr(options.leaf, nodeName, node.auxiliary) << '\n'; - ss << node.name << " " + ss << nodeName << " " << ((node.internalsBuilder != nullptr) ? shapeStr(options.leaf) : shapeStr(options.gap)) << '\n'; } else { - ss << node.name << " " - << labelStr(options.branch, node.name, node.auxiliary) << '\n'; - ss << node.name << " " << shapeStr(options.branch) << '\n'; + ss << nodeName << " " << labelStr(options.branch, nodeName, node.auxiliary) + << '\n'; + ss << nodeName << " " << shapeStr(options.branch) << '\n'; } // Recursive for children for (const auto& c : node.children) { - ss << node.name << " -> " << c->name << ";" << '\n'; + // Replace the "/" in node names + std::string childName = c->name; + std::replace(childName.begin(), childName.end(), '/', '_'); + ss << nodeName << " -> " << childName << ";" << '\n'; dotStream(ss, *c, options); } @@ -86,29 +93,29 @@ void Acts::Experimental::detail::BlueprintDrawer::dotStream( Options::Node shape = node.isLeaf() ? options.shape : options.virtualShape; std::stringstream bts; bts << node.boundsType; - ss << node.name + "_shape " << shapeStr(shape) << '\n'; - ss << node.name + "_shape " + ss << nodeName + "_shape " << shapeStr(shape) << '\n'; + ss << nodeName + "_shape " << labelStr(shape, bts.str(), {"t = " + toString(node.transform.translation(), 1), "b = " + toString(node.boundaryValues, 1)}) << '\n'; - ss << node.name << " -> " << node.name + "_shape [ arrowhead = \"none\" ];" + ss << nodeName << " -> " << nodeName + "_shape [ arrowhead = \"none\" ];" << '\n'; // Sub node detection if (node.internalsBuilder != nullptr) { - ss << node.name + "_int " << shapeStr(options.internals) << '\n'; - ss << node.name << " -> " << node.name + "_int;" << '\n'; + ss << nodeName + "_int " << shapeStr(options.internals) << '\n'; + ss << nodeName << " -> " << nodeName + "_int;" << '\n'; } if (node.geoIdGenerator != nullptr) { - ss << node.name + "_geoID " << shapeStr(options.geoID) << '\n'; - ss << node.name << " -> " << node.name + "_geoID;" << '\n'; + ss << nodeName + "_geoID " << shapeStr(options.geoID) << '\n'; + ss << nodeName << " -> " << nodeName + "_geoID;" << '\n'; } if (node.rootVolumeFinderBuilder != nullptr) { - ss << node.name + "_roots " << shapeStr(options.roots) << '\n'; - ss << node.name << " -> " << node.name + "_roots;" << '\n'; + ss << nodeName + "_roots " << shapeStr(options.roots) << '\n'; + ss << nodeName << " -> " << nodeName + "_roots;" << '\n'; } if (node.isRoot()) { diff --git a/Core/src/Geometry/CMakeLists.txt b/Core/src/Geometry/CMakeLists.txt index fa5d70c1c02..74f6e0e126d 100644 --- a/Core/src/Geometry/CMakeLists.txt +++ b/Core/src/Geometry/CMakeLists.txt @@ -42,4 +42,5 @@ target_sources( CompositePortalLink.cpp PortalLinkBase.cpp PortalError.cpp + PortalShell.cpp ) diff --git a/Core/src/Geometry/CompositePortalLink.cpp b/Core/src/Geometry/CompositePortalLink.cpp index 7d80e3b331e..d8010bbbce2 100644 --- a/Core/src/Geometry/CompositePortalLink.cpp +++ b/Core/src/Geometry/CompositePortalLink.cpp @@ -8,21 +8,74 @@ #include "Acts/Geometry/CompositePortalLink.hpp" +#include "Acts/Geometry/GridPortalLink.hpp" #include "Acts/Geometry/PortalError.hpp" +#include "Acts/Geometry/TrivialPortalLink.hpp" #include "Acts/Surfaces/CylinderSurface.hpp" #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" #include "Acts/Surfaces/RegularSurface.hpp" +#include "Acts/Utilities/Axis.hpp" +#include #include +#include #include +#include + namespace Acts { +namespace { +std::shared_ptr mergedSurface(const Surface& a, + const Surface& b, + BinningValue direction) { + if (a.type() != b.type()) { + throw std::invalid_argument{"Cannot merge surfaces of different types"}; + } + + if (const auto* cylinderA = dynamic_cast(&a); + cylinderA != nullptr) { + const auto& cylinderB = dynamic_cast(b); + + auto [merged, reversed] = cylinderA->mergedWith(cylinderB, direction, true); + return merged; + } else if (const auto* discA = dynamic_cast(&a); + discA != nullptr) { + const auto& discB = dynamic_cast(b); + auto [merged, reversed] = discA->mergedWith(discB, direction, true); + return merged; + } else if (const auto* planeA = dynamic_cast(&a); + planeA != nullptr) { + throw std::logic_error{"Plane surfaces not implemented yet"}; + } else { + throw std::invalid_argument{"Unsupported surface type"}; + } +} + +std::shared_ptr mergePortalLinks( + const std::vector>& links, + BinningValue direction) { + assert(std::ranges::all_of(links, + [](const auto& link) { return link != nullptr; })); + assert(!links.empty()); + + std::shared_ptr result = links.front()->surfacePtr(); + for (auto it = std::next(links.begin()); it != links.end(); ++it) { + assert(result != nullptr); + result = mergedSurface(*result, it->get()->surface(), direction); + } + + return result; +} +} // namespace + CompositePortalLink::CompositePortalLink(std::unique_ptr a, std::unique_ptr b, BinningValue direction, bool flatten) - : PortalLinkBase(mergedSurface(a.get(), b.get(), direction)) { + : PortalLinkBase(mergedSurface(a->surface(), b->surface(), direction)), + m_direction{direction} { if (!flatten) { m_children.push_back(std::move(a)); m_children.push_back(std::move(b)); @@ -45,6 +98,35 @@ CompositePortalLink::CompositePortalLink(std::unique_ptr a, } } +CompositePortalLink::CompositePortalLink( + std::vector> links, BinningValue direction, + bool flatten) + : PortalLinkBase(mergePortalLinks(links, direction)), + m_direction(direction) { + if (!flatten) { + for (auto& child : links) { + m_children.push_back(std::move(child)); + } + + } else { + auto handle = [&](std::unique_ptr link) { + if (auto* composite = dynamic_cast(link.get()); + composite != nullptr) { + m_children.insert( + m_children.end(), + std::make_move_iterator(composite->m_children.begin()), + std::make_move_iterator(composite->m_children.end())); + } else { + m_children.push_back(std::move(link)); + } + }; + + for (auto& child : links) { + handle(std::move(child)); + } + } +} + Result CompositePortalLink::resolveVolume( const GeometryContext& gctx, const Vector2& position, double tolerance) const { @@ -74,36 +156,6 @@ Result CompositePortalLink::resolveVolume( return PortalError::PositionNotOnAnyChildPortalLink; } -std::shared_ptr CompositePortalLink::mergedSurface( - const PortalLinkBase* a, const PortalLinkBase* b, BinningValue direction) { - assert(a != nullptr); - assert(b != nullptr); - if (a->surface().type() != b->surface().type()) { - throw std::invalid_argument{"Cannot merge surfaces of different types"}; - } - - if (const auto* cylinderA = - dynamic_cast(&a->surface()); - cylinderA != nullptr) { - const auto& cylinderB = dynamic_cast(b->surface()); - - auto [merged, reversed] = cylinderA->mergedWith(cylinderB, direction, true); - return merged; - } else if (const auto* discA = - dynamic_cast(&a->surface()); - discA != nullptr) { - const auto& discB = dynamic_cast(b->surface()); - auto [merged, reversed] = discA->mergedWith(discB, direction, true); - return merged; - } else if (const auto* planeA = - dynamic_cast(&a->surface()); - planeA != nullptr) { - throw std::logic_error{"Plane surfaces not implemented yet"}; - } else { - throw std::invalid_argument{"Unsupported surface type"}; - } -} - std::size_t CompositePortalLink::depth() const { std::size_t result = 1; @@ -125,4 +177,122 @@ void CompositePortalLink::toStream(std::ostream& os) const { os << "CompositePortalLink"; } +std::unique_ptr CompositePortalLink::makeGrid( + const GeometryContext& gctx, const Logger& logger) const { + ACTS_VERBOSE("Attempting to make a grid from a composite portal link"); + std::vector trivialLinks; + std::ranges::transform(m_children, std::back_inserter(trivialLinks), + [](const auto& child) { + return dynamic_cast(child.get()); + }); + + if (std::ranges::any_of(trivialLinks, + [](auto link) { return link == nullptr; })) { + ACTS_VERBOSE( + "Failed to make a grid from a composite portal link -> returning " + "nullptr"); + return nullptr; + } + + // we already know all children have surfaces that are compatible, we produced + // a merged surface for the overall dimensions. + + auto printEdges = [](const auto& edges) -> std::string { + std::vector strings; + std::ranges::transform(edges, std::back_inserter(strings), + [](const auto& v) { return std::to_string(v); }); + return boost::algorithm::join(strings, ", "); + }; + + if (surface().type() == Surface::SurfaceType::Cylinder) { + ACTS_VERBOSE("Combining composite into cylinder grid"); + + if (m_direction != BinningValue::binZ) { + ACTS_ERROR("Cylinder grid only supports binning in Z direction"); + throw std::runtime_error{"Unsupported binning direction"}; + } + + std::vector edges; + edges.reserve(m_children.size() + 1); + + const Transform3& groupTransform = m_surface->transform(gctx); + Transform3 itransform = groupTransform.inverse(); + + std::ranges::sort( + trivialLinks, [&itransform, &gctx](const auto& a, const auto& b) { + return (itransform * a->surface().transform(gctx)).translation()[eZ] < + (itransform * b->surface().transform(gctx)).translation()[eZ]; + }); + + for (const auto& [i, child] : enumerate(trivialLinks)) { + const auto& bounds = + dynamic_cast(child->surface().bounds()); + Transform3 ltransform = itransform * child->surface().transform(gctx); + ActsScalar hlZ = bounds.get(CylinderBounds::eHalfLengthZ); + ActsScalar minZ = ltransform.translation()[eZ] - hlZ; + ActsScalar maxZ = ltransform.translation()[eZ] + hlZ; + if (i == 0) { + edges.push_back(minZ); + } + edges.push_back(maxZ); + } + + ACTS_VERBOSE("~> Determined bin edges to be " << printEdges(edges)); + + Axis axis{AxisBound, edges}; + + auto grid = GridPortalLink::make(m_surface, m_direction, std::move(axis)); + for (const auto& [i, child] : enumerate(trivialLinks)) { + grid->atLocalBins({i + 1}) = &child->volume(); + } + + return grid; + + } else if (surface().type() == Surface::SurfaceType::Disc) { + ACTS_VERBOSE("Combining composite into disc grid"); + + if (m_direction != BinningValue::binR) { + ACTS_ERROR("Disc grid only supports binning in R direction"); + throw std::runtime_error{"Unsupported binning direction"}; + } + + std::vector edges; + edges.reserve(m_children.size() + 1); + + std::ranges::sort(trivialLinks, [](const auto& a, const auto& b) { + const auto& boundsA = + dynamic_cast(a->surface().bounds()); + const auto& boundsB = + dynamic_cast(b->surface().bounds()); + return boundsA.get(RadialBounds::eMinR) < + boundsB.get(RadialBounds::eMinR); + }); + + for (const auto& [i, child] : enumerate(trivialLinks)) { + const auto& bounds = + dynamic_cast(child->surface().bounds()); + + if (i == 0) { + edges.push_back(bounds.get(RadialBounds::eMinR)); + } + edges.push_back(bounds.get(RadialBounds::eMaxR)); + } + + ACTS_VERBOSE("~> Determined bin edges to be " << printEdges(edges)); + + Axis axis{AxisBound, edges}; + + auto grid = GridPortalLink::make(m_surface, m_direction, std::move(axis)); + for (const auto& [i, child] : enumerate(trivialLinks)) { + grid->atLocalBins({i + 1}) = &child->volume(); + } + + return grid; + } else if (surface().type() == Surface::SurfaceType::Plane) { + throw std::runtime_error{"Plane surfaces not implemented yet"}; + } else { + throw std::invalid_argument{"Unsupported surface type"}; + } +} + } // namespace Acts diff --git a/Core/src/Geometry/CylinderVolumeBounds.cpp b/Core/src/Geometry/CylinderVolumeBounds.cpp index c7bb8192f4a..d9af620ea45 100644 --- a/Core/src/Geometry/CylinderVolumeBounds.cpp +++ b/Core/src/Geometry/CylinderVolumeBounds.cpp @@ -309,4 +309,7 @@ void CylinderVolumeBounds::set( } } +CylinderVolumeBounds::CylinderVolumeBounds(const CylinderVolumeBounds& cylbo) = + default; + } // namespace Acts diff --git a/Core/src/Geometry/CylinderVolumeBuilder.cpp b/Core/src/Geometry/CylinderVolumeBuilder.cpp index bb741167519..47de379d9be 100644 --- a/Core/src/Geometry/CylinderVolumeBuilder.cpp +++ b/Core/src/Geometry/CylinderVolumeBuilder.cpp @@ -274,19 +274,17 @@ Acts::CylinderVolumeBuilder::trackingVolume( double tolerance = m_cfg.ringTolerance; // Search for the rmin value - and insert if necessary double rMin = discBounds->rMin(); - auto innerSearch = std::find_if( - innerRadii.begin(), innerRadii.end(), [&](double reference) { - return std::abs(rMin - reference) < tolerance; - }); + auto innerSearch = std::ranges::find_if(innerRadii, [&](double r) { + return std::abs(rMin - r) < tolerance; + }); if (innerSearch == innerRadii.end()) { innerRadii.push_back(rMin); } // Search for the rmax value - and insert if necessary double rMax = discBounds->rMax(); - auto outerSearch = std::find_if( - outerRadii.begin(), outerRadii.end(), [&](double reference) { - return std::abs(rMax - reference) < tolerance; - }); + auto outerSearch = std::ranges::find_if(outerRadii, [&](double r) { + return std::abs(rMax - r) < tolerance; + }); if (outerSearch == outerRadii.end()) { outerRadii.push_back(rMax); } @@ -356,10 +354,9 @@ Acts::CylinderVolumeBuilder::trackingVolume( double test = elay->surfaceRepresentation().binningPositionValue( gctx, BinningValue::binR); // Find the right bin - auto ringVolume = std::find_if( - volumeRminRmax.begin(), volumeRminRmax.end(), - [&](const auto& reference) { - return (test > reference.first && test < reference.second); + auto ringVolume = + std::ranges::find_if(volumeRminRmax, [&](const auto& vrr) { + return (test > vrr.first && test < vrr.second); }); if (ringVolume != volumeRminRmax.end()) { unsigned int ringBin = diff --git a/Core/src/Geometry/CylinderVolumeStack.cpp b/Core/src/Geometry/CylinderVolumeStack.cpp index cb1cfdeb822..9155087d57e 100644 --- a/Core/src/Geometry/CylinderVolumeStack.cpp +++ b/Core/src/Geometry/CylinderVolumeStack.cpp @@ -67,7 +67,7 @@ struct CylinderVolumeStack::VolumeTuple { transformDirty = true; } - void commit() { + void commit(const Logger& logger) { // make a copy so we can't accidentally modify in-place auto copy = std::make_shared(*updatedBounds); @@ -76,7 +76,7 @@ struct CylinderVolumeStack::VolumeTuple { transform = globalTransform; } - volume->update(std::move(updatedBounds), transform); + volume->update(std::move(updatedBounds), transform, logger); bounds = copy.get(); updatedBounds = std::move(copy); transformDirty = false; @@ -151,7 +151,9 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, const auto* cylBounds = dynamic_cast( &m_volumes.front()->volumeBounds()); assert(cylBounds != nullptr && "Volume bounds are not cylinder bounds"); - Volume::update(std::make_shared(*cylBounds)); + Volume::update(std::make_shared(*cylBounds), + std::nullopt, logger); + ACTS_VERBOSE("Transform is now: " << m_transform.matrix()); return; } @@ -185,7 +187,7 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, << vt.localTransform.translation()[eZ]); ACTS_VERBOSE(*vt.updatedBounds); - vt.commit(); + vt.commit(logger); } ACTS_VERBOSE("*** Volume configuration after r synchronization:"); @@ -209,7 +211,8 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, m_transform = m_groupTransform * Translation3{0, 0, midZ}; - Volume::update(std::make_shared(minR, maxR, hlZ)); + Volume::update(std::make_shared(minR, maxR, hlZ), + std::nullopt, logger); ACTS_DEBUG("Outer bounds are:\n" << volumeBounds()); ACTS_DEBUG("Outer transform / new group transform is:\n" << m_transform.matrix()); @@ -241,7 +244,7 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, for (auto& vt : volumeTuples) { ACTS_VERBOSE("Updated bounds for volume at r: " << vt.midR()); ACTS_VERBOSE(*vt.updatedBounds); - vt.commit(); + vt.commit(logger); } ACTS_VERBOSE("*** Volume configuration after z synchronization:"); @@ -265,7 +268,8 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, m_transform = m_groupTransform * Translation3{0, 0, midZ}; - Volume::update(std::make_shared(minR, maxR, hlZ)); + Volume::update(std::make_shared(minR, maxR, hlZ), + std::nullopt, logger); ACTS_DEBUG("Outer bounds are:\n" << volumeBounds()); ACTS_DEBUG("Outer transform is:\n" << m_transform.matrix()); @@ -283,25 +287,40 @@ void CylinderVolumeStack::initializeOuterVolume(BinningValue direction, } void CylinderVolumeStack::overlapPrint( - const CylinderVolumeStack::VolumeTuple& a, + BinningValue direction, const CylinderVolumeStack::VolumeTuple& a, const CylinderVolumeStack::VolumeTuple& b, const Logger& logger) { if (logger().doPrint(Acts::Logging::DEBUG)) { std::stringstream ss; ss << std::fixed; ss << std::setprecision(3); ss << std::setfill(' '); - ACTS_VERBOSE("Checking overlap between"); + int w = 9; - ss << " - " - << " z: [ " << std::setw(w) << a.minZ() << " <- " << std::setw(w) - << a.midZ() << " -> " << std::setw(w) << a.maxZ() << " ]"; - ACTS_VERBOSE(ss.str()); - ss.str(""); - ss << " - " - << " z: [ " << std::setw(w) << b.minZ() << " <- " << std::setw(w) - << b.midZ() << " -> " << std::setw(w) << b.maxZ() << " ]"; - ACTS_VERBOSE(ss.str()); + ACTS_VERBOSE("Checking overlap between"); + if (direction == BinningValue::binZ) { + ss << " - " + << " z: [ " << std::setw(w) << a.minZ() << " <- " << std::setw(w) + << a.midZ() << " -> " << std::setw(w) << a.maxZ() << " ]"; + ACTS_VERBOSE(ss.str()); + + ss.str(""); + ss << " - " + << " z: [ " << std::setw(w) << b.minZ() << " <- " << std::setw(w) + << b.midZ() << " -> " << std::setw(w) << b.maxZ() << " ]"; + ACTS_VERBOSE(ss.str()); + } else { + ss << " - " + << " r: [ " << std::setw(w) << a.minR() << " <-> " << std::setw(w) + << a.maxR() << " ]"; + ACTS_VERBOSE(ss.str()); + + ss.str(""); + ss << " - " + << " r: [ " << std::setw(w) << b.minR() << " <-> " << std::setw(w) + << b.maxR() << " ]"; + ACTS_VERBOSE(ss.str()); + } } } @@ -316,7 +335,7 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( auto& a = volumes.at(i); auto& b = volumes.at(j); - overlapPrint(a, b, logger); + overlapPrint(BinningValue::binZ, a, b, logger); if (a.maxZ() > b.minZ()) { ACTS_ERROR(" -> Overlap in z"); @@ -344,8 +363,9 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( << (aZMidNew - aHlZNew) << " <- " << aZMidNew << " -> " << (aZMidNew + aHlZNew) << "]"); - assert(a.minZ() == aZMidNew - aHlZNew && "Volume shrunk"); - assert(a.maxZ() <= aZMidNew + aHlZNew && "Volume shrunk"); + assert(std::abs(a.minZ() - (aZMidNew - aHlZNew)) < 1e-9 && + "Volume shrunk"); + assert(aHlZNew >= a.halfLengthZ() && "Volume shrunk"); ActsScalar bZMidNew = (b.minZ() + b.maxZ()) / 2.0 - gapWidth / 4.0; ActsScalar bHlZNew = b.halfLengthZ() + gapWidth / 4.0; @@ -354,15 +374,16 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( << (bZMidNew - bHlZNew) << " <- " << bZMidNew << " -> " << (bZMidNew + bHlZNew) << "]"); - assert(b.minZ() >= bZMidNew - bHlZNew && "Volume shrunk"); - assert(b.maxZ() == bZMidNew + bHlZNew && "Volume shrunk"); + assert(bHlZNew >= b.halfLengthZ() && "Volume shrunk"); + assert(std::abs(b.maxZ() - (bZMidNew + bHlZNew)) < 1e-9 && + "Volume shrunk"); - a.localTransform = Translation3{0, 0, aZMidNew}; - a.volume->setTransform(m_groupTransform * a.localTransform); + a.setLocalTransform(Transform3{Translation3{0, 0, aZMidNew}}, + m_groupTransform); a.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, aHlZNew); - b.localTransform = Translation3{0, 0, bZMidNew}; - b.volume->setTransform(m_groupTransform * b.localTransform); + b.setLocalTransform(Transform3{Translation3{0, 0, bZMidNew}}, + m_groupTransform); b.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, bHlZNew); break; @@ -376,11 +397,12 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( << (aZMidNew - aHlZNew) << " <- " << aZMidNew << " -> " << (aZMidNew + aHlZNew) << "]"); - assert(a.minZ() == aZMidNew - aHlZNew && "Volume shrunk"); - assert(a.maxZ() <= aZMidNew + aHlZNew && "Volume shrunk"); + assert(std::abs(a.minZ() - (aZMidNew - aHlZNew)) < 1e-9 && + "Volume shrunk"); + assert(aHlZNew >= a.halfLengthZ() && "Volume shrunk"); - a.localTransform = Translation3{0, 0, aZMidNew}; - a.volume->setTransform(m_groupTransform * a.localTransform); + a.setLocalTransform(Transform3{Translation3{0, 0, aZMidNew}}, + m_groupTransform); a.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, aHlZNew); break; @@ -394,11 +416,12 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( << (bZMidNew - bHlZNew) << " <- " << bZMidNew << " -> " << (bZMidNew + bHlZNew) << "]"); - assert(b.minZ() >= bZMidNew - bHlZNew && "Volume shrunk"); - assert(b.maxZ() == bZMidNew + bHlZNew && "Volume shrunk"); + assert(bHlZNew >= b.halfLengthZ() && "Volume shrunk"); + assert(std::abs(b.maxZ() - (bZMidNew + bHlZNew)) < 1e-9 && + "Volume shrunk"); - b.localTransform = Translation3{0, 0, bZMidNew}; - b.volume->setTransform(m_groupTransform * b.localTransform); + b.setLocalTransform(Transform3{Translation3{0, 0, bZMidNew}}, + m_groupTransform); b.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, bHlZNew); break; } @@ -410,10 +433,13 @@ CylinderVolumeStack::checkOverlapAndAttachInZ( ACTS_VERBOSE(" - Gap half length: " << gapHlZ << " at z: " << gapMidZ); + ActsScalar minR = std::min(a.minR(), b.minR()); + ActsScalar maxR = std::max(a.maxR(), b.maxR()); + Transform3 gapLocalTransform{Translation3{0, 0, gapMidZ}}; Transform3 gapGlobalTransform = m_groupTransform * gapLocalTransform; - auto gapBounds = std::make_shared( - a.minR(), b.maxR(), gapHlZ); + auto gapBounds = + std::make_shared(minR, maxR, gapHlZ); auto gap = addGapVolume(gapGlobalTransform, gapBounds); gapVolumes.emplace_back(*gap, m_groupTransform); @@ -443,7 +469,7 @@ CylinderVolumeStack::checkOverlapAndAttachInR( auto& a = volumes.at(i); auto& b = volumes.at(j); - overlapPrint(a, b, logger); + overlapPrint(BinningValue::binR, a, b, logger); if (a.maxR() > b.minR()) { ACTS_ERROR(" -> Overlap in r"); @@ -622,44 +648,45 @@ std::pair CylinderVolumeStack::synchronizeZBounds( } void CylinderVolumeStack::update(std::shared_ptr volbounds, - std::optional transform) { - if (volbounds == nullptr) { - throw std::invalid_argument("New bounds are nullptr"); + std::optional transform, + const Logger& logger) { + ACTS_DEBUG( + "Resizing CylinderVolumeStack with strategy: " << m_resizeStrategy); + ACTS_DEBUG("Currently have " << m_volumes.size() << " children"); + ACTS_DEBUG(m_gaps.size() << " gaps"); + for (const auto& v : m_volumes) { + ACTS_DEBUG(" - volume bounds: \n" << v->volumeBounds()); + ACTS_DEBUG(" transform: \n" << v->transform().matrix()); } + + ACTS_DEBUG("New bounds are: \n" << *volbounds); + auto cylBounds = std::dynamic_pointer_cast(volbounds); if (cylBounds == nullptr) { throw std::invalid_argument( "CylinderVolumeStack requires CylinderVolumeBounds"); } - update(std::move(cylBounds), transform, - *Acts::getDefaultLogger("CYLSTACK", Logging::VERBOSE)); -} -void CylinderVolumeStack::update( - std::shared_ptr newBounds, - std::optional transform, const Logger& logger) { - ACTS_DEBUG( - "Resizing CylinderVolumeStack with strategy: " << m_resizeStrategy); - - if (newBounds == nullptr) { + if (cylBounds == nullptr) { throw std::invalid_argument("New bounds are nullptr"); } - if (*newBounds == volumeBounds()) { + if (*cylBounds == volumeBounds()) { ACTS_VERBOSE("Bounds are the same, no resize needed"); return; } + ACTS_VERBOSE("Group transform is:\n" << m_groupTransform.matrix()); + ACTS_VERBOSE("Current transform is:\n" << m_transform.matrix()); if (transform.has_value()) { - ACTS_VERBOSE(transform.value().matrix()); + ACTS_VERBOSE("Input transform:\n" << transform.value().matrix()); } VolumeTuple oldVolume{*this, m_transform}; VolumeTuple newVolume{*this, m_transform}; - newVolume.updatedBounds = std::make_shared(*newBounds); + newVolume.updatedBounds = std::make_shared(*cylBounds); newVolume.globalTransform = transform.value_or(m_transform); - newVolume.localTransform = - m_groupTransform.inverse() * newVolume.globalTransform; + newVolume.localTransform = m_transform.inverse() * newVolume.globalTransform; if (!transform.has_value()) { ACTS_VERBOSE("Local transform does not change"); @@ -673,7 +700,7 @@ void CylinderVolumeStack::update( checkVolumeAlignment(volTemp, logger); } - checkNoPhiOrBevel(*newBounds, logger); + checkNoPhiOrBevel(*cylBounds, logger); const ActsScalar newMinR = newVolume.minR(); const ActsScalar newMaxR = newVolume.maxR(); @@ -690,32 +717,48 @@ void CylinderVolumeStack::update( const ActsScalar oldHlZ = oldVolume.halfLengthZ(); ACTS_VERBOSE("Previous bounds are: z: [ " - << oldMinZ << " <- " << oldMidZ << " -> " << oldMaxZ - << " ], r: [ " << oldMinR << " <-> " << oldMaxR << " ]"); + << oldMinZ << " <- " << oldMidZ << " -> " << oldMaxZ << " ] (" + << oldHlZ << "), r: [ " << oldMinR << " <-> " << oldMaxR + << " ]"); ACTS_VERBOSE("New bounds are: z: [ " - << newMinZ << " <- " << newMidZ << " -> " << newMaxZ - << " ], r: [ " << newMinR << " <-> " << newMaxR << " ]"); - - if (newMinZ > oldMinZ) { - ACTS_ERROR("Shrinking the stack size in z is not supported"); - throw std::invalid_argument("Shrinking the stack is not supported"); + << newMinZ << " <- " << newMidZ << " -> " << newMaxZ << " ] (" + << newHlZ << "), r: [ " << newMinR << " <-> " << newMaxR + << " ]"); + + constexpr auto tolerance = s_onSurfaceTolerance; + auto same = [](ActsScalar a, ActsScalar b) { + return std::abs(a - b) < tolerance; + }; + + if (!same(newMinZ, oldMinZ) && newMinZ > oldMinZ) { + ACTS_ERROR("Shrinking the stack size in z is not supported: " + << newMinZ << " -> " << oldMinZ); + throw std::invalid_argument("Shrinking the stack in z is not supported"); } - if (newMaxZ < oldMaxZ) { - ACTS_ERROR("Shrinking the stack size in z is not supported"); - throw std::invalid_argument("Shrinking the stack is not supported"); + if (!same(newMaxZ, oldMaxZ) && newMaxZ < oldMaxZ) { + ACTS_ERROR("Shrinking the stack size in z is not supported: " + << newMaxZ << " -> " << oldMaxZ); + throw std::invalid_argument("Shrinking the stack in z is not supported"); } - if (newMinR > oldMinR) { - ACTS_ERROR("Shrinking the stack size in r is not supported"); - throw std::invalid_argument("Shrinking the stack is not supported"); + if (!same(newMinR, oldMinR) && newMinR > oldMinR) { + ACTS_ERROR("Shrinking the stack size in r is not supported: " + << newMinR << " -> " << oldMinR); + throw std::invalid_argument("Shrinking the stack in r is not supported"); } - if (newMaxR < oldMaxR) { - ACTS_ERROR("Shrinking the stack size in r is not supported"); - throw std::invalid_argument("Shrinking the stack is not supported"); + if (!same(newMaxR, oldMaxR) && newMaxR < oldMaxR) { + ACTS_ERROR("Shrinking the stack size in r is not supported: " + << newMaxR << " -> " << oldMaxR); + throw std::invalid_argument("Shrinking the stack is r in not supported"); } + auto isGap = [this](const Volume* vol) { + return std::ranges::any_of( + m_gaps, [&](const auto& gap) { return vol == gap.get(); }); + }; + if (m_direction == BinningValue::binZ) { ACTS_VERBOSE("Stack direction is z"); @@ -730,7 +773,7 @@ void CylinderVolumeStack::update( ACTS_VERBOSE("*** Initial volume configuration:"); printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG); - if (newMinR != oldMinR || newMaxR != oldMaxR) { + if (!same(newMinR, oldMinR) || !same(newMaxR, oldMaxR)) { ACTS_VERBOSE("Resize all volumes to new r bounds"); for (auto& volume : volumeTuples) { volume.set({ @@ -744,7 +787,7 @@ void CylinderVolumeStack::update( ACTS_VERBOSE("R bounds are the same, no r resize needed"); } - if (newHlZ == oldHlZ) { + if (same(newHlZ, oldHlZ)) { ACTS_VERBOSE("Halflength z is the same, no z resize needed"); } else { if (m_resizeStrategy == ResizeStrategy::Expand) { @@ -784,43 +827,86 @@ void CylinderVolumeStack::update( } else if (m_resizeStrategy == ResizeStrategy::Gap) { ACTS_VERBOSE("Creating gap volumes to fill the new z bounds"); - if (newMinZ < oldMinZ) { - ACTS_VERBOSE("Adding gap volume at negative z"); + auto printGapDimensions = [&](const VolumeTuple& gap, + const std::string& prefix = "") { + ACTS_VERBOSE(" -> gap" << prefix << ": [ " << gap.minZ() << " <- " + << gap.midZ() << " -> " << gap.maxZ() + << " ], r: [ " << gap.minR() << " <-> " + << gap.maxR() << " ]"); + }; + if (!same(newMinZ, oldMinZ) && newMinZ < oldMinZ) { ActsScalar gap1MinZ = newVolume.minZ(); ActsScalar gap1MaxZ = oldVolume.minZ(); ActsScalar gap1HlZ = (gap1MaxZ - gap1MinZ) / 2.0; ActsScalar gap1PZ = (gap1MaxZ + gap1MinZ) / 2.0; - ACTS_VERBOSE(" -> gap1 z: [ " << gap1MinZ << " <- " << gap1PZ - << " -> " << gap1MaxZ - << " ] (hl: " << gap1HlZ << ")"); - - auto gap1Bounds = - std::make_shared(newMinR, newMaxR, gap1HlZ); - auto gap1Transform = m_groupTransform * Translation3{0, 0, gap1PZ}; - auto gap1 = addGapVolume(gap1Transform, std::move(gap1Bounds)); - volumeTuples.insert(volumeTuples.begin(), - VolumeTuple{*gap1, m_groupTransform}); + // // check if we need a new gap volume or reuse an existing one + auto& candidate = volumeTuples.front(); + if (isGap(candidate.volume)) { + ACTS_VERBOSE("~> Reusing existing gap volume at negative z"); + + gap1HlZ = + candidate.bounds->get(CylinderVolumeBounds::eHalfLengthZ) + + gap1HlZ; + gap1MaxZ = gap1MinZ + gap1HlZ * 2; + gap1PZ = (gap1MaxZ + gap1MinZ) / 2.0; + + printGapDimensions(candidate, " before"); + auto gap1Bounds = std::make_shared( + newMinR, newMaxR, gap1HlZ); + auto gap1Transform = m_groupTransform * Translation3{0, 0, gap1PZ}; + candidate.volume->update(std::move(gap1Bounds), gap1Transform); + candidate = VolumeTuple{*candidate.volume, m_groupTransform}; + ACTS_VERBOSE("After:"); + printGapDimensions(candidate, " after "); + + } else { + ACTS_VERBOSE("~> Creating new gap volume at negative z"); + auto gap1Bounds = std::make_shared( + newMinR, newMaxR, gap1HlZ); + auto gap1Transform = m_groupTransform * Translation3{0, 0, gap1PZ}; + auto gap1 = addGapVolume(gap1Transform, std::move(gap1Bounds)); + volumeTuples.insert(volumeTuples.begin(), + VolumeTuple{*gap1, m_groupTransform}); + printGapDimensions(volumeTuples.front()); + } } - if (newMaxZ > oldMaxZ) { - ACTS_VERBOSE("Adding gap volume at positive z"); - + if (!same(newMaxZ, oldMaxZ) && newMaxZ > oldMaxZ) { ActsScalar gap2MinZ = oldVolume.maxZ(); ActsScalar gap2MaxZ = newVolume.maxZ(); ActsScalar gap2HlZ = (gap2MaxZ - gap2MinZ) / 2.0; ActsScalar gap2PZ = (gap2MaxZ + gap2MinZ) / 2.0; - ACTS_VERBOSE(" -> gap2 z: [ " << gap2MinZ << " <- " << gap2PZ - << " -> " << gap2MaxZ - << " ] (hl: " << gap2HlZ << ")"); - - auto gap2Bounds = - std::make_shared(newMinR, newMaxR, gap2HlZ); - auto gap2Transform = m_groupTransform * Translation3{0, 0, gap2PZ}; - auto gap2 = addGapVolume(gap2Transform, std::move(gap2Bounds)); - volumeTuples.emplace_back(*gap2, m_groupTransform); + // check if we need a new gap volume or reuse an existing one + auto& candidate = volumeTuples.back(); + if (isGap(candidate.volume)) { + ACTS_VERBOSE("~> Reusing existing gap volume at positive z"); + + gap2HlZ = + candidate.bounds->get(CylinderVolumeBounds::eHalfLengthZ) + + gap2HlZ; + gap2MinZ = newVolume.maxZ() - gap2HlZ * 2; + gap2PZ = (gap2MaxZ + gap2MinZ) / 2.0; + + printGapDimensions(candidate, " before"); + auto gap2Bounds = std::make_shared( + newMinR, newMaxR, gap2HlZ); + auto gap2Transform = m_groupTransform * Translation3{0, 0, gap2PZ}; + + candidate.volume->update(std::move(gap2Bounds), gap2Transform); + candidate = VolumeTuple{*candidate.volume, m_groupTransform}; + printGapDimensions(candidate, " after "); + } else { + ACTS_VERBOSE("~> Creating new gap volume at positive z"); + auto gap2Bounds = std::make_shared( + newMinR, newMaxR, gap2HlZ); + auto gap2Transform = m_groupTransform * Translation3{0, 0, gap2PZ}; + auto gap2 = addGapVolume(gap2Transform, std::move(gap2Bounds)); + volumeTuples.emplace_back(*gap2, m_groupTransform); + printGapDimensions(volumeTuples.back()); + } } } @@ -831,7 +917,7 @@ void CylinderVolumeStack::update( ACTS_VERBOSE("Commit and update outer vector of volumes"); m_volumes.clear(); for (auto& vt : volumeTuples) { - vt.commit(); + vt.commit(logger); m_volumes.push_back(vt.volume); } @@ -887,32 +973,56 @@ void CylinderVolumeStack::update( << " ]"); } } else if (m_resizeStrategy == ResizeStrategy::Gap) { + auto printGapDimensions = [&](const VolumeTuple& gap, + const std::string& prefix = "") { + ACTS_VERBOSE(" -> gap" << prefix << ": [ " << gap.minZ() << " <- " + << gap.midZ() << " -> " << gap.maxZ() + << " ], r: [ " << gap.minR() << " <-> " + << gap.maxR() << " ]"); + }; + if (oldMinR > newMinR) { - ACTS_VERBOSE("Creating gap volume at the innermost end"); - auto gapBounds = - std::make_shared(newMinR, oldMinR, newHlZ); - auto gapTransform = m_groupTransform; - auto gapVolume = addGapVolume(gapTransform, gapBounds); - volumeTuples.insert(volumeTuples.begin(), - VolumeTuple{*gapVolume, m_groupTransform}); - auto gap = volumeTuples.front(); - ACTS_VERBOSE(" -> gap inner: [ " - << gap.minZ() << " <- " << gap.midZ() << " -> " - << gap.maxZ() << " ], r: [ " << gap.minR() << " <-> " - << gap.maxR() << " ]"); + auto& candidate = volumeTuples.front(); + if (isGap(candidate.volume)) { + ACTS_VERBOSE("~> Reusing existing gap volume at inner r"); + auto& candidateCylBounds = dynamic_cast( + candidate.volume->volumeBounds()); + printGapDimensions(candidate, " before"); + candidateCylBounds.set(CylinderVolumeBounds::eMinR, newMinR); + candidate = VolumeTuple{*candidate.volume, m_groupTransform}; + printGapDimensions(candidate, " after "); + } else { + ACTS_VERBOSE("~> Creating new gap volume at inner r"); + auto gapBounds = std::make_shared( + newMinR, oldMinR, newHlZ); + auto gapTransform = m_groupTransform; + auto gapVolume = addGapVolume(gapTransform, gapBounds); + volumeTuples.insert(volumeTuples.begin(), + VolumeTuple{*gapVolume, m_groupTransform}); + auto gap = volumeTuples.front(); + printGapDimensions(gap); + } } if (oldMaxR < newMaxR) { - ACTS_VERBOSE("Creating gap volume at the outermost end"); - auto gapBounds = - std::make_shared(oldMaxR, newMaxR, newHlZ); - auto gapTransform = m_groupTransform; - auto gapVolume = addGapVolume(gapTransform, gapBounds); - volumeTuples.emplace_back(*gapVolume, m_groupTransform); - auto gap = volumeTuples.back(); - ACTS_VERBOSE(" -> gap outer: [ " - << gap.minZ() << " <- " << gap.midZ() << " -> " - << gap.maxZ() << " ], r: [ " << gap.minR() << " <-> " - << gap.maxR() << " ]"); + auto& candidate = volumeTuples.back(); + if (isGap(candidate.volume)) { + ACTS_VERBOSE("~> Reusing existing gap volume at outer r"); + auto& candidateCylBounds = dynamic_cast( + candidate.volume->volumeBounds()); + printGapDimensions(candidate, " before"); + candidateCylBounds.set(CylinderVolumeBounds::eMaxR, newMaxR); + candidate = VolumeTuple{*candidate.volume, m_groupTransform}; + printGapDimensions(candidate, " after "); + } else { + ACTS_VERBOSE("~> Creating new gap volume at outer r"); + auto gapBounds = std::make_shared( + oldMaxR, newMaxR, newHlZ); + auto gapTransform = m_groupTransform; + auto gapVolume = addGapVolume(gapTransform, gapBounds); + volumeTuples.emplace_back(*gapVolume, m_groupTransform); + auto gap = volumeTuples.back(); + printGapDimensions(gap); + } } } @@ -923,13 +1033,15 @@ void CylinderVolumeStack::update( ACTS_VERBOSE("Commit and update outer vector of volumes"); m_volumes.clear(); for (auto& vt : volumeTuples) { - vt.commit(); + vt.commit(logger); m_volumes.push_back(vt.volume); } } m_transform = newVolume.globalTransform; - Volume::update(std::move(newBounds)); + // @TODO: We probably can reuse m_transform + m_groupTransform = m_transform; + Volume::update(std::move(cylBounds), std::nullopt, logger); } void CylinderVolumeStack::checkNoPhiOrBevel(const CylinderVolumeBounds& bounds, diff --git a/Core/src/Geometry/Extent.cpp b/Core/src/Geometry/Extent.cpp index 9e520d171a3..2f07f6358bc 100644 --- a/Core/src/Geometry/Extent.cpp +++ b/Core/src/Geometry/Extent.cpp @@ -144,8 +144,7 @@ bool Acts::Extent::contains(const Extent& rhs, // Check all if (!bValue.has_value()) { - return std::all_of(allBinningValues().begin(), allBinningValues().end(), - checkContainment); + return std::ranges::all_of(allBinningValues(), checkContainment); } // Check specific return checkContainment(bValue.value()); @@ -163,8 +162,7 @@ bool Acts::Extent::intersects(const Extent& rhs, // Check all if (!bValue.has_value()) { - return std::any_of(allBinningValues().begin(), allBinningValues().end(), - checkIntersect); + return std::ranges::any_of(allBinningValues(), checkIntersect); } // Check specific return checkIntersect(bValue.value()); diff --git a/Core/src/Geometry/GridPortalLink.cpp b/Core/src/Geometry/GridPortalLink.cpp index 2ad7ea11ae5..57a3e1d3141 100644 --- a/Core/src/Geometry/GridPortalLink.cpp +++ b/Core/src/Geometry/GridPortalLink.cpp @@ -89,7 +89,10 @@ void GridPortalLink::checkConsistency(const CylinderSurface& cyl) const { ActsScalar hlZ = cyl.bounds().get(CylinderBounds::eHalfLengthZ); if (!same(axis.getMin(), -hlZ) || !same(axis.getMax(), hlZ)) { throw std::invalid_argument( - "GridPortalLink: CylinderBounds: invalid length setup."); + "GridPortalLink: CylinderBounds: invalid length setup: " + + std::to_string(axis.getMin()) + " != " + std::to_string(-hlZ) + + " or " + std::to_string(axis.getMax()) + + " != " + std::to_string(hlZ)); } }; auto checkRPhi = [&cyl, same](const IAxis& axis) { @@ -295,7 +298,7 @@ void GridPortalLink::fillGrid1dTo2d(FillDirection dir, assert(locDest.size() == 2); for (std::size_t i = 0; i <= locSource[0] + 1; ++i) { - TrackingVolume* source = grid1d.atLocalBins({i}); + const TrackingVolume* source = grid1d.atLocalBins({i}); if (dir == FillDirection::loc1) { for (std::size_t j = 0; j <= locDest[1] + 1; ++j) { diff --git a/Core/src/Geometry/Layer.cpp b/Core/src/Geometry/Layer.cpp index dca511b3978..024f99e9c42 100644 --- a/Core/src/Geometry/Layer.cpp +++ b/Core/src/Geometry/Layer.cpp @@ -126,11 +126,9 @@ Acts::Layer::compatibleSurfaces( double farLimit = options.farLimit; auto isUnique = [&](const SurfaceIntersection& b) { - auto find_it = std::find_if( - sIntersections.begin(), sIntersections.end(), [&b](const auto& a) { - return a.object() == b.object() && a.index() == b.index(); - }); - return find_it == sIntersections.end(); + return std::ranges::none_of(sIntersections, [&b](const auto& a) { + return a.object() == b.object() && a.index() == b.index(); + }); }; // lemma 0 : accept the surface @@ -140,7 +138,7 @@ Acts::Layer::compatibleSurfaces( if (sensitive && options.resolveSensitive) { return true; } - // next option: it's a material surface and you want to have it + // next option: it's a material surface, and you want to have it if (options.resolveMaterial && sf.surfaceMaterial() != nullptr) { return true; } diff --git a/Core/src/Geometry/Portal.cpp b/Core/src/Geometry/Portal.cpp index 2a9d09b5f68..69cab13b8e0 100644 --- a/Core/src/Geometry/Portal.cpp +++ b/Core/src/Geometry/Portal.cpp @@ -9,13 +9,17 @@ #include "Acts/Geometry/Portal.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Tolerance.hpp" +#include "Acts/Geometry/CompositePortalLink.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GridPortalLink.hpp" #include "Acts/Geometry/PortalLinkBase.hpp" #include "Acts/Geometry/TrivialPortalLink.hpp" #include "Acts/Surfaces/RegularSurface.hpp" #include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Zip.hpp" -#include +#include #include #include @@ -245,7 +249,7 @@ Portal Portal::fuse(const GeometryContext& gctx, Portal& aPortal, Portal& bPortal, const Logger& logger) { ACTS_VERBOSE("Fusing two portals"); if (&aPortal == &bPortal) { - ACTS_ERROR("Cannot merge a portal with itself"); + ACTS_ERROR("Cannot fuse a portal with itself"); throw PortalMergingException{}; } @@ -267,6 +271,10 @@ Portal Portal::fuse(const GeometryContext& gctx, Portal& aPortal, if (!isSameSurface(gctx, *aPortal.m_surface, *bPortal.m_surface)) { ACTS_ERROR("Portals have different surfaces"); + ACTS_ERROR("A: " << aPortal.m_surface->bounds()); + ACTS_ERROR("\n" << aPortal.m_surface->transform(gctx).matrix()); + ACTS_ERROR("B: " << bPortal.m_surface->bounds()); + ACTS_ERROR("\n" << bPortal.m_surface->transform(gctx).matrix()); throw PortalFusingException(); } @@ -282,16 +290,27 @@ Portal Portal::fuse(const GeometryContext& gctx, Portal& aPortal, throw PortalFusingException(); } + auto maybeConvertToGrid = [&](std::unique_ptr link) + -> std::unique_ptr { + auto* composite = dynamic_cast(link.get()); + if (composite == nullptr) { + return link; + } + + ACTS_VERBOSE("Converting composite to grid during portal fusing"); + return composite->makeGrid(gctx, logger); + }; + aPortal.m_surface.reset(); bPortal.m_surface.reset(); if (aHasAlongNormal) { ACTS_VERBOSE("Taking along normal from lhs, opposite normal from rhs"); - return Portal{gctx, std::move(aPortal.m_alongNormal), - std::move(bPortal.m_oppositeNormal)}; + return Portal{gctx, maybeConvertToGrid(std::move(aPortal.m_alongNormal)), + maybeConvertToGrid(std::move(bPortal.m_oppositeNormal))}; } else { ACTS_VERBOSE("Taking along normal from rhs, opposite normal from lhs"); - return Portal{gctx, std::move(bPortal.m_alongNormal), - std::move(aPortal.m_oppositeNormal)}; + return Portal{gctx, maybeConvertToGrid(std::move(bPortal.m_alongNormal)), + maybeConvertToGrid(std::move(aPortal.m_oppositeNormal))}; } } @@ -305,12 +324,30 @@ bool Portal::isSameSurface(const GeometryContext& gctx, const Surface& a, return false; } - if (a.bounds() != b.bounds()) { + std::vector aValues = a.bounds().values(); + std::vector bValues = b.bounds().values(); + bool different = false; + for (auto [aVal, bVal] : zip(aValues, bValues)) { + if (std::abs(aVal - bVal) > s_onSurfaceTolerance) { + different = true; + break; + } + } + + if (a.bounds().type() != b.bounds().type() || different) { return false; } - if (!a.transform(gctx).isApprox(b.transform(gctx), - s_transformEquivalentTolerance)) { + if (!a.transform(gctx).linear().isApprox(b.transform(gctx).linear(), + s_transformEquivalentTolerance)) { + return false; + } + + Vector3 delta = + (a.transform(gctx).translation() - b.transform(gctx).translation()) + .cwiseAbs(); + + if (delta.maxCoeff() > s_onSurfaceTolerance) { return false; } diff --git a/Core/src/Geometry/PortalLinkBase.cpp b/Core/src/Geometry/PortalLinkBase.cpp index 459c144272e..bdc65f8a552 100644 --- a/Core/src/Geometry/PortalLinkBase.cpp +++ b/Core/src/Geometry/PortalLinkBase.cpp @@ -109,8 +109,10 @@ std::unique_ptr PortalLinkBase::merge( } else if (const auto* bTrivial = dynamic_cast(b.get()); bTrivial != nullptr) { - ACTS_VERBOSE("Merging a grid portal with a trivial portal"); - return gridMerge(*aGrid, *bTrivial->makeGrid(direction)); + ACTS_VERBOSE( + "Merging a grid portal with a trivial portal (via composite)"); + return std::make_unique(std::move(a), std::move(b), + direction); } else if (dynamic_cast(b.get()) != nullptr) { ACTS_WARNING("Merging a grid portal with a composite portal"); @@ -126,15 +128,17 @@ std::unique_ptr PortalLinkBase::merge( aTrivial != nullptr) { if (const auto* bGrid = dynamic_cast(b.get()); bGrid) { - ACTS_VERBOSE("Merging a trivial portal with a grid portal"); - return gridMerge(*aTrivial->makeGrid(direction), *bGrid); + ACTS_VERBOSE( + "Merging a trivial portal with a grid portal (via composite)"); + return std::make_unique(std::move(a), std::move(b), + direction); } else if (const auto* bTrivial = dynamic_cast(b.get()); bTrivial != nullptr) { - ACTS_VERBOSE("Merging two trivial portals"); - return gridMerge(*aTrivial->makeGrid(direction), - *bTrivial->makeGrid(direction)); + ACTS_VERBOSE("Merging two trivial portals (via composite"); + return std::make_unique(std::move(a), std::move(b), + direction); } else if (dynamic_cast(b.get()) != nullptr) { ACTS_WARNING("Merging a trivial portal with a composite portal"); diff --git a/Core/src/Geometry/PortalShell.cpp b/Core/src/Geometry/PortalShell.cpp new file mode 100644 index 00000000000..b9baf403812 --- /dev/null +++ b/Core/src/Geometry/PortalShell.cpp @@ -0,0 +1,388 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/PortalShell.hpp" + +#include "Acts/Geometry/BoundarySurfaceFace.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/Portal.hpp" +#include "Acts/Geometry/PortalLinkBase.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" + +#include +#include +#include + +#include + +namespace Acts { + +void CylinderPortalShell::connectOuter(TrackingVolume& volume) { + for (Face face : {PositiveDisc, NegativeDisc, OuterCylinder, InnerCylinder, + NegativePhiPlane, PositivePhiPlane}) { + const auto& portalAtFace = portalPtr(face); + if (portalAtFace != nullptr) { + portalAtFace->fill(volume); + volume.addPortal(portalAtFace); + } + } +} + +SingleCylinderPortalShell::SingleCylinderPortalShell(TrackingVolume& volume) + : m_volume{&volume} { + if (m_volume->volumeBounds().type() != VolumeBounds::BoundsType::eCylinder) { + throw std::invalid_argument("Invalid volume bounds type"); + } + + const auto& bounds = + dynamic_cast(m_volume->volumeBounds()); + + std::vector orientedSurfaces = + bounds.orientedSurfaces(m_volume->transform()); + + auto handle = [&](Face face, std::size_t from) { + const auto& source = orientedSurfaces.at(from); + m_portals.at(toUnderlying(face)) = + std::make_shared(source.direction, source.surface, *m_volume); + }; + + if (orientedSurfaces.size() == 6) { + // Fully equipped cylinder + handle(PositiveDisc, positiveFaceXY); + handle(NegativeDisc, negativeFaceXY); + handle(OuterCylinder, tubeOuterCover); + handle(InnerCylinder, tubeInnerCover); + handle(NegativePhiPlane, tubeSectorNegativePhi); + handle(PositivePhiPlane, tubeSectorPositivePhi); + } else if (orientedSurfaces.size() == 5) { + // Phi sector but no inner cylinder (rMin == 0) + handle(PositiveDisc, positiveFaceXY); + handle(NegativeDisc, negativeFaceXY); + handle(OuterCylinder, tubeOuterCover); + // Skip inner tube cover, requires offsetting + handle(NegativePhiPlane, tubeSectorNegativePhi - 1); + handle(PositivePhiPlane, tubeSectorPositivePhi - 1); + } else if (orientedSurfaces.size() == 4) { + // No phi sector but rMin > 0 + handle(PositiveDisc, positiveFaceXY); + handle(NegativeDisc, negativeFaceXY); + handle(OuterCylinder, tubeOuterCover); + handle(InnerCylinder, tubeInnerCover); + } else if (orientedSurfaces.size() == 3) { + // No phi sector and rMin == 0 + handle(PositiveDisc, positiveFaceXY); + handle(NegativeDisc, negativeFaceXY); + handle(OuterCylinder, tubeOuterCover); + } else { + throw std::invalid_argument("Invalid number of oriented surfaces"); + } +} + +Portal* SingleCylinderPortalShell::portal(Face face) { + return portalPtr(face).get(); +} + +std::shared_ptr SingleCylinderPortalShell::portalPtr(Face face) { + return m_portals.at(toUnderlying(face)); +} + +void SingleCylinderPortalShell::setPortal(std::shared_ptr portal, + Face face) { + assert(portal != nullptr); + assert(portal->isValid()); + m_portals.at(toUnderlying(face)) = std::move(portal); +} + +std::size_t SingleCylinderPortalShell::size() const { + std::size_t count = 0; + std::ranges::for_each( + m_portals, [&count](const auto& portal) { count += portal ? 1 : 0; }); + return count; +} + +void SingleCylinderPortalShell::applyToVolume() { + for (std::size_t i = 0; i < m_portals.size(); i++) { + const auto& portal = m_portals.at(i); + if (portal != nullptr) { + if (!portal->isValid()) { + std::stringstream ss; + ss << static_cast(i); + throw std::runtime_error{"Invalid portal found in shell at " + + ss.str()}; + } + m_volume->addPortal(portal); + } + } +} + +bool SingleCylinderPortalShell::isValid() const { + return std::ranges::all_of(m_portals, [](const auto& portal) { + return portal == nullptr || portal->isValid(); + }); +}; + +std::string SingleCylinderPortalShell::label() const { + std::stringstream ss; + ss << "CylinderShell(vol=" << m_volume->volumeName() << ")"; + return ss.str(); +} + +CylinderStackPortalShell::CylinderStackPortalShell( + const GeometryContext& gctx, std::vector shells, + BinningValue direction, const Logger& logger) + : m_direction{direction}, m_shells{std::move(shells)} { + ACTS_VERBOSE("Making cylinder stack shell in " << m_direction + << " direction"); + if (std::ranges::any_of(m_shells, + [](const auto* shell) { return shell == nullptr; })) { + ACTS_ERROR("Invalid shell pointer"); + throw std::invalid_argument("Invalid shell pointer"); + } + + ACTS_VERBOSE(" ~> " << label()); + + if (!std::ranges::all_of( + m_shells, [](const auto* shell) { return shell->isValid(); })) { + ACTS_ERROR("Invalid shell"); + throw std::invalid_argument("Invalid shell"); + } + + auto merge = [&](Face face) { + std::vector> portals; + std::ranges::transform( + m_shells, std::back_inserter(portals), + [face](auto* shell) { return shell->portalPtr(face); }); + + auto merged = std::accumulate( + std::next(portals.begin()), portals.end(), portals.front(), + [&](const auto& aPortal, + const auto& bPortal) -> std::shared_ptr { + assert(aPortal != nullptr); + assert(bPortal != nullptr); + + return std::make_shared( + Portal::merge(gctx, *aPortal, *bPortal, direction, logger)); + }); + + assert(merged != nullptr); + assert(merged->isValid()); + + // reset merged portal on all shells + for (auto& shell : m_shells) { + shell->setPortal(merged, face); + } + }; + + auto fuse = [&](Face faceA, Face faceB) { + for (std::size_t i = 1; i < m_shells.size(); i++) { + auto& shellA = m_shells.at(i - 1); + auto& shellB = m_shells.at(i); + ACTS_VERBOSE("Fusing " << shellA->label() << " and " << shellB->label()); + + auto fused = std::make_shared(Portal::fuse( + gctx, *shellA->portalPtr(faceA), *shellB->portalPtr(faceB), logger)); + + assert(fused != nullptr && "Invalid fused portal"); + assert(fused->isValid() && "Fused portal is invalid"); + + shellA->setPortal(fused, faceA); + shellB->setPortal(fused, faceB); + + assert(shellA->isValid() && "Shell A is not valid after fusing"); + assert(shellB->isValid() && "Shell B is not valid after fusing"); + } + }; + + if (direction == BinningValue::binR) { + ACTS_VERBOSE("Merging portals at positive and negative discs"); + merge(PositiveDisc); + merge(NegativeDisc); + + ACTS_VERBOSE("Fusing portals at outer and inner cylinders"); + fuse(OuterCylinder, InnerCylinder); + + } else if (direction == BinningValue::binZ) { + bool allHaveInnerCylinders = std::ranges::all_of( + m_shells, [](const auto* shell) { return shell->size() == 4; }); + + bool noneHaveInnerCylinders = std::ranges::all_of( + m_shells, [](const auto* shell) { return shell->size() == 3; }); + + if (!allHaveInnerCylinders && !noneHaveInnerCylinders) { + ACTS_ERROR("Invalid inner cylinder configuration"); + throw std::invalid_argument("Invalid inner cylinder configuration"); + } + + m_hasInnerCylinder = allHaveInnerCylinders; + + ACTS_VERBOSE("Merging portals at outer cylinders"); + merge(OuterCylinder); + assert(isValid() && "Shell is not valid after outer merging"); + + if (m_hasInnerCylinder) { + ACTS_VERBOSE("Merging portals at inner cylinders"); + merge(InnerCylinder); + assert(isValid() && "Shell is not valid after inner merging"); + } + + ACTS_VERBOSE("Fusing portals at positive and negative discs"); + fuse(PositiveDisc, NegativeDisc); + assert(isValid() && "Shell is not valid after disc fusing"); + + } else { + throw std::invalid_argument("Invalid direction"); + } + + assert(isValid() && "Shell is not valid after construction"); +} + +std::size_t CylinderStackPortalShell::size() const { + return m_hasInnerCylinder ? 4 : 3; +} + +Portal* CylinderStackPortalShell::portal(Face face) { + return portalPtr(face).get(); +} + +std::shared_ptr CylinderStackPortalShell::portalPtr(Face face) { + if (m_direction == BinningValue::binR) { + switch (face) { + case NegativeDisc: + return m_shells.front()->portalPtr(NegativeDisc); + case PositiveDisc: + return m_shells.front()->portalPtr(PositiveDisc); + case OuterCylinder: + return m_shells.back()->portalPtr(OuterCylinder); + case InnerCylinder: + return m_shells.front()->portalPtr(InnerCylinder); + case NegativePhiPlane: + [[fallthrough]]; + case PositivePhiPlane: + return nullptr; + default: + std::stringstream ss; + ss << "Invalid face: " << face; + throw std::invalid_argument(ss.str()); + } + + } else { + switch (face) { + case NegativeDisc: + return m_shells.front()->portalPtr(NegativeDisc); + case PositiveDisc: + return m_shells.back()->portalPtr(PositiveDisc); + case OuterCylinder: + [[fallthrough]]; + case InnerCylinder: + return m_shells.front()->portalPtr(face); + case NegativePhiPlane: + [[fallthrough]]; + case PositivePhiPlane: + return nullptr; + default: + std::stringstream ss; + ss << "Invalid face: " << face; + throw std::invalid_argument(ss.str()); + } + } +} + +void CylinderStackPortalShell::setPortal(std::shared_ptr portal, + Face face) { + assert(portal != nullptr); + + if (m_direction == BinningValue::binR) { + switch (face) { + case NegativeDisc: + [[fallthrough]]; + case PositiveDisc: + for (auto* shell : m_shells) { + shell->setPortal(portal, face); + } + break; + case OuterCylinder: + m_shells.back()->setPortal(std::move(portal), OuterCylinder); + break; + case InnerCylinder: + if (!m_hasInnerCylinder) { + throw std::invalid_argument("Inner cylinder not available"); + } + m_shells.front()->setPortal(std::move(portal), InnerCylinder); + break; + default: + std::stringstream ss; + ss << "Invalid face: " << face; + throw std::invalid_argument(ss.str()); + } + + } else { + switch (face) { + case NegativeDisc: + m_shells.front()->setPortal(std::move(portal), NegativeDisc); + break; + case PositiveDisc: + m_shells.back()->setPortal(std::move(portal), PositiveDisc); + break; + case InnerCylinder: + if (!m_hasInnerCylinder) { + throw std::invalid_argument("Inner cylinder not available"); + } + [[fallthrough]]; + case OuterCylinder: + for (auto* shell : m_shells) { + shell->setPortal(portal, face); + } + break; + default: + std::stringstream ss; + ss << "Invalid face: " << face; + throw std::invalid_argument(ss.str()); + } + } +} + +bool CylinderStackPortalShell::isValid() const { + return std::ranges::all_of(m_shells, [](const auto* shell) { + assert(shell != nullptr); + return shell->isValid(); + }); +} + +std::string CylinderStackPortalShell::label() const { + std::stringstream ss; + ss << "CylinderStackShell(dir=" << m_direction << ", children="; + + std::vector labels; + std::ranges::transform(m_shells, std::back_inserter(labels), + [](const auto* shell) { return shell->label(); }); + ss << boost::algorithm::join(labels, "|"); + ss << ")"; + return ss.str(); +} + +std::ostream& operator<<(std::ostream& os, CylinderPortalShell::Face face) { + switch (face) { + using enum CylinderVolumeBounds::Face; + case PositiveDisc: + return os << "PositiveDisc"; + case NegativeDisc: + return os << "NegativeDisc"; + case OuterCylinder: + return os << "OuterCylinder"; + case InnerCylinder: + return os << "InnerCylinder"; + case NegativePhiPlane: + return os << "NegativePhiPlane"; + case PositivePhiPlane: + return os << "PositivePhiPlane"; + default: + return os << "Invalid face"; + } +} + +} // namespace Acts diff --git a/Core/src/Geometry/ProtoLayer.cpp b/Core/src/Geometry/ProtoLayer.cpp index 659b34b1168..c8f190d4293 100644 --- a/Core/src/Geometry/ProtoLayer.cpp +++ b/Core/src/Geometry/ProtoLayer.cpp @@ -13,26 +13,34 @@ #include "Acts/Surfaces/RegularSurface.hpp" #include "Acts/Utilities/Helpers.hpp" -#include -#include -#include -#include - using Acts::VectorHelpers::perp; using Acts::VectorHelpers::phi; namespace Acts { ProtoLayer::ProtoLayer(const GeometryContext& gctx, - const std::vector& surfaces) - : m_surfaces(surfaces) { + const std::vector& surfaces, + const Transform3& transformIn) + : transform(transformIn), m_surfaces(surfaces) { measure(gctx, surfaces); } ProtoLayer::ProtoLayer( const GeometryContext& gctx, - const std::vector>& surfaces) - : m_surfaces(unpack_shared_vector(surfaces)) { + const std::vector>& surfaces, + const Transform3& transformIn) + : transform(transformIn), m_surfaces(unpack_shared_vector(surfaces)) { + measure(gctx, m_surfaces); +} + +ProtoLayer::ProtoLayer(const GeometryContext& gctx, + const std::vector>& surfaces, + const Transform3& transformIn) + : transform(transformIn) { + m_surfaces.reserve(surfaces.size()); + for (const auto& sf : surfaces) { + m_surfaces.push_back(sf.get()); + } measure(gctx, m_surfaces); } @@ -78,15 +86,13 @@ void ProtoLayer::measure(const GeometryContext& gctx, double thickness = element->thickness(); // We need a translation along and opposite half thickness Vector3 sfNormal = regSurface->normal(gctx, sf->center(gctx)); - std::vector deltaT = {-0.5 * thickness, 0.5 * thickness}; - for (const auto& dT : deltaT) { - Transform3 dtransform = Transform3::Identity(); - dtransform.pretranslate(dT * sfNormal); + for (const auto& dT : {-0.5 * thickness, 0.5 * thickness}) { + Transform3 dtransform = transform * Translation3{dT * sfNormal}; extent.extend(sfPolyhedron.extent(dtransform)); } continue; } - extent.extend(sfPolyhedron.extent()); + extent.extend(sfPolyhedron.extent(transform)); } } diff --git a/Core/src/Geometry/TrackingGeometry.cpp b/Core/src/Geometry/TrackingGeometry.cpp index b68d788d08b..f9207afbd18 100644 --- a/Core/src/Geometry/TrackingGeometry.cpp +++ b/Core/src/Geometry/TrackingGeometry.cpp @@ -85,3 +85,11 @@ const std::unordered_map& Acts::TrackingGeometry::geoIdSurfaceMap() const { return m_surfacesById; } + +void Acts::TrackingGeometry::visualize( + IVisualization3D& helper, const GeometryContext& gctx, + const ViewConfig& viewConfig, const ViewConfig& portalViewConfig, + const ViewConfig& sensitiveViewConfig) const { + highestTrackingVolume()->visualize(helper, gctx, viewConfig, portalViewConfig, + sensitiveViewConfig); +} diff --git a/Core/src/Geometry/TrackingVolume.cpp b/Core/src/Geometry/TrackingVolume.cpp index 79ffdc9d180..e7d069b80c5 100644 --- a/Core/src/Geometry/TrackingVolume.cpp +++ b/Core/src/Geometry/TrackingVolume.cpp @@ -347,6 +347,22 @@ void TrackingVolume::closeGeometry( std::unordered_map& volumeMap, std::size_t& vol, const GeometryIdentifierHook& hook, const Logger& logger) { + if (m_confinedVolumes && !volumes().empty()) { + ACTS_ERROR( + "TrackingVolume::closeGeometry: Volume " + << volumeName() + << " has both confined volumes and volumes. This is not supported."); + throw std::invalid_argument("Volume has both confined volumes and volumes"); + } + + if (m_confinedLayers && !surfaces().empty()) { + ACTS_ERROR( + "TrackingVolume::closeGeometry: Volume " + << volumeName() + << " has both confined layers and surfaces. This is not supported."); + throw std::invalid_argument("Volume has both confined layers and surfaces"); + } + // we can construct the volume ID from this auto volumeID = GeometryIdentifier().setVolume(++vol); // assign the Volume ID to the volume itself @@ -379,7 +395,8 @@ void TrackingVolume::closeGeometry( // get the intersection solution auto& bSurface = bSurfIter->surfaceRepresentation(); // create the boundary surface id - auto boundaryID = GeometryIdentifier(volumeID).setBoundary(++iboundary); + iboundary++; + auto boundaryID = GeometryIdentifier(volumeID).setBoundary(iboundary); // now assign to the boundary surface auto& mutableBSurface = *(const_cast(&bSurface)); mutableBSurface.assignGeometryId(boundaryID); @@ -397,7 +414,8 @@ void TrackingVolume::closeGeometry( // loop over the layers for (auto& layerPtr : m_confinedLayers->arrayObjects()) { // create the layer identification - auto layerID = GeometryIdentifier(volumeID).setLayer(++ilayer); + ilayer++; + auto layerID = GeometryIdentifier(volumeID).setLayer(ilayer); // now close the geometry auto mutableLayerPtr = std::const_pointer_cast(layerPtr); mutableLayerPtr->closeGeometry(materialDecorator, layerID, hook, @@ -428,12 +446,24 @@ void TrackingVolume::closeGeometry( GeometryIdentifier::Value iportal = 0; for (auto& portal : portals()) { - auto portalId = GeometryIdentifier(volumeID).setBoundary(++iportal); + iportal++; + auto portalId = GeometryIdentifier(volumeID).setBoundary(iportal); assert(portal.isValid() && "Invalid portal encountered during closing"); portal.surface().assignGeometryId(portalId); } + GeometryIdentifier::Value isensitive = 0; + + for (auto& surface : surfaces()) { + if (surface.associatedDetectorElement() == nullptr) { + continue; + } + isensitive++; + auto sensitiveId = GeometryIdentifier(volumeID).setSensitive(isensitive); + surface.assignGeometryId(sensitiveId); + } + for (auto& volume : volumes()) { volume.closeGeometry(materialDecorator, volumeMap, vol, hook, logger); } @@ -684,4 +714,25 @@ void TrackingVolume::addSurface(std::shared_ptr surface) { m_surfaces.push_back(std::move(surface)); } +void TrackingVolume::visualize(IVisualization3D& helper, + const GeometryContext& gctx, + const ViewConfig& viewConfig, + const ViewConfig& portalViewConfig, + const ViewConfig& sensitiveViewConfig) const { + helper.object(volumeName()); + Volume::visualize(helper, gctx, viewConfig); + + if (!surfaces().empty()) { + helper.object(volumeName() + "_sensitives"); + } + for (const auto& surface : surfaces()) { + surface.visualize(helper, gctx, sensitiveViewConfig); + } + + for (const auto& child : volumes()) { + child.visualize(helper, gctx, viewConfig, portalViewConfig, + sensitiveViewConfig); + } +} + } // namespace Acts diff --git a/Core/src/Geometry/TrivialPortalLink.cpp b/Core/src/Geometry/TrivialPortalLink.cpp index abcdb44929c..6976388dbc0 100644 --- a/Core/src/Geometry/TrivialPortalLink.cpp +++ b/Core/src/Geometry/TrivialPortalLink.cpp @@ -39,7 +39,11 @@ Result TrivialPortalLink::resolveVolume( } void TrivialPortalLink::toStream(std::ostream& os) const { - os << "TrivialPortalLink"; + os << "TrivialPortalLinkvolumeName() << ">"; +} + +const TrackingVolume& TrivialPortalLink::volume() const { + return *m_volume; } } // namespace Acts diff --git a/Core/src/Geometry/Volume.cpp b/Core/src/Geometry/Volume.cpp index c1aefc96186..97a026b039f 100644 --- a/Core/src/Geometry/Volume.cpp +++ b/Core/src/Geometry/Volume.cpp @@ -79,7 +79,8 @@ void Volume::assignVolumeBounds(std::shared_ptr volbounds) { } void Volume::update(std::shared_ptr volbounds, - std::optional transform) { + std::optional transform, + const Logger& /*logger*/) { if (volbounds) { m_volumeBounds = std::move(volbounds); } diff --git a/Core/src/Material/Interactions.cpp b/Core/src/Material/Interactions.cpp index 9a63f59db8f..a0612e4d0f8 100644 --- a/Core/src/Material/Interactions.cpp +++ b/Core/src/Material/Interactions.cpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Material/Material.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include #include @@ -51,7 +52,7 @@ struct RelativisticQuantities { betaGamma = pOverM; assert((betaGamma >= 0) && "Negative betaGamma"); // gamma = sqrt(m² + p²)/m = sqrt(1 + (p/m)²) - gamma = std::sqrt(1.0f + pOverM * pOverM); + gamma = Acts::fastHypot(1.0f, pOverM); } }; @@ -395,7 +396,7 @@ float Acts::computeEnergyLossRadiative(const MaterialSlab& slab, // particle momentum and energy // do not need to care about the sign since it is only used squared const float momentum = absQ / qOverP; - const float energy = std::hypot(m, momentum); + const float energy = fastHypot(m, momentum); float dEdx = computeBremsstrahlungLossMean(m, energy); @@ -424,7 +425,7 @@ float Acts::deriveEnergyLossRadiativeQOverP(const MaterialSlab& slab, // particle momentum and energy // do not need to care about the sign since it is only used squared const float momentum = absQ / qOverP; - const float energy = std::hypot(m, momentum); + const float energy = fastHypot(m, momentum); // compute derivative w/ respect to energy. float derE = deriveBremsstrahlungLossMeanE(m); diff --git a/Core/src/Propagator/SympyStepper.cpp b/Core/src/Propagator/SympyStepper.cpp index 167f44dd7f5..a33e1f567bb 100644 --- a/Core/src/Propagator/SympyStepper.cpp +++ b/Core/src/Propagator/SympyStepper.cpp @@ -10,7 +10,6 @@ #include "Acts/Propagator/detail/SympyCovarianceEngine.hpp" #include "Acts/Propagator/detail/SympyJacobianEngine.hpp" -#include "Acts/Utilities/QuickMath.hpp" #include #include @@ -127,17 +126,11 @@ Result SympyStepper::stepImpl( // This is given by the order of the Runge-Kutta method constexpr double exponent = 0.25; - // Whether to use fast power function if available - constexpr bool tryUseFastPow{false}; - double x = stepTolerance / errorEstimate_; - if constexpr (exponent == 0.25 && !tryUseFastPow) { + if constexpr (exponent == 0.25) { // This is 3x faster than std::pow x = std::sqrt(std::sqrt(x)); - } else if constexpr (std::numeric_limits::is_iec559 && - tryUseFastPow) { - x = fastPow(x, exponent); } else { x = std::pow(x, exponent); } diff --git a/Core/src/Seeding/CMakeLists.txt b/Core/src/Seeding/CMakeLists.txt new file mode 100644 index 00000000000..770037b1dfd --- /dev/null +++ b/Core/src/Seeding/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(ActsCore PRIVATE EstimateTrackParamsFromSeed.cpp) diff --git a/Core/src/Seeding/EstimateTrackParamsFromSeed.cpp b/Core/src/Seeding/EstimateTrackParamsFromSeed.cpp new file mode 100644 index 00000000000..83d950fc3a1 --- /dev/null +++ b/Core/src/Seeding/EstimateTrackParamsFromSeed.cpp @@ -0,0 +1,50 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" + +#include "Acts/Definitions/TrackParametrization.hpp" + +Acts::BoundMatrix Acts::estimateTrackParamCovariance( + const EstimateTrackParamCovarianceConfig& config, const BoundVector& params, + bool hasTime) { + assert((params[eBoundTheta] > 0 && params[eBoundTheta] < M_PI) && + "Theta must be in the range (0, pi)"); + + BoundSquareMatrix result = BoundSquareMatrix::Zero(); + + for (std::size_t i = eBoundLoc0; i < eBoundSize; ++i) { + double sigma = config.initialSigmas[i]; + double variance = sigma * sigma; + + if (i == eBoundQOverP) { + // note that we rely on the fact that sigma theta is already computed + double varianceTheta = result(eBoundTheta, eBoundTheta); + + // transverse momentum contribution + variance += std::pow(config.initialSigmaPtRel * params[eBoundQOverP], 2); + + // theta contribution + variance += + varianceTheta * + std::pow(params[eBoundQOverP] / std::tan(params[eBoundTheta]), 2); + } + + if (i == eBoundTime && !hasTime) { + // Inflate the time uncertainty if no time measurement is available + variance *= config.noTimeVarInflation; + } + + // Inflate the initial covariance + variance *= config.initialVarInflation[i]; + + result(i, i) = variance; + } + + return result; +} diff --git a/Core/src/Surfaces/AnnulusBounds.cpp b/Core/src/Surfaces/AnnulusBounds.cpp index d96b3bc7e80..34c7b5c468b 100644 --- a/Core/src/Surfaces/AnnulusBounds.cpp +++ b/Core/src/Surfaces/AnnulusBounds.cpp @@ -103,33 +103,30 @@ std::vector Acts::AnnulusBounds::corners() const { } std::vector Acts::AnnulusBounds::vertices( - unsigned int lseg) const { - if (lseg > 0) { - // List of vertices counter-clockwise starting with left inner - std::vector rvertices; - + unsigned int quarterSegments) const { + if (quarterSegments > 0u) { using VectorHelpers::phi; - auto phisInner = detail::VerticesHelper::phiSegments( - phi(m_inRightStripXY - m_moduleOrigin), - phi(m_inLeftStripXY - m_moduleOrigin)); - auto phisOuter = detail::VerticesHelper::phiSegments( - phi(m_outLeftStripXY - m_moduleOrigin), - phi(m_outRightStripXY - m_moduleOrigin)); - - // Inner bow from phi_min -> phi_max - for (unsigned int iseg = 0; iseg < phisInner.size() - 1; ++iseg) { - int addon = (iseg == phisInner.size() - 2) ? 1 : 0; - detail::VerticesHelper::createSegment( - rvertices, {get(eMinR), get(eMinR)}, phisInner[iseg], - phisInner[iseg + 1], lseg, addon); - } - // Upper bow from phi_max -> phi_min - for (unsigned int iseg = 0; iseg < phisOuter.size() - 1; ++iseg) { - int addon = (iseg == phisOuter.size() - 2) ? 1 : 0; - detail::VerticesHelper::createSegment( - rvertices, {get(eMaxR), get(eMaxR)}, phisOuter[iseg], - phisOuter[iseg + 1], lseg, addon); - } + + ActsScalar phiMinInner = phi(m_inRightStripXY - m_moduleOrigin); + ActsScalar phiMaxInner = phi(m_inLeftStripXY - m_moduleOrigin); + + ActsScalar phiMinOuter = phi(m_outRightStripXY - m_moduleOrigin); + ActsScalar phiMaxOuter = phi(m_outLeftStripXY - m_moduleOrigin); + + // Inner bow from phi_min -> phi_max (needs to be reversed) + std::vector rvertices = + detail::VerticesHelper::segmentVertices( + {get(eMinR), get(eMinR)}, phiMinInner, phiMaxInner, {}, + quarterSegments); + std::reverse(rvertices.begin(), rvertices.end()); + + // Outer bow from phi_min -> phi_max + auto overtices = + detail::VerticesHelper::segmentVertices( + {get(eMaxR), get(eMaxR)}, phiMinOuter, phiMaxOuter, {}, + quarterSegments); + rvertices.insert(rvertices.end(), overtices.begin(), overtices.end()); + std::for_each(rvertices.begin(), rvertices.end(), [&](Acts::Vector2& rv) { rv += m_moduleOrigin; }); return rvertices; diff --git a/Core/src/Surfaces/CMakeLists.txt b/Core/src/Surfaces/CMakeLists.txt index 2f704f595c6..07cc26cbfcf 100644 --- a/Core/src/Surfaces/CMakeLists.txt +++ b/Core/src/Surfaces/CMakeLists.txt @@ -24,7 +24,7 @@ target_sources( SurfaceArray.cpp SurfaceError.cpp TrapezoidBounds.cpp - VerticesHelper.cpp + detail/VerticesHelper.cpp RegularSurface.cpp CurvilinearSurface.cpp detail/AlignmentHelper.cpp diff --git a/Core/src/Surfaces/ConeSurface.cpp b/Core/src/Surfaces/ConeSurface.cpp index 98f1484609d..ce10c59815f 100644 --- a/Core/src/Surfaces/ConeSurface.cpp +++ b/Core/src/Surfaces/ConeSurface.cpp @@ -183,19 +183,18 @@ const Acts::ConeBounds& Acts::ConeSurface::bounds() const { } Acts::Polyhedron Acts::ConeSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t lseg) const { + const GeometryContext& gctx, unsigned int quarterSegments) const { // Prepare vertices and faces std::vector vertices; std::vector faces; std::vector triangularMesh; - - double minZ = bounds().get(ConeBounds::eMinZ); - double maxZ = bounds().get(ConeBounds::eMaxZ); + ActsScalar minZ = bounds().get(ConeBounds::eMinZ); + ActsScalar maxZ = bounds().get(ConeBounds::eMaxZ); if (minZ == -std::numeric_limits::infinity() || maxZ == std::numeric_limits::infinity()) { throw std::domain_error( - "Polyhedron repr of boundless surface not possible"); + "Polyhedron representation of boundless surface is not possible"); } auto ctransform = transform(gctx); @@ -208,64 +207,53 @@ Acts::Polyhedron Acts::ConeSurface::polyhedronRepresentation( } // Cone parameters - double hPhiSec = bounds().get(ConeBounds::eHalfPhiSector); - double avgPhi = bounds().get(ConeBounds::eAveragePhi); - bool fullCone = (hPhiSec == M_PI); - - // Get the phi segments from the helper - auto phiSegs = fullCone ? detail::VerticesHelper::phiSegments() - : detail::VerticesHelper::phiSegments( - avgPhi - hPhiSec, avgPhi + hPhiSec, - {static_cast(avgPhi)}); - - // Negative cone if exists - std::vector coneSides; + ActsScalar hPhiSec = bounds().get(ConeBounds::eHalfPhiSector); + ActsScalar avgPhi = bounds().get(ConeBounds::eAveragePhi); + std::vector refPhi = {}; + if (bool fullCone = (hPhiSec == M_PI); !fullCone) { + refPhi = {avgPhi}; + } + + // Add the cone sizes + std::vector coneSides; if (std::abs(minZ) > s_onSurfaceTolerance) { coneSides.push_back(minZ); } if (std::abs(maxZ) > s_onSurfaceTolerance) { coneSides.push_back(maxZ); } + for (auto& z : coneSides) { - // Remember the first vertex std::size_t firstIv = vertices.size(); // Radius and z offset double r = std::abs(z) * bounds().tanAlpha(); Vector3 zoffset(0., 0., z); - for (unsigned int iseg = 0; iseg < phiSegs.size() - 1; ++iseg) { - int addon = (iseg == phiSegs.size() - 2 && !fullCone) ? 1 : 0; - detail::VerticesHelper::createSegment(vertices, {r, r}, phiSegs[iseg], - phiSegs[iseg + 1], lseg, addon, - zoffset, ctransform); - } - // Create the faces + auto svertices = detail::VerticesHelper::segmentVertices( + {r, r}, avgPhi - hPhiSec, avgPhi + hPhiSec, refPhi, quarterSegments, + zoffset, ctransform); + vertices.insert(vertices.end(), svertices.begin(), svertices.end()); + // If the tip exists, the faces need to be triangular if (tipExists) { - for (std::size_t iv = firstIv + 2; iv < vertices.size() + 1; ++iv) { - std::size_t one = 0, two = iv - 1, three = iv - 2; + for (std::size_t iv = firstIv + 1; iv < svertices.size() + firstIv; + ++iv) { + std::size_t one = 0, two = iv, three = iv - 1; if (z < 0.) { std::swap(two, three); } faces.push_back({one, two, three}); } - // Complete cone if necessary - if (fullCone) { - if (z > 0.) { - faces.push_back({0, firstIv, vertices.size() - 1}); - } else { - faces.push_back({0, vertices.size() - 1, firstIv}); - } - } } } + // if no tip exists, connect the two bows if (tipExists) { triangularMesh = faces; } else { - auto facesMesh = - detail::FacesHelper::cylindricalFaceMesh(vertices, fullCone); + auto facesMesh = detail::FacesHelper::cylindricalFaceMesh(vertices); faces = facesMesh.first; triangularMesh = facesMesh.second; } + return Polyhedron(vertices, faces, triangularMesh, false); } diff --git a/Core/src/Surfaces/ConvexPolygonBounds.cpp b/Core/src/Surfaces/ConvexPolygonBounds.cpp index c32bfe9141d..f6b08624308 100644 --- a/Core/src/Surfaces/ConvexPolygonBounds.cpp +++ b/Core/src/Surfaces/ConvexPolygonBounds.cpp @@ -8,6 +8,7 @@ #include "Acts/Surfaces/ConvexPolygonBounds.hpp" +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Surfaces/detail/BoundaryCheckHelper.hpp" @@ -51,8 +52,9 @@ Acts::ConvexPolygonBounds::type() const { bool Acts::ConvexPolygonBounds::inside( const Acts::Vector2& lposition, const Acts::BoundaryTolerance& boundaryTolerance) const { - return detail::insidePolygon(m_vertices, boundaryTolerance, lposition, - std::nullopt); + return detail::insidePolygon( + std::span(m_vertices.data(), m_vertices.size()), + boundaryTolerance, lposition, std::nullopt); } std::vector Acts::ConvexPolygonBounds< diff --git a/Core/src/Surfaces/CylinderBounds.cpp b/Core/src/Surfaces/CylinderBounds.cpp index 76e3e902ea7..27123d29ec0 100644 --- a/Core/src/Surfaces/CylinderBounds.cpp +++ b/Core/src/Surfaces/CylinderBounds.cpp @@ -104,45 +104,40 @@ std::ostream& Acts::CylinderBounds::toStream(std::ostream& sl) const { return sl; } -std::vector Acts::CylinderBounds::createCircles( - const Transform3 ctrans, std::size_t lseg) const { +std::vector Acts::CylinderBounds::circleVertices( + const Transform3 transform, unsigned int quarterSegments) const { std::vector vertices; double avgPhi = get(eAveragePhi); double halfPhi = get(eHalfPhiSector); - bool fullCylinder = coversFullAzimuth(); - - // Get the phi segments from the helper - ensures extra points - auto phiSegs = fullCylinder ? detail::VerticesHelper::phiSegments() - : detail::VerticesHelper::phiSegments( - avgPhi - halfPhi, avgPhi + halfPhi, - {static_cast(avgPhi)}); + std::vector phiRef = {}; + if (bool fullCylinder = coversFullAzimuth(); fullCylinder) { + phiRef = {static_cast(avgPhi)}; + } // Write the two bows/circles on either side std::vector sides = {-1, 1}; for (auto& side : sides) { - for (std::size_t iseg = 0; iseg < phiSegs.size() - 1; ++iseg) { - int addon = (iseg == phiSegs.size() - 2 && !fullCylinder) ? 1 : 0; - /// Helper method to create the segment - detail::VerticesHelper::createSegment( - vertices, {get(eR), get(eR)}, phiSegs[iseg], phiSegs[iseg + 1], lseg, - addon, Vector3(0., 0., side * get(eHalfLengthZ)), ctrans); - } + /// Helper method to create the segment + auto svertices = detail::VerticesHelper::segmentVertices( + {get(eR), get(eR)}, avgPhi - halfPhi, avgPhi + halfPhi, phiRef, + quarterSegments, Vector3(0., 0., side * get(eHalfLengthZ)), transform); + vertices.insert(vertices.end(), svertices.begin(), svertices.end()); } - double bevelMinZ = get(eBevelMinZ); - double bevelMaxZ = get(eBevelMaxZ); + ActsScalar bevelMinZ = get(eBevelMinZ); + ActsScalar bevelMaxZ = get(eBevelMaxZ); // Modify the vertices position if bevel is defined if ((bevelMinZ != 0. || bevelMaxZ != 0.) && vertices.size() % 2 == 0) { auto halfWay = vertices.end() - vertices.size() / 2; - double mult{1}; - auto invCtrans = ctrans.inverse(); - auto func = [&mult, &ctrans, &invCtrans](Vector3& v) { - v = invCtrans * v; + ActsScalar mult{1}; + auto invTransform = transform.inverse(); + auto func = [&mult, &transform, &invTransform](Vector3& v) { + v = invTransform * v; v(2) += v(1) * mult; - v = ctrans * v; + v = transform * v; }; if (bevelMinZ != 0.) { mult = std::tan(-bevelMinZ); @@ -158,10 +153,12 @@ std::vector Acts::CylinderBounds::createCircles( void Acts::CylinderBounds::checkConsistency() noexcept(false) { if (get(eR) <= 0.) { - throw std::invalid_argument("CylinderBounds: invalid radial setup."); + throw std::invalid_argument( + "CylinderBounds: invalid radial setup: radius is negative"); } if (get(eHalfLengthZ) <= 0.) { - throw std::invalid_argument("CylinderBounds: invalid length setup."); + throw std::invalid_argument( + "CylinderBounds: invalid length setup: half length is negative"); } if (get(eHalfPhiSector) <= 0. || get(eHalfPhiSector) > M_PI) { throw std::invalid_argument("CylinderBounds: invalid phi sector setup."); diff --git a/Core/src/Surfaces/CylinderSurface.cpp b/Core/src/Surfaces/CylinderSurface.cpp index 29ca1edf150..a39dce4a4fa 100644 --- a/Core/src/Surfaces/CylinderSurface.cpp +++ b/Core/src/Surfaces/CylinderSurface.cpp @@ -188,19 +188,15 @@ const Acts::CylinderBounds& Acts::CylinderSurface::bounds() const { } Acts::Polyhedron Acts::CylinderSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t lseg) const { + const GeometryContext& gctx, unsigned int quarterSegments) const { auto ctrans = transform(gctx); // Prepare vertices and faces - std::vector vertices = bounds().createCircles(ctrans, lseg); - std::vector faces; - std::vector triangularMesh; - - bool fullCylinder = bounds().coversFullAzimuth(); - - auto facesMesh = - detail::FacesHelper::cylindricalFaceMesh(vertices, fullCylinder); - return Polyhedron(vertices, facesMesh.first, facesMesh.second, false); + std::vector vertices = + bounds().circleVertices(ctrans, quarterSegments); + auto [faces, triangularMesh] = + detail::FacesHelper::cylindricalFaceMesh(vertices); + return Polyhedron(vertices, faces, triangularMesh, false); } Acts::Vector3 Acts::CylinderSurface::rotSymmetryAxis( diff --git a/Core/src/Surfaces/DiamondBounds.cpp b/Core/src/Surfaces/DiamondBounds.cpp index b0239eca3f3..aa913404c0f 100644 --- a/Core/src/Surfaces/DiamondBounds.cpp +++ b/Core/src/Surfaces/DiamondBounds.cpp @@ -8,6 +8,7 @@ #include "Acts/Surfaces/DiamondBounds.hpp" +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Surfaces/detail/BoundaryCheckHelper.hpp" @@ -22,12 +23,22 @@ Acts::SurfaceBounds::BoundsType Acts::DiamondBounds::type() const { bool Acts::DiamondBounds::inside( const Acts::Vector2& lposition, const Acts::BoundaryTolerance& boundaryTolerance) const { - return detail::insidePolygon(vertices(), boundaryTolerance, lposition, + // Vertices starting at lower left (min rel. phi) + // counter-clockwise + double x1 = get(DiamondBounds::eHalfLengthXnegY); + double y1 = get(DiamondBounds::eHalfLengthYneg); + double x2 = get(DiamondBounds::eHalfLengthXzeroY); + double y2 = 0.; + double x3 = get(DiamondBounds::eHalfLengthXposY); + double y3 = get(DiamondBounds::eHalfLengthYpos); + Vector2 vertices[] = {{-x1, -y1}, {x1, -y1}, {x2, y2}, + {x3, y3}, {-x3, y3}, {-x2, y2}}; + return detail::insidePolygon(vertices, boundaryTolerance, lposition, std::nullopt); } std::vector Acts::DiamondBounds::vertices( - unsigned int /*lseg*/) const { + unsigned int /*ignoredSegments*/) const { // Vertices starting at lower left (min rel. phi) // counter-clockwise double x1 = get(DiamondBounds::eHalfLengthXnegY); diff --git a/Core/src/Surfaces/DiscSurface.cpp b/Core/src/Surfaces/DiscSurface.cpp index c46390ff1c9..da1aa2b54bc 100644 --- a/Core/src/Surfaces/DiscSurface.cpp +++ b/Core/src/Surfaces/DiscSurface.cpp @@ -154,12 +154,9 @@ const Acts::SurfaceBounds& Acts::DiscSurface::bounds() const { } Acts::Polyhedron Acts::DiscSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t lseg) const { + const GeometryContext& gctx, unsigned int quarterSegments) const { // Prepare vertices and faces std::vector vertices; - std::vector faces; - std::vector triangularMesh; - // Understand the disc bool fullDisc = m_bounds->coversFullAzimuth(); bool toCenter = m_bounds->rMin() < s_onSurfaceTolerance; @@ -167,7 +164,7 @@ Acts::Polyhedron Acts::DiscSurface::polyhedronRepresentation( bool exactPolyhedron = (m_bounds->type() == SurfaceBounds::eDiscTrapezoid); bool addCentreFromConvexFace = (m_bounds->type() != SurfaceBounds::eAnnulus); if (m_bounds) { - auto vertices2D = m_bounds->vertices(lseg); + auto vertices2D = m_bounds->vertices(quarterSegments); vertices.reserve(vertices2D.size() + 1); Vector3 wCenter(0., 0., 0); for (const auto& v2D : vertices2D) { @@ -182,22 +179,19 @@ Acts::Polyhedron Acts::DiscSurface::polyhedronRepresentation( if (addCentreFromConvexFace) { vertices.push_back(wCenter); } - auto facesMesh = detail::FacesHelper::convexFaceMesh(vertices, true); - faces = facesMesh.first; - triangularMesh = facesMesh.second; + auto [faces, triangularMesh] = + detail::FacesHelper::convexFaceMesh(vertices, true); + return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); } else { // Two concentric rings, we use the pure concentric method momentarily, // but that creates too many unneccesarry faces, when only two // are needed to describe the mesh, @todo investigate merging flag - auto facesMesh = detail::FacesHelper::cylindricalFaceMesh(vertices, true); - faces = facesMesh.first; - triangularMesh = facesMesh.second; + auto [faces, triangularMesh] = + detail::FacesHelper::cylindricalFaceMesh(vertices); + return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); } - } else { - throw std::domain_error( - "Polyhedron repr of boundless surface not possible."); } - return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); + throw std::domain_error("Polyhedron repr of boundless surface not possible."); } Acts::Vector2 Acts::DiscSurface::localPolarToCartesian( @@ -208,7 +202,7 @@ Acts::Vector2 Acts::DiscSurface::localPolarToCartesian( Acts::Vector2 Acts::DiscSurface::localCartesianToPolar( const Vector2& lcart) const { - return Vector2(std::hypot(lcart[eBoundLoc0], lcart[eBoundLoc1]), + return Vector2(lcart.norm(), std::atan2(lcart[eBoundLoc1], lcart[eBoundLoc0])); } diff --git a/Core/src/Surfaces/DiscTrapezoidBounds.cpp b/Core/src/Surfaces/DiscTrapezoidBounds.cpp index 6cebe5433d5..ed02bac17de 100644 --- a/Core/src/Surfaces/DiscTrapezoidBounds.cpp +++ b/Core/src/Surfaces/DiscTrapezoidBounds.cpp @@ -64,7 +64,7 @@ bool Acts::DiscTrapezoidBounds::inside( } std::vector Acts::DiscTrapezoidBounds::vertices( - unsigned int /*lseg*/) const { + unsigned int /*ignoredSegments*/) const { Vector2 cAxis(std::cos(get(eAveragePhi)), std::sin(get(eAveragePhi))); Vector2 nAxis(cAxis.y(), -cAxis.x()); auto ymin = std::sqrt(get(eMinR) * get(eMinR) - diff --git a/Core/src/Surfaces/EllipseBounds.cpp b/Core/src/Surfaces/EllipseBounds.cpp index d6357eabcc3..485f36736d8 100644 --- a/Core/src/Surfaces/EllipseBounds.cpp +++ b/Core/src/Surfaces/EllipseBounds.cpp @@ -61,10 +61,10 @@ bool Acts::EllipseBounds::inside( } std::vector Acts::EllipseBounds::vertices( - unsigned int lseg) const { + unsigned int quarterSegments) const { return detail::VerticesHelper::ellipsoidVertices( get(eInnerRx), get(eInnerRy), get(eOuterRx), get(eOuterRy), - get(eAveragePhi), get(eHalfPhiSector), lseg); + get(eAveragePhi), get(eHalfPhiSector), quarterSegments); } const Acts::RectangleBounds& Acts::EllipseBounds::boundingBox() const { diff --git a/Core/src/Surfaces/PerigeeSurface.cpp b/Core/src/Surfaces/PerigeeSurface.cpp index 328db94478e..777d061b2b1 100644 --- a/Core/src/Surfaces/PerigeeSurface.cpp +++ b/Core/src/Surfaces/PerigeeSurface.cpp @@ -58,7 +58,7 @@ std::ostream& Acts::PerigeeSurface::toStreamImpl(const GeometryContext& gctx, } Acts::Polyhedron Acts::PerigeeSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t /*lseg*/) const { + const GeometryContext& gctx, unsigned int /*quarterSegments*/) const { // Prepare vertices and faces std::vector vertices; std::vector faces; diff --git a/Core/src/Surfaces/PlaneSurface.cpp b/Core/src/Surfaces/PlaneSurface.cpp index 414f2611eef..b33ef82eb46 100644 --- a/Core/src/Surfaces/PlaneSurface.cpp +++ b/Core/src/Surfaces/PlaneSurface.cpp @@ -88,16 +88,14 @@ const Acts::SurfaceBounds& Acts::PlaneSurface::bounds() const { } Acts::Polyhedron Acts::PlaneSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t lseg) const { + const GeometryContext& gctx, unsigned int quarterSegments) const { // Prepare vertices and faces std::vector vertices; - std::vector faces; - std::vector triangularMesh; bool exactPolyhedron = true; // If you have bounds you can create a polyhedron representation if (m_bounds) { - auto vertices2D = m_bounds->vertices(lseg); + auto vertices2D = m_bounds->vertices(quarterSegments); vertices.reserve(vertices2D.size() + 1); for (const auto& v2D : vertices2D) { vertices.push_back(transform(gctx) * Vector3(v2D.x(), v2D.y(), 0.)); @@ -116,22 +114,20 @@ Acts::Polyhedron Acts::PlaneSurface::polyhedronRepresentation( // @todo same as for Discs: coversFull is not the right criterium // for triangulation if (!isEllipse || !innerExists || !coversFull) { - auto facesMesh = detail::FacesHelper::convexFaceMesh(vertices); - faces = facesMesh.first; - triangularMesh = facesMesh.second; + auto [faces, triangularMesh] = + detail::FacesHelper::convexFaceMesh(vertices); + return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); } else { // Two concentric rings, we use the pure concentric method momentarily, // but that creates too many unneccesarry faces, when only two // are needed to describe the mesh, @todo investigate merging flag - auto facesMesh = detail::FacesHelper::cylindricalFaceMesh(vertices, true); - faces = facesMesh.first; - triangularMesh = facesMesh.second; + auto [faces, triangularMesh] = + detail::FacesHelper::cylindricalFaceMesh(vertices); + return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); } - } else { - throw std::domain_error( - "Polyhedron repr of boundless surface not possible."); } - return Polyhedron(vertices, faces, triangularMesh, exactPolyhedron); + throw std::domain_error( + "Polyhedron representation of boundless surface not possible."); } Acts::Vector3 Acts::PlaneSurface::normal(const GeometryContext& gctx, diff --git a/Core/src/Surfaces/StrawSurface.cpp b/Core/src/Surfaces/StrawSurface.cpp index 1ac8f8eb264..2ce1ef74b42 100644 --- a/Core/src/Surfaces/StrawSurface.cpp +++ b/Core/src/Surfaces/StrawSurface.cpp @@ -48,7 +48,7 @@ Acts::StrawSurface& Acts::StrawSurface::operator=(const StrawSurface& other) { } Acts::Polyhedron Acts::StrawSurface::polyhedronRepresentation( - const GeometryContext& gctx, std::size_t lseg) const { + const GeometryContext& gctx, unsigned int quarterSegments) const { // Prepare vertices and faces std::vector vertices; std::vector faces; @@ -56,20 +56,17 @@ Acts::Polyhedron Acts::StrawSurface::polyhedronRepresentation( const Transform3& ctransform = transform(gctx); // Draw the bounds if more than one segment are chosen - if (lseg > 1) { + if (quarterSegments > 0u) { double r = m_bounds->get(LineBounds::eR); - auto phiSegs = detail::VerticesHelper::phiSegments(); // Write the two bows/circles on either side std::vector sides = {-1, 1}; for (auto& side : sides) { - for (std::size_t iseg = 0; iseg < phiSegs.size() - 1; ++iseg) { - int addon = (iseg == phiSegs.size() - 2) ? 1 : 0; - /// Helper method to create the segment - detail::VerticesHelper::createSegment( - vertices, {r, r}, phiSegs[iseg], phiSegs[iseg + 1], lseg, addon, - Vector3(0., 0., side * m_bounds->get(LineBounds::eHalfLengthZ)), - ctransform); - } + /// Helper method to create the segment + auto svertices = detail::VerticesHelper::segmentVertices( + {r, r}, -M_PI, M_PI, {}, quarterSegments, + Vector3(0., 0., side * m_bounds->get(LineBounds::eHalfLengthZ)), + ctransform); + vertices.insert(vertices.end(), svertices.begin(), svertices.end()); } auto facesMesh = detail::FacesHelper::cylindricalFaceMesh(vertices); faces = facesMesh.first; diff --git a/Core/src/Surfaces/Surface.cpp b/Core/src/Surfaces/Surface.cpp index 3ebf11d8e92..cadef176cda 100644 --- a/Core/src/Surfaces/Surface.cpp +++ b/Core/src/Surfaces/Surface.cpp @@ -359,6 +359,7 @@ void Acts::Surface::associateLayer(const Acts::Layer& lay) { void Acts::Surface::visualize(IVisualization3D& helper, const GeometryContext& gctx, const ViewConfig& viewConfig) const { - Polyhedron polyhedron = polyhedronRepresentation(gctx, viewConfig.nSegments); + Polyhedron polyhedron = + polyhedronRepresentation(gctx, viewConfig.quarterSegments); polyhedron.visualize(helper, viewConfig); } diff --git a/Core/src/Surfaces/TrapezoidBounds.cpp b/Core/src/Surfaces/TrapezoidBounds.cpp index 06da2fa0fef..3450e8cea97 100644 --- a/Core/src/Surfaces/TrapezoidBounds.cpp +++ b/Core/src/Surfaces/TrapezoidBounds.cpp @@ -89,14 +89,14 @@ bool Acts::TrapezoidBounds::inside( // at this stage, the point can only be in the triangles // run slow-ish polygon check - std::vector vertices = { + Vector2 vertices[] = { {-hlXnY, -hlY}, {hlXnY, -hlY}, {hlXpY, hlY}, {-hlXpY, hlY}}; return detail::insidePolygon(vertices, boundaryTolerance, extPosition, std::nullopt); } std::vector Acts::TrapezoidBounds::vertices( - unsigned int /*lseg*/) const { + unsigned int /*ignoredSegments*/) const { const double hlXnY = get(TrapezoidBounds::eHalfLengthXnegY); const double hlXpY = get(TrapezoidBounds::eHalfLengthXposY); const double hlY = get(TrapezoidBounds::eHalfLengthY); diff --git a/Core/src/Surfaces/VerticesHelper.cpp b/Core/src/Surfaces/detail/VerticesHelper.cpp similarity index 54% rename from Core/src/Surfaces/VerticesHelper.cpp rename to Core/src/Surfaces/detail/VerticesHelper.cpp index 3ff4785fe37..b8bed1fb50f 100644 --- a/Core/src/Surfaces/VerticesHelper.cpp +++ b/Core/src/Surfaces/detail/VerticesHelper.cpp @@ -14,44 +14,61 @@ std::vector Acts::detail::VerticesHelper::phiSegments( ActsScalar phiMin, ActsScalar phiMax, - const std::vector& phiRefs, ActsScalar phiTolerance) { - // This is to ensure that the extrema are built regardless of number - // of segments - std::vector phiSegments; - std::vector quarters = {-M_PI, -0.5 * M_PI, 0., 0.5 * M_PI, M_PI}; - // It does not cover the full azimuth - if (phiMin != -M_PI || phiMax != M_PI) { - phiSegments.push_back(phiMin); - for (unsigned int iq = 1; iq < 4; ++iq) { - if (phiMin < quarters[iq] && phiMax > quarters[iq]) { - phiSegments.push_back(quarters[iq]); - } + const std::vector& phiRefs, unsigned int quarterSegments) { + // Check that the phi range is valid + if (phiMin > phiMax) { + throw std::invalid_argument( + "VerticesHelper::phiSegments ... Minimum phi must be smaller than " + "maximum phi"); + } + + // First check that no reference phi is outside the range + for (ActsScalar phiRef : phiRefs) { + if (phiRef < phiMin || phiRef > phiMax) { + throw std::invalid_argument( + "VerticesHelper::phiSegments ... Reference phi is outside the range " + "of the segment"); } - phiSegments.push_back(phiMax); - } else { - phiSegments = quarters; } - // Insert the reference phis if - if (!phiRefs.empty()) { - for (const auto& phiRef : phiRefs) { - // Trying to find the right patch - auto match = std::find_if( - phiSegments.begin(), phiSegments.end(), [&](ActsScalar phiSeg) { - return std::abs(phiSeg - phiRef) < phiTolerance; - }); - if (match == phiSegments.end()) { + if (quarterSegments == 0u) { + throw std::invalid_argument( + "VerticesHelper::phiSegments ... Number of segments must be larger " + "than 0."); + } + std::vector phiSegments = {phiMin, phiMax}; + // Minimum approximation for a circle need + // - if the circle is closed the last point is given twice + for (unsigned int i = 0; i < 4 * quarterSegments + 1; ++i) { + ActsScalar phiExt = -M_PI + i * 2 * M_PI / (4 * quarterSegments); + if (phiExt > phiMin && phiExt < phiMax && + std::ranges::none_of(phiSegments, [&phiExt](ActsScalar phi) { + return std::abs(phi - phiExt) < + std::numeric_limits::epsilon(); + })) { + phiSegments.push_back(phiExt); + } + } + // Add the reference phis + for (const auto& phiRef : phiRefs) { + if (phiRef > phiMin && phiRef < phiMax) { + if (std::ranges::none_of(phiSegments, [&phiRef](ActsScalar phi) { + return std::abs(phi - phiRef) < + std::numeric_limits::epsilon(); + })) { phiSegments.push_back(phiRef); } } - std::ranges::sort(phiSegments); } + + // Sort the phis + std::ranges::sort(phiSegments); return phiSegments; } std::vector Acts::detail::VerticesHelper::ellipsoidVertices( ActsScalar innerRx, ActsScalar innerRy, ActsScalar outerRx, ActsScalar outerRy, ActsScalar avgPhi, ActsScalar halfPhi, - unsigned int lseg) { + unsigned int quarterSegments) { // List of vertices counter-clockwise starting at smallest phi w.r.t center, // for both inner/outer ring/segment std::vector rvertices; // return vertices @@ -61,22 +78,20 @@ std::vector Acts::detail::VerticesHelper::ellipsoidVertices( bool innerExists = (innerRx > 0. && innerRy > 0.); bool closed = std::abs(halfPhi - M_PI) < s_onSurfaceTolerance; - // Get the phi segments from the helper method - auto phiSegs = detail::VerticesHelper::phiSegments( - avgPhi - halfPhi, avgPhi + halfPhi, {avgPhi}); + std::vector refPhi = {}; + if (avgPhi != 0.) { + refPhi.push_back(avgPhi); + } // The inner (if exists) and outer bow - for (unsigned int iseg = 0; iseg < phiSegs.size() - 1; ++iseg) { - int addon = (iseg == phiSegs.size() - 2 && !closed) ? 1 : 0; - if (innerExists) { - createSegment(ivertices, {innerRx, innerRy}, - phiSegs[iseg], phiSegs[iseg + 1], lseg, - addon); - } - createSegment(overtices, {outerRx, outerRy}, - phiSegs[iseg], phiSegs[iseg + 1], lseg, - addon); + if (innerExists) { + ivertices = segmentVertices( + {innerRx, innerRy}, avgPhi - halfPhi, avgPhi + halfPhi, refPhi, + quarterSegments); } + overtices = segmentVertices( + {outerRx, outerRy}, avgPhi - halfPhi, avgPhi + halfPhi, refPhi, + quarterSegments); // We want to keep the same counter-clockwise orientation for displaying if (!innerExists) { @@ -97,9 +112,9 @@ std::vector Acts::detail::VerticesHelper::ellipsoidVertices( std::vector Acts::detail::VerticesHelper::circularVertices( ActsScalar innerR, ActsScalar outerR, ActsScalar avgPhi, ActsScalar halfPhi, - unsigned int lseg) { + unsigned int quarterSegments) { return ellipsoidVertices(innerR, innerR, outerR, outerR, avgPhi, halfPhi, - lseg); + quarterSegments); } bool Acts::detail::VerticesHelper::onHyperPlane( diff --git a/Core/src/TrackFinding/MeasurementSelector.cpp b/Core/src/TrackFinding/MeasurementSelector.cpp index cde6670e4ca..130c8381536 100644 --- a/Core/src/TrackFinding/MeasurementSelector.cpp +++ b/Core/src/TrackFinding/MeasurementSelector.cpp @@ -118,10 +118,10 @@ MeasurementSelector::Cuts MeasurementSelector::getCutsByTheta( // look at the positive half of the Z axis const double constrainedTheta = std::min(theta, M_PI - theta); - auto it = std::find_if(config.begin(), config.end(), - [constrainedTheta](const InternalCutBin& cuts) { - return constrainedTheta < cuts.maxTheta; - }); + auto it = std::ranges::find_if( + config, [constrainedTheta](const InternalCutBin& cuts) { + return constrainedTheta < cuts.maxTheta; + }); assert(it != config.end()); return {it->maxNumMeasurements, it->maxChi2Measurement, it->maxChi2Outlier}; } diff --git a/Core/src/TrackFitting/BetheHeitlerApprox.cpp b/Core/src/TrackFitting/BetheHeitlerApprox.cpp index b19b9921902..272d6e08c8d 100644 --- a/Core/src/TrackFitting/BetheHeitlerApprox.cpp +++ b/Core/src/TrackFitting/BetheHeitlerApprox.cpp @@ -8,7 +8,8 @@ #include "Acts/TrackFitting/BetheHeitlerApprox.hpp" -Acts::AtlasBetheHeitlerApprox<6, 5> Acts::makeDefaultBetheHeitlerApprox() { +Acts::AtlasBetheHeitlerApprox<6, 5> Acts::makeDefaultBetheHeitlerApprox( + bool clampToRange) { // Tracking/TrkFitter/TrkGaussianSumFilterUtils/Data/BetheHeitler_cdf_nC6_O5.par // clang-format off constexpr static AtlasBetheHeitlerApprox<6, 5>::Data cdf_cmps6_order5_data = {{ @@ -51,6 +52,7 @@ Acts::AtlasBetheHeitlerApprox<6, 5> Acts::makeDefaultBetheHeitlerApprox() { }}; // clang-format on - return AtlasBetheHeitlerApprox<6, 5>( - cdf_cmps6_order5_data, cdf_cmps6_order5_data, true, true, 0.2, 0.2); + return AtlasBetheHeitlerApprox<6, 5>(cdf_cmps6_order5_data, + cdf_cmps6_order5_data, true, true, 0.2, + 0.2, clampToRange); } diff --git a/Core/src/Utilities/BinningType.cpp b/Core/src/Utilities/BinningType.cpp index e1ed3ecea71..8faa7dcd856 100644 --- a/Core/src/Utilities/BinningType.cpp +++ b/Core/src/Utilities/BinningType.cpp @@ -32,8 +32,7 @@ const std::vector& allBinningValues() { } BinningValue binningValueFromName(const std::string& name) { - auto it = - std::find(s_binningValueNames.begin(), s_binningValueNames.end(), name); + auto it = std::ranges::find(s_binningValueNames, name); if (it == s_binningValueNames.end()) { throw std::invalid_argument("Unknown binning value name: " + name); } diff --git a/Core/src/Utilities/CMakeLists.txt b/Core/src/Utilities/CMakeLists.txt index 6c5553387a0..1fb61813cd3 100644 --- a/Core/src/Utilities/CMakeLists.txt +++ b/Core/src/Utilities/CMakeLists.txt @@ -2,7 +2,6 @@ target_sources( ActsCore PRIVATE AnnealingUtility.cpp - BinUtility.cpp Logger.cpp SpacePointUtility.cpp TrackHelpers.cpp diff --git a/Core/src/Utilities/SpacePointUtility.cpp b/Core/src/Utilities/SpacePointUtility.cpp index c93154091ce..4b79fc03cc3 100644 --- a/Core/src/Utilities/SpacePointUtility.cpp +++ b/Core/src/Utilities/SpacePointUtility.cpp @@ -15,6 +15,7 @@ #include "Acts/SpacePointFormation/SpacePointBuilderOptions.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include #include @@ -76,7 +77,7 @@ SpacePointUtility::globalCoords( // auto x = globalPos[ePos0]; auto y = globalPos[ePos1]; - auto scale = 2 / std::hypot(x, y); + auto scale = 2 / fastHypot(x, y); ActsMatrix<2, 3> jacXyzToRhoZ = ActsMatrix<2, 3>::Zero(); jacXyzToRhoZ(0, ePos0) = scale * x; jacXyzToRhoZ(0, ePos1) = scale * y; @@ -112,8 +113,9 @@ Vector2 SpacePointUtility::calcRhoZVars( const auto var2 = paramCovAccessor(slinkBack).second(0, 0); // strip1 and strip2 are tilted at +/- theta/2 - double sigma_x = std::hypot(var1, var2) / (2 * sin(theta * 0.5)); - double sigma_y = std::hypot(var1, var2) / (2 * cos(theta * 0.5)); + double sigma = fastHypot(var1, var2); + double sigma_x = sigma / (2 * sin(theta * 0.5)); + double sigma_y = sigma / (2 * cos(theta * 0.5)); // projection to the surface with strip1. double sig_x1 = sigma_x * cos(0.5 * theta) + sigma_y * sin(0.5 * theta); @@ -138,7 +140,7 @@ Vector2 SpacePointUtility::rhoZCovariance(const GeometryContext& gctx, auto x = globalPos[ePos0]; auto y = globalPos[ePos1]; - auto scale = 2 / std::hypot(x, y); + auto scale = 2 / globalPos.head<2>().norm(); ActsMatrix<2, 3> jacXyzToRhoZ = ActsMatrix<2, 3>::Zero(); jacXyzToRhoZ(0, ePos0) = scale * x; jacXyzToRhoZ(0, ePos1) = scale * y; diff --git a/Core/src/Vertexing/AdaptiveMultiVertexFinder.cpp b/Core/src/Vertexing/AdaptiveMultiVertexFinder.cpp index 655ee0b218f..34d20e32e0a 100644 --- a/Core/src/Vertexing/AdaptiveMultiVertexFinder.cpp +++ b/Core/src/Vertexing/AdaptiveMultiVertexFinder.cpp @@ -12,6 +12,8 @@ #include "Acts/Vertexing/IVertexFinder.hpp" #include "Acts/Vertexing/VertexingError.hpp" +#include + namespace Acts { Result> AdaptiveMultiVertexFinder::find( @@ -364,10 +366,7 @@ std::pair AdaptiveMultiVertexFinder::checkVertexAndCompatibleTracks( !m_cfg.useFastCompatibility)) { // TODO: Understand why looking for compatible tracks only in seed tracks // and not also in all tracks - auto foundIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [&trk](auto seedTrk) { return trk == seedTrk; }); - if (foundIter != seedTracks.end()) { + if (rangeContainsValue(seedTracks, trk)) { nCompatibleTracks++; ACTS_DEBUG("Compatible track found."); @@ -399,9 +398,7 @@ auto AdaptiveMultiVertexFinder::removeCompatibleTracksFromSeedTracks( trkAtVtx.chi2Track < m_cfg.maxVertexChi2 && !m_cfg.useFastCompatibility)) { // Find and remove track from seedTracks - auto foundSeedIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [&trk](auto seedTrk) { return trk == seedTrk; }); + auto foundSeedIter = std::ranges::find(seedTracks, trk); if (foundSeedIter != seedTracks.end()) { seedTracks.erase(foundSeedIter); removedSeedTracks.push_back(trk); @@ -425,9 +422,7 @@ bool AdaptiveMultiVertexFinder::removeTrackIfIncompatible( double compatibility = trkAtVtx.vertexCompatibility; if (compatibility > maxCompatibility) { // Try to find track in seed tracks - auto foundSeedIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [&trk](auto seedTrk) { return trk == seedTrk; }); + auto foundSeedIter = std::ranges::find(seedTracks, trk); if (foundSeedIter != seedTracks.end()) { maxCompatibility = compatibility; maxCompSeedIt = foundSeedIter; diff --git a/Core/src/Vertexing/HelicalTrackLinearizer.cpp b/Core/src/Vertexing/HelicalTrackLinearizer.cpp index 2d814b95c4f..9d529745559 100644 --- a/Core/src/Vertexing/HelicalTrackLinearizer.cpp +++ b/Core/src/Vertexing/HelicalTrackLinearizer.cpp @@ -10,6 +10,7 @@ #include "Acts/Propagator/PropagatorOptions.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include "Acts/Vertexing/LinearizerTrackParameters.hpp" Acts::Result @@ -85,7 +86,7 @@ Acts::HelicalTrackLinearizer::linearizeTrack( ActsScalar p = params.particleHypothesis().extractMomentum(qOvP); // Speed in units of c - ActsScalar beta = p / std::hypot(p, m0); + ActsScalar beta = p / fastHypot(p, m0); // Transverse speed (i.e., speed in the x-y plane) ActsScalar betaT = beta * sinTheta; diff --git a/Core/src/Vertexing/ImpactPointEstimator.cpp b/Core/src/Vertexing/ImpactPointEstimator.cpp index 3c6d29e4bae..53e19277c85 100644 --- a/Core/src/Vertexing/ImpactPointEstimator.cpp +++ b/Core/src/Vertexing/ImpactPointEstimator.cpp @@ -13,6 +13,7 @@ #include "Acts/Propagator/PropagatorOptions.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include "Acts/Vertexing/VertexingError.hpp" namespace Acts { @@ -207,7 +208,7 @@ Result> getDistanceAndMomentumImpl( ActsScalar p = trkParams.particleHypothesis().extractMomentum(qOvP); // Speed in units of c - ActsScalar beta = p / std::hypot(p, m0); + ActsScalar beta = p / fastHypot(p, m0); pcaStraightTrack[3] = timeOnTrack + distanceToPca / beta; } @@ -282,7 +283,7 @@ Result> getDistanceAndMomentumImpl( ActsScalar p = trkParams.particleHypothesis().extractMomentum(qOvP); // Speed in units of c - ActsScalar beta = p / std::hypot(p, m0); + ActsScalar beta = p / fastHypot(p, m0); pca[3] = tP - rho / (beta * sinTheta) * (phi - phiP); } diff --git a/Core/src/Vertexing/IterativeVertexFinder.cpp b/Core/src/Vertexing/IterativeVertexFinder.cpp index e455dca47ec..9c03ba8a8f8 100644 --- a/Core/src/Vertexing/IterativeVertexFinder.cpp +++ b/Core/src/Vertexing/IterativeVertexFinder.cpp @@ -222,10 +222,9 @@ inline void Acts::IterativeVertexFinder::removeTracks( const BoundTrackParameters& params = m_cfg.extractParameters(trk); // Find track in seedTracks auto foundIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [¶ms, this](const auto seedTrk) { - return params == m_cfg.extractParameters(seedTrk); - }); + std::ranges::find_if(seedTracks, [¶ms, this](const auto seedTrk) { + return params == m_cfg.extractParameters(seedTrk); + }); if (foundIter != seedTracks.end()) { // Remove track from seed tracks seedTracks.erase(foundIter); @@ -284,10 +283,7 @@ Acts::Result Acts::IterativeVertexFinder::removeUsedCompatibleTracks( } // Find and remove track from seedTracks auto foundSeedIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [&trackAtVtx](const auto& seedTrk) { - return trackAtVtx.originalParams == seedTrk; - }); + std::ranges::find(seedTracks, trackAtVtx.originalParams); if (foundSeedIter != seedTracks.end()) { seedTracks.erase(foundSeedIter); } else { @@ -296,10 +292,7 @@ Acts::Result Acts::IterativeVertexFinder::removeUsedCompatibleTracks( // Find and remove track from tracksToFit auto foundFitIter = - std::find_if(tracksToFit.begin(), tracksToFit.end(), - [&trackAtVtx](const auto& fitTrk) { - return trackAtVtx.originalParams == fitTrk; - }); + std::ranges::find(tracksToFit, trackAtVtx.originalParams); if (foundFitIter != tracksToFit.end()) { tracksToFit.erase(foundFitIter); } else { @@ -334,9 +327,7 @@ Acts::Result Acts::IterativeVertexFinder::removeUsedCompatibleTracks( // check if sufficiently compatible with last fitted vertex // (quite loose constraint) if (chi2 < m_cfg.maximumChi2cutForSeeding) { - auto foundIter = - std::find_if(seedTracks.begin(), seedTracks.end(), - [&trk](const auto& seedTrk) { return trk == seedTrk; }); + auto foundIter = std::ranges::find(seedTracks, trk); if (foundIter != seedTracks.end()) { // Remove track from seed tracks seedTracks.erase(foundIter); @@ -345,8 +336,8 @@ Acts::Result Acts::IterativeVertexFinder::removeUsedCompatibleTracks( } else { // Track not compatible with vertex // Remove track from current vertex - auto foundIter = std::find_if( - tracksAtVertex.begin(), tracksAtVertex.end(), + auto foundIter = std::ranges::find_if( + tracksAtVertex, [&trk](auto trkAtVtx) { return trk == trkAtVtx.originalParams; }); if (foundIter != tracksAtVertex.end()) { // Remove track from seed tracks @@ -495,8 +486,8 @@ Acts::Result Acts::IterativeVertexFinder::reassignTracksToNewVertex( // delete it later // when all tracks used to fit current vertex are deleted seedTracks.push_back(tracksIter->originalParams); - // seedTracks.push_back(*std::find_if( - // origTracks.begin(), origTracks.end(), + // seedTracks.push_back(*std::ranges::find_if( + // origTracks, // [&origParams, this](auto origTrack) { // return origParams == m_extractParameters(*origTrack); // })); diff --git a/Core/src/Visualization/CMakeLists.txt b/Core/src/Visualization/CMakeLists.txt index 5d4f916af1c..567b1e1e236 100644 --- a/Core/src/Visualization/CMakeLists.txt +++ b/Core/src/Visualization/CMakeLists.txt @@ -1 +1,4 @@ -target_sources(ActsCore PRIVATE GeometryView3D.cpp EventDataView3D.cpp) +target_sources( + ActsCore + PRIVATE GeometryView3D.cpp EventDataView3D.cpp ObjVisualization3D.cpp +) diff --git a/Core/src/Visualization/EventDataView3D.cpp b/Core/src/Visualization/EventDataView3D.cpp index 63644d0204f..ac3a62a1e79 100644 --- a/Core/src/Visualization/EventDataView3D.cpp +++ b/Core/src/Visualization/EventDataView3D.cpp @@ -27,12 +27,13 @@ void Acts::EventDataView3D::drawCovarianceCartesian( std::vector ellipse = createEllipse( lambda0 * locErrorScale, lambda1 * locErrorScale, theta, - viewConfig.nSegments, viewConfig.offset, lposition, transform); + viewConfig.quarterSegments, viewConfig.offset, lposition, transform); ellipse.push_back(transform * Vector3(lposition.x(), lposition.y(), viewConfig.offset)); - auto faces = detail::FacesHelper::convexFaceMesh(ellipse, true); - Polyhedron ellipseHedron(ellipse, faces.first, faces.second); + auto [faces, triangularMesh] = + detail::FacesHelper::convexFaceMesh(ellipse, true); + Polyhedron ellipseHedron(ellipse, faces, triangularMesh); Acts::GeometryView3D::drawPolyhedron(helper, ellipseHedron, viewConfig); } @@ -56,12 +57,13 @@ void Acts::EventDataView3D::drawCovarianceAngular( std::vector ellipse = createEllipse(angularErrorScale * directionScale * lambda0 * sin(dtheta), angularErrorScale * directionScale * lambda1, theta, - viewConfig.nSegments, 0., {0., 0.}, eplane); + viewConfig.quarterSegments, 0., {0., 0.}, eplane); std::vector coneTop = ellipse; coneTop.push_back(anker); - auto coneTopFaces = detail::FacesHelper::convexFaceMesh(coneTop, true); - Polyhedron coneTopHedron(coneTop, coneTopFaces.first, coneTopFaces.second); + auto [faces, triangularMesh] = + detail::FacesHelper::convexFaceMesh(coneTop, true); + Polyhedron coneTopHedron(coneTop, faces, triangularMesh); GeometryView3D::drawPolyhedron(helper, coneTopHedron, viewConfig); std::vector cone = ellipse; @@ -69,7 +71,8 @@ void Acts::EventDataView3D::drawCovarianceAngular( // Force triangular ViewConfig coneViewConfig = viewConfig; coneViewConfig.triangulate = true; - auto coneFaces = detail::FacesHelper::convexFaceMesh(cone, true); - Polyhedron coneHedron(cone, coneFaces.first, coneFaces.second); + auto [facesCone, triangularMeshCone] = + detail::FacesHelper::convexFaceMesh(cone, true); + Polyhedron coneHedron(cone, facesCone, triangularMeshCone); GeometryView3D::drawPolyhedron(helper, coneHedron, coneViewConfig); } diff --git a/Core/src/Visualization/GeometryView3D.cpp b/Core/src/Visualization/GeometryView3D.cpp index e0a7c52a5a7..f16d9e809cc 100644 --- a/Core/src/Visualization/GeometryView3D.cpp +++ b/Core/src/Visualization/GeometryView3D.cpp @@ -26,7 +26,6 @@ #include "Acts/Surfaces/RadialBounds.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/SurfaceArray.hpp" -#include "Acts/Utilities/BinnedArray.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/IAxis.hpp" #include "Acts/Utilities/UnitVectors.hpp" @@ -36,18 +35,9 @@ #include #include #include -#include #include #include -namespace Acts::Experimental { -ViewConfig s_viewSensitive = {.color = {0, 180, 240}}; -ViewConfig s_viewPassive = {.color = {240, 280, 0}}; -ViewConfig s_viewVolume = {.color = {220, 220, 0}}; -ViewConfig s_viewGrid = {.color = {220, 0, 0}}; -ViewConfig s_viewLine = {.color = {0, 0, 220}}; -} // namespace Acts::Experimental - void Acts::GeometryView3D::drawPolyhedron(IVisualization3D& helper, const Polyhedron& polyhedron, const ViewConfig& viewConfig) { @@ -95,7 +85,6 @@ void Acts::GeometryView3D::drawSurfaceArray( auto phiValues = axes[0]->getBinEdges(); auto zValues = axes[1]->getBinEdges(); ViewConfig gridRadConfig = gridConfig; - gridRadConfig.nSegments = phiValues.size(); // Longitudinal lines for (auto phi : phiValues) { double cphi = std::cos(phi); @@ -120,7 +109,7 @@ void Acts::GeometryView3D::drawSurfaceArray( auto rValues = axes[0]->getBinEdges(); auto phiValues = axes[1]->getBinEdges(); ViewConfig gridRadConfig = gridConfig; - gridRadConfig.nSegments = phiValues.size(); + gridRadConfig.quarterSegments = phiValues.size(); for (auto r : rValues) { CylinderVolumeBounds cvb(r - 0.5 * thickness, r + 0.5 * thickness, 0.5 * thickness); @@ -244,7 +233,7 @@ void Acts::GeometryView3D::drawTrackingVolume( ViewConfig lConfig = layerView; ViewConfig sConfig = sensitiveView; ViewConfig gConfig = gridView; - gConfig.nSegments = 8; + gConfig.quarterSegments = 8; ViewConfig vcConfig = cConfig; std::string vname = tVolume.volumeName(); diff --git a/Core/src/Visualization/ObjVisualization3D.cpp b/Core/src/Visualization/ObjVisualization3D.cpp new file mode 100644 index 00000000000..777e883b3d3 --- /dev/null +++ b/Core/src/Visualization/ObjVisualization3D.cpp @@ -0,0 +1,211 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Visualization/ObjVisualization3D.hpp" + +#include +#include +#include + +namespace Acts { + +void ObjVisualization3D::vertex(const Vector3& vtx, Color color) { + auto& o = object(); + o.vertexColors[o.vertices.size()] = color; + o.vertices.push_back(vtx.template cast()); +} + +void ObjVisualization3D::line(const Vector3& a, const Vector3& b, Color color) { + auto& o = object(); + if (color != Color{0, 0, 0}) { + o.lineColors[o.lines.size()] = color; + } + // not implemented + vertex(a, color); + vertex(b, color); + o.lines.push_back({o.vertices.size() - 2, o.vertices.size() - 1}); +} + +void ObjVisualization3D::face(const std::vector& vtxs, Color color) { + auto& o = object(); + if (color != Color{0, 0, 0}) { + o.faceColors[o.faces.size()] = color; + } + FaceType idxs; + idxs.reserve(vtxs.size()); + for (const auto& vtx : vtxs) { + vertex(vtx, color); + idxs.push_back(o.vertices.size() - 1); + } + o.faces.push_back(std::move(idxs)); +} + +void ObjVisualization3D::faces(const std::vector& vtxs, + const std::vector& faces, + Color color) { + auto& o = object(); + // No faces given - call the face() method + if (faces.empty()) { + face(vtxs, color); + } else { + if (color != Color{0, 0, 0}) { + o.faceColors[o.faces.size()] = color; + } + auto vtxoffs = o.vertices.size(); + if (color != Color{0, 0, 0}) { + o.vertexColors[o.vertices.size()] = color; + } + o.vertices.insert(o.vertices.end(), vtxs.begin(), vtxs.end()); + for (const auto& face : faces) { + if (face.size() == 2) { + o.lines.push_back({face[0] + vtxoffs, face[2] + vtxoffs}); + } else { + FaceType rawFace; + std::ranges::transform( + face, std::back_inserter(rawFace), + [&](unsigned long iv) { return (iv + vtxoffs); }); + o.faces.push_back(rawFace); + } + } + } +} + +void ObjVisualization3D::write(const std::filesystem::path& path) const { + std::ofstream os; + std::filesystem::path objectpath = path; + if (!objectpath.has_extension()) { + objectpath.replace_extension(std::filesystem::path("obj")); + } + os.open(std::filesystem::absolute(objectpath).string()); + std::filesystem::path mtlpath = objectpath; + mtlpath.replace_extension(std::filesystem::path("mtl")); + + const std::string mtlpathString = std::filesystem::absolute(mtlpath).string(); + os << "mtllib " << mtlpathString << "\n"; + std::ofstream mtlos; + mtlos.open(mtlpathString); + + write(os, mtlos); + os.close(); + mtlos.close(); +} + +void ObjVisualization3D::write(std::ostream& os) const { + std::stringstream sterile; + write(os, sterile); +} + +void ObjVisualization3D::write(std::ostream& os, std::ostream& mos) const { + std::map> materials; + + auto mixColor = [&](const Color& color) { + std::string materialName; + materialName = "material_"; + materialName += std::to_string(color[0]) + std::string("_"); + materialName += std::to_string(color[1]) + std::string("_"); + materialName += std::to_string(color[2]); + + if (!materials.contains(materialName)) { + mos << "newmtl " << materialName << "\n"; + std::vector shadings = {"Ka", "Kd", "Ks"}; + for (const auto& shd : shadings) { + mos << shd << " " << std::to_string(color[0] / 256.) << " "; + mos << std::to_string(color[1] / 256.) << " "; + mos << std::to_string(color[2] / 256.) << " " << "\n"; + } + mos << "\n"; + } + return std::string("usemtl ") + materialName; + }; + + std::size_t vertexOffset = 0; + for (const auto& o : m_objects) { + if (!o.name.empty()) { + os << "o " << o.name << "\n"; + } + + std::size_t iv = 0; + Color lastVertexColor = {0, 0, 0}; + for (const VertexType& vtx : o.vertices) { + if (o.vertexColors.contains(iv)) { + auto color = o.vertexColors.find(iv)->second; + if (color != lastVertexColor) { + os << mixColor(color) << "\n"; + lastVertexColor = color; + } + } + + os << "v " << std::setprecision(m_outputPrecision) + << m_outputScalor * vtx.x() << " " << m_outputScalor * vtx.y() << " " + << m_outputScalor * vtx.z() << "\n"; + ++iv; + } + std::size_t il = 0; + Color lastLineColor = {0, 0, 0}; + for (const auto& [start, end] : o.lines) { + if (o.lineColors.contains(il)) { + auto color = o.lineColors.find(il)->second; + if (color != lastLineColor) { + os << mixColor(color) << "\n"; + lastLineColor = color; + } + } + os << "l " << vertexOffset + start + 1 << " " << vertexOffset + end + 1 + << "\n"; + ++il; + } + std::size_t is = 0; + Color lastFaceColor = {0, 0, 0}; + for (const FaceType& fc : o.faces) { + if (o.faceColors.contains(is)) { + auto color = o.faceColors.find(is)->second; + if (color != lastFaceColor) { + os << mixColor(color) << "\n"; + lastFaceColor = color; + } + } + os << "f"; + for (std::size_t fi : fc) { + os << " " << vertexOffset + fi + 1; + } + os << "\n"; + ++is; + } + + vertexOffset += iv; + } +} + +void ObjVisualization3D::clear() { + m_objects.clear(); +} + +void ObjVisualization3D::object(const std::string& name) { + if (name.empty()) { + throw std::invalid_argument{"Object name can not be empty"}; + } + m_objects.push_back(Object{.name = name}); +} + +ObjVisualization3D::Object& ObjVisualization3D::object() { + if (m_objects.empty()) { + m_objects.push_back(Object{.name = ""}); + } + + return m_objects.back(); +} + +const ObjVisualization3D::Object& ObjVisualization3D::object() const { + if (m_objects.empty()) { + throw std::runtime_error{"No objects present"}; + } + + return m_objects.back(); +} + +} // namespace Acts diff --git a/Examples/Algorithms/Alignment/include/ActsExamples/Alignment/AlignmentAlgorithm.hpp b/Examples/Algorithms/Alignment/include/ActsExamples/Alignment/AlignmentAlgorithm.hpp index fe3442b1a29..db5833e5549 100644 --- a/Examples/Algorithms/Alignment/include/ActsExamples/Alignment/AlignmentAlgorithm.hpp +++ b/Examples/Algorithms/Alignment/include/ActsExamples/Alignment/AlignmentAlgorithm.hpp @@ -11,7 +11,6 @@ #include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/TrackFitting/KalmanFitter.hpp" #include "ActsAlignment/Kernel/Alignment.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/Track.hpp" @@ -88,8 +87,6 @@ class AlignmentAlgorithm final : public IAlgorithm { struct Config { /// Input measurements collection. std::string inputMeasurements; - /// Input source links collection. - std::string inputSourceLinks; /// Input proto tracks collection, i.e. groups of hit indices. std::string inputProtoTracks; /// Input initial track parameter estimates for for each proto track. @@ -133,8 +130,6 @@ class AlignmentAlgorithm final : public IAlgorithm { ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; ReadDataHandle m_inputInitialTrackParameters{ this, "InputInitialTrackParameters"}; ReadDataHandle m_inputProtoTracks{this, diff --git a/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp b/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp index aa685217f05..2f1f450e23c 100644 --- a/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp +++ b/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp @@ -11,6 +11,7 @@ #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/Trajectories.hpp" @@ -23,9 +24,6 @@ ActsExamples::AlignmentAlgorithm::AlignmentAlgorithm(Config cfg, if (m_cfg.inputMeasurements.empty()) { throw std::invalid_argument("Missing input measurement collection"); } - if (m_cfg.inputSourceLinks.empty()) { - throw std::invalid_argument("Missing input source links collection"); - } if (m_cfg.inputProtoTracks.empty()) { throw std::invalid_argument("Missing input proto tracks collection"); } @@ -39,7 +37,6 @@ ActsExamples::AlignmentAlgorithm::AlignmentAlgorithm(Config cfg, } m_inputMeasurements.initialize(m_cfg.inputMeasurements); - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_inputProtoTracks.initialize(m_cfg.inputProtoTracks); m_inputInitialTrackParameters.initialize(m_cfg.inputInitialTrackParameters); m_outputAlignmentParameters.initialize(m_cfg.outputAlignmentParameters); @@ -49,7 +46,6 @@ ActsExamples::ProcessCode ActsExamples::AlignmentAlgorithm::execute( const ActsExamples::AlgorithmContext& ctx) const { // Read input data const auto& measurements = m_inputMeasurements(ctx); - const auto& sourceLinks = m_inputSourceLinks(ctx); const auto& protoTracks = m_inputProtoTracks(ctx); const auto& initialParameters = m_inputInitialTrackParameters(ctx); @@ -79,14 +75,11 @@ ActsExamples::ProcessCode ActsExamples::AlignmentAlgorithm::execute( trackSourceLinks.reserve(protoTrack.size()); // Fill the source links via their indices from the container - for (auto hitIndex : protoTrack) { - auto sourceLink = sourceLinks.nth(hitIndex); - if (sourceLink == sourceLinks.end()) { - ACTS_FATAL("Proto track " << itrack << " contains invalid hit index" - << hitIndex); - return ProcessCode::ABORT; - } - trackSourceLinks.push_back(*sourceLink); + for (auto measIndex : protoTrack) { + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(measIndex); + IndexSourceLink sourceLink(measurement.geometryId(), measIndex); + trackSourceLinks.push_back(sourceLink); } sourceLinkTrackContainer.push_back(trackSourceLinks); } diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp index 560fcc9b3e5..fd8937bd300 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp @@ -18,7 +18,6 @@ #include "ActsExamples/Digitization/SmearingConfig.hpp" #include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/Framework/DataHandle.hpp" @@ -102,8 +101,6 @@ class DigitizationAlgorithm final : public IAlgorithm { ReadDataHandle m_simContainerReadHandle{this, "SimHitContainer"}; - WriteDataHandle m_sourceLinkWriteHandle{ - this, "SourceLinks"}; WriteDataHandle m_measurementWriteHandle{ this, "Measurements"}; WriteDataHandle m_cellsWriteHandle{this, "Cells"}; diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp index 941d01ae494..3521d0bef7d 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp @@ -135,8 +135,6 @@ class DigitizationConfig { /// Input collection of simulated hits. std::string inputSimHits = "simhits"; - /// Output source links collection. - std::string outputSourceLinks = "sourcelinks"; /// Output measurements collection. std::string outputMeasurements = "measurements"; /// Output cells map (geoID -> collection of cells). diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp index b2c76a9820e..667e0c9f4c3 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp @@ -10,8 +10,8 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "ActsExamples/EventData/Cluster.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include @@ -20,7 +20,6 @@ #include namespace ActsExamples { -class IndexSourceLink; /// Struct to identify digitized parameters /// @@ -35,15 +34,16 @@ struct DigitizedParameters { /// Helper method for created a measurement from digitized parameters /// +/// @param container The measurement container to insert into +/// @param geometryId The geometry ID of the measurement surface /// @param dParams The digitized parameters of variable size -/// @param isl The indexed source link for the measurement /// /// To be used also by the e I/O system /// /// @return the measurement proxy ActsExamples::VariableBoundMeasurementProxy createMeasurement( - MeasurementContainer& container, const DigitizedParameters& dParams, - const IndexSourceLink& isl) noexcept(false); + MeasurementContainer& container, Acts::GeometryIdentifier geometryId, + const DigitizedParameters& dParams) noexcept(false); /// Construct the constituents of a measurement. /// diff --git a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp index d6babfe24d0..72f3905ab57 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp @@ -18,7 +18,6 @@ #include "ActsExamples/Digitization/ModuleClusters.hpp" #include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/GroupBy.hpp" @@ -55,9 +54,6 @@ ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( } if (m_cfg.doClusterization) { - if (m_cfg.outputSourceLinks.empty()) { - throw std::invalid_argument("Missing source links output collection"); - } if (m_cfg.outputMeasurements.empty()) { throw std::invalid_argument("Missing measurements output collection"); } @@ -73,7 +69,6 @@ ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( "Missing hit-to-simulated-hits map output collection"); } - m_sourceLinkWriteHandle.initialize(m_cfg.outputSourceLinks); m_measurementWriteHandle.initialize(m_cfg.outputMeasurements); m_clusterWriteHandle.initialize(m_cfg.outputClusters); m_measurementParticlesMapWriteHandle.initialize( @@ -152,12 +147,10 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( // Prepare output containers // need list here for stable addresses - IndexSourceLinkContainer sourceLinks; MeasurementContainer measurements; ClusterContainer clusters; IndexMultimap measurementParticlesMap; IndexMultimap measurementSimHitsMap; - sourceLinks.reserve(simHits.size()); measurements.reserve(simHits.size()); measurementParticlesMap.reserve(simHits.size()); measurementSimHitsMap.reserve(simHits.size()); @@ -292,15 +285,8 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( // The measurement container is unordered and the index under // which the measurement will be stored is known before adding it. Index measurementIdx = measurements.size(); - IndexSourceLink sourceLink{moduleGeoId, measurementIdx}; - - // Add to output containers: - // index map and source link container are geometry-ordered. - // since the input is also geometry-ordered, new items can - // be added at the end. - sourceLinks.insert(sourceLinks.end(), sourceLink); - createMeasurement(measurements, dParameters, sourceLink); + createMeasurement(measurements, moduleGeoId, dParameters); clusters.emplace_back(std::move(dParameters.cluster)); // this digitization does hit merging so there can be more than // one mapping entry for each digitized hit. @@ -328,7 +314,6 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( } if (m_cfg.doClusterization) { - m_sourceLinkWriteHandle(ctx, std::move(sourceLinks)); m_measurementWriteHandle(ctx, std::move(measurements)); m_clusterWriteHandle(ctx, std::move(clusters)); m_measurementParticlesMapWriteHandle(ctx, diff --git a/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp b/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp index e7a9e6b379e..9d5169ca6c9 100644 --- a/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp +++ b/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp @@ -10,6 +10,7 @@ #include "Acts/EventData/MeasurementHelpers.hpp" #include "Acts/EventData/SourceLink.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" @@ -18,10 +19,8 @@ #include ActsExamples::VariableBoundMeasurementProxy ActsExamples::createMeasurement( - MeasurementContainer& container, const DigitizedParameters& dParams, - const IndexSourceLink& isl) { - Acts::SourceLink sl{isl}; - + MeasurementContainer& container, Acts::GeometryIdentifier geometryId, + const DigitizedParameters& dParams) { if (dParams.indices.size() > 4u) { std::string errorMsg = "Invalid/mismatching measurement dimension: " + std::to_string(dParams.indices.size()); @@ -31,6 +30,6 @@ ActsExamples::VariableBoundMeasurementProxy ActsExamples::createMeasurement( return Acts::visit_measurement( dParams.indices.size(), [&](auto dim) -> VariableBoundMeasurementProxy { auto [indices, par, cov] = measurementConstituents(dParams); - return container.emplaceMeasurement(sl, indices, par, cov); + return container.emplaceMeasurement(geometryId, indices, par, cov); }); } diff --git a/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp b/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp index cb763ecc582..17df42bdb21 100644 --- a/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp +++ b/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp @@ -18,6 +18,7 @@ #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp" #include "ActsFatras/EventData/Barcode.hpp" +#include #include #include #include @@ -254,11 +255,10 @@ void ActsExamples::SensitiveSteppingAction::UserSteppingAction( buffer.back().momentum4After(), eventStore().particleHitCount.at(particleId) - 1); - assert(std::all_of(buffer.begin(), buffer.end(), - [&](const auto& h) { return h.geometryId() == geoId; })); - assert(std::all_of(buffer.begin(), buffer.end(), [&](const auto& h) { - return h.particleId() == particleId; - })); + assert(std::ranges::all_of( + buffer, [&](const auto& h) { return h.geometryId() == geoId; })); + assert(std::ranges::all_of( + buffer, [&](const auto& h) { return h.particleId() == particleId; })); eventStore().numberGeantSteps += buffer.size(); eventStore().maxStepsForHit = diff --git a/Examples/Algorithms/Geant4/src/SensitiveSurfaceMapper.cpp b/Examples/Algorithms/Geant4/src/SensitiveSurfaceMapper.cpp index ced44d89e94..2fd1a924d94 100644 --- a/Examples/Algorithms/Geant4/src/SensitiveSurfaceMapper.cpp +++ b/Examples/Algorithms/Geant4/src/SensitiveSurfaceMapper.cpp @@ -331,7 +331,7 @@ bool ActsExamples::SensitiveSurfaceMapper::checkMapping( if (writeMissingSurfacesAsObj) { Acts::ObjVisualization3D visualizer; Acts::ViewConfig vcfg; - vcfg.nSegments = 720; + vcfg.quarterSegments = 720; for (auto srf : missing) { Acts::GeometryView3D::drawSurface(visualizer, *srf, gctx, Acts::Transform3::Identity(), vcfg); diff --git a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp index 91de17b9eef..e19e785ac5c 100644 --- a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp +++ b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp @@ -8,6 +8,7 @@ #include "ActsExamples/Generators/Pythia8ProcessGenerator.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include "ActsExamples/EventData/SimVertex.hpp" #include "ActsFatras/EventData/Barcode.hpp" #include "ActsFatras/EventData/Particle.hpp" @@ -154,11 +155,10 @@ Pythia8Generator::operator()(RandomEngine& rng) { // check if an existing vertex is close enough auto it = - std::find_if(vertices.begin(), vertices.end(), - [&pos4, this](const SimVertex& other) { - return (pos4.head<3>() - other.position()).norm() < - m_cfg.spatialVertexThreshold; - }); + std::ranges::find_if(vertices, [&pos4, this](const SimVertex& v) { + return (pos4.head<3>() - v.position()).norm() < + m_cfg.spatialVertexThreshold; + }); if (it != vertices.end()) { particleId.setVertexSecondary(std::distance(vertices.begin(), it)); @@ -184,7 +184,7 @@ Pythia8Generator::operator()(RandomEngine& rng) { // normalization/ units are not import for the direction particle.setDirection(genParticle.px(), genParticle.py(), genParticle.pz()); particle.setAbsoluteMomentum( - std::hypot(genParticle.px(), genParticle.py(), genParticle.pz()) * + Acts::fastHypot(genParticle.px(), genParticle.py(), genParticle.pz()) * 1_GeV); particles.push_back(std::move(particle)); diff --git a/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MappingMaterialDecorator.hpp b/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MappingMaterialDecorator.hpp index 565b04c9ae3..b06e538885c 100644 --- a/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MappingMaterialDecorator.hpp +++ b/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MappingMaterialDecorator.hpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -93,14 +94,10 @@ class MappingMaterialDecorator : public IMaterialDecorator { /// /// @param volume to be looped onto void volumeLoop(const Acts::TrackingVolume* tVolume) { - auto sameId = - [tVolume]( - const std::pair>& pair) { - return (tVolume->geometryId() == pair.first); - }; - if (std::find_if(m_volumeMaterialMap.begin(), m_volumeMaterialMap.end(), - sameId) != m_volumeMaterialMap.end()) { + auto sameId = [tVolume](const auto& pair) { + return (tVolume->geometryId() == pair.first); + }; + if (std::ranges::any_of(m_volumeMaterialMap, sameId)) { // this volume was already visited return; } diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/GbtsSeedingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/GbtsSeedingAlgorithm.hpp index b931c72f979..b422eab9e85 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/GbtsSeedingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/GbtsSeedingAlgorithm.hpp @@ -44,8 +44,6 @@ class GbtsSeedingAlgorithm final : public IAlgorithm { std::vector geometrySelection; - std::string inputSourceLinks; - std::shared_ptr trackingGeometry; std::map, std::pair> ActsGbtsMap; @@ -88,9 +86,6 @@ class GbtsSeedingAlgorithm final : public IAlgorithm { WriteDataHandle m_outputSeeds{this, "OutputSeeds"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; - ReadDataHandle m_inputClusters{this, "InputClusters"}; }; diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/HoughTransformSeeder.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/HoughTransformSeeder.hpp index 6ea2400a712..b74047ca6a2 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/HoughTransformSeeder.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/HoughTransformSeeder.hpp @@ -76,7 +76,6 @@ #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/SimSpacePoint.hpp" @@ -168,8 +167,6 @@ class HoughTransformSeeder final : public IAlgorithm { std::string outputSeeds; /// Output hough track collection. std::string outputProtoTracks; - /// Input source links collection. - std::string inputSourceLinks; /// Tracking geometry required to access global-to-local transforms. std::shared_ptr trackingGeometry; /// For which part of the detector geometry should space points be created. @@ -281,9 +278,6 @@ class HoughTransformSeeder final : public IAlgorithm { ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; - //////////////////////////////////////////////////////////////////////// /// Convenience diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithm.hpp index 61042cd5d09..73bb81d5d7f 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithm.hpp @@ -100,8 +100,8 @@ class SeedingAlgorithm final : public IAlgorithm { Acts::SeedFinder> m_seedFinder; - std::unique_ptr> m_bottomBinFinder; - std::unique_ptr> m_topBinFinder; + std::unique_ptr> m_bottomBinFinder{nullptr}; + std::unique_ptr> m_topBinFinder{nullptr}; Config m_cfg; diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithmHashing.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithmHashing.hpp index 194ef4944a1..78a0ef2ae89 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithmHashing.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithmHashing.hpp @@ -135,8 +135,8 @@ class SeedingAlgorithmHashing final : public IAlgorithm { Acts::SeedFinder> m_seedFinder; - std::unique_ptr> m_bottomBinFinder; - std::unique_ptr> m_topBinFinder; + std::unique_ptr> m_bottomBinFinder{nullptr}; + std::unique_ptr> m_topBinFinder{nullptr}; Config m_cfg; diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SpacePointMaker.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SpacePointMaker.hpp index 0d5604e5ec4..db4678a6200 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SpacePointMaker.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SpacePointMaker.hpp @@ -11,7 +11,6 @@ #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/SpacePointFormation/SpacePointBuilder.hpp" #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimSpacePoint.hpp" #include "ActsExamples/Framework/DataHandle.hpp" @@ -46,8 +45,6 @@ struct AlgorithmContext; class SpacePointMaker final : public IAlgorithm { public: struct Config { - /// Input source links collection. - std::string inputSourceLinks; /// Input measurements collection. std::string inputMeasurements; /// Output space points collection. @@ -86,9 +83,6 @@ class SpacePointMaker final : public IAlgorithm { Acts::SpacePointBuilder m_spacePointBuilder; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; - ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index 999bf7ef7fe..c56e9508c9b 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -8,12 +8,10 @@ #pragma once -#include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackProxy.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" -#include "Acts/EventData/VectorTrackContainer.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" #include "Acts/TrackFinding/MeasurementSelector.hpp" @@ -28,11 +26,9 @@ #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" -#include "ActsExamples/MagneticField/MagneticField.hpp" #include #include -#include #include #include #include @@ -84,8 +80,6 @@ class TrackFindingAlgorithm final : public IAlgorithm { struct Config { /// Input measurements collection. std::string inputMeasurements; - /// Input source links collection. - std::string inputSourceLinks; /// Input initial track parameter estimates for for each proto track. std::string inputInitialTrackParameters; /// Input seeds. These are optional and allow for seed deduplication. @@ -130,12 +124,17 @@ class TrackFindingAlgorithm final : public IAlgorithm { bool trimTracks = true; // Pixel and strip volume ids to be used for maxPixel/StripHoles cuts - std::set pixelVolumes; - std::set stripVolumes; + std::vector pixelVolumeIds; + std::vector stripVolumeIds; - /// additional track selector settings + // additional track selector settings std::size_t maxPixelHoles = std::numeric_limits::max(); std::size_t maxStripHoles = std::numeric_limits::max(); + + /// The volume ids to constrain the track finding to + std::vector constrainToVolumeIds; + /// The volume ids to stop the track finding at + std::vector endOfWorldVolumeIds; }; /// Constructor of the track finding algorithm @@ -155,9 +154,8 @@ class TrackFindingAlgorithm final : public IAlgorithm { const Config& config() const { return m_cfg; } private: - template - void computeSharedHits(const source_link_accessor_container_t& sourceLinks, - TrackContainer& tracks) const; + void computeSharedHits(TrackContainer& tracks, + const MeasurementContainer& measurements) const; ActsExamples::ProcessCode finalize() override; @@ -167,8 +165,6 @@ class TrackFindingAlgorithm final : public IAlgorithm { ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; ReadDataHandle m_inputInitialTrackParameters{ this, "InputInitialTrackParameters"}; ReadDataHandle m_inputSeeds{this, "InputSeeds"}; @@ -183,6 +179,7 @@ class TrackFindingAlgorithm final : public IAlgorithm { mutable std::atomic m_nFoundTracks{0}; mutable std::atomic m_nSelectedTracks{0}; mutable std::atomic m_nStoppedBranches{0}; + mutable std::atomic m_nSkippedSecondPass{0}; mutable tbb::combinable m_memoryStatistics{[]() { @@ -191,56 +188,4 @@ class TrackFindingAlgorithm final : public IAlgorithm { }}; }; -// TODO this is somewhat duplicated in AmbiguityResolutionAlgorithm.cpp -// TODO we should make a common implementation in the core at some point -template -void TrackFindingAlgorithm::computeSharedHits( - const source_link_accessor_container_t& sourceLinks, - TrackContainer& tracks) const { - // Compute shared hits from all the reconstructed tracks - // Compute nSharedhits and Update ckf results - // hit index -> list of multi traj indexes [traj, meas] - - std::vector firstTrackOnTheHit( - sourceLinks.size(), std::numeric_limits::max()); - std::vector firstStateOnTheHit( - sourceLinks.size(), std::numeric_limits::max()); - - for (auto track : tracks) { - for (auto state : track.trackStatesReversed()) { - if (!state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) { - continue; - } - - std::size_t hitIndex = state.getUncalibratedSourceLink() - .template get() - .index(); - - // Check if hit not already used - if (firstTrackOnTheHit.at(hitIndex) == - std::numeric_limits::max()) { - firstTrackOnTheHit.at(hitIndex) = track.index(); - firstStateOnTheHit.at(hitIndex) = state.index(); - continue; - } - - // if already used, control if first track state has been marked - // as shared - int indexFirstTrack = firstTrackOnTheHit.at(hitIndex); - int indexFirstState = firstStateOnTheHit.at(hitIndex); - - auto firstState = tracks.getTrack(indexFirstTrack) - .container() - .trackStateContainer() - .getTrackState(indexFirstState); - if (!firstState.typeFlags().test(Acts::TrackStateFlag::SharedHitFlag)) { - firstState.typeFlags().set(Acts::TrackStateFlag::SharedHitFlag); - } - - // Decorate this track state - state.typeFlags().set(Acts::TrackStateFlag::SharedHitFlag); - } - } -} - } // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFinding/src/GbtsSeedingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/GbtsSeedingAlgorithm.cpp index 835b027862f..385a3db7ec1 100644 --- a/Examples/Algorithms/TrackFinding/src/GbtsSeedingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/GbtsSeedingAlgorithm.cpp @@ -61,8 +61,6 @@ ActsExamples::GbtsSeedingAlgorithm::GbtsSeedingAlgorithm( m_outputSeeds.initialize(m_cfg.outputSeeds); - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); - m_inputClusters.initialize(m_cfg.inputClusters); // map diff --git a/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp b/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp index 26559797158..c8126773194 100644 --- a/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp +++ b/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp @@ -15,6 +15,7 @@ #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/MathHelpers.hpp" #include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" @@ -75,13 +76,7 @@ ActsExamples::HoughTransformSeeder::HoughTransformSeeder( "HoughTransformSeeder: Missing hough track seeds output collection"); } - if (m_cfg.inputSourceLinks.empty()) { - throw std::invalid_argument( - "HoughTransformSeeder: Missing source link input collection"); - } - m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_inputMeasurements.initialize(m_cfg.inputMeasurements); if (!m_cfg.trackingGeometry) { @@ -478,7 +473,7 @@ void ActsExamples::HoughTransformSeeder::addSpacePoints( ACTS_DEBUG("Inserting " << spContainer.size() << " space points from " << isp->key()); for (auto& sp : spContainer) { - double r = std::hypot(sp.x(), sp.y()); + double r = Acts::fastHypot(sp.x(), sp.y()); double z = sp.z(); float phi = std::atan2(sp.y(), sp.x()); ResultUnsigned hitlayer = m_cfg.layerIDFinder(r).value(); @@ -502,14 +497,14 @@ void ActsExamples::HoughTransformSeeder::addSpacePoints( void ActsExamples::HoughTransformSeeder::addMeasurements( const AlgorithmContext& ctx) const { const auto& measurements = m_inputMeasurements(ctx); - const auto& sourceLinks = m_inputSourceLinks(ctx); ACTS_DEBUG("Inserting " << measurements.size() << " space points from " << m_cfg.inputMeasurements); for (Acts::GeometryIdentifier geoId : m_cfg.geometrySelection) { // select volume/layer depending on what is set in the geometry id - auto range = selectLowestNonZeroGeometryObject(sourceLinks, geoId); + auto range = + selectLowestNonZeroGeometryObject(measurements.orderedIndices(), geoId); // groupByModule only works with geometry containers, not with an // arbitrary range. do the equivalent grouping manually auto groupedByModule = makeGroupBy(range, detail::GeometryIdGetter()); @@ -548,7 +543,7 @@ void ActsExamples::HoughTransformSeeder::addMeasurements( Acts::Vector3 globalFakeMom(1, 1, 1); Acts::Vector3 globalPos = surface->localToGlobal(ctx.geoContext, localPos, globalFakeMom); - double r = std::hypot(globalPos[Acts::ePos0], globalPos[Acts::ePos1]); + double r = globalPos.head<2>().norm(); double phi = std::atan2(globalPos[Acts::ePos1], globalPos[Acts::ePos0]); double z = globalPos[Acts::ePos2]; ResultUnsigned hitlayer = m_cfg.layerIDFinder(r); diff --git a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp index 40111376a9c..ed5b11e4df2 100644 --- a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/EventData/Seed.hpp" #include "Acts/EventData/SpacePointData.hpp" -#include "Acts/Geometry/Extent.hpp" #include "Acts/Seeding/BinnedGroup.hpp" #include "Acts/Seeding/SeedFilter.hpp" #include "Acts/Utilities/BinningType.hpp" @@ -191,10 +190,10 @@ ActsExamples::SeedingAlgorithm::SeedingAlgorithm( ActsExamples::SpacePointContainer>, Acts::detail::RefHolder>::SpacePointProxyType; - m_bottomBinFinder = std::make_unique>( - m_cfg.numPhiNeighbors, cfg.zBinNeighborsBottom); - m_topBinFinder = std::make_unique>( - m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsTop); + m_bottomBinFinder = std::make_unique>( + m_cfg.numPhiNeighbors, cfg.zBinNeighborsBottom, 0); + m_topBinFinder = std::make_unique>( + m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsTop, 0); m_cfg.seedFinderConfig.seedFilter = std::make_unique>( @@ -243,33 +242,41 @@ ActsExamples::ProcessCode ActsExamples::SeedingAlgorithm::execute( using value_type = typename decltype(spContainer)::SpacePointProxyType; using seed_type = Acts::Seed; - // extent used to store r range for middle spacepoint - Acts::Extent rRangeSPExtent; - Acts::CylindricalSpacePointGrid grid = Acts::CylindricalSpacePointGridCreator::createGrid( m_cfg.gridConfig, m_cfg.gridOptions); - Acts::CylindricalSpacePointGridCreator::fillGrid( - m_cfg.seedFinderConfig, m_cfg.seedFinderOptions, grid, - spContainer.begin(), spContainer.end(), rRangeSPExtent); + Acts::CylindricalSpacePointGridCreator::fillGrid( + m_cfg.seedFinderConfig, m_cfg.seedFinderOptions, grid, spContainer); + + // Compute radius Range + // we rely on the fact the grid is storing the proxies + // with a sorting in the radius + float minRange = std::numeric_limits::max(); + float maxRange = std::numeric_limits::lowest(); + for (const auto& coll : grid) { + if (coll.empty()) { + continue; + } + const auto* firstEl = coll.front(); + const auto* lastEl = coll.back(); + minRange = std::min(firstEl->radius(), minRange); + maxRange = std::max(lastEl->radius(), maxRange); + } - std::array, 2ul> navigation; + std::array, 3ul> navigation; navigation[1ul] = m_cfg.seedFinderConfig.zBinsCustomLooping; auto spacePointsGrouping = Acts::CylindricalBinnedGroup( std::move(grid), *m_bottomBinFinder, *m_topBinFinder, std::move(navigation)); - // safely clamp double to float - float up = Acts::clampValue( - std::floor(rRangeSPExtent.max(Acts::BinningValue::binR) / 2) * 2); - /// variable middle SP radial region of interest const Acts::Range1D rMiddleSPRange( - std::floor(rRangeSPExtent.min(Acts::BinningValue::binR) / 2) * 2 + + std::floor(minRange / 2) * 2 + m_cfg.seedFinderConfig.deltaRMiddleMinSPRange, - up - m_cfg.seedFinderConfig.deltaRMiddleMaxSPRange); + std::floor(maxRange / 2) * 2 - + m_cfg.seedFinderConfig.deltaRMiddleMaxSPRange); // run the seeding static thread_local std::vector seeds; diff --git a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithmHashing.cpp b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithmHashing.cpp index ec664aef437..c722a9e5a2b 100644 --- a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithmHashing.cpp +++ b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithmHashing.cpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/EventData/Seed.hpp" #include "Acts/EventData/SpacePointData.hpp" -#include "Acts/Geometry/Extent.hpp" #include "Acts/Plugins/Hashing/HashingAlgorithm.hpp" #include "Acts/Plugins/Hashing/HashingTraining.hpp" #include "Acts/Seeding/BinnedGroup.hpp" @@ -175,10 +174,10 @@ ActsExamples::SeedingAlgorithmHashing::SeedingAlgorithmHashing( m_cfg.seedFinderConfig.experimentCuts.connect(); } - m_bottomBinFinder = std::make_unique>( - m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsBottom); - m_topBinFinder = std::make_unique>( - m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsTop); + m_bottomBinFinder = std::make_unique>( + m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsBottom, 0); + m_topBinFinder = std::make_unique>( + m_cfg.numPhiNeighbors, m_cfg.zBinNeighborsTop, 0); m_cfg.seedFinderConfig.seedFilter = std::make_unique>( @@ -269,17 +268,30 @@ ActsExamples::ProcessCode ActsExamples::SeedingAlgorithmHashing::execute( Acts::SpacePointContainer spContainer(spConfig, spOptions, container); - // extent used to store r range for middle spacepoint - Acts::Extent rRangeSPExtent; // construct the seeding tools Acts::CylindricalSpacePointGrid grid = Acts::CylindricalSpacePointGridCreator::createGrid( m_cfg.gridConfig, m_cfg.gridOptions); Acts::CylindricalSpacePointGridCreator::fillGrid( m_cfg.seedFinderConfig, m_cfg.seedFinderOptions, grid, - spContainer.begin(), spContainer.end(), rRangeSPExtent); + spContainer.begin(), spContainer.end()); + + // Compute radius Range + // we rely on the fact the grid is storing the proxies + // with a sorting in the radius + float minRange = std::numeric_limits::max(); + float maxRange = std::numeric_limits::lowest(); + for (const auto& coll : grid) { + if (coll.empty()) { + continue; + } + const auto* firstEl = coll.front(); + const auto* lastEl = coll.back(); + minRange = std::min(firstEl->radius(), minRange); + maxRange = std::max(lastEl->radius(), maxRange); + } - std::array, 2ul> navigation; + std::array, 3ul> navigation; navigation[1ul] = m_cfg.seedFinderConfig.zBinsCustomLooping; // groups spacepoints @@ -287,15 +299,10 @@ ActsExamples::ProcessCode ActsExamples::SeedingAlgorithmHashing::execute( std::move(grid), *m_bottomBinFinder, *m_topBinFinder, std::move(navigation)); - // safely clamp double to float - float up = Acts::clampValue( - std::floor(rRangeSPExtent.max(Acts::BinningValue::binR) / 2) * 2); - /// variable middle SP radial region of interest const Acts::Range1D rMiddleSPRange( - std::floor(rRangeSPExtent.min(Acts::BinningValue::binR) / 2) * 2 + - m_cfg.seedFinderConfig.deltaRMiddleMinSPRange, - up - m_cfg.seedFinderConfig.deltaRMiddleMaxSPRange); + minRange + m_cfg.seedFinderConfig.deltaRMiddleMinSPRange, + maxRange - m_cfg.seedFinderConfig.deltaRMiddleMaxSPRange); // this creates seeds of proxy, we need to convert it to seed of space // points diff --git a/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp b/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp index e6fed997a02..673ffa8b103 100644 --- a/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp +++ b/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp @@ -32,9 +32,6 @@ ActsExamples::SpacePointMaker::SpacePointMaker(Config cfg, Acts::Logging::Level lvl) : IAlgorithm("SpacePointMaker", lvl), m_cfg(std::move(cfg)) { - if (m_cfg.inputSourceLinks.empty()) { - throw std::invalid_argument("Missing source link input collection"); - } if (m_cfg.inputMeasurements.empty()) { throw std::invalid_argument("Missing measurement input collection"); } @@ -48,7 +45,6 @@ ActsExamples::SpacePointMaker::SpacePointMaker(Config cfg, throw std::invalid_argument("Missing space point maker geometry selection"); } - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_inputMeasurements.initialize(m_cfg.inputMeasurements); m_outputSpacePoints.initialize(m_cfg.outputSpacePoints); @@ -119,7 +115,6 @@ ActsExamples::SpacePointMaker::SpacePointMaker(Config cfg, ActsExamples::ProcessCode ActsExamples::SpacePointMaker::execute( const AlgorithmContext& ctx) const { - const auto& sourceLinks = m_inputSourceLinks(ctx); const auto& measurements = m_inputMeasurements(ctx); // TODO Support strip measurements @@ -136,7 +131,8 @@ ActsExamples::ProcessCode ActsExamples::SpacePointMaker::execute( SimSpacePointContainer spacePoints; for (Acts::GeometryIdentifier geoId : m_cfg.geometrySelection) { // select volume/layer depending on what is set in the geometry id - auto range = selectLowestNonZeroGeometryObject(sourceLinks, geoId); + auto range = + selectLowestNonZeroGeometryObject(measurements.orderedIndices(), geoId); // groupByModule only works with geometry containers, not with an // arbitrary range. do the equivalent grouping manually auto groupedByModule = makeGroupBy(range, detail::GeometryIdGetter()); diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index 0a5ae601d20..f2a5104e2b0 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -28,8 +28,6 @@ #include "Acts/Surfaces/Surface.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" -#include "Acts/TrackFitting/KalmanFitter.hpp" -#include "Acts/Utilities/Delegate.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/TrackHelpers.hpp" @@ -177,13 +175,13 @@ class BranchStopper { using BranchStopperResult = Acts::CombinatorialKalmanFilterBranchStopperResult; - struct BrachState { + struct BranchState { std::size_t nPixelHoles = 0; std::size_t nStripHoles = 0; }; - static constexpr Acts::ProxyAccessor branchStateAccessor = - Acts::ProxyAccessor(Acts::hashString("MyBranchState")); + static constexpr Acts::ProxyAccessor branchStateAccessor = + Acts::ProxyAccessor(Acts::hashString("MyBranchState")); mutable std::atomic m_nStoppedBranches{0}; @@ -217,16 +215,18 @@ class BranchStopper { } bool tooManyHolesPS = false; - if (!(m_cfg.pixelVolumes.empty() && m_cfg.stripVolumes.empty())) { + if (!(m_cfg.pixelVolumeIds.empty() && m_cfg.stripVolumeIds.empty())) { auto& branchState = branchStateAccessor(track); // count both holes and outliers as holes for pixel/strip counts if (trackState.typeFlags().test(Acts::TrackStateFlag::HoleFlag) || trackState.typeFlags().test(Acts::TrackStateFlag::OutlierFlag)) { - if (m_cfg.pixelVolumes.contains( - trackState.referenceSurface().geometryId().volume())) { + auto volumeId = trackState.referenceSurface().geometryId().volume(); + if (std::find(m_cfg.pixelVolumeIds.begin(), m_cfg.pixelVolumeIds.end(), + volumeId) != m_cfg.pixelVolumeIds.end()) { ++branchState.nPixelHoles; - } else if (m_cfg.stripVolumes.contains( - trackState.referenceSurface().geometryId().volume())) { + } else if (std::find(m_cfg.stripVolumeIds.begin(), + m_cfg.stripVolumeIds.end(), + volumeId) != m_cfg.stripVolumeIds.end()) { ++branchState.nStripHoles; } } @@ -263,9 +263,6 @@ TrackFindingAlgorithm::TrackFindingAlgorithm(Config config, if (m_cfg.inputMeasurements.empty()) { throw std::invalid_argument("Missing measurements input collection"); } - if (m_cfg.inputSourceLinks.empty()) { - throw std::invalid_argument("Missing source links input collection"); - } if (m_cfg.inputInitialTrackParameters.empty()) { throw std::invalid_argument( "Missing initial track parameters input collection"); @@ -294,7 +291,6 @@ TrackFindingAlgorithm::TrackFindingAlgorithm(Config config, } m_inputMeasurements.initialize(m_cfg.inputMeasurements); - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_inputInitialTrackParameters.initialize(m_cfg.inputInitialTrackParameters); m_inputSeeds.maybeInitialize(m_cfg.inputSeeds); m_outputTracks.initialize(m_cfg.outputTracks); @@ -303,7 +299,6 @@ TrackFindingAlgorithm::TrackFindingAlgorithm(Config config, ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { // Read input data const auto& measurements = m_inputMeasurements(ctx); - const auto& sourceLinks = m_inputSourceLinks(ctx); const auto& initialParameters = m_inputInitialTrackParameters(ctx); const SimSeedContainer* seeds = nullptr; @@ -340,7 +335,7 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { extensions.branchStopper.connect<&BranchStopper::operator()>(&branchStopper); IndexSourceLinkAccessor slAccessor; - slAccessor.container = &sourceLinks; + slAccessor.container = &measurements.orderedIndices(); Acts::SourceLinkAccessorDelegate slAccessorDelegate; slAccessorDelegate.connect<&IndexSourceLinkAccessor::range>(&slAccessor); @@ -350,11 +345,15 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { firstPropOptions.maxSteps = m_cfg.maxSteps; firstPropOptions.direction = m_cfg.reverseSearch ? Acts::Direction::Backward : Acts::Direction::Forward; + firstPropOptions.constrainToVolumeIds = m_cfg.constrainToVolumeIds; + firstPropOptions.endOfWorldVolumeIds = m_cfg.endOfWorldVolumeIds; Acts::PropagatorPlainOptions secondPropOptions(ctx.geoContext, ctx.magFieldContext); secondPropOptions.maxSteps = m_cfg.maxSteps; secondPropOptions.direction = firstPropOptions.direction.invert(); + secondPropOptions.constrainToVolumeIds = m_cfg.constrainToVolumeIds; + secondPropOptions.endOfWorldVolumeIds = m_cfg.endOfWorldVolumeIds; // Set the CombinatorialKalmanFilter options TrackFinderOptions firstOptions(ctx.geoContext, ctx.magFieldContext, @@ -379,6 +378,8 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { logger().cloneWithSuffix("Propagator")); ExtrapolatorOptions extrapolationOptions(ctx.geoContext, ctx.magFieldContext); + extrapolationOptions.constrainToVolumeIds = m_cfg.constrainToVolumeIds; + extrapolationOptions.endOfWorldVolumeIds = m_cfg.endOfWorldVolumeIds; // Perform the track finding for all initial parameters ACTS_DEBUG("Invoke track finding with " << initialParameters.size() @@ -395,8 +396,8 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { TrackContainer tracksTemp(trackContainerTemp, trackStateContainerTemp); // Note that not all backends support PODs as column types - tracks.addColumn("MyBranchState"); - tracksTemp.addColumn("MyBranchState"); + tracks.addColumn("MyBranchState"); + tracksTemp.addColumn("MyBranchState"); tracks.addColumn("trackGroup"); tracksTemp.addColumn("trackGroup"); @@ -531,6 +532,15 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { Acts::BoundTrackParameters secondInitialParameters = trackCandidate.createParametersFromState(firstMeasurement); + if (!secondInitialParameters.referenceSurface().insideBounds( + secondInitialParameters.localPosition())) { + m_nSkippedSecondPass++; + ACTS_DEBUG( + "Smoothing of first pass fit produced out-of-bounds parameters " + "relative to the surface. Skipping second pass."); + continue; + } + auto secondRootBranch = tracksTemp.makeTrack(); secondRootBranch.copyFrom(trackCandidate, false); auto secondResult = @@ -643,7 +653,7 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { // Compute shared hits from all the reconstructed tracks if (m_cfg.computeSharedHits) { - computeSharedHits(sourceLinks, tracks); + computeSharedHits(tracks, measurements); } ACTS_DEBUG("Finalized track finding with " << tracks.size() @@ -680,6 +690,7 @@ ProcessCode TrackFindingAlgorithm::finalize() { ACTS_INFO("- found tracks: " << m_nFoundTracks); ACTS_INFO("- selected tracks: " << m_nSelectedTracks); ACTS_INFO("- stopped branches: " << m_nStoppedBranches); + ACTS_INFO("- skipped second pass: " << m_nSkippedSecondPass); auto memoryStatistics = m_memoryStatistics.combine([](const auto& a, const auto& b) { @@ -693,4 +704,54 @@ ProcessCode TrackFindingAlgorithm::finalize() { return ProcessCode::SUCCESS; } +// TODO this is somewhat duplicated in AmbiguityResolutionAlgorithm.cpp +// TODO we should make a common implementation in the core at some point +void TrackFindingAlgorithm::computeSharedHits( + TrackContainer& tracks, const MeasurementContainer& measurements) const { + // Compute shared hits from all the reconstructed tracks + // Compute nSharedhits and Update ckf results + // hit index -> list of multi traj indexes [traj, meas] + + std::vector firstTrackOnTheHit( + measurements.size(), std::numeric_limits::max()); + std::vector firstStateOnTheHit( + measurements.size(), std::numeric_limits::max()); + + for (auto track : tracks) { + for (auto state : track.trackStatesReversed()) { + if (!state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) { + continue; + } + + std::size_t hitIndex = state.getUncalibratedSourceLink() + .template get() + .index(); + + // Check if hit not already used + if (firstTrackOnTheHit.at(hitIndex) == + std::numeric_limits::max()) { + firstTrackOnTheHit.at(hitIndex) = track.index(); + firstStateOnTheHit.at(hitIndex) = state.index(); + continue; + } + + // if already used, control if first track state has been marked + // as shared + int indexFirstTrack = firstTrackOnTheHit.at(hitIndex); + int indexFirstState = firstStateOnTheHit.at(hitIndex); + + auto firstState = tracks.getTrack(indexFirstTrack) + .container() + .trackStateContainer() + .getTrackState(indexFirstState); + if (!firstState.typeFlags().test(Acts::TrackStateFlag::SharedHitFlag)) { + firstState.typeFlags().set(Acts::TrackStateFlag::SharedHitFlag); + } + + // Decorate this track state + state.typeFlags().set(Acts::TrackStateFlag::SharedHitFlag); + } + } +} + } // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp index aaed21134d3..65e0cf41970 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp @@ -6,26 +6,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Definitions/Direction.hpp" -#include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackContainer.hpp" -#include "Acts/EventData/TrackStatePropMask.hpp" -#include "Acts/EventData/VectorMultiTrajectory.hpp" -#include "Acts/EventData/VectorTrackContainer.hpp" #include "Acts/Propagator/Navigator.hpp" #include "Acts/Propagator/Propagator.hpp" #include "Acts/Propagator/SympyStepper.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" -#include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp" -#include -#include #include #include -#include namespace Acts { class MagneticFieldProvider; @@ -53,7 +44,7 @@ struct TrackFinderFunctionImpl ActsExamples::TrackProxy rootBranch) const override { return trackFinder.findTracks(initialParameters, options, tracks, rootBranch); - }; + } }; } // namespace diff --git a/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp index c5dbc8bb625..c8a30c7147d 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp @@ -10,12 +10,9 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/ParticleHypothesis.hpp" #include "Acts/EventData/Seed.hpp" -#include "Acts/EventData/SourceLink.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/MagneticField/MagneticFieldProvider.hpp" #include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Result.hpp" @@ -35,53 +32,9 @@ namespace ActsExamples { -namespace { - -Acts::BoundSquareMatrix makeInitialCovariance( - const TrackParamsEstimationAlgorithm::Config& config, - const Acts::BoundVector& params, const SimSpacePoint& sp) { - Acts::BoundSquareMatrix result = Acts::BoundSquareMatrix::Zero(); - - for (std::size_t i = Acts::eBoundLoc0; i < Acts::eBoundSize; ++i) { - double sigma = config.initialSigmas[i]; - double variance = sigma * sigma; - - if (i == Acts::eBoundQOverP) { - // note that we rely on the fact that sigma theta is already computed - double varianceTheta = result(Acts::eBoundTheta, Acts::eBoundTheta); - - // transverse momentum contribution - variance += - std::pow(config.initialSigmaPtRel * params[Acts::eBoundQOverP], 2); - - // theta contribution - variance += - varianceTheta * std::pow(params[Acts::eBoundQOverP] * - std::tan(params[Acts::eBoundTheta]), - 2); - } - - // Inflate the time uncertainty if no time measurement is available - if (i == Acts::eBoundTime && !sp.t().has_value()) { - variance *= config.noTimeVarInflation; - } - - // Inflate the initial covariance - variance *= config.initialVarInflation[i]; - - result(i, i) = variance; - } - - return result; -} - -} // namespace - -ActsExamples::TrackParamsEstimationAlgorithm::TrackParamsEstimationAlgorithm( - ActsExamples::TrackParamsEstimationAlgorithm::Config cfg, - Acts::Logging::Level lvl) - : ActsExamples::IAlgorithm("TrackParamsEstimationAlgorithm", lvl), - m_cfg(std::move(cfg)) { +TrackParamsEstimationAlgorithm::TrackParamsEstimationAlgorithm( + TrackParamsEstimationAlgorithm::Config cfg, Acts::Logging::Level lvl) + : IAlgorithm("TrackParamsEstimationAlgorithm", lvl), m_cfg(std::move(cfg)) { if (m_cfg.inputSeeds.empty()) { throw std::invalid_argument("Missing seeds input collection"); } @@ -103,8 +56,8 @@ ActsExamples::TrackParamsEstimationAlgorithm::TrackParamsEstimationAlgorithm( m_outputTracks.maybeInitialize(m_cfg.outputProtoTracks); } -ActsExamples::ProcessCode ActsExamples::TrackParamsEstimationAlgorithm::execute( - const ActsExamples::AlgorithmContext& ctx) const { +ProcessCode TrackParamsEstimationAlgorithm::execute( + const AlgorithmContext& ctx) const { auto const& seeds = m_inputSeeds(ctx); ACTS_VERBOSE("Read " << seeds.size() << " seeds"); @@ -171,8 +124,15 @@ ActsExamples::ProcessCode ActsExamples::TrackParamsEstimationAlgorithm::execute( const auto& params = optParams.value(); - Acts::BoundSquareMatrix cov = - makeInitialCovariance(m_cfg, params, *bottomSP); + Acts::EstimateTrackParamCovarianceConfig config{ + .initialSigmas = + Eigen::Map{m_cfg.initialSigmas.data()}, + .initialSigmaPtRel = m_cfg.initialSigmaPtRel, + .initialVarInflation = Eigen::Map{ + m_cfg.initialVarInflation.data()}}; + + Acts::BoundSquareMatrix cov = Acts::estimateTrackParamCovariance( + config, params, bottomSP->t().has_value()); trackParameters.emplace_back(surface->getSharedPtr(), params, cov, m_cfg.particleHypothesis); @@ -197,4 +157,5 @@ ActsExamples::ProcessCode ActsExamples::TrackParamsEstimationAlgorithm::execute( return ProcessCode::SUCCESS; } + } // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp index 91b3ce43cdb..abfc29b462c 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp @@ -16,7 +16,6 @@ #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/Utilities/Zip.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/Trajectories.hpp" @@ -44,9 +43,6 @@ class TrackFindingFromPrototrackAlgorithm final : public IAlgorithm { /// Input measurements std::string inputMeasurements; - /// Input source links - std::string inputSourceLinks; - /// Input track parameters std::string inputInitialTrackParameters; @@ -98,8 +94,6 @@ class TrackFindingFromPrototrackAlgorithm final : public IAlgorithm { "InputProtoTracks"}; ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; ReadDataHandle m_inputInitialTrackParameters{ this, "InputInitialTrackParameters"}; diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp index 06fcb1c9438..05fe35a6ea0 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp @@ -19,6 +19,7 @@ #include "ActsExamples/EventData/SimSpacePoint.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" +#include #include using namespace ActsExamples; @@ -97,8 +98,8 @@ ActsExamples::TrackFindingAlgorithmExaTrkX::TrackFindingAlgorithmExaTrkX( NodeFeature::eClusterX, NodeFeature::eClusterY, NodeFeature::eCellCount, NodeFeature::eCellSum, NodeFeature::eCluster1R, NodeFeature::eCluster2R}; - auto wantClFeatures = std::any_of( - m_cfg.nodeFeatures.begin(), m_cfg.nodeFeatures.end(), + auto wantClFeatures = std::ranges::any_of( + m_cfg.nodeFeatures, [&](const auto& f) { return Acts::rangeContainsValue(clFeatures, f); }); if (wantClFeatures && !m_inputClusters.isInitialized()) { diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp index 4eef98a6f83..bf28b1743ba 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp @@ -48,6 +48,7 @@ struct ProtoTrackSourceLinkAccessor return {Iterator{begin}, Iterator{end}}; } }; + } // namespace namespace ActsExamples { @@ -58,14 +59,12 @@ TrackFindingFromPrototrackAlgorithm::TrackFindingFromPrototrackAlgorithm( m_inputInitialTrackParameters.initialize(m_cfg.inputInitialTrackParameters); m_inputMeasurements.initialize(m_cfg.inputMeasurements); m_inputProtoTracks.initialize(m_cfg.inputProtoTracks); - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_outputTracks.initialize(m_cfg.outputTracks); } ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( const ActsExamples::AlgorithmContext& ctx) const { const auto& measurements = m_inputMeasurements(ctx); - const auto& sourceLinks = m_inputSourceLinks(ctx); const auto& protoTracks = m_inputProtoTracks(ctx); const auto& initialParameters = m_inputInitialTrackParameters(ctx); @@ -98,7 +97,7 @@ ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( // The source link accessor ProtoTrackSourceLinkAccessor sourceLinkAccessor; sourceLinkAccessor.loggerPtr = logger().clone("SourceLinkAccessor"); - sourceLinkAccessor.container = &sourceLinks; + sourceLinkAccessor.container = &measurements.orderedIndices(); Acts::SourceLinkAccessorDelegate slAccessorDelegate; @@ -133,7 +132,8 @@ ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( // Fill the source links via their indices from the container for (const auto hitIndex : protoTracks.at(i)) { - if (auto it = sourceLinks.nth(hitIndex); it != sourceLinks.end()) { + if (auto it = measurements.orderedIndices().nth(hitIndex); + it != measurements.orderedIndices().end()) { sourceLinkAccessor.protoTrackSourceLinks.insert(*it); } else { ACTS_FATAL("Proto track " << i << " contains invalid hit index" @@ -177,7 +177,7 @@ ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( // once this is done. // Compute shared hits from all the reconstructed tracks if // (m_cfg.computeSharedHits) { - // computeSharedHits(sourceLinks, tracks); + // computeSharedHits(measurements, tracks); // } ACTS_INFO("Event " << ctx.eventNumber << ": " << nFailed << " / " << nSeed diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp index 3c3a62c4c4c..934203aeeaa 100644 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp @@ -72,8 +72,6 @@ class TrackFittingAlgorithm final : public IAlgorithm { ReadDataHandle m_inputMeasurements{this, "InputMeasurements"}; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; ReadDataHandle m_inputProtoTracks{this, "InputProtoTracks"}; ReadDataHandle m_inputInitialTrackParameters{ diff --git a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp index 49ed2c285cd..50360e3d3b4 100644 --- a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp @@ -18,6 +18,7 @@ #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Result.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" @@ -126,10 +127,11 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingAlgorithm::execute( trackSourceLinks.reserve(protoTrack.size()); // Fill the source links via their indices from the container - for (auto hitIndex : protoTrack) { + for (auto measIndex : protoTrack) { ConstVariableBoundMeasurementProxy measurement = - measurements.getMeasurement(hitIndex); - trackSourceLinks.push_back(measurement.sourceLink()); + measurements.getMeasurement(measIndex); + IndexSourceLink sourceLink(measurement.geometryId(), measIndex); + trackSourceLinks.push_back(Acts::SourceLink(sourceLink)); } ACTS_DEBUG("Invoke direct fitter for track " << itrack); diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp index d9248358160..5fa3ee0e4b2 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp @@ -29,9 +29,16 @@ ActsExamples::ParticleSelector::ParticleSelector(const Config& config, if (m_cfg.outputParticles.empty()) { throw std::invalid_argument("Missing output particles collection"); } + if (!m_cfg.outputParticlesFinal.empty() && + m_cfg.inputParticlesFinal.empty()) { + throw std::invalid_argument( + "Output final particles collection requires input final particles"); + } m_inputParticles.initialize(m_cfg.inputParticles); + m_inputParticlesFinal.maybeInitialize(m_cfg.inputParticlesFinal); m_outputParticles.initialize(m_cfg.outputParticles); + m_outputParticlesFinal.maybeInitialize(m_cfg.outputParticlesFinal); ACTS_DEBUG("selection particle rho [" << m_cfg.rhoMin << "," << m_cfg.rhoMax << ")"); @@ -52,29 +59,15 @@ ActsExamples::ParticleSelector::ParticleSelector(const Config& config, ACTS_DEBUG("remove charged particles " << m_cfg.removeCharged); ACTS_DEBUG("remove neutral particles " << m_cfg.removeNeutral); ACTS_DEBUG("remove secondary particles " << m_cfg.removeSecondaries); - - // We only initialize this if we actually select on this - if (m_cfg.measurementsMin > 0 || - m_cfg.measurementsMax < std::numeric_limits::max()) { - m_inputMap.initialize(m_cfg.inputMeasurementParticlesMap); - ACTS_DEBUG("selection particle number of measurements [" - << m_cfg.measurementsMin << "," << m_cfg.measurementsMax << ")"); - } } ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( const AlgorithmContext& ctx) const { - using ParticlesMeasurmentMap = - boost::container::flat_multimap; - // prepare input/ output types - const auto& inputParticles = m_inputParticles(ctx); - - // Make global particles measurement map if necessary - std::optional particlesMeasMap; - if (m_inputMap.isInitialized()) { - particlesMeasMap = invertIndexMultimap(m_inputMap(ctx)); - } + const SimParticleContainer& inputParticles = m_inputParticles(ctx); + const SimParticleContainer& inputParticlesFinal = + (m_inputParticlesFinal.isInitialized()) ? m_inputParticlesFinal(ctx) + : inputParticles; std::size_t nInvalidCharge = 0; std::size_t nInvalidMeasurementCount = 0; @@ -96,17 +89,14 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( nInvalidCharge += static_cast(!validCharge); - // default valid measurement count to true and only change if we have loaded - // the measurement particles map bool validMeasurementCount = true; - if (particlesMeasMap) { - auto [b, e] = particlesMeasMap->equal_range(p.particleId()); + if (auto finalParticleIt = inputParticlesFinal.find(p.particleId()); + finalParticleIt != inputParticlesFinal.end()) { validMeasurementCount = - within(static_cast(std::distance(b, e)), - m_cfg.measurementsMin, m_cfg.measurementsMax); - - ACTS_VERBOSE("Found " << std::distance(b, e) << " measurements for " - << p.particleId()); + within(finalParticleIt->numberOfHits(), m_cfg.measurementsMin, + m_cfg.measurementsMax); + } else { + ACTS_WARNING("No final particle found for " << p.particleId()); } nInvalidMeasurementCount += @@ -136,14 +126,30 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( SimParticleContainer outputParticles; outputParticles.reserve(inputParticles.size()); + SimParticleContainer outputParticlesFinal; + if (m_outputParticlesFinal.isInitialized()) { + outputParticlesFinal.reserve(inputParticles.size()); + } + // copy selected particles for (const auto& inputParticle : inputParticles) { - if (isValidParticle(inputParticle)) { - // the input parameters should already be - outputParticles.insert(outputParticles.end(), inputParticle); + if (!isValidParticle(inputParticle)) { + continue; + } + + outputParticles.insert(outputParticles.end(), inputParticle); + + if (m_outputParticlesFinal.isInitialized()) { + if (auto particleFinalIt = + inputParticlesFinal.find(inputParticle.particleId()); + particleFinalIt != inputParticlesFinal.end()) { + outputParticlesFinal.insert(outputParticlesFinal.end(), + *particleFinalIt); + } } } outputParticles.shrink_to_fit(); + outputParticlesFinal.shrink_to_fit(); ACTS_DEBUG("event " << ctx.eventNumber << " selected " << outputParticles.size() << " from " @@ -153,5 +159,9 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( << nInvalidMeasurementCount); m_outputParticles(ctx, std::move(outputParticles)); + if (m_outputParticlesFinal.isInitialized()) { + m_outputParticlesFinal(ctx, std::move(outputParticlesFinal)); + } + return ProcessCode::SUCCESS; } diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp index d6c8440fdc7..de05bf621ca 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp @@ -29,10 +29,14 @@ class ParticleSelector final : public IAlgorithm { struct Config { /// The input particles collection. std::string inputParticles; - /// Input measurement particles map (Optional) - std::string inputMeasurementParticlesMap; + /// Optional. The input final state particles collection. + /// If provided, this will be used to access the number of measurements. + std::string inputParticlesFinal; /// The output particles collection. std::string outputParticles; + /// Optional. The output final state particles collection. + std::string outputParticlesFinal; + // Minimum/maximum distance from the origin in the transverse plane. double rhoMin = 0; double rhoMax = std::numeric_limits::infinity(); @@ -79,11 +83,13 @@ class ParticleSelector final : public IAlgorithm { Config m_cfg; ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle> m_inputMap{ - this, "InputMeasurementParticlesMap"}; + ReadDataHandle m_inputParticlesFinal{ + this, "InputParticlesFinal"}; WriteDataHandle m_outputParticles{this, "OutputParticles"}; + WriteDataHandle m_outputParticlesFinal{ + this, "OutputParticlesFinal"}; }; } // namespace ActsExamples diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp index f0d20f1a370..cfc62ab9da9 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp @@ -11,6 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/ParticleHypothesis.hpp" +#include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Helpers.hpp" @@ -128,31 +129,16 @@ ActsExamples::ProcessCode ActsExamples::ParticleSmearing::execute( Acts::BoundSquareMatrix cov = Acts::BoundSquareMatrix::Zero(); if (m_cfg.initialSigmas) { // use the initial sigmas if set - for (std::size_t i = Acts::eBoundLoc0; i < Acts::eBoundSize; ++i) { - double sigma = (*m_cfg.initialSigmas)[i]; - double variance = sigma * sigma; - - if (i == Acts::eBoundQOverP) { - // note that we rely on the fact that sigma theta is already - // computed - double varianceTheta = cov(Acts::eBoundTheta, Acts::eBoundTheta); - // transverse momentum contribution - variance += std::pow( - m_cfg.initialSigmaPtRel * params[Acts::eBoundQOverP], 2); + Acts::EstimateTrackParamCovarianceConfig config{ + .initialSigmas = + Eigen::Map{ + m_cfg.initialSigmas->data()}, + .initialSigmaPtRel = m_cfg.initialSigmaPtRel, + .initialVarInflation = Eigen::Map{ + m_cfg.initialVarInflation.data()}}; - // theta contribution - variance += varianceTheta * - std::pow(params[Acts::eBoundQOverP] * - std::tan(params[Acts::eBoundTheta]), - 2); - } - - // Inflate the initial covariance - variance *= m_cfg.initialVarInflation[i]; - - cov(i, i) = variance; - } + cov = Acts::estimateTrackParamCovariance(config, params, false); } else { // otherwise use the smearing sigmas diff --git a/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp index 3cf5eace6d9..ef495b3a9a9 100644 --- a/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp +++ b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp @@ -17,6 +17,7 @@ #include "ActsFatras/EventData/Barcode.hpp" #include +#include #include namespace ActsExamples { @@ -30,12 +31,12 @@ class MeasurementMapSelector final : public IAlgorithm { public: struct Config { + /// Input measurements + std::string inputMeasurements; + /// Input spacepoints collection. std::string inputMeasurementParticleMap; - /// Input source links - std::string inputSourceLinks; - /// Output protoTracks collection. std::string outputMeasurementParticleMap; @@ -48,27 +49,26 @@ class MeasurementMapSelector final : public IAlgorithm { /// @param cfg is the config struct to configure the algorithm /// @param level is the logging level MeasurementMapSelector(Config cfg, Acts::Logging::Level lvl) - : IAlgorithm("MeasurementMapSelector", lvl), m_cfg(cfg) { - m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); + : IAlgorithm("MeasurementMapSelector", lvl), m_cfg(std::move(cfg)) { + m_inputMeasurements.initialize(m_cfg.inputMeasurements); m_inputMap.initialize(m_cfg.inputMeasurementParticleMap); m_outputMap.initialize(m_cfg.outputMeasurementParticleMap); } - virtual ~MeasurementMapSelector() = default; - /// Filter the measurements /// /// @param ctx is the algorithm context that holds event-wise information /// @return a process code to steer the algorithm flow ActsExamples::ProcessCode execute( const ActsExamples::AlgorithmContext& ctx) const final { - const auto& inputSourceLinks = m_inputSourceLinks(ctx); + const auto& inputMeasurements = m_inputMeasurements(ctx); const auto& inputMap = m_inputMap(ctx); Map outputMap; for (const auto geoId : m_cfg.geometrySelection) { - auto range = selectLowestNonZeroGeometryObject(inputSourceLinks, geoId); + auto range = selectLowestNonZeroGeometryObject( + inputMeasurements.orderedIndices(), geoId); for (const auto& sl : range) { const auto [begin, end] = inputMap.equal_range(sl.index()); outputMap.insert(begin, end); @@ -86,8 +86,8 @@ class MeasurementMapSelector final : public IAlgorithm { // configuration Config m_cfg; - ReadDataHandle m_inputSourceLinks{ - this, "InputSourceLinks"}; + ReadDataHandle m_inputMeasurements{this, + "InputMeasurements"}; ReadDataHandle m_inputMap{this, "InputMeasurementParticleMap"}; WriteDataHandle m_outputMap{this, "OutputMeasurementParticleMap"}; }; diff --git a/Examples/Algorithms/Utilities/src/PrototracksToTracks.cpp b/Examples/Algorithms/Utilities/src/PrototracksToTracks.cpp index 01e2fac6a38..12a2b70e6cf 100644 --- a/Examples/Algorithms/Utilities/src/PrototracksToTracks.cpp +++ b/Examples/Algorithms/Utilities/src/PrototracksToTracks.cpp @@ -8,6 +8,7 @@ #include "ActsExamples/Utilities/ProtoTracksToTracks.hpp" +#include "Acts/EventData/SourceLink.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" @@ -27,16 +28,12 @@ PrototracksToTracks::PrototracksToTracks(Config cfg, Acts::Logging::Level lvl) } ProcessCode PrototracksToTracks::execute(const AlgorithmContext& ctx) const { + const auto& measurements = m_inputMeasurements(ctx); + auto trackContainer = std::make_shared(); auto mtj = std::make_shared(); TrackContainer tracks(trackContainer, mtj); - boost::container::flat_map slMap; - for (const auto& m : m_inputMeasurements(ctx)) { - const auto idx = m.sourceLink().template get().index(); - slMap.insert(std::pair{idx, m.sourceLink()}); - } - const auto& prototracks = m_inputProtoTracks(ctx); ACTS_DEBUG("Received " << prototracks.size() << " prototracks"); @@ -54,12 +51,15 @@ ProcessCode PrototracksToTracks::execute(const AlgorithmContext& ctx) const { maxSize = std::max(maxSize, protoTrack.size()); auto track = tracks.makeTrack(); - for (auto idx : protoTrack) { + for (auto measIndex : protoTrack) { + ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(measIndex); + IndexSourceLink sourceLink(measurement.geometryId(), measIndex); + auto trackStateProxy = track.appendTrackState(Acts::TrackStatePropMask::None); trackStateProxy.typeFlags().set(Acts::TrackStateFlag::MeasurementFlag); - trackStateProxy.setUncalibratedSourceLink( - Acts::SourceLink{slMap.at(idx)}); + trackStateProxy.setUncalibratedSourceLink(Acts::SourceLink(sourceLink)); } track.nMeasurements() = static_cast(protoTrack.size()); diff --git a/Examples/Algorithms/Utilities/src/TrajectoriesToPrototracks.cpp b/Examples/Algorithms/Utilities/src/TrajectoriesToPrototracks.cpp index cd961ab6141..c92d985d492 100644 --- a/Examples/Algorithms/Utilities/src/TrajectoriesToPrototracks.cpp +++ b/Examples/Algorithms/Utilities/src/TrajectoriesToPrototracks.cpp @@ -9,6 +9,7 @@ #include "ActsExamples/Utilities/TrajectoriesToPrototracks.hpp" #include "Acts/EventData/MultiTrajectory.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/Trajectories.hpp" @@ -16,7 +17,6 @@ #include namespace ActsExamples { -class IndexSourceLink; struct AlgorithmContext; TrajectoriesToPrototracks::TrajectoriesToPrototracks(Config cfg, diff --git a/Examples/Algorithms/Vertexing/src/VertexingHelpers.hpp b/Examples/Algorithms/Vertexing/src/VertexingHelpers.hpp index 9d3afe19646..3222578a3da 100644 --- a/Examples/Algorithms/Vertexing/src/VertexingHelpers.hpp +++ b/Examples/Algorithms/Vertexing/src/VertexingHelpers.hpp @@ -15,6 +15,7 @@ #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Framework/DataHandle.hpp" +#include #include #include @@ -55,8 +56,7 @@ inline ProtoVertexContainer makeProtoVertices( protoVertex.reserve(vertex.tracks().size()); for (const auto& track : vertex.tracks()) { - auto it = std::find(inputTracks.begin(), inputTracks.end(), - track.originalParams); + auto it = std::ranges::find(inputTracks, track.originalParams); if (it != inputTracks.end()) { protoVertex.push_back(std::distance(inputTracks.begin(), it)); } else { diff --git a/Examples/Detectors/CMakeLists.txt b/Examples/Detectors/CMakeLists.txt index a04112d572c..7e7aed5daed 100644 --- a/Examples/Detectors/CMakeLists.txt +++ b/Examples/Detectors/CMakeLists.txt @@ -4,5 +4,6 @@ add_subdirectory(GenericDetector) add_subdirectory_if(Geant4Detector ACTS_BUILD_EXAMPLES_GEANT4) add_subdirectory(MagneticField) add_subdirectory(TGeoDetector) +add_subdirectory(ITkModuleSplitting) add_subdirectory(TelescopeDetector) add_subdirectory_if(MuonSpectrometerMockupDetector ACTS_BUILD_EXAMPLES_GEANT4) diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp index 6856ff94a5c..2912e39b757 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/Examples/Detectors/ITkModuleSplitting/CMakeLists.txt b/Examples/Detectors/ITkModuleSplitting/CMakeLists.txt new file mode 100644 index 00000000000..affd7146336 --- /dev/null +++ b/Examples/Detectors/ITkModuleSplitting/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(ActsExamplesITkModuleSplitting INTERFACE) +target_include_directories( + ActsExamplesITkModuleSplitting + INTERFACE $ +) + +install(DIRECTORY include/ActsExamples DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/Examples/Detectors/ITkModuleSplitting/include/ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp b/Examples/Detectors/ITkModuleSplitting/include/ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp new file mode 100644 index 00000000000..591671bffb7 --- /dev/null +++ b/Examples/Detectors/ITkModuleSplitting/include/ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp @@ -0,0 +1,147 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/Definitions/Algebra.hpp" +#include "Acts/Surfaces/AnnulusBounds.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Surfaces/SurfaceBounds.hpp" + +#include +#include +#include +#include + +namespace ActsExamples::ITk { + +template +inline std::vector> splitBarrelModule( + const Acts::GeometryContext& gctx, + const std::shared_ptr& detElement, + unsigned int nSegments, const element_factory_t& factory, + const std::string& name, + const Acts::Logger& logger = Acts::getDummyLogger()) { + // Retrieve the surface + const Acts::Surface& surface = detElement->surface(); + const Acts::SurfaceBounds& bounds = surface.bounds(); + if (bounds.type() != Acts::SurfaceBounds::eRectangle || nSegments <= 1u) { + ACTS_WARNING("Invalid splitting config for barrel node: " + << name << "! Node will not be slpit."); + return {detElement}; + } + + // Output container for the submodules + std::vector> detElements = {}; + detElements.reserve(nSegments); + + // Get the geometric information + const Acts::Transform3& transform = surface.transform(gctx); + // Determine the new bounds + const std::vector boundsValues = bounds.values(); + + double lengthX = (boundsValues[Acts::RectangleBounds::eMaxX] - + boundsValues[Acts::RectangleBounds::eMinX]) / + nSegments; + double lengthY = boundsValues[Acts::RectangleBounds::eMaxY] - + boundsValues[Acts::RectangleBounds::eMinY]; + auto rectBounds = + std::make_shared(0.5 * lengthX, 0.5 * lengthY); + // Translation for every subelement + auto localTranslation = Acts::Vector2(-0.5 * lengthX * (nSegments - 1), 0.); + const auto step = Acts::Vector2(lengthX, 0.); + ACTS_DEBUG("Rectangle bounds for new node (half length): " + + std::to_string(rectBounds->halfLengthX()) + ", " + + std::to_string(rectBounds->halfLengthY())); + + for (std::size_t i = 0; i < nSegments; i++) { + Acts::Vector3 globalTranslation = + surface.localToGlobal(gctx, localTranslation, {}) - + transform.translation(); + auto elemTransform = + Acts::Transform3(transform).pretranslate(globalTranslation); + detElements.emplace_back(factory(elemTransform, rectBounds)); + + localTranslation += step; + } + return detElements; +} + +template +inline std::vector> splitDiscModule( + const Acts::GeometryContext& gctx, + const std::shared_ptr& detElement, + const std::vector>& splitRanges, + const element_factory_t& factory, const std::string& name, + const Acts::Logger& logger = Acts::getDummyLogger()) { + // Retrieve the surface + const Acts::Surface& surface = detElement->surface(); + const Acts::SurfaceBounds& bounds = surface.bounds(); + + // Check annulus bounds origin + auto printOrigin = [&](const Acts::Surface& sf) { + Acts::Vector3 discOrigin = + sf.localToGlobal(gctx, Acts::Vector2(0., 0.), Acts::Vector3::Zero()); + std::string out = + "Disc surface origin at: " + std::to_string(discOrigin[0]) + ", " + + std::to_string(discOrigin[1]) + ", " + std::to_string(discOrigin[2]); + return out; + }; + ACTS_DEBUG(printOrigin(surface)); + + if (bounds.type() != Acts::SurfaceBounds::eAnnulus || splitRanges.empty()) { + ACTS_WARNING("Invalid splitting config for disk node: " + << name << "! Node will not be slpit."); + return {detElement}; + } + + auto nSegments = splitRanges.size(); + + // Output container for the submodules + std::vector> detElements = {}; + detElements.reserve(nSegments); + + // Get the geometric information + const Acts::Transform3& transform = surface.transform(gctx); + const std::vector boundsValues = bounds.values(); + std::array values{}; + + std::copy_n(boundsValues.begin(), Acts::AnnulusBounds::eSize, values.begin()); + + for (std::size_t i = 0; i < nSegments; i++) { + if (boundsValues[Acts::AnnulusBounds::eMinR] > splitRanges[i].first || + boundsValues[Acts::AnnulusBounds::eMaxR] < splitRanges[i].second) { + ACTS_WARNING( + "Radius pattern not within the original bounds, node will not be " + "split!"); + return {detElement}; + } + + values[Acts::AnnulusBounds::eMinR] = splitRanges[i].first; + values[Acts::AnnulusBounds::eMaxR] = splitRanges[i].second; + auto annulusBounds = std::make_shared(values); + ACTS_DEBUG( + "New r bounds for node: " + std::to_string(annulusBounds->rMin()) + + ", " + std::to_string(annulusBounds->rMax())); + + auto element = factory(transform, annulusBounds); + detElements.push_back(std::move(element)); + } + return detElements; +} + +} // namespace ActsExamples::ITk diff --git a/Examples/Detectors/TGeoDetector/CMakeLists.txt b/Examples/Detectors/TGeoDetector/CMakeLists.txt index 04d065adaa5..3df36c62f15 100644 --- a/Examples/Detectors/TGeoDetector/CMakeLists.txt +++ b/Examples/Detectors/TGeoDetector/CMakeLists.txt @@ -17,6 +17,7 @@ target_link_libraries( ActsPluginJson ActsExamplesFramework ActsExamplesDetectorGeneric + ActsExamplesITkModuleSplitting ) install( diff --git a/Examples/Detectors/TGeoDetector/src/TGeoITkModuleSplitter.cpp b/Examples/Detectors/TGeoDetector/src/TGeoITkModuleSplitter.cpp index 0aafbe2d7d7..42bbbd9c882 100644 --- a/Examples/Detectors/TGeoDetector/src/TGeoITkModuleSplitter.cpp +++ b/Examples/Detectors/TGeoDetector/src/TGeoITkModuleSplitter.cpp @@ -14,6 +14,7 @@ #include "Acts/Surfaces/RectangleBounds.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/SurfaceBounds.hpp" +#include "ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp" #include #include @@ -86,55 +87,16 @@ ActsExamples::TGeoITkModuleSplitter::splitBarrelModule( const Acts::GeometryContext& gctx, const std::shared_ptr& detElement, unsigned int nSegments) const { - // Retrieve the surface - auto identifier = detElement->identifier(); - const Acts::Surface& surface = detElement->surface(); - const Acts::SurfaceBounds& bounds = surface.bounds(); - if (bounds.type() != Acts::SurfaceBounds::eRectangle || nSegments <= 1u) { - ACTS_WARNING("Invalid splitting config for barrel node: " + - std::string(detElement->tgeoNode().GetName()) + - "! Node will not be slpit."); - return {detElement}; - } - - // Output container for the submodules - std::vector> detElements = - {}; - detElements.reserve(nSegments); - - // Get the geometric information - double thickness = detElement->thickness(); - const Acts::Transform3& transform = surface.transform(gctx); - // Determine the new bounds - const std::vector boundsValues = bounds.values(); - double lengthX = (boundsValues[Acts::RectangleBounds::eMaxX] - - boundsValues[Acts::RectangleBounds::eMinX]) / - nSegments; - double lengthY = boundsValues[Acts::RectangleBounds::eMaxY] - - boundsValues[Acts::RectangleBounds::eMinY]; - auto rectBounds = - std::make_shared(0.5 * lengthX, 0.5 * lengthY); - // Translation for every subelement - auto localTranslation = Acts::Vector2(-0.5 * lengthX * (nSegments - 1), 0.); - const auto step = Acts::Vector2(lengthX, 0.); - ACTS_DEBUG("Rectangle bounds for new node (half length): " + - std::to_string(rectBounds->halfLengthX()) + ", " + - std::to_string(rectBounds->halfLengthY())); + auto name = detElement->tgeoNode().GetName(); - for (std::size_t i = 0; i < nSegments; i++) { - Acts::Vector3 globalTranslation = - surface.localToGlobal(gctx, localTranslation, {}) - - transform.translation(); - auto elemTransform = - Acts::Transform3(transform).pretranslate(globalTranslation); - auto element = std::make_shared( - identifier, detElement->tgeoNode(), elemTransform, rectBounds, - thickness); - detElements.push_back(std::move(element)); + auto factory = [&](const auto& trafo, const auto& bounds) { + return std::make_shared( + detElement->identifier(), detElement->tgeoNode(), trafo, bounds, + detElement->thickness()); + }; - localTranslation += step; - } - return detElements; + return ITk::splitBarrelModule(gctx, detElement, nSegments, factory, name, + logger()); } /// If applicable, returns a split detector element @@ -144,55 +106,14 @@ ActsExamples::TGeoITkModuleSplitter::splitDiscModule( const std::shared_ptr& detElement, const std::vector& splitRanges) const { - // Retrieve the surface - auto identifier = detElement->identifier(); - const Acts::Surface& surface = detElement->surface(); - const Acts::SurfaceBounds& bounds = surface.bounds(); + auto name = detElement->tgeoNode().GetName(); - // Check annulus bounds origin - auto printOrigin = [&](const Acts::Surface& sf) { - Acts::Vector3 discOrigin = - sf.localToGlobal(gctx, Acts::Vector2(0., 0.), Acts::Vector3::Zero()); - std::string out = - "Disc surface origin at: " + std::to_string(discOrigin[0]) + ", " + - std::to_string(discOrigin[1]) + ", " + std::to_string(discOrigin[2]); - return out; + auto factory = [&](const auto& trafo, const auto& bounds) { + return std::make_shared( + detElement->identifier(), detElement->tgeoNode(), trafo, bounds, + detElement->thickness()); }; - ACTS_DEBUG(printOrigin(surface)); - - if (bounds.type() != Acts::SurfaceBounds::eAnnulus || splitRanges.empty()) { - ACTS_WARNING("Invalid splitting config for disk node: " + - std::string(detElement->tgeoNode().GetName()) + - "! Node will not be slpit."); - return {detElement}; - } - - auto nSegments = splitRanges.size(); - - // Output container for the submodules - std::vector> detElements = - {}; - detElements.reserve(nSegments); - // Get the geometric information - double thickness = detElement->thickness(); - const Acts::Transform3& transform = surface.transform(gctx); - const std::vector boundsValues = bounds.values(); - std::array values{}; - std::copy_n(boundsValues.begin(), Acts::AnnulusBounds::eSize, values.begin()); - - for (std::size_t i = 0; i < nSegments; i++) { - values[Acts::AnnulusBounds::eMinR] = splitRanges[i].first; - values[Acts::AnnulusBounds::eMaxR] = splitRanges[i].second; - auto annulusBounds = std::make_shared(values); - ACTS_DEBUG( - "New r bounds for node: " + std::to_string(annulusBounds->rMin()) + - ", " + std::to_string(annulusBounds->rMax())); - - auto element = std::make_shared( - identifier, detElement->tgeoNode(), transform, annulusBounds, - thickness); - detElements.push_back(std::move(element)); - } - return detElements; + return ITk::splitDiscModule(gctx, detElement, splitRanges, factory, name, + logger()); } diff --git a/Examples/Framework/ML/src/NeuralCalibrator.cpp b/Examples/Framework/ML/src/NeuralCalibrator.cpp index ee1a8566aa4..68d24d35468 100644 --- a/Examples/Framework/ML/src/NeuralCalibrator.cpp +++ b/Examples/Framework/ML/src/NeuralCalibrator.cpp @@ -14,6 +14,7 @@ #include "Acts/Utilities/CalibrationContext.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/UnitVectors.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include diff --git a/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp b/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp index d0e7bed55d6..3eb850b1214 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp @@ -25,6 +25,14 @@ struct Cluster { // TODO make this be provided by Fatras? Acts::Vector3 globalPosition = Acts::Vector3::Zero(); + Acts::Vector3 localDirection = Acts::Vector3::Zero(); + Acts::Vector3 lengthDirection = Acts::Vector3::Zero(); + float localEta = 0.f; + float localPhi = 0.f; + float globalEta = 0.f; + float globalPhi = 0.f; + float etaAngle = 0.f; + float phiAngle = 0.f; double sumActivations() const { return std::accumulate( diff --git a/Examples/Framework/include/ActsExamples/EventData/GeometryContainers.hpp b/Examples/Framework/include/ActsExamples/EventData/GeometryContainers.hpp index e6cec85a47f..0724ed8c4ae 100644 --- a/Examples/Framework/include/ActsExamples/EventData/GeometryContainers.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/GeometryContainers.hpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -229,4 +230,9 @@ struct GeometryIdMultisetAccessor { const Container* container = nullptr; }; +/// A map that allows mapping back and forth between ACTS and Athena Geometry +/// Ids +using GeometryIdMapActsAthena = + boost::bimap; + } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/IndexSourceLink.hpp b/Examples/Framework/include/ActsExamples/EventData/IndexSourceLink.hpp index cc2e6249a2e..ea60a030b96 100644 --- a/Examples/Framework/include/ActsExamples/EventData/IndexSourceLink.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/IndexSourceLink.hpp @@ -84,11 +84,6 @@ struct IndexSourceLinkSurfaceAccessor { } // namespace Experimental -/// Container of index source links. -/// -/// Since the source links provide a `.geometryId()` accessor, they can be -/// stored in an ordered geometry container. -using IndexSourceLinkContainer = GeometryIdMultiset; /// Accessor for the above source link container /// /// It wraps up a few lookup methods to be used in the Combinatorial Kalman @@ -105,4 +100,5 @@ struct IndexSourceLinkAccessor : GeometryIdMultisetAccessor { return {Iterator{begin}, Iterator{end}}; } }; + } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp index 5d57c82b531..12286417c61 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp @@ -11,18 +11,23 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/MeasurementHelpers.hpp" -#include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/SubspaceHelpers.hpp" #include "Acts/EventData/Types.hpp" #include "Acts/EventData/detail/CalculateResiduals.hpp" #include "Acts/EventData/detail/ParameterTraits.hpp" #include "Acts/EventData/detail/PrintParameters.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Utilities/Iterator.hpp" +#include "ActsExamples/EventData/GeometryContainers.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/MeasurementConcept.hpp" #include +#include #include #include #include +#include #include #include #include @@ -58,13 +63,15 @@ using ConstVariableBoundMeasurementProxy = /// provide access to the individual measurements. class MeasurementContainer { public: - using Index = std::size_t; + using size_type = std::size_t; + using Index = size_type; template using FixedProxy = FixedMeasurementProxy; template using ConstFixedProxy = FixedMeasurementProxy; using VariableProxy = VariableMeasurementProxy; using ConstVariableProxy = VariableMeasurementProxy; + using OrderedIndices = GeometryIdMultiset; MeasurementContainer(); @@ -78,8 +85,18 @@ class MeasurementContainer { /// @brief Add a measurement of a given size /// @param size The size of the measurement + /// @param geometryId The geometry identifier of the measurement surface /// @return The index of the added measurement - Index addMeasurement(std::uint8_t size); + Index addMeasurement(std::uint8_t size, Acts::GeometryIdentifier geometryId); + + /// @brief Get a variable-size measurement proxy + /// @param index The index of the measurement + /// @return The variable-size measurement proxy + VariableProxy at(Index index); + /// @brief Get a const variable-size measurement proxy + /// @param index The index of the measurement + /// @return The const variable-size measurement proxy + ConstVariableProxy at(Index index) const; /// @brief Get a variable-size measurement proxy /// @param index The index of the measurement @@ -109,64 +126,35 @@ class MeasurementContainer { /// @brief Make a measurement of a given size /// @param size The size of the measurement + /// @param geometryId The geometry identifier of the measurement surface /// @return The variable-size measurement proxy - VariableProxy makeMeasurement(std::uint8_t size); + VariableProxy makeMeasurement(std::uint8_t size, + Acts::GeometryIdentifier geometryId); /// @brief Make a fixed-size measurement /// @tparam Size The size of the measurement + /// @param geometryId The geometry identifier of the measurement surface /// @return The fixed-size measurement proxy template - FixedProxy makeMeasurement() { - return getMeasurement(addMeasurement(Size)); + FixedProxy makeMeasurement(Acts::GeometryIdentifier geometryId) { + return getMeasurement(addMeasurement(Size, geometryId)); } template - VariableProxy emplaceMeasurement(std::uint8_t size, Args&&... args); + VariableProxy emplaceMeasurement(std::uint8_t size, + Acts::GeometryIdentifier geometryId, + Args&&... args); template - FixedProxy emplaceMeasurement(Args&&... args); + FixedProxy emplaceMeasurement(Acts::GeometryIdentifier geometryId, + Args&&... args); - template - class IteratorImpl { - public: - using value_type = - std::conditional_t; - using reference = value_type; - using pointer = value_type*; - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; + const OrderedIndices& orderedIndices() const; - using Container = std::conditional_t; - - IteratorImpl(Container& container, std::size_t index) - : m_container(container), m_index(index) {} - - reference operator*() const { return m_container.getMeasurement(m_index); } - - pointer operator->() const { return &operator*(); } - - IteratorImpl& operator++() { - ++m_index; - return *this; - } - - IteratorImpl operator++(int) { - auto copy = *this; - ++*this; - return copy; - } - - bool operator==(const IteratorImpl& other) const { - return m_index == other.m_index; - } - - private: - Container& m_container; - Index m_index; - }; - - using iterator = IteratorImpl; - using const_iterator = IteratorImpl; + using iterator = + Acts::ContainerIndexIterator; + using const_iterator = + Acts::ContainerIndexIterator; iterator begin(); iterator end(); @@ -185,10 +173,12 @@ class MeasurementContainer { std::vector m_entries; - std::vector> m_sourceLinks; + std::vector m_geometryIds; std::vector m_subspaceIndices; std::vector m_parameters; std::vector m_covariances; + + OrderedIndices m_orderedIndices; }; /// @brief Base class for measurement proxies @@ -247,18 +237,10 @@ class MeasurementProxyBase { return self().subspaceHelper().indexOf(i); } - /// @brief Set the source link of the measurement - /// @param sourceLink The source link - void setSourceLink(const Acts::SourceLink& sourceLink) - requires(!ReadOnly) - { - container().m_sourceLinks.at(m_index) = sourceLink; - } - - /// @brief Get the source link of the measurement - /// @return The source link - const Acts::SourceLink& sourceLink() const { - return container().m_sourceLinks.at(m_index).value(); + /// @brief Get the geometry ID of the measurement + /// @return The geometry ID + Acts::GeometryIdentifier geometryId() const { + return container().m_geometryIds.at(m_index); } /// @brief Set the subspace indices of the measurement @@ -286,23 +268,22 @@ class MeasurementProxyBase { return self().subspaceHelper().expandMatrix(self().covariance()); } - /// @brief Construct the measurement from a sourcelink, subspace vector, + /// @brief Construct the measurement from a subspace vector, /// parameters, and covariance. /// template - void fill(const Acts::SourceLink& source_link, Subspace&& subspace, + void fill(Subspace&& subspace, const Eigen::DenseBase& parameters, const Eigen::DenseBase& covariance) requires(!ReadOnly) { - setSourceLink(source_link); self().setSubspaceIndices(std::forward(subspace)); self().parameters() = parameters; self().covariance() = covariance; } - /// @brief Construct the measurement from a sourcelink, subspace vector, + /// @brief Construct the measurement from a subspace vector, /// parameters, and covariance. /// template @@ -310,8 +291,7 @@ class MeasurementProxyBase { requires(!ReadOnly) { assert(size() == other.size() && "Size mismatch"); - fill(other.sourceLink(), other.subspaceIndexVector(), other.parameters(), - other.covariance()); + fill(other.subspaceIndexVector(), other.parameters(), other.covariance()); } /// @brief Copy the data from another measurement @@ -517,8 +497,8 @@ class VariableMeasurementProxy template MeasurementContainer::VariableProxy MeasurementContainer::emplaceMeasurement( - std::uint8_t size, Args&&... args) { - VariableProxy meas = makeMeasurement(size); + std::uint8_t size, Acts::GeometryIdentifier geometryId, Args&&... args) { + VariableProxy meas = makeMeasurement(size, geometryId); meas.fill(std::forward(args)...); @@ -527,12 +507,16 @@ MeasurementContainer::VariableProxy MeasurementContainer::emplaceMeasurement( template MeasurementContainer::FixedProxy MeasurementContainer::emplaceMeasurement( - Args&&... args) { - FixedProxy meas = makeMeasurement(); + Acts::GeometryIdentifier geometryId, Args&&... args) { + FixedProxy meas = makeMeasurement(geometryId); meas.fill(std::forward(args)...); return meas; } +static_assert( + std::random_access_iterator && + std::random_access_iterator); + } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp b/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp index 248edacc032..f0b61363fba 100644 --- a/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp @@ -14,7 +14,6 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/CalibrationContext.hpp" #include "ActsExamples/EventData/Cluster.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include #include diff --git a/Examples/Framework/include/ActsExamples/EventData/MeasurementConcept.hpp b/Examples/Framework/include/ActsExamples/EventData/MeasurementConcept.hpp index f95ab117728..14627a78c53 100644 --- a/Examples/Framework/include/ActsExamples/EventData/MeasurementConcept.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/MeasurementConcept.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/EventData/SourceLink.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include @@ -17,7 +18,7 @@ namespace ActsExamples { template concept MeasurementConcept = requires(const T& m) { { m.size() } -> std::integral; - { m.sourceLink() } -> Acts::Concepts::decayed_same_as; + { m.geometryId() } -> std::same_as; { m.subspaceIndexVector() }; { m.parameters() }; { m.covariance() }; diff --git a/Examples/Framework/include/ActsExamples/EventData/SimSeed.hpp b/Examples/Framework/include/ActsExamples/EventData/SimSeed.hpp index 627b160b08f..5c211673f2a 100644 --- a/Examples/Framework/include/ActsExamples/EventData/SimSeed.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/SimSeed.hpp @@ -11,7 +11,6 @@ #include "Acts/EventData/Seed.hpp" #include "ActsExamples/EventData/SimSpacePoint.hpp" -#include #include namespace ActsExamples { diff --git a/Examples/Framework/include/ActsExamples/Framework/AlgorithmContext.hpp b/Examples/Framework/include/ActsExamples/Framework/AlgorithmContext.hpp index f78e5b9c798..c70eaea6539 100644 --- a/Examples/Framework/include/ActsExamples/Framework/AlgorithmContext.hpp +++ b/Examples/Framework/include/ActsExamples/Framework/AlgorithmContext.hpp @@ -13,8 +13,6 @@ #include #include -#include - namespace ActsExamples { class WhiteBoard; diff --git a/Examples/Framework/include/ActsExamples/Framework/IAlgorithm.hpp b/Examples/Framework/include/ActsExamples/Framework/IAlgorithm.hpp index 46856ed7103..77fd2b41c98 100644 --- a/Examples/Framework/include/ActsExamples/Framework/IAlgorithm.hpp +++ b/Examples/Framework/include/ActsExamples/Framework/IAlgorithm.hpp @@ -44,7 +44,7 @@ class IAlgorithm : public SequenceElement { /// @param context The algorithm context ProcessCode internalExecute(const AlgorithmContext& context) final { return execute(context); - }; + } /// Initialize the algorithm ProcessCode initialize() override { return ProcessCode::SUCCESS; } diff --git a/Examples/Framework/src/EventData/Measurement.cpp b/Examples/Framework/src/EventData/Measurement.cpp index 09c1e0a257a..ab2dad07489 100644 --- a/Examples/Framework/src/EventData/Measurement.cpp +++ b/Examples/Framework/src/EventData/Measurement.cpp @@ -8,6 +8,9 @@ #include "ActsExamples/EventData/Measurement.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" + namespace ActsExamples { MeasurementContainer::MeasurementContainer() = default; @@ -17,20 +20,36 @@ std::size_t MeasurementContainer::size() const { } void MeasurementContainer::reserve(std::size_t size) { - m_sourceLinks.reserve(size); + m_geometryIds.reserve(size); m_subspaceIndices.reserve(size * 2); m_parameters.reserve(size * 2); m_covariances.reserve(size * 2 * 2); } -std::size_t MeasurementContainer::addMeasurement(std::uint8_t size) { +std::size_t MeasurementContainer::addMeasurement( + std::uint8_t size, Acts::GeometryIdentifier geometryId) { m_entries.push_back({m_subspaceIndices.size(), m_parameters.size(), m_covariances.size(), size}); - m_sourceLinks.emplace_back(); + m_geometryIds.emplace_back(geometryId); m_subspaceIndices.resize(m_subspaceIndices.size() + size); m_parameters.resize(m_parameters.size() + size); m_covariances.resize(m_covariances.size() + size * size); - return m_entries.size() - 1; + + std::size_t index = m_entries.size() - 1; + IndexSourceLink sourceLink(geometryId, index); + m_orderedIndices.emplace_hint(m_orderedIndices.end(), sourceLink); + + return index; +} + +MeasurementContainer::VariableProxy MeasurementContainer::at( + std::size_t index) { + return VariableProxy{*this, index}; +} + +MeasurementContainer::ConstVariableProxy MeasurementContainer::at( + std::size_t index) const { + return ConstVariableProxy{*this, index}; } MeasurementContainer::VariableProxy MeasurementContainer::getMeasurement( @@ -44,8 +63,13 @@ MeasurementContainer::ConstVariableProxy MeasurementContainer::getMeasurement( } MeasurementContainer::VariableProxy MeasurementContainer::makeMeasurement( - std::uint8_t size) { - return getMeasurement(addMeasurement(size)); + std::uint8_t size, Acts::GeometryIdentifier geometryId) { + return getMeasurement(addMeasurement(size, geometryId)); +} + +const MeasurementContainer::OrderedIndices& +MeasurementContainer::orderedIndices() const { + return m_orderedIndices; } MeasurementContainer::iterator MeasurementContainer::begin() { diff --git a/Examples/Framework/src/Framework/Sequencer.cpp b/Examples/Framework/src/Framework/Sequencer.cpp index 100f55cf93c..f6bb69d44c0 100644 --- a/Examples/Framework/src/Framework/Sequencer.cpp +++ b/Examples/Framework/src/Framework/Sequencer.cpp @@ -126,6 +126,13 @@ Sequencer::Sequencer(const Sequencer::Config& cfg) "ACTS_SEQUENCER_DISABLE_FPEMON"); m_cfg.trackFpes = false; } + + if (m_cfg.trackFpes && !m_cfg.fpeMasks.empty() && + !Acts::FpeMonitor::canSymbolize()) { + ACTS_ERROR("FPE monitoring is enabled but symbolization is not available"); + throw std::runtime_error( + "FPE monitoring is enabled but symbolization is not available"); + } } void Sequencer::addContextDecorator( @@ -604,7 +611,7 @@ void Sequencer::fpeReport() const { auto merged = std::accumulate( fpe.begin(), fpe.end(), Acts::FpeMonitor::Result{}, [](const auto& lhs, const auto& rhs) { return lhs.merged(rhs); }); - if (!merged) { + if (!merged.hasStackTraces()) { // no FPEs to report continue; } diff --git a/Examples/Framework/src/Utilities/EventDataTransforms.cpp b/Examples/Framework/src/Utilities/EventDataTransforms.cpp index 9e0cbbff3fd..79d3d543a05 100644 --- a/Examples/Framework/src/Utilities/EventDataTransforms.cpp +++ b/Examples/Framework/src/Utilities/EventDataTransforms.cpp @@ -32,12 +32,12 @@ const ActsExamples::SimSpacePoint* ActsExamples::findSpacePointForIndex( ActsExamples::Index index, const SimSpacePointContainer& spacepoints) { auto match = [&](const SimSpacePoint& sp) { const auto& sls = sp.sourceLinks(); - return std::any_of(sls.begin(), sls.end(), [&](const auto& sl) { + return std::ranges::any_of(sls, [&](const auto& sl) { return sl.template get().index() == index; }); }; - auto found = std::find_if(spacepoints.begin(), spacepoints.end(), match); + auto found = std::ranges::find_if(spacepoints, match); if (found == spacepoints.end()) { return nullptr; diff --git a/Examples/Framework/src/Validation/TrackClassification.cpp b/Examples/Framework/src/Validation/TrackClassification.cpp index 0341dc09e5a..a89f7c2a07a 100644 --- a/Examples/Framework/src/Validation/TrackClassification.cpp +++ b/Examples/Framework/src/Validation/TrackClassification.cpp @@ -25,10 +25,9 @@ inline void increaseHitCount( std::vector& particleHitCounts, ActsFatras::Barcode particleId) { // linear search since there is no ordering - auto it = std::find_if(particleHitCounts.begin(), particleHitCounts.end(), - [=](const ActsExamples::ParticleHitCount& phc) { - return (phc.particleId == particleId); - }); + auto it = std::ranges::find_if(particleHitCounts, [=](const auto& phc) { + return (phc.particleId == particleId); + }); // either increase count if we saw the particle before or add it if (it != particleHitCounts.end()) { it->hitCount += 1u; diff --git a/Examples/Io/CMakeLists.txt b/Examples/Io/CMakeLists.txt index dcf7c16a0a1..8b7f2861a6e 100644 --- a/Examples/Io/CMakeLists.txt +++ b/Examples/Io/CMakeLists.txt @@ -2,8 +2,6 @@ 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) add_subdirectory(Obj) -add_subdirectory(Performance) add_subdirectory(Root) add_subdirectory_if(Svg ACTS_BUILD_PLUGIN_ACTSVG) diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvInputOutput.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvInputOutput.hpp index 3cdbe789e3c..7f4e7b894c3 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvInputOutput.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvInputOutput.hpp @@ -38,6 +38,7 @@ #include "Acts/Utilities/Concepts.hpp" #include "Acts/Utilities/Helpers.hpp" +#include #include #include #include @@ -605,7 +606,7 @@ inline void NamedTupleDsvReader::parse_header( m_extra_columns.clear(); for (std::size_t i = 0; i < m_columns.size(); ++i) { // find the position of the column in the tuple. - auto it = std::find(names.begin(), names.end(), m_columns[i]); + auto it = std::ranges::find(names, m_columns[i]); if (it != names.end()) { // establish mapping between column and tuple item position m_tuple_column_map[std::distance(names.begin(), it)] = i; diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp index c26387e1260..d3c5359d16e 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp @@ -13,7 +13,6 @@ #include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" @@ -32,7 +31,6 @@ class Surface; } namespace ActsExamples { -class IndexSourceLink; struct AlgorithmContext; /// Read in a measurement cluster collection in comma-separated-value format. @@ -60,8 +58,6 @@ class CsvMeasurementReader final : public IReader { 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; @@ -102,9 +98,6 @@ class CsvMeasurementReader final : public IReader { WriteDataHandle> m_outputMeasurementSimHitsMap{ this, "OutputMeasurementSimHitsMap"}; - WriteDataHandle> m_outputSourceLinks{ - this, "OutputSourceLinks"}; - WriteDataHandle m_outputClusters{this, "OutputClusters"}; WriteDataHandle> diff --git a/Examples/Io/Csv/src/CsvMeasurementReader.cpp b/Examples/Io/Csv/src/CsvMeasurementReader.cpp index cbc030c6939..f426784ea30 100644 --- a/Examples/Io/Csv/src/CsvMeasurementReader.cpp +++ b/Examples/Io/Csv/src/CsvMeasurementReader.cpp @@ -45,7 +45,6 @@ ActsExamples::CsvMeasurementReader::CsvMeasurementReader( m_outputMeasurements.initialize(m_cfg.outputMeasurements); m_outputMeasurementSimHitsMap.initialize(m_cfg.outputMeasurementSimHitsMap); - m_outputSourceLinks.initialize(m_cfg.outputSourceLinks); m_outputClusters.maybeInitialize(m_cfg.outputClusters); m_outputMeasurementParticlesMap.maybeInitialize( m_cfg.outputMeasurementParticlesMap); @@ -196,15 +195,11 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( MeasurementContainer tmpMeasurements; GeometryIdMultimap orderedMeasurements; IndexMultimap measurementSimHitsMap; - IndexSourceLinkContainer sourceLinks; - // need list here for stable addresses - std::list sourceLinkStorage; tmpMeasurements.reserve(measurementData.size()); orderedMeasurements.reserve(measurementData.size()); // Safe long as we have single particle to sim hit association measurementSimHitsMap.reserve(measurementData.size()); - sourceLinks.reserve(measurementData.size()); auto measurementSimHitLinkData = readEverything( @@ -252,10 +247,7 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( // The measurement container is unordered and the index under which // the measurement will be stored is known before adding it. - const Index index = orderedMeasurements.size(); - IndexSourceLink& sourceLink = sourceLinkStorage.emplace_back(geoId, index); - auto measurement = - createMeasurement(tmpMeasurements, dParameters, sourceLink); + auto measurement = createMeasurement(tmpMeasurements, geoId, dParameters); // Due to the previous sorting of the raw hit data by geometry id, new // measurements should always end up at the end of the container. previous @@ -267,13 +259,11 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( ACTS_FATAL("Something went horribly wrong with the hit sorting"); return ProcessCode::ABORT; } - - sourceLinks.insert(sourceLinks.end(), std::cref(sourceLink)); } MeasurementContainer measurements; for (auto& [_, meas] : orderedMeasurements) { - measurements.emplaceMeasurement(meas.size(), meas); + measurements.emplaceMeasurement(meas.size(), meas.geometryId(), meas); } // Generate measurement-particles-map @@ -294,7 +284,6 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( // Write the data to the EventStore m_outputMeasurements(ctx, std::move(measurements)); m_outputMeasurementSimHitsMap(ctx, std::move(measurementSimHitsMap)); - m_outputSourceLinks(ctx, std::move(sourceLinks)); ///////////////////////// // Cluster information // diff --git a/Examples/Io/Csv/src/CsvMeasurementWriter.cpp b/Examples/Io/Csv/src/CsvMeasurementWriter.cpp index f421c8c2501..851966e8fba 100644 --- a/Examples/Io/Csv/src/CsvMeasurementWriter.cpp +++ b/Examples/Io/Csv/src/CsvMeasurementWriter.cpp @@ -13,7 +13,6 @@ #include "Acts/Geometry/GeometryIdentifier.hpp" #include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Io/Csv/CsvInputOutput.hpp" @@ -101,8 +100,7 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementWriter::writeT( writerMeasurementSimHitMap.append({measIdx, simHitIdx}); } - Acts::GeometryIdentifier geoId = - measurement.sourceLink().template get().geometryId(); + Acts::GeometryIdentifier geoId = measurement.geometryId(); // MEASUREMENT information ------------------------------------ // Encoded geometry identifier. same for all hits on the module diff --git a/Examples/Io/Csv/src/CsvTrackWriter.cpp b/Examples/Io/Csv/src/CsvTrackWriter.cpp index 26ac3ad2e6b..9f6d74ec730 100644 --- a/Examples/Io/Csv/src/CsvTrackWriter.cpp +++ b/Examples/Io/Csv/src/CsvTrackWriter.cpp @@ -33,10 +33,6 @@ #include #include -namespace ActsExamples { -class IndexSourceLink; -} // namespace ActsExamples - using namespace ActsExamples; CsvTrackWriter::CsvTrackWriter(const CsvTrackWriter::Config& config, diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp index a81341f0edc..34a95d3709a 100644 --- a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp @@ -41,8 +41,6 @@ class EDM4hepMeasurementReader final : public IReader { 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; }; @@ -79,9 +77,6 @@ class EDM4hepMeasurementReader final : public IReader { WriteDataHandle> m_outputMeasurementSimHitsMap{ this, "OutputMeasurementSimHitsMap"}; - WriteDataHandle> m_outputSourceLinks{ - this, "OutputSourceLinks"}; - WriteDataHandle m_outputClusters{this, "OutputClusters"}; }; diff --git a/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp index 5a9d6840999..237ff19e3b6 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp @@ -37,7 +37,6 @@ EDM4hepMeasurementReader::EDM4hepMeasurementReader( m_outputMeasurements.initialize(m_cfg.outputMeasurements); m_outputMeasurementSimHitsMap.initialize(m_cfg.outputMeasurementSimHitsMap); - m_outputSourceLinks.initialize(m_cfg.outputSourceLinks); m_outputClusters.maybeInitialize(m_cfg.outputClusters); } @@ -55,7 +54,6 @@ ProcessCode EDM4hepMeasurementReader::read(const AlgorithmContext& ctx) { ClusterContainer clusters; // TODO what about those? IndexMultimap measurementSimHitsMap; - IndexSourceLinkContainer sourceLinks; podio::Frame frame = reader().readEntry("events", ctx.eventNumber); @@ -76,7 +74,6 @@ ProcessCode EDM4hepMeasurementReader::read(const AlgorithmContext& ctx) { // Write the data to the EventStore m_outputMeasurements(ctx, std::move(measurements)); m_outputMeasurementSimHitsMap(ctx, std::move(measurementSimHitsMap)); - m_outputSourceLinks(ctx, std::move(sourceLinks)); if (!m_cfg.outputClusters.empty()) { m_outputClusters(ctx, std::move(clusters)); } diff --git a/Examples/Io/EDM4hep/src/EDM4hepReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepReader.cpp index ec84083f2aa..dfb11c84c9e 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepReader.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepReader.cpp @@ -181,11 +181,9 @@ ProcessCode EDM4hepReader::read(const AlgorithmContext& ctx) { vtxPos /= Acts::UnitConstants::mm; // linear search for vector - auto it = std::find_if( - primaryVertices.begin(), primaryVertices.end(), - [&vtxPos]( - const std::pair>& - pair) { return pair.first == vtxPos; }); + auto it = std::ranges::find_if(primaryVertices, [&vtxPos](const auto& v) { + return v.first == vtxPos; + }); if (it == primaryVertices.end()) { ACTS_DEBUG("Found primary vertex at " << vtx.x << ", " << vtx.y << ", " diff --git a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp index ef6b794cc4c..09dcbc6331b 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp @@ -152,9 +152,6 @@ VariableBoundMeasurementProxy EDM4hepUtil::readMeasurement( // no need for digitization as we only want to identify the sensor Acts::GeometryIdentifier geometryId = geometryMapper(from.getCellID()); - IndexSourceLink sourceLink{ - geometryId, static_cast(podioObjectIDToInteger(from.id()))}; - auto pos = from.getPosition(); auto cov = from.getCovMatrix(); @@ -173,7 +170,7 @@ VariableBoundMeasurementProxy EDM4hepUtil::readMeasurement( dParameters.values.push_back(pos.z); dParameters.variances.push_back(cov[5]); - auto to = createMeasurement(container, dParameters, sourceLink); + auto to = createMeasurement(container, geometryId, dParameters); if (fromClusters != nullptr) { for (const auto objectId : from.getRawHits()) { @@ -202,8 +199,7 @@ void EDM4hepUtil::writeMeasurement( edm4hep::MutableTrackerHitPlane to, const Cluster* fromCluster, edm4hep::TrackerHitCollection& toClusters, const MapGeometryIdTo& geometryMapper) { - Acts::GeometryIdentifier geoId = - from.sourceLink().template get().geometryId(); + Acts::GeometryIdentifier geoId = from.geometryId(); if (geometryMapper) { // no need for digitization as we only want to identify the sensor diff --git a/Examples/Io/Json/src/JsonDigitizationConfig.cpp b/Examples/Io/Json/src/JsonDigitizationConfig.cpp index 524fd4d32ef..489c86087bf 100644 --- a/Examples/Io/Json/src/JsonDigitizationConfig.cpp +++ b/Examples/Io/Json/src/JsonDigitizationConfig.cpp @@ -139,7 +139,9 @@ void ActsExamples::to_json(nlohmann::json& j, j["thickness"] = gdc.thickness; j["threshold"] = gdc.threshold; j["digital"] = gdc.digital; - to_json(j["charge-smearing"], gdc.chargeSmearer); + if (j.find("charge-smearing") != j.end()) { + to_json(j["charge-smearing"], gdc.chargeSmearer); + } } void ActsExamples::from_json(const nlohmann::json& j, @@ -161,7 +163,9 @@ void ActsExamples::from_json(const nlohmann::json& j, gdc.varianceMap[idx] = vars; } } - from_json(j["charge-smearing"], gdc.chargeSmearer); + if (j.find("charge-smearing") != j.end()) { + from_json(j["charge-smearing"], gdc.chargeSmearer); + } } void ActsExamples::to_json(nlohmann::json& j, diff --git a/Examples/Io/NuclearInteractions/CMakeLists.txt b/Examples/Io/NuclearInteractions/CMakeLists.txt deleted file mode 100644 index 3f7ad2b9b9b..00000000000 --- a/Examples/Io/NuclearInteractions/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -add_library( - ActsExamplesIoNuclearInteractions - SHARED - src/RootNuclearInteractionParametersWriter.cpp - src/detail/NuclearInteractionParametrisation.cpp -) -target_include_directories( - ActsExamplesIoNuclearInteractions - PUBLIC $ -) -target_link_libraries( - ActsExamplesIoNuclearInteractions - PUBLIC ActsCore ActsExamplesFramework Threads::Threads - PRIVATE ROOT::Core ROOT::Hist ROOT::Tree -) - -install( - TARGETS ActsExamplesIoNuclearInteractions - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} -) diff --git a/Examples/Io/Root/CMakeLists.txt b/Examples/Io/Root/CMakeLists.txt index 3ac12286395..f384c291bc1 100644 --- a/Examples/Io/Root/CMakeLists.txt +++ b/Examples/Io/Root/CMakeLists.txt @@ -23,6 +23,13 @@ add_library( src/RootVertexReader.cpp src/RootVertexWriter.cpp src/RootAthenaDumpReader.cpp + src/RootNuclearInteractionParametersWriter.cpp + src/detail/NuclearInteractionParametrisation.cpp + src/CKFPerformanceWriter.cpp + src/SeedingPerformanceWriter.cpp + src/TrackFinderPerformanceWriter.cpp + src/TrackFitterPerformanceWriter.cpp + src/VertexPerformanceWriter.cpp ) target_include_directories( ActsExamplesIoRoot diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/CKFPerformanceWriter.hpp similarity index 100% rename from Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/CKFPerformanceWriter.hpp diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaDumpReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaDumpReader.hpp index 33919b4bae1..2a09d8492b2 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaDumpReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaDumpReader.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimSpacePoint.hpp" #include "ActsExamples/Framework/DataHandle.hpp" @@ -62,13 +63,33 @@ class RootAthenaDumpReader : public IReader { // name of the track parameters (fitted by athena?) std::string outputTrackParameters = "athena_track_parameters"; + /// Only extract spacepoints + bool onlySpacepoints = false; + /// Only extract particles that passed the tracking requirements, for /// details see: /// https://gitlab.cern.ch/atlas/athena/-/blob/main/InnerDetector/InDetGNNTracking/src/DumpObjects.cxx?ref_type=heads#L1363 bool onlyPassedParticles = false; + /// Skip spacepoints with phi overlap bool skipOverlapSPsPhi = false; + + /// Skip spacepoints with eta overlap bool skipOverlapSPsEta = false; + + /// A map that provides a mapping between ACTS and Athena surface + /// identifiers + std::shared_ptr geometryIdMap = + nullptr; + + /// Tracking Geometry that contains the surfaces where we project + /// the measurements on + std::shared_ptr trackingGeometry = nullptr; + + /// When projecting measurements on ACTS surfaces, which euclidean boundary + /// tolerance should be allowed. If a value above zero is needed, this + /// indicates that the ACTS surfaces do not 100% include the athena surfaces + double absBoundaryTolerance = 0.0; }; RootAthenaDumpReader(const RootAthenaDumpReader &) = delete; @@ -94,12 +115,42 @@ class RootAthenaDumpReader : public IReader { const Config &config() const { return m_cfg; } private: + /// Particles with barcodes larger then this value are considered to be + /// secondary particles + /// https://gitlab.cern.ch/atlas/athena/-/blob/main/InnerDetector/InDetGNNTracking/src/DumpObjects.h?ref_type=heads#L101 + constexpr static int s_maxBarcodeForPrimary = 200000; + /// Private access to the logging instance const Acts::Logger &logger() const { return *m_logger; } /// The config class Config m_cfg; + /// Helper method to read particles + SimParticleContainer readParticles() const; + + /// Helper method to read measurements + std::tuple, + std::unordered_map> + readMeasurements(SimParticleContainer &particles, + const Acts::GeometryContext &gctx) const; + + /// Helper method to read spacepoints + /// @param imIdxMap optional remapping of indices. Since the measurement + /// index must be continuous, we need to remap the measurements indices + /// if we skip measurements in the first place + std::tuple + readSpacepoints(const std::optional> + &imIdxMap) const; + + /// Helper method to reprocess particle ids + std::pair> + reprocessParticles( + const SimParticleContainer &particles, + const IndexMultimap &measPartMap) const; + /// Write handlers WriteDataHandle m_outputPixelSpacePoints{ this, "outputPixelSpacepoints"}; @@ -122,6 +173,7 @@ class RootAthenaDumpReader : public IReader { std::vector> m_eventMap; std::shared_ptr m_inputchain; long unsigned int m_events; + bool m_haveStripFeatures = true; static constexpr unsigned int maxCL = 1500000; static constexpr unsigned int maxSP = 1500000; @@ -138,7 +190,7 @@ class RootAthenaDumpReader : public IReader { int CLindex[maxCL] = {}; //[nCL] // Clusters - std::vector *CLhardware; + std::vector *CLhardware{}; Double_t CLx[maxCL] = {}; //[nCL] Double_t CLy[maxCL] = {}; //[nCL] Double_t CLz[maxCL] = {}; //[nCL] @@ -148,13 +200,13 @@ class RootAthenaDumpReader : public IReader { Int_t CLphi_module[maxCL] = {}; //[nCL] Int_t CLside[maxCL] = {}; //[nCL] ULong64_t CLmoduleID[maxCL] = {}; //[nCL] - std::vector> *CLparticleLink_eventIndex; - std::vector> *CLparticleLink_barcode; - std::vector> *CLbarcodesLinked; - std::vector> *CLparticle_charge; - std::vector> *CLphis; - std::vector> *CLetas; - std::vector> *CLtots; + std::vector> *CLparticleLink_eventIndex{}; + std::vector> *CLparticleLink_barcode{}; + std::vector> *CLbarcodesLinked{}; + std::vector> *CLparticle_charge{}; + std::vector> *CLphis{}; + std::vector> *CLetas{}; + std::vector> *CLtots{}; Double_t CLloc_direction1[maxCL] = {}; //[nCL] Double_t CLloc_direction2[maxCL] = {}; //[nCL] Double_t CLloc_direction3[maxCL] = {}; //[nCL] @@ -172,7 +224,7 @@ class RootAthenaDumpReader : public IReader { Float_t CLnorm_x[maxCL] = {}; //[nCL] Float_t CLnorm_y[maxCL] = {}; //[nCL] Float_t CLnorm_z[maxCL] = {}; //[nCL] - std::vector> *CLlocal_cov; + std::vector> *CLlocal_cov{}; // Particles Int_t nPartEVT = 0; @@ -195,8 +247,8 @@ class RootAthenaDumpReader : public IReader { Int_t Part_vProdNout[maxPart] = {}; //[nPartEVT] Int_t Part_vProdStatus[maxPart] = {}; //[nPartEVT] Int_t Part_vProdBarcode[maxPart] = {}; //[nPartEVT] - std::vector> *Part_vParentID; - std::vector> *Part_vParentBarcode; + std::vector> *Part_vParentID{}; + std::vector> *Part_vParentBarcode{}; // Spacepoints Int_t nSP = 0; @@ -212,27 +264,27 @@ class RootAthenaDumpReader : public IReader { double SPcovz[maxSP] = {}; //[nSP] float SPhl_topstrip[maxSP] = {}; //[nSP] float SPhl_botstrip[maxSP] = {}; //[nSP] - std::vector> *SPtopStripDirection; - std::vector> *SPbottomStripDirection; - std::vector> *SPstripCenterDistance; - std::vector> *SPtopStripCenterPosition; + std::vector> *SPtopStripDirection{}; + std::vector> *SPbottomStripDirection{}; + std::vector> *SPstripCenterDistance{}; + std::vector> *SPtopStripCenterPosition{}; // Tracks Int_t nTRK = 0; Int_t TRKindex[maxTRK] = {}; //[nTRK] Int_t TRKtrack_fitter[maxTRK] = {}; //[nTRK] Int_t TRKparticle_hypothesis[maxTRK] = {}; //[nTRK] - std::vector> *TRKproperties; - std::vector> *TRKpattern; + std::vector> *TRKproperties{}; + std::vector> *TRKpattern{}; Int_t TRKndof[maxTRK] = {}; //[nTRK] Int_t TRKmot[maxTRK] = {}; //[nTRK] Int_t TRKoot[maxTRK] = {}; //[nTRK] Float_t TRKchiSq[maxTRK] = {}; //[nTRK] - std::vector> *TRKmeasurementsOnTrack_pixcl_sctcl_index; - std::vector> *TRKoutliersOnTrack_pixcl_sctcl_index; + std::vector> *TRKmeasurementsOnTrack_pixcl_sctcl_index{}; + std::vector> *TRKoutliersOnTrack_pixcl_sctcl_index{}; Int_t TRKcharge[maxTRK] = {}; //[nTRK] - std::vector> *TRKperigee_position; - std::vector> *TRKperigee_momentum; + std::vector> *TRKperigee_position{}; + std::vector> *TRKperigee_momentum{}; Int_t TTCindex[maxTRK] = {}; //[nTRK] Int_t TTCevent_index[maxTRK] = {}; //[nTRK] Int_t TTCparticle_link[maxTRK] = {}; //[nTRK] @@ -242,10 +294,10 @@ class RootAthenaDumpReader : public IReader { Int_t nDTT = 0; Int_t DTTindex[maxDTT] = {}; //[nDTT] Int_t DTTsize[maxDTT] = {}; //[nDTT] - std::vector> *DTTtrajectory_eventindex; - std::vector> *DTTtrajectory_barcode; - std::vector> *DTTstTruth_subDetType; - std::vector> *DTTstTrack_subDetType; - std::vector> *DTTstCommon_subDetType; + std::vector> *DTTtrajectory_eventindex{}; + std::vector> *DTTtrajectory_barcode{}; + std::vector> *DTTstTruth_subDetType{}; + std::vector> *DTTstTrack_subDetType{}; + std::vector> *DTTstCommon_subDetType{}; }; } // namespace ActsExamples diff --git a/Examples/Io/NuclearInteractions/include/ActsExamples/Io/NuclearInteractions/RootNuclearInteractionParametersWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootNuclearInteractionParametersWriter.hpp similarity index 97% rename from Examples/Io/NuclearInteractions/include/ActsExamples/Io/NuclearInteractions/RootNuclearInteractionParametersWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/RootNuclearInteractionParametersWriter.hpp index 8edad2c2a7d..d688920fc76 100644 --- a/Examples/Io/NuclearInteractions/include/ActsExamples/Io/NuclearInteractions/RootNuclearInteractionParametersWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootNuclearInteractionParametersWriter.hpp @@ -12,7 +12,7 @@ #include "ActsExamples/EventData/ExtractedSimulationProcess.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/WriterT.hpp" -#include "ActsExamples/Io/NuclearInteractions/detail/NuclearInteractionParametrisation.hpp" +#include "ActsExamples/Io/Root/detail/NuclearInteractionParametrisation.hpp" #include #include diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleWriter.hpp index 99cf6b5cd61..8be9f05b735 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleWriter.hpp @@ -39,7 +39,7 @@ class RootParticleWriter final : public WriterT { std::string inputParticles; /// Optional. If given, the the energy loss and traversed material is /// computed and written. - std::string inputFinalParticles; + std::string inputParticlesFinal; /// Path to the output file. std::string filePath; /// Output file access mode. @@ -74,8 +74,8 @@ class RootParticleWriter final : public WriterT { private: Config m_cfg; - ReadDataHandle m_inputFinalParticles{ - this, "InputFinalParticles"}; + ReadDataHandle m_inputParticlesFinal{ + this, "InputParticlesFinal"}; std::mutex m_writeMutex; diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp similarity index 100% rename from Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFinderPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderPerformanceWriter.hpp similarity index 100% rename from Examples/Io/Performance/ActsExamples/Io/Performance/TrackFinderPerformanceWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderPerformanceWriter.hpp diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFitterPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFitterPerformanceWriter.hpp similarity index 100% rename from Examples/Io/Performance/ActsExamples/Io/Performance/TrackFitterPerformanceWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/TrackFitterPerformanceWriter.hpp diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/VertexPerformanceWriter.hpp similarity index 100% rename from Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/VertexPerformanceWriter.hpp diff --git a/Examples/Io/NuclearInteractions/include/ActsExamples/Io/NuclearInteractions/detail/NuclearInteractionParametrisation.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/detail/NuclearInteractionParametrisation.hpp similarity index 100% rename from Examples/Io/NuclearInteractions/include/ActsExamples/Io/NuclearInteractions/detail/NuclearInteractionParametrisation.hpp rename to Examples/Io/Root/include/ActsExamples/Io/Root/detail/NuclearInteractionParametrisation.hpp diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp b/Examples/Io/Root/src/CKFPerformanceWriter.cpp similarity index 99% rename from Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp rename to Examples/Io/Root/src/CKFPerformanceWriter.cpp index e7fa1525615..7b5b5aaf291 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp +++ b/Examples/Io/Root/src/CKFPerformanceWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/Performance/CKFPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/CKFPerformanceWriter.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Utilities/VectorHelpers.hpp" diff --git a/Examples/Io/Root/src/RootAthenaDumpReader.cpp b/Examples/Io/Root/src/RootAthenaDumpReader.cpp index a76bee1997a..a732bb3b49e 100644 --- a/Examples/Io/Root/src/RootAthenaDumpReader.cpp +++ b/Examples/Io/Root/src/RootAthenaDumpReader.cpp @@ -8,6 +8,7 @@ #include "ActsExamples/Io/Root/RootAthenaDumpReader.hpp" +#include "Acts/Definitions/Units.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Utilities/Zip.hpp" @@ -21,60 +22,47 @@ #include #include -class BarcodeConstructor { - /// Particles with barcodes larger then this value are considered to be - /// secondary particles - /// https://gitlab.cern.ch/atlas/athena/-/blob/main/InnerDetector/InDetGNNTracking/src/DumpObjects.h?ref_type=heads#L101 - constexpr static int s_maxBarcodeForPrimary = 200000; - - std::uint16_t m_primaryCount = 0; - std::uint16_t m_secondaryCount = 0; - std::unordered_map m_barcodeMap; - - static std::uint64_t concatInts(int a, int b) { - auto va = static_cast(a); - auto vb = static_cast(b); - std::uint64_t value = (static_cast(va) << 32) | vb; - return value; - } - - public: - ActsFatras::Barcode getBarcode(int barcode, int evtnumber) { - auto v = concatInts(barcode, evtnumber); - auto found = m_barcodeMap.find(v); - if (found != m_barcodeMap.end()) { - return found->second; - } +using namespace Acts::UnitLiterals; - auto primary = (barcode < s_maxBarcodeForPrimary); +namespace { +std::uint64_t concatInts(int a, int b) { + auto va = static_cast(a); + auto vb = static_cast(b); + std::uint64_t value = (static_cast(va) << 32) | vb; + return value; +} - ActsFatras::Barcode fBarcode; +std::pair splitInt(std::uint64_t v) { + return {static_cast((v & 0xFFFFFFFF00000000LL) >> 32), + static_cast(v & 0xFFFFFFFFLL)}; +} - // vertex primary shouldn't be zero for a valid particle - fBarcode.setVertexPrimary(1); - if (primary) { - fBarcode.setVertexSecondary(0); - fBarcode.setParticle(m_primaryCount); - assert(m_primaryCount < std::numeric_limits::max()); - m_primaryCount++; - } else { - fBarcode.setVertexSecondary(1); - fBarcode.setParticle(m_secondaryCount); - assert(m_primaryCount < std::numeric_limits::max()); - m_secondaryCount++; - } +/// In cases when there is built up a particle collection in an iterative way it +/// can be way faster to build up a vector and afterwards use a special +/// constructor to speed up the set creation. +inline auto particleVectorToSet(std::vector& particles) { + using namespace ActsExamples; + auto cmp = [](const auto& a, const auto& b) { + return a.particleId().value() == b.particleId().value(); + }; + + std::sort(particles.begin(), particles.end(), detail::CompareParticleId{}); + particles.erase(std::unique(particles.begin(), particles.end(), cmp), + particles.end()); + + return SimParticleContainer(boost::container::ordered_unique_range_t{}, + particles.begin(), particles.end()); +} - m_barcodeMap[v] = fBarcode; - return fBarcode; - } -}; +} // namespace enum SpacePointType { ePixel = 1, eStrip = 2 }; -ActsExamples::RootAthenaDumpReader::RootAthenaDumpReader( - const ActsExamples::RootAthenaDumpReader::Config& config, - Acts::Logging::Level level) - : ActsExamples::IReader(), +namespace ActsExamples { + +RootAthenaDumpReader::RootAthenaDumpReader( + const RootAthenaDumpReader::Config& config, Acts::Logging::Level level) + : IReader(), m_cfg(config), m_logger(Acts::getDefaultLogger(name(), level)) { if (m_cfg.inputfile.empty()) { @@ -89,10 +77,17 @@ ActsExamples::RootAthenaDumpReader::RootAthenaDumpReader( m_outputPixelSpacePoints.initialize(m_cfg.outputPixelSpacePoints); m_outputStripSpacePoints.initialize(m_cfg.outputStripSpacePoints); m_outputSpacePoints.initialize(m_cfg.outputSpacePoints); - m_outputClusters.initialize(m_cfg.outputClusters); - m_outputParticles.initialize(m_cfg.outputParticles); - m_outputMeasParticleMap.initialize(m_cfg.outputMeasurementParticlesMap); - m_outputMeasurements.initialize(m_cfg.outputMeasurements); + if (!m_cfg.onlySpacepoints) { + m_outputClusters.initialize(m_cfg.outputClusters); + m_outputParticles.initialize(m_cfg.outputParticles); + m_outputMeasParticleMap.initialize(m_cfg.outputMeasurementParticlesMap); + m_outputMeasurements.initialize(m_cfg.outputMeasurements); + } + + if (m_inputchain->GetBranch("SPtopStripDirection") == nullptr) { + ACTS_WARNING("Additional SP strip features not available"); + m_haveStripFeatures = false; + } // Set the branches @@ -202,13 +197,13 @@ ActsExamples::RootAthenaDumpReader::RootAthenaDumpReader( m_inputchain->SetBranchAddress("SPcovz", SPcovz); m_inputchain->SetBranchAddress("SPhl_topstrip", SPhl_topstrip); m_inputchain->SetBranchAddress("SPhl_botstrip", SPhl_botstrip); - m_inputchain->SetBranchAddress("SPtopStripDirection", SPtopStripDirection); + m_inputchain->SetBranchAddress("SPtopStripDirection", &SPtopStripDirection); m_inputchain->SetBranchAddress("SPbottomStripDirection", - SPbottomStripDirection); + &SPbottomStripDirection); m_inputchain->SetBranchAddress("SPstripCenterDistance", - SPstripCenterDistance); + &SPstripCenterDistance); m_inputchain->SetBranchAddress("SPtopStripCenterPosition", - SPtopStripCenterPosition); + &SPtopStripCenterPosition); m_inputchain->SetBranchAddress("nTRK", &nTRK); m_inputchain->SetBranchAddress("TRKindex", TRKindex); @@ -252,35 +247,19 @@ ActsExamples::RootAthenaDumpReader::RootAthenaDumpReader( m_events = m_inputchain->GetEntries(); ACTS_DEBUG("End of constructor. In total available events=" << m_events); - } // constructor -ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( - const ActsExamples::AlgorithmContext& ctx) { - ACTS_DEBUG("Reading event " << ctx.eventNumber); - auto entry = ctx.eventNumber; - if (entry >= m_events) { - ACTS_ERROR("event out of bounds"); - return ProcessCode::ABORT; - } - - std::lock_guard lock(m_read_mutex); - - m_inputchain->GetEntry(entry); - - // Concat the two 32bit integers from athena to a Fatras barcode - - SimParticleContainer particles; - BarcodeConstructor barcodeConstructor; +SimParticleContainer RootAthenaDumpReader::readParticles() const { + std::vector particles; + particles.reserve(nPartEVT); for (auto ip = 0; ip < nPartEVT; ++ip) { if (m_cfg.onlyPassedParticles && !static_cast(Part_passed[ip])) { continue; } - auto barcode = - barcodeConstructor.getBarcode(Part_barcode[ip], Part_event_number[ip]); - SimParticle particle(barcode, + auto dummyBarcode = concatInts(Part_barcode[ip], Part_event_number[ip]); + SimParticle particle(dummyBarcode, static_cast(Part_pdg_id[ip])); Acts::Vector3 p = Acts::Vector3{Part_px[ip], Part_py[ip], Part_pz[ip]} * @@ -292,26 +271,52 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( auto x = Acts::Vector4{Part_vx[ip], Part_vy[ip], Part_vz[ip], 0.0}; particle.setPosition4(x); - particles.insert(particle); + particles.push_back(particle); + } + + ACTS_DEBUG("Created " << particles.size() << " particles"); + auto before = particles.size(); + + auto particlesSet = particleVectorToSet(particles); + + if (particlesSet.size() < before) { + ACTS_WARNING("Particle IDs not unique for " << before - particles.size() + << " particles!"); } + return particlesSet; +} + +std::tuple, + std::unordered_map> +RootAthenaDumpReader::readMeasurements( + SimParticleContainer& particles, const Acts::GeometryContext& gctx) const { ClusterContainer clusters(nCL); + clusters.reserve(nCL); + MeasurementContainer measurements; measurements.reserve(nCL); + std::size_t nTotalTotZero = 0; + + const auto prevParticlesSize = particles.size(); IndexMultimap measPartMap; + // We cannot use im for the index since we might skip measurements + std::unordered_map imIdxMap; + for (int im = 0; im < nCL; im++) { if (!(CLhardware->at(im) == "PIXEL" || CLhardware->at(im) == "STRIP")) { - ACTS_ERROR("hardware is neither 'PIXEL' or 'STRIP'"); - return ActsExamples::ProcessCode::ABORT; + ACTS_ERROR("hardware is neither 'PIXEL' or 'STRIP', skip particle"); + continue; } ACTS_VERBOSE("Cluster " << im << ": " << CLhardware->at(im)); auto type = (CLhardware->at(im) == "PIXEL") ? ePixel : eStrip; // Make cluster - // TODO refactor ActsExamples::Cluster class so it is not so tedious + // TODO refactor Cluster class so it is not so tedious Cluster cluster; const auto& etas = CLetas->at(im); @@ -326,12 +331,18 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( cluster.sizeLoc0 = *maxEta - *minEta; cluster.sizeLoc1 = *maxPhi - *minPhi; + if (totalTot == 0.0) { + ACTS_VERBOSE("total time over threshold is 0, set all activations to 0"); + nTotalTotZero++; + } + for (const auto& [eta, phi, tot] : Acts::zip(etas, phis, tots)) { // Make best out of what we have: // Weight the overall collected charge corresponding to the // time-over-threshold of each cell Use this as activation (does this make // sense?) - auto activation = CLcharge_count[im] * tot / totalTot; + auto activation = + (totalTot != 0.0) ? CLcharge_count[im] * tot / totalTot : 0.0; // This bases every cluster at zero, but shouldn't matter right now ActsFatras::Segmentizer::Bin2D bin; @@ -344,6 +355,17 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( } cluster.globalPosition = {CLx[im], CLy[im], CLz[im]}; + cluster.localDirection = {CLloc_direction1[im], CLloc_direction2[im], + CLloc_direction3[im]}; + cluster.lengthDirection = {CLJan_loc_direction1[im], + CLJan_loc_direction2[im], + CLJan_loc_direction3[im]}; + cluster.localEta = CLloc_eta[im]; + cluster.localPhi = CLloc_phi[im]; + cluster.globalEta = CLglob_eta[im]; + cluster.globalPhi = CLglob_phi[im]; + cluster.etaAngle = CLeta_angle[im]; + cluster.phiAngle = CLphi_angle[im]; ACTS_VERBOSE("CL shape: " << cluster.channels.size() << "cells, dimensions: " << cluster.sizeLoc0 @@ -357,37 +379,126 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( << CLloc_direction3[im]); const auto& locCov = CLlocal_cov->at(im); + Acts::GeometryIdentifier geoId; + std::vector localParams; + if (m_cfg.geometryIdMap && m_cfg.trackingGeometry) { + const auto& geoIdMap = m_cfg.geometryIdMap->left; + if (geoIdMap.find(CLmoduleID[im]) == geoIdMap.end()) { + ACTS_WARNING("Missing geo id for " << CLmoduleID[im] << ", skip hit"); + continue; + } + + geoId = m_cfg.geometryIdMap->left.at(CLmoduleID[im]); + + auto surface = m_cfg.trackingGeometry->findSurface(geoId); + if (surface == nullptr) { + ACTS_WARNING("Did not find " << geoId + << " in tracking geometry, skip hit"); + continue; + } + + bool inside = + surface->isOnSurface(gctx, cluster.globalPosition, {}, + Acts::BoundaryTolerance::AbsoluteEuclidean{ + m_cfg.absBoundaryTolerance}, + std::numeric_limits::max()); + + if (!inside) { + const Acts::Vector3 v = + surface->transform(gctx).inverse() * cluster.globalPosition; + ACTS_WARNING("Projected position is not in surface bounds for " + << surface->geometryId() << ", skip hit"); + ACTS_WARNING("Position in local coordinates: " << v.transpose()); + ACTS_WARNING("Surface details:\n" << surface->toStream(gctx)); + continue; + } + + const double tol = (type == ePixel) ? Acts::s_onSurfaceTolerance : 1.3_mm; + auto loc = surface->globalToLocal(gctx, cluster.globalPosition, {}, tol); + + if (!loc.ok()) { + const Acts::Vector3 v = + surface->transform(gctx).inverse() * cluster.globalPosition; + ACTS_WARNING("Global-to-local fit failed on " + << geoId << " (z dist: " << v[2] + << ", projected on surface: " << std::boolalpha << inside + << ") , skip hit"); + continue; + } + + // TODO is this in strip coordinates or in polar coordinates for annulus + // bounds? + localParams = std::vector(loc->begin(), loc->end()); + } else { + geoId = Acts::GeometryIdentifier(CLmoduleID[im]); + localParams = {CLloc_direction1[im], CLloc_direction2[im]}; + } + DigitizedParameters digiPars; if (type == ePixel) { digiPars.indices = {Acts::eBoundLoc0, Acts::eBoundLoc1}; - digiPars.values = {CLloc_direction1[im], CLloc_direction2[im]}; assert(locCov.size() == 4); digiPars.variances = {locCov[0], locCov[3]}; + digiPars.values = localParams; } else { - digiPars.values = {CLloc_direction1[im]}; digiPars.indices = {Acts::eBoundLoc0}; assert(!locCov.empty()); digiPars.variances = {locCov[0]}; + digiPars.values = {localParams[0]}; } - IndexSourceLink sl(Acts::GeometryIdentifier{CLmoduleID[im]}, im); - - createMeasurement(measurements, digiPars, sl); + std::size_t measIndex = measurements.size(); + createMeasurement(measurements, geoId, digiPars); // Create measurement particles map and particles container - for (const auto& [subevt, bc] : Acts::zip(CLparticleLink_eventIndex->at(im), - CLparticleLink_barcode->at(im))) { - auto barcode = barcodeConstructor.getBarcode(bc, subevt); - measPartMap.insert(std::pair{im, barcode}); + for (const auto& [subevt, barcode] : + Acts::zip(CLparticleLink_eventIndex->at(im), + CLparticleLink_barcode->at(im))) { + auto dummyBarcode = concatInts(barcode, subevt); + // If we don't find the particle, create one with default values + if (particles.find(dummyBarcode) == particles.end()) { + ACTS_VERBOSE("Particle with subevt " << subevt << ", barcode " + << barcode + << "not found, create dummy one"); + particles.emplace(dummyBarcode, Acts::PdgParticle::eInvalid); + } + measPartMap.insert( + std::pair{measIndex, dummyBarcode}); } + + imIdxMap.emplace(im, measIndex); + } + + if (measurements.size() < static_cast(nCL)) { + ACTS_WARNING("Could not convert " << nCL - measurements.size() << " / " + << nCL << " measurements"); + } + + if (particles.size() - prevParticlesSize > 0) { + ACTS_DEBUG("Created " << particles.size() - prevParticlesSize + << " dummy particles"); + } + + if (nTotalTotZero > 0) { + ACTS_WARNING(nTotalTotZero << " / " << nCL + << " clusters have zero time-over-threshold"); } - // Prepare pixel space points + return {clusters, measurements, measPartMap, imIdxMap}; +} + +std::tuple +RootAthenaDumpReader::readSpacepoints( + const std::optional>& imIdxMap) const { SimSpacePointContainer pixelSpacePoints; + pixelSpacePoints.reserve(nSP); + + SimSpacePointContainer stripSpacePoints; + stripSpacePoints.reserve(nSP); - // Prepare space-point container - // They contain both pixel and SCT space points SimSpacePointContainer spacePoints; + spacePoints.reserve(nSP); // Loop on space points std::size_t skippedSpacePoints = 0; @@ -403,63 +514,215 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( continue; } - Acts::Vector3 globalPos{SPx[isp], SPy[isp], SPz[isp]}; - double sp_covr = SPcovr[isp]; - double sp_covz = SPcovz[isp]; + const Acts::Vector3 globalPos{SPx[isp], SPy[isp], SPz[isp]}; + const double spCovr = SPcovr[isp]; + const double spCovz = SPcovz[isp]; // PIX=1 STRIP = 2 auto type = SPCL2_index[isp] == -1 ? ePixel : eStrip; ACTS_VERBOSE("SP:: " << type << " [" << globalPos.transpose() << "] " - << sp_covr << " " << sp_covz); + << spCovr << " " << spCovz); boost::container::static_vector sLinks; const auto cl1Index = SPCL1_index[isp]; assert(cl1Index >= 0 && cl1Index < nCL); - // NOTE This of course does not produce a valid Acts-stlye geometry id, but - // we can use it for the module map - IndexSourceLink first(Acts::GeometryIdentifier{CLmoduleID[cl1Index]}, - cl1Index); - sLinks.emplace_back(first); - - if (type == eStrip) { - const auto cl2Index = SPCL2_index[isp]; - assert(cl2Index >= 0 && cl2Index < nCL); + auto getGeoId = + [&](auto athenaId) -> std::optional { + if (m_cfg.geometryIdMap == nullptr) { + return Acts::GeometryIdentifier{athenaId}; + } + if (m_cfg.geometryIdMap->left.find(athenaId) == + m_cfg.geometryIdMap->left.end()) { + return std::nullopt; + } + return m_cfg.geometryIdMap->left.at(athenaId); + }; + + auto cl1GeoId = getGeoId(CLmoduleID[cl1Index]); + if (!cl1GeoId) { + ACTS_WARNING("Could not find geoId for spacepoint cluster 1"); + continue; + } - // NOTE This of course does not produce a valid Acts-stlye geometry id, - // but we can use it for the module map - IndexSourceLink second(Acts::GeometryIdentifier{CLmoduleID[cl2Index]}, - cl2Index); - sLinks.emplace_back(second); + if (imIdxMap && !imIdxMap->contains(cl1Index)) { + ACTS_WARNING("Measurement 1 for spacepoint " << isp << " not created"); + continue; } - SimSpacePoint sp(globalPos, std::nullopt, sp_covr, sp_covz, std::nullopt, + IndexSourceLink first(*cl1GeoId, + imIdxMap ? imIdxMap->at(cl1Index) : cl1Index); + sLinks.emplace_back(first); + + // First create pixel spacepoint here, later maybe overwrite with strip + // spacepoint + SimSpacePoint sp(globalPos, std::nullopt, spCovr, spCovz, std::nullopt, sLinks); if (type == ePixel) { pixelSpacePoints.push_back(sp); + } else { + const auto cl2Index = SPCL2_index[isp]; + assert(cl2Index >= 0 && cl2Index < nCL); + + auto cl2GeoId = getGeoId(CLmoduleID[cl1Index]); + if (!cl2GeoId) { + ACTS_WARNING("Could not find geoId for spacepoint cluster 2"); + continue; + } + + if (imIdxMap && !imIdxMap->contains(cl2Index)) { + ACTS_WARNING("Measurement 2 for spacepoint " << isp << " not created"); + continue; + } + + IndexSourceLink second(*cl2GeoId, + imIdxMap ? imIdxMap->at(cl2Index) : cl2Index); + sLinks.emplace_back(second); + + using Vector3f = Eigen::Matrix; + Vector3f topStripDirection = Vector3f::Zero(); + Vector3f bottomStripDirection = Vector3f::Zero(); + Vector3f stripCenterDistance = Vector3f::Zero(); + Vector3f topStripCenterPosition = Vector3f::Zero(); + + if (m_haveStripFeatures) { + topStripDirection = {SPtopStripDirection->at(isp).at(0), + SPtopStripDirection->at(isp).at(1), + SPtopStripDirection->at(isp).at(2)}; + bottomStripDirection = {SPbottomStripDirection->at(isp).at(0), + SPbottomStripDirection->at(isp).at(1), + SPbottomStripDirection->at(isp).at(2)}; + stripCenterDistance = {SPstripCenterDistance->at(isp).at(0), + SPstripCenterDistance->at(isp).at(1), + SPstripCenterDistance->at(isp).at(2)}; + topStripCenterPosition = {SPtopStripCenterPosition->at(isp).at(0), + SPtopStripCenterPosition->at(isp).at(1), + SPtopStripCenterPosition->at(isp).at(2)}; + } + sp = SimSpacePoint(globalPos, std::nullopt, spCovr, spCovz, std::nullopt, + sLinks, SPhl_topstrip[isp], SPhl_botstrip[isp], + topStripDirection.cast(), + bottomStripDirection.cast(), + stripCenterDistance.cast(), + topStripCenterPosition.cast()); + + stripSpacePoints.push_back(sp); } spacePoints.push_back(sp); } - ACTS_DEBUG("Created " << particles.size() << " particles"); if (m_cfg.skipOverlapSPsEta || m_cfg.skipOverlapSPsPhi) { ACTS_DEBUG("Skipped " << skippedSpacePoints << " because of eta/phi overlaps"); } + if (spacePoints.size() < static_cast(nSP)) { + ACTS_WARNING("Could not convert " << nSP - spacePoints.size() << " of " + << nSP << " spacepoints"); + } + ACTS_DEBUG("Created " << spacePoints.size() << " overall space points"); ACTS_DEBUG("Created " << pixelSpacePoints.size() << " " << " pixel space points"); + return {spacePoints, pixelSpacePoints, stripSpacePoints}; +} + +std::pair> +RootAthenaDumpReader::reprocessParticles( + const SimParticleContainer& particles, + const IndexMultimap& measPartMap) const { + std::vector newParticles; + newParticles.reserve(particles.size()); + IndexMultimap newMeasPartMap; + + const auto partMeasMap = invertIndexMultimap(measPartMap); + + std::uint16_t primaryCount = 0; + std::uint16_t secondaryCount = 0; + + for (const auto& particle : particles) { + const auto [begin, end] = partMeasMap.equal_range(particle.particleId()); + + if (begin == end) { + ACTS_VERBOSE("Particle " << particle.particleId().value() + << " has no measurements"); + continue; + } + + auto [athBarcode, athSubevent] = splitInt(particle.particleId().value()); + auto primary = (athBarcode < s_maxBarcodeForPrimary); + + ActsFatras::Barcode fatrasBarcode; + + // vertex primary shouldn't be zero for a valid particle + fatrasBarcode.setVertexPrimary(1); + if (primary) { + fatrasBarcode.setVertexSecondary(0); + fatrasBarcode.setParticle(primaryCount); + assert(primaryCount < std::numeric_limits::max()); + primaryCount++; + } else { + fatrasBarcode.setVertexSecondary(1); + fatrasBarcode.setParticle(secondaryCount); + assert(primaryCount < std::numeric_limits::max()); + secondaryCount++; + } + + auto newParticle = particle.withParticleId(fatrasBarcode); + newParticles.push_back(newParticle); + + for (auto it = begin; it != end; ++it) { + newMeasPartMap.insert( + std::pair{it->second, fatrasBarcode}); + } + } + + ACTS_DEBUG("After reprocessing particles " << newParticles.size() << " of " + << particles.size() << " remain"); + return {particleVectorToSet(newParticles), newMeasPartMap}; +} + +ProcessCode RootAthenaDumpReader::read(const AlgorithmContext& ctx) { + ACTS_DEBUG("Reading event " << ctx.eventNumber); + auto entry = ctx.eventNumber; + if (entry >= m_events) { + ACTS_ERROR("event out of bounds"); + return ProcessCode::ABORT; + } + + std::lock_guard lock(m_read_mutex); + + m_inputchain->GetEntry(entry); + + std::optional> optImIdxMap; + + if (!m_cfg.onlySpacepoints) { + auto candidateParticles = readParticles(); + + auto [clusters, measurements, candidateMeasPartMap, imIdxMap] = + readMeasurements(candidateParticles, ctx.geoContext); + optImIdxMap.emplace(std::move(imIdxMap)); + + auto [particles, measPartMap] = + reprocessParticles(candidateParticles, candidateMeasPartMap); + + m_outputClusters(ctx, std::move(clusters)); + m_outputParticles(ctx, std::move(particles)); + m_outputMeasParticleMap(ctx, std::move(measPartMap)); + m_outputMeasurements(ctx, std::move(measurements)); + } + + auto [spacePoints, pixelSpacePoints, stripSpacePoints] = + readSpacepoints(optImIdxMap); + m_outputPixelSpacePoints(ctx, std::move(pixelSpacePoints)); + m_outputStripSpacePoints(ctx, std::move(stripSpacePoints)); m_outputSpacePoints(ctx, std::move(spacePoints)); - m_outputClusters(ctx, std::move(clusters)); - m_outputParticles(ctx, std::move(particles)); - m_outputMeasParticleMap(ctx, std::move(measPartMap)); - m_outputMeasurements(ctx, std::move(measurements)); return ProcessCode::SUCCESS; } +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootMaterialDecorator.cpp b/Examples/Io/Root/src/RootMaterialDecorator.cpp index b8f49c53ea1..37f396bb86c 100644 --- a/Examples/Io/Root/src/RootMaterialDecorator.cpp +++ b/Examples/Io/Root/src/RootMaterialDecorator.cpp @@ -148,8 +148,8 @@ ActsExamples::RootMaterialDecorator::RootMaterialDecorator( std::vector hists{n, v, o, min, max, t, x0, l0, A, Z, rho}; // Only go on when you have all histograms - if (std::all_of(hists.begin(), hists.end(), - [](const auto* hist) { return hist != nullptr; })) { + if (std::ranges::all_of( + hists, [](const auto* hist) { return hist != nullptr; })) { // Get the number of bins int nbins0 = t->GetNbinsX(); int nbins1 = t->GetNbinsY(); diff --git a/Examples/Io/Root/src/RootMeasurementWriter.cpp b/Examples/Io/Root/src/RootMeasurementWriter.cpp index a716cb721a3..b7ab332fdea 100644 --- a/Examples/Io/Root/src/RootMeasurementWriter.cpp +++ b/Examples/Io/Root/src/RootMeasurementWriter.cpp @@ -12,7 +12,6 @@ #include "Acts/Utilities/Enumerate.hpp" #include "ActsExamples/EventData/AverageSimHits.hpp" #include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/Range.hpp" @@ -270,8 +269,7 @@ ProcessCode RootMeasurementWriter::writeT( const ConstVariableBoundMeasurementProxy meas = measurements.getMeasurement(hitIdx); - Acts::GeometryIdentifier geoId = - meas.sourceLink().template get().geometryId(); + Acts::GeometryIdentifier geoId = meas.geometryId(); // find the corresponding surface auto surfaceItr = m_cfg.surfaceByIdentifier.find(geoId); if (surfaceItr == m_cfg.surfaceByIdentifier.end()) { diff --git a/Examples/Io/NuclearInteractions/src/RootNuclearInteractionParametersWriter.cpp b/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp similarity index 99% rename from Examples/Io/NuclearInteractions/src/RootNuclearInteractionParametersWriter.cpp rename to Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp index 64a324242c8..e0d635443e5 100644 --- a/Examples/Io/NuclearInteractions/src/RootNuclearInteractionParametersWriter.cpp +++ b/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/NuclearInteractions/RootNuclearInteractionParametersWriter.hpp" +#include "ActsExamples/Io/Root/RootNuclearInteractionParametersWriter.hpp" #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" diff --git a/Examples/Io/Root/src/RootParticleWriter.cpp b/Examples/Io/Root/src/RootParticleWriter.cpp index fbd1333c38c..436601eb97d 100644 --- a/Examples/Io/Root/src/RootParticleWriter.cpp +++ b/Examples/Io/Root/src/RootParticleWriter.cpp @@ -36,7 +36,7 @@ ActsExamples::RootParticleWriter::RootParticleWriter( throw std::invalid_argument("Missing tree name"); } - m_inputFinalParticles.maybeInitialize(m_cfg.inputFinalParticles); + m_inputParticlesFinal.maybeInitialize(m_cfg.inputParticlesFinal); // open root file and create the tree m_outputFile = TFile::Open(m_cfg.filePath.c_str(), m_cfg.fileMode.c_str()); @@ -73,7 +73,7 @@ ActsExamples::RootParticleWriter::RootParticleWriter( m_outputTree->Branch("generation", &m_generation); m_outputTree->Branch("sub_particle", &m_subParticle); - if (m_inputFinalParticles.isInitialized()) { + if (m_inputParticlesFinal.isInitialized()) { m_outputTree->Branch("e_loss", &m_eLoss); m_outputTree->Branch("total_x0", &m_pathInX0); m_outputTree->Branch("total_l0", &m_pathInL0); @@ -102,8 +102,8 @@ ActsExamples::ProcessCode ActsExamples::RootParticleWriter::finalize() { ActsExamples::ProcessCode ActsExamples::RootParticleWriter::writeT( const AlgorithmContext& ctx, const SimParticleContainer& particles) { const SimParticleContainer* finalParticles = nullptr; - if (m_inputFinalParticles.isInitialized()) { - finalParticles = &m_inputFinalParticles(ctx); + if (m_inputParticlesFinal.isInitialized()) { + finalParticles = &m_inputParticlesFinal(ctx); } // ensure exclusive access to tree/file while writing diff --git a/Examples/Io/Root/src/RootSimHitReader.cpp b/Examples/Io/Root/src/RootSimHitReader.cpp index 1e503d8711c..4f0d805dc7e 100644 --- a/Examples/Io/Root/src/RootSimHitReader.cpp +++ b/Examples/Io/Root/src/RootSimHitReader.cpp @@ -108,9 +108,9 @@ std::pair RootSimHitReader::availableEvents() const { } ProcessCode RootSimHitReader::read(const AlgorithmContext& context) { - auto it = std::find_if( - m_eventMap.begin(), m_eventMap.end(), - [&](const auto& a) { return std::get<0>(a) == context.eventNumber; }); + auto it = std::ranges::find_if(m_eventMap, [&](const auto& a) { + return std::get<0>(a) == context.eventNumber; + }); if (it == m_eventMap.end()) { // explicitly warn if it happens for the first or last event as that might diff --git a/Examples/Io/Root/src/RootTrackStatesWriter.cpp b/Examples/Io/Root/src/RootTrackStatesWriter.cpp index ad8467f239c..160eada776b 100644 --- a/Examples/Io/Root/src/RootTrackStatesWriter.cpp +++ b/Examples/Io/Root/src/RootTrackStatesWriter.cpp @@ -44,8 +44,6 @@ namespace ActsExamples { -class IndexSourceLink; - using Acts::VectorHelpers::eta; using Acts::VectorHelpers::perp; using Acts::VectorHelpers::phi; diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp b/Examples/Io/Root/src/SeedingPerformanceWriter.cpp similarity index 99% rename from Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp rename to Examples/Io/Root/src/SeedingPerformanceWriter.cpp index b2d87be68f9..4ac7ea269ef 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp +++ b/Examples/Io/Root/src/SeedingPerformanceWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "SeedingPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/SeedingPerformanceWriter.hpp" #include "Acts/Utilities/MultiIndex.hpp" #include "Acts/Utilities/VectorHelpers.hpp" diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFinderPerformanceWriter.cpp b/Examples/Io/Root/src/TrackFinderPerformanceWriter.cpp similarity index 99% rename from Examples/Io/Performance/ActsExamples/Io/Performance/TrackFinderPerformanceWriter.cpp rename to Examples/Io/Root/src/TrackFinderPerformanceWriter.cpp index abef2ef0664..14e04252283 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFinderPerformanceWriter.cpp +++ b/Examples/Io/Root/src/TrackFinderPerformanceWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/Performance/TrackFinderPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/TrackFinderPerformanceWriter.hpp" #include "Acts/Definitions/Units.hpp" #include "ActsExamples/EventData/Index.hpp" diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFitterPerformanceWriter.cpp b/Examples/Io/Root/src/TrackFitterPerformanceWriter.cpp similarity index 98% rename from Examples/Io/Performance/ActsExamples/Io/Performance/TrackFitterPerformanceWriter.cpp rename to Examples/Io/Root/src/TrackFitterPerformanceWriter.cpp index 54441ad7aa8..2063f03b53b 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/TrackFitterPerformanceWriter.cpp +++ b/Examples/Io/Root/src/TrackFitterPerformanceWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/Performance/TrackFitterPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/TrackFitterPerformanceWriter.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/EventData/TrackParameters.hpp" diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp b/Examples/Io/Root/src/VertexPerformanceWriter.cpp similarity index 99% rename from Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp rename to Examples/Io/Root/src/VertexPerformanceWriter.cpp index dfc790433f9..f032e39af35 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp +++ b/Examples/Io/Root/src/VertexPerformanceWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/Performance/VertexPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/VertexPerformanceWriter.hpp" #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" diff --git a/Examples/Io/NuclearInteractions/src/detail/NuclearInteractionParametrisation.cpp b/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp similarity index 99% rename from Examples/Io/NuclearInteractions/src/detail/NuclearInteractionParametrisation.cpp rename to Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp index f9df3d38f85..91f59c56648 100644 --- a/Examples/Io/NuclearInteractions/src/detail/NuclearInteractionParametrisation.cpp +++ b/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/NuclearInteractions/detail/NuclearInteractionParametrisation.hpp" +#include "ActsExamples/Io/Root/detail/NuclearInteractionParametrisation.hpp" #include "Acts/Definitions/Common.hpp" #include "ActsFatras/EventData/Particle.hpp" diff --git a/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgDefaults.hpp b/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgDefaults.hpp index 14d0474776e..b4e6c4f8178 100644 --- a/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgDefaults.hpp +++ b/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgDefaults.hpp @@ -22,7 +22,7 @@ static inline Acts::Svg::Style layerStyle() { lStyle.highlights = {"mouseover", "mouseout"}; lStyle.strokeColor = {25, 25, 25}; lStyle.strokeWidth = 0.5; - lStyle.nSegments = 72u; + lStyle.quarterSegments = 72u; return lStyle; } @@ -43,7 +43,7 @@ static inline Acts::Svg::Style backgroundStyle() { bgStyle.highlights = {}; bgStyle.strokeColor = {25, 25, 25}; bgStyle.strokeWidth = 0.5; - bgStyle.nSegments = 72u; + bgStyle.quarterSegments = 72u; return bgStyle; } @@ -55,7 +55,7 @@ static inline Acts::Svg::Style pointStyle() { pStyle.highlights = {"mouseover", "mouseout"}; pStyle.strokeColor = {0, 0, 0}; pStyle.strokeWidth = 0.5; - pStyle.nSegments = 72u; + pStyle.quarterSegments = 72u; return pStyle; } diff --git a/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgPointWriter.hpp b/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgPointWriter.hpp index 632ce5efab0..3ad6527a882 100644 --- a/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgPointWriter.hpp +++ b/Examples/Io/Svg/include/ActsExamples/Io/Svg/SvgPointWriter.hpp @@ -87,6 +87,7 @@ class SvgPointWriter final : public WriterT> { s_pointStyle; //!< The style of the space point to be drawn std::string infoBoxTitle = ""; //!< If an info box title is set, draw it + Acts::Svg::Style infoTitleStyle = s_infoStyle; Acts::Svg::Style infoBoxStyle = s_infoStyle; // The style of the info box bool projectionXY = true; ///< xy projection @@ -170,7 +171,8 @@ ActsExamples::ProcessCode ActsExamples::SvgPointWriter::writeT( auto xyIbox = Acts::Svg::infoBox( static_cast(point3D.x() + 10.), static_cast(point3D.y() - 10.), m_cfg.infoBoxTitle, - {"Position: " + Acts::toString(point3D)}, m_cfg.infoBoxStyle, p); + m_cfg.infoTitleStyle, {"Position: " + Acts::toString(point3D)}, + m_cfg.infoBoxStyle, p); xyView.add_object(xyIbox); } } diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index ff1e5901f43..c932b3ef081 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -54,11 +54,9 @@ target_link_libraries( ActsExamplesDetectorTGeo ActsExamplesMagneticField ActsExamplesIoRoot - ActsExamplesIoNuclearInteractions ActsExamplesIoCsv ActsExamplesIoObj ActsExamplesIoJson - ActsExamplesIoPerformance ActsExamplesGenerators ActsExamplesPrinters ActsExamplesTrackFinding diff --git a/Examples/Python/python/acts/examples/itk.py b/Examples/Python/python/acts/examples/itk.py index b9d92b04001..b8e2eba8796 100644 --- a/Examples/Python/python/acts/examples/itk.py +++ b/Examples/Python/python/acts/examples/itk.py @@ -372,8 +372,8 @@ def itkSeedingAlgConfig( ) zOriginWeightFactor = 1 compatSeedWeight = 100 - phiMin = 0 - phiMax = 2 * math.pi + phiMin = -math.pi + phiMax = math.pi phiBinDeflectionCoverage = 3 numPhiNeighbors = 1 maxPhiBins = 200 diff --git a/Examples/Python/python/acts/examples/odd.py b/Examples/Python/python/acts/examples/odd.py index 4e1efb1c783..a28058af78b 100644 --- a/Examples/Python/python/acts/examples/odd.py +++ b/Examples/Python/python/acts/examples/odd.py @@ -76,8 +76,9 @@ def getOpenDataDetector( } def geoid_hook(geoid, surface): + gctx = acts.GeometryContext() if geoid.volume() in volumeRadiusCutsMap: - r = math.sqrt(surface.center()[0] ** 2 + surface.center()[1] ** 2) + r = math.sqrt(surface.center(gctx)[0] ** 2 + surface.center(gctx)[1] ** 2) geoid.setExtra(1) for cut in volumeRadiusCutsMap[geoid.volume()]: diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index e47b435e72b..38d64100fd3 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -2,6 +2,7 @@ from typing import Optional, Union, List from enum import Enum from collections import namedtuple +import math import acts import acts.examples @@ -13,12 +14,6 @@ "Default TruthSmeared TruthEstimated Orthogonal HoughTransform Gbts Hashing", ) -TruthSeedRanges = namedtuple( - "TruthSeedRanges", - ["rho", "z", "phi", "eta", "absEta", "pt", "nHits"], - defaults=[(None, None)] * 7, -) - ParticleSmearingSigmas = namedtuple( "ParticleSmearingSigmas", ["d0", "d0PtA", "d0PtB", "z0", "z0PtA", "z0PtB", "t0", "phi", "theta", "ptRel"], @@ -147,8 +142,24 @@ "maxPixelHoles", "maxStripHoles", "trimTracks", + "constrainToVolumes", + "endOfWorldVolumes", + ], + defaults=[ + 15.0, + 25.0, + 10, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, ], - defaults=[15.0, 25.0, 10, None, None, None, None, None, None, None, None], ) AmbiguityResolutionConfig = namedtuple( @@ -214,7 +225,6 @@ class VertexFinder(Enum): @acts.examples.NamedTypeArgs( seedingAlgorithm=SeedingAlgorithm, - truthSeedRanges=TruthSeedRanges, particleSmearingSigmas=ParticleSmearingSigmas, seedFinderConfigArg=SeedFinderConfigArg, seedFinderOptionsArg=SeedFinderOptionsArg, @@ -234,7 +244,6 @@ def addSeeding( layerMappingConfigFile: Optional[Union[Path, str]] = None, connector_inputConfigFile: Optional[Union[Path, str]] = None, seedingAlgorithm: SeedingAlgorithm = SeedingAlgorithm.Default, - truthSeedRanges: Optional[TruthSeedRanges] = TruthSeedRanges(), particleSmearingSigmas: ParticleSmearingSigmas = ParticleSmearingSigmas(), initialSigmas: Optional[list] = None, initialSigmaPtRel: Optional[float] = None, @@ -256,6 +265,7 @@ def addSeeding( acts.ParticleHypothesis ] = acts.ParticleHypothesis.pion, inputParticles: str = "particles", + selectedParticles: str = "particles_selected", outputDirRoot: Optional[Union[Path, str]] = None, outputDirCsv: Optional[Union[Path, str]] = None, logLevel: Optional[acts.logging.Level] = None, @@ -272,10 +282,6 @@ def addSeeding( Json file for space point geometry selection. Not required for SeedingAlgorithm.TruthSmeared. seedingAlgorithm : SeedingAlgorithm, Default seeding algorithm to use: one of Default (no truth information used), TruthSmeared, TruthEstimated - truthSeedRanges : TruthSeedRanges(rho, z, phi, eta, absEta, pt, nHits) - TruthSeedSelector configuration. Each range is specified as a tuple of (min,max). - Defaults of no cuts specified in Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedSelector.hpp - If specified as None, don't run ParticleSmearing at all (and use addCKFTracks(selectedParticles="particles")) particleSmearingSigmas : ParticleSmearingSigmas(d0, d0PtA, d0PtB, z0, z0PtA, z0PtB, t0, phi, theta, ptRel) ParticleSmearing configuration. Defaults specified in Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.hpp @@ -307,6 +313,8 @@ def addSeeding( The hypothesis used for track finding. Defaults to pion. inputParticles : str, "particles" input particles name in the WhiteBoard + selectedParticles : str, "particles_selected" + selected particles name in the WhiteBoard outputDirRoot : Path|str, path, None the output folder for the Root output, None triggers no output logLevel : acts.logging.Level, None @@ -319,18 +327,6 @@ def addSeeding( logger = acts.logging.getLogger("addSeeding") logger.setLevel(logLevel) - if truthSeedRanges is not None: - selectedParticles = "truth_seeds_selected" - addSeedingTruthSelection( - s, - inputParticles, - selectedParticles, - truthSeedRanges, - logLevel, - ) - else: - selectedParticles = inputParticles - # Create starting parameters from either particle smearing or combined seed # finding and track parameters estimation if seedingAlgorithm == SeedingAlgorithm.TruthSmeared: @@ -386,7 +382,6 @@ def addSeeding( logger.info("Using Hough Transform seeding") houghTransformConfig.inputSpacePoints = [spacePoints] houghTransformConfig.inputMeasurements = "measurements" - houghTransformConfig.inputSourceLinks = "sourcelinks" houghTransformConfig.outputProtoTracks = "prototracks" houghTransformConfig.outputSeeds = "seeds" houghTransformConfig.trackingGeometry = trackingGeometry @@ -490,41 +485,6 @@ def addSeeding( return s -def addSeedingTruthSelection( - s: acts.examples.Sequencer, - inputParticles: str, - outputParticles: str, - truthSeedRanges: TruthSeedRanges, - logLevel: acts.logging.Level = None, -): - """adds truth particles filtering before filtering - For parameters description see addSeeding - """ - selAlg = acts.examples.TruthSeedSelector( - **acts.examples.defaultKWArgs( - ptMin=truthSeedRanges.pt[0], - ptMax=truthSeedRanges.pt[1], - etaMin=truthSeedRanges.eta[0], - etaMax=truthSeedRanges.eta[1], - nHitsMin=truthSeedRanges.nHits[0], - nHitsMax=truthSeedRanges.nHits[1], - rhoMin=truthSeedRanges.rho[0], - rhoMax=truthSeedRanges.rho[1], - zMin=truthSeedRanges.z[0], - zMax=truthSeedRanges.z[1], - phiMin=truthSeedRanges.phi[0], - phiMax=truthSeedRanges.phi[1], - absEtaMin=truthSeedRanges.absEta[0], - absEtaMax=truthSeedRanges.absEta[1], - ), - level=logLevel, - inputParticles=inputParticles, - inputMeasurementParticlesMap="measurement_particles_map", - outputParticles=outputParticles, - ) - s.addAlgorithm(selAlg) - - def addTruthSmearedSeeding( s: acts.examples.Sequencer, rnd: Optional[acts.examples.RandomNumbers], @@ -618,7 +578,6 @@ def addSpacePointsMaking( logLevel = acts.examples.defaultLogging(sequence, logLevel)() spAlg = acts.examples.SpacePointMaker( level=logLevel, - inputSourceLinks="sourcelinks", inputMeasurements="measurements", outputSpacePoints="spacepoints", trackingGeometry=trackingGeometry, @@ -1151,7 +1110,6 @@ def addGbtsSeeding( geometrySelection=acts.examples.readJsonGeometryList( str(geoSelectionConfigFile) ), - inputSourceLinks="sourcelinks", trackingGeometry=trackingGeometry, fill_module_csv=False, inputClusters="clusters", @@ -1219,7 +1177,7 @@ def addSeedFilterML( from acts.examples.onnx import SeedFilterMLAlgorithm inputParticles = "particles" - selectedParticles = "truth_seeds_selected" + selectedParticles = "particles_selected" seeds = "seeds" estParams = "estimatedparameters" @@ -1361,8 +1319,13 @@ def addTruthTrackingGsf( ) -> None: customLogLevel = acts.examples.defaultLogging(s, logLevel) + # NOTE we specify clampToRange as True to silence warnings in the test about + # queries to the loss distribution outside the specified range, since no dedicated + # approximation for the ODD is done yet. + bha = acts.examples.AtlasBetheHeitlerApprox.makeDefault(clampToRange=True) + gsfOptions = { - "betheHeitlerApprox": acts.examples.AtlasBetheHeitlerApprox.makeDefault(), + "betheHeitlerApprox": bha, "maxComponents": 12, "componentMergeMethod": acts.examples.ComponentMergeMethod.maxWeight, "mixtureReductionAlgorithm": acts.examples.MixtureReductionAlgorithm.KLDistance, @@ -1511,7 +1474,6 @@ def addCKFTracks( ] ), inputMeasurements="measurements", - inputSourceLinks="sourcelinks", inputInitialTrackParameters="estimatedparameters", inputSeeds=( "estimatedseeds" @@ -1531,11 +1493,13 @@ def addCKFTracks( reverseSearch=reverseSearch, seedDeduplication=ckfConfig.seedDeduplication, stayOnSeed=ckfConfig.stayOnSeed, - pixelVolumes=ckfConfig.pixelVolumes, - stripVolumes=ckfConfig.stripVolumes, + pixelVolumeIds=ckfConfig.pixelVolumes, + stripVolumeIds=ckfConfig.stripVolumes, maxPixelHoles=ckfConfig.maxPixelHoles, maxStripHoles=ckfConfig.maxStripHoles, trimTracks=ckfConfig.trimTracks, + constrainToVolumeIds=ckfConfig.constrainToVolumes, + endOfWorldVolumeIds=ckfConfig.endOfWorldVolumes, ), ) s.addAlgorithm(trackFinder) @@ -1658,10 +1622,6 @@ def addTrackWriters( trackStatesWriter = acts.examples.RootTrackStatesWriter( level=customLogLevel(), inputTracks=tracks, - # @note The full particles collection is used here to avoid lots of warnings - # since the unselected CKF track might have a majority particle not in the - # filtered particle collection. This could be avoided when a separate track - # selection algorithm is used. inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", inputSimHits="simhits", @@ -1676,10 +1636,6 @@ def addTrackWriters( trackSummaryWriter = acts.examples.RootTrackSummaryWriter( level=customLogLevel(), inputTracks=tracks, - # @note The full particles collection is used here to avoid lots of warnings - # since the unselected CKF track might have a majority particle not in the - # filtered particle collection. This could be avoided when a separate track - # selection algorithm is used. inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDirRoot / f"tracksummary_{name}.root"), @@ -1693,7 +1649,7 @@ def addTrackWriters( ckfPerfWriter = acts.examples.CKFPerformanceWriter( level=customLogLevel(), inputTracks=tracks, - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", inputParticleTrackMatching="particle_track_matching", filePath=str(outputDirRoot / f"performance_{name}.root"), @@ -1794,7 +1750,6 @@ def addExaTrkX( s.addAlgorithm( acts.examples.SpacePointMaker( level=customLogLevel(), - inputSourceLinks="sourcelinks", inputMeasurements="measurements", outputSpacePoints="spacepoints", trackingGeometry=trackingGeometry, diff --git a/Examples/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index 125ce422c16..8f6b6d0e603 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -355,7 +355,8 @@ def addParticleSelection( config: ParticleSelectorConfig, inputParticles: str, outputParticles: str, - inputMeasurementParticlesMap: str = "", + inputParticlesFinal: Optional[str] = None, + outputParticlesFinal: Optional[str] = None, logLevel: Optional[acts.logging.Level] = None, ) -> None: """ @@ -371,12 +372,18 @@ def addParticleSelection( the identifier for the input particles to be selected outputParticles: str the identifier for the final selected particle collection + inputParticlesFinal: str, None + the identifier for the input final particles to be selected + outputParticlesFinal: str, None + the identifier for the final selected final particle collection """ customLogLevel = acts.examples.defaultLogging(s, logLevel) s.addAlgorithm( acts.examples.ParticleSelector( **acts.examples.defaultKWArgs( + inputParticlesFinal=inputParticlesFinal, + outputParticlesFinal=outputParticlesFinal, rhoMin=config.rho[0], rhoMax=config.rho[1], absZMin=config.absZ[0], @@ -402,7 +409,6 @@ def addParticleSelection( level=customLogLevel(), inputParticles=inputParticles, outputParticles=outputParticles, - inputMeasurementParticlesMap=inputMeasurementParticlesMap, ) ) @@ -489,27 +495,22 @@ def addFatras( # Selector if postSelectParticles is not None: particlesInitial = "fatras_particles_initial_selected" - addParticleSelection( - s, - postSelectParticles, - inputParticles=alg.config.outputParticlesInitial, - outputParticles=particlesInitial, - ) - particlesFinal = "fatras_particles_final_selected" addParticleSelection( s, postSelectParticles, - inputParticles=alg.config.outputParticlesFinal, - outputParticles=particlesFinal, + inputParticles=outputParticlesInitial, + inputParticlesFinal=outputParticlesFinal, + outputParticles=particlesInitial, + outputParticlesFinal=particlesFinal, ) - s.addWhiteboardAlias("particles_selected", particlesFinal) + s.addWhiteboardAlias("particles_selected", particlesInitial) else: - particlesInitial = alg.config.outputParticlesInitial - particlesFinal = alg.config.outputParticlesFinal + particlesInitial = outputParticlesInitial + particlesFinal = outputParticlesFinal # Only add alias for 'particles_initial' as this is the one we use most - s.addWhiteboardAlias("particles", particlesInitial) + s.addWhiteboardAlias("particles", outputParticlesInitial) # Output addSimWriters( @@ -573,7 +574,7 @@ def addSimWriters( acts.examples.RootParticleWriter( level=customLogLevel(), inputParticles=particlesInitial, - inputFinalParticles=particlesFinal, + inputParticlesFinal=particlesFinal, filePath=str(outputDirRoot / "particles_simulation.root"), ) ) @@ -739,27 +740,22 @@ def addGeant4( # Selector if postSelectParticles is not None: particlesInitial = "geant4_particles_initial_postselected" - addParticleSelection( - s, - postSelectParticles, - inputParticles=alg.config.outputParticlesInitial, - outputParticles=particlesInitial, - ) - particlesFinal = "geant4_particles_final_postselected" addParticleSelection( s, postSelectParticles, - inputParticles=alg.config.outputParticlesFinal, - outputParticles=particlesFinal, + inputParticles=outputParticlesInitial, + inputParticlesFinal=outputParticlesFinal, + outputParticles=particlesInitial, + outputParticlesFinal=particlesFinal, ) - s.addWhiteboardAlias("particles_selected", particlesFinal) + s.addWhiteboardAlias("particles_selected", particlesInitial) else: - particlesInitial = alg.config.outputParticlesInitial - particlesFinal = alg.config.outputParticlesFinal + particlesInitial = outputParticlesInitial + particlesFinal = outputParticlesFinal # Only add alias for 'particles_initial' as this is the one we use most - s.addWhiteboardAlias("particles", particlesInitial) + s.addWhiteboardAlias("particles", outputParticlesInitial) # Output addSimWriters( @@ -818,7 +814,6 @@ def addDigitization( surfaceByIdentifier=trackingGeometry.geoIdSurfaceMap(), randomNumbers=rnd, inputSimHits="simhits", - outputSourceLinks="sourcelinks", outputMeasurements="measurements", outputMeasurementParticlesMap="measurement_particles_map", outputMeasurementSimHitsMap="measurement_simhits_map", diff --git a/Examples/Python/src/Base.cpp b/Examples/Python/src/Base.cpp index 4d5eec557bd..cc8a5f67dc1 100644 --- a/Examples/Python/src/Base.cpp +++ b/Examples/Python/src/Base.cpp @@ -364,12 +364,16 @@ void addBinning(Context& ctx) { .value("binY", Acts::BinningValue::binY) .value("binZ", Acts::BinningValue::binZ) .value("binR", Acts::BinningValue::binR) - .value("binPhi", Acts::BinningValue::binPhi); + .value("binPhi", Acts::BinningValue::binPhi) + .value("binRPhi", Acts::BinningValue::binRPhi) + .value("binH", Acts::BinningValue::binH) + .value("binEta", Acts::BinningValue::binEta) + .value("binMag", Acts::BinningValue::binMag); auto boundaryType = py::enum_(m, "AxisBoundaryType") - .value("bound", Acts::AxisBoundaryType::Bound) - .value("closed", Acts::AxisBoundaryType::Closed) - .value("open", Acts::AxisBoundaryType::Open); + .value("Bound", Acts::AxisBoundaryType::Bound) + .value("Closed", Acts::AxisBoundaryType::Closed) + .value("Open", Acts::AxisBoundaryType::Open); auto axisType = py::enum_(m, "AxisType") .value("equidistant", Acts::AxisType::Equidistant) diff --git a/Examples/Python/src/Digitization.cpp b/Examples/Python/src/Digitization.cpp index a68d4871724..c392d6dcc8c 100644 --- a/Examples/Python/src/Digitization.cpp +++ b/Examples/Python/src/Digitization.cpp @@ -62,7 +62,6 @@ void addDigitization(Context& ctx) { ACTS_PYTHON_STRUCT_BEGIN(c, Config); ACTS_PYTHON_MEMBER(inputSimHits); - ACTS_PYTHON_MEMBER(outputSourceLinks); ACTS_PYTHON_MEMBER(outputMeasurements); ACTS_PYTHON_MEMBER(outputClusters); ACTS_PYTHON_MEMBER(outputMeasurementParticlesMap); diff --git a/Examples/Python/src/EDM4hepComponent.cpp b/Examples/Python/src/EDM4hepComponent.cpp index 5017596b7db..75746587be8 100644 --- a/Examples/Python/src/EDM4hepComponent.cpp +++ b/Examples/Python/src/EDM4hepComponent.cpp @@ -42,7 +42,7 @@ PYBIND11_MODULE(ActsPythonBindingsEDM4hep, m) { ACTS_PYTHON_DECLARE_READER(ActsExamples::EDM4hepMeasurementReader, m, "EDM4hepMeasurementReader", inputPath, outputMeasurements, outputMeasurementSimHitsMap, - outputSourceLinks, outputClusters); + outputClusters); ACTS_PYTHON_DECLARE_WRITER(ActsExamples::EDM4hepMeasurementWriter, m, "EDM4hepMeasurementWriter", inputMeasurements, diff --git a/Examples/Python/src/ExaTrkXTrackFinding.cpp b/Examples/Python/src/ExaTrkXTrackFinding.cpp index db9190e7f66..3778e8576f5 100644 --- a/Examples/Python/src/ExaTrkXTrackFinding.cpp +++ b/Examples/Python/src/ExaTrkXTrackFinding.cpp @@ -251,9 +251,8 @@ void addExaTrkXTrackFinding(Context &ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::TrackFindingFromPrototrackAlgorithm, mex, "TrackFindingFromPrototrackAlgorithm", inputProtoTracks, - inputMeasurements, inputSourceLinks, inputInitialTrackParameters, - outputTracks, measurementSelectorCfg, trackingGeometry, magneticField, - findTracks, tag); + inputMeasurements, inputInitialTrackParameters, outputTracks, + measurementSelectorCfg, trackingGeometry, magneticField, findTracks, tag); } } // namespace Acts::Python diff --git a/Examples/Python/src/Geant4Component.cpp b/Examples/Python/src/Geant4Component.cpp index 14e7b138888..ff34da94cc0 100644 --- a/Examples/Python/src/Geant4Component.cpp +++ b/Examples/Python/src/Geant4Component.cpp @@ -334,7 +334,8 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { const std::vector& sensitiveMatches, const std::vector& - passiveMatches) { + passiveMatches, + bool convertMaterial) { // Initiate the detector construction & retrieve world ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName); const auto* world = gdmlContruction.Construct(); @@ -351,6 +352,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { Acts::Geant4DetectorSurfaceFactory::Options options; options.sensitiveSurfaceSelector = sensitiveSelectors; options.passiveSurfaceSelector = passiveSelectors; + options.convertMaterial = convertMaterial; G4Transform3D nominal; Acts::Geant4DetectorSurfaceFactory factory; diff --git a/Examples/Python/src/GeoModel.cpp b/Examples/Python/src/GeoModel.cpp index 162062aa970..58609a573d5 100644 --- a/Examples/Python/src/GeoModel.cpp +++ b/Examples/Python/src/GeoModel.cpp @@ -17,12 +17,17 @@ #include "Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp" #include "Acts/Plugins/GeoModel/GeoModelConverters.hpp" #include "Acts/Plugins/GeoModel/GeoModelDetectorElement.hpp" +#include "Acts/Plugins/GeoModel/GeoModelDetectorElementITk.hpp" #include "Acts/Plugins/GeoModel/GeoModelDetectorObjectFactory.hpp" #include "Acts/Plugins/GeoModel/GeoModelReader.hpp" #include "Acts/Plugins/GeoModel/GeoModelTree.hpp" #include "Acts/Plugins/GeoModel/IGeoShapeConverter.hpp" #include "Acts/Plugins/Python/Utilities.hpp" -#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Surfaces/AnnulusBounds.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp" #include @@ -46,7 +51,13 @@ void addGeoModel(Context& ctx) { py::class_>( - gm, "GeoModelDetectorElement"); + gm, "GeoModelDetectorElement") + .def("logVolName", &Acts::GeoModelDetectorElement::logVolName) + .def("databaseEntryName", + &Acts::GeoModelDetectorElement::databaseEntryName) + .def("surface", [](Acts::GeoModelDetectorElement self) { + return self.surface().getSharedPtr(); + }); // Shape converters { @@ -181,8 +192,60 @@ void addGeoModel(Context& ctx) { .def_readwrite( "topBoundsOverride", &Acts::GeoModelBlueprintCreater::Options::topBoundsOverride) - .def_readwrite("table", - &Acts::GeoModelBlueprintCreater::Options::table); + .def_readwrite("table", &Acts::GeoModelBlueprintCreater::Options::table) + .def_readwrite("dotGraph", + &Acts::GeoModelBlueprintCreater::Options::dotGraph); } + + gm.def( + "splitBarrelModule", + [](const Acts::GeometryContext& gctx, + std::shared_ptr detElement, + unsigned nSegments, Acts::Logging::Level logLevel) { + auto logger = Acts::getDefaultLogger("ITkSlitBarrel", logLevel); + auto name = detElement->databaseEntryName(); + + auto factory = [&](const auto& trafo, const auto& bounds) { + return Acts::GeoModelDetectorElement::createDetectorElement< + Acts::PlaneSurface, Acts::RectangleBounds>( + detElement->physicalVolume(), bounds, trafo, + detElement->thickness()); + }; + + return ActsExamples::ITk::splitBarrelModule(gctx, detElement, nSegments, + factory, name, *logger); + }, + "gxtx"_a, "detElement"_a, "nSegments"_a, + "logLevel"_a = Acts::Logging::INFO); + + gm.def( + "splitDiscModule", + [](const Acts::GeometryContext& gctx, + std::shared_ptr detElement, + const std::vector>& patterns, + Acts::Logging::Level logLevel) { + auto logger = Acts::getDefaultLogger("ITkSlitBarrel", logLevel); + auto name = detElement->databaseEntryName(); + + auto factory = [&](const auto& trafo, const auto& bounds) { + return Acts::GeoModelDetectorElement::createDetectorElement< + Acts::DiscSurface, Acts::AnnulusBounds>( + detElement->physicalVolume(), bounds, trafo, + detElement->thickness()); + }; + + return ActsExamples::ITk::splitDiscModule(gctx, detElement, patterns, + factory, name, *logger); + }, + "gxtx"_a, "detElement"_a, "splitRanges"_a, + "logLevel"_a = Acts::Logging::INFO); + + py::class_>( + gm, "GeoModelDetectorElementITk") + .def("surface", [](Acts::GeoModelDetectorElementITk& self) { + return self.surface().getSharedPtr(); + }); + gm.def("convertToItk", &GeoModelDetectorElementITk::convertFromGeomodel); } } // namespace Acts::Python diff --git a/Examples/Python/src/Geometry.cpp b/Examples/Python/src/Geometry.cpp index 4fb0f07d07a..d676dbe6243 100644 --- a/Examples/Python/src/Geometry.cpp +++ b/Examples/Python/src/Geometry.cpp @@ -25,6 +25,7 @@ #include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Detector/interface/IRootVolumeFinderBuilder.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryHierarchyMap.hpp" @@ -32,11 +33,14 @@ #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Material/ISurfaceMaterial.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/SurfaceArray.hpp" +#include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/RangeXD.hpp" +#include "Acts/Visualization/ViewConfig.hpp" #include "ActsExamples/Geometry/VolumeAssociationTest.hpp" #include @@ -44,6 +48,7 @@ #include #include +#include #include #include @@ -104,18 +109,23 @@ void addGeometry(Context& ctx) { .def("approach", &Acts::GeometryIdentifier::approach) .def("sensitive", &Acts::GeometryIdentifier::sensitive) .def("extra", &Acts::GeometryIdentifier::extra) - .def("value", &Acts::GeometryIdentifier::value); + .def("value", &Acts::GeometryIdentifier::value) + .def("__str__", [](const Acts::GeometryIdentifier& self) { + std::stringstream ss; + ss << self; + return ss.str(); + }); } { py::class_>(m, "Surface") + // Can't bind directly because GeometryObject is virtual base of Surface .def("geometryId", - [](Acts::Surface& self) { return self.geometryId(); }) - .def("center", - [](Acts::Surface& self) { - return self.center(Acts::GeometryContext{}); - }) - .def("type", [](Acts::Surface& self) { return self.type(); }); + [](const Surface& self) { return self.geometryId(); }) + .def("center", &Surface::center) + .def("type", &Surface::type) + .def("visualize", &Surface::visualize) + .def("surfaceMaterial", &Acts::Surface::surfaceMaterialSharedPtr); } { @@ -167,20 +177,40 @@ void addGeometry(Context& ctx) { }) .def_property_readonly( "highestTrackingVolume", - &Acts::TrackingGeometry::highestTrackingVolumePtr); + &Acts::TrackingGeometry::highestTrackingVolumePtr) + .def("visualize", &Acts::TrackingGeometry::visualize, py::arg("helper"), + py::arg("gctx"), py::arg("viewConfig") = s_viewVolume, + py::arg("portalViewConfig") = s_viewPortal, + py::arg("sensitiveViewConfig") = s_viewSensitive); } { py::class_>( - m, "VolumeBounds"); - - py::class_, Acts::VolumeBounds>( - m, "CylinderVolumeBounds") - .def(py::init(), - "rmin"_a, "rmax"_a, "halfz"_a, "halfphi"_a = M_PI, "avgphi"_a = 0., - "bevelMinZ"_a = 0., "bevelMaxZ"_a = 0.); + m, "VolumeBounds") + .def("type", &Acts::VolumeBounds::type) + .def("__str__", [](const Acts::VolumeBounds& self) { + std::stringstream ss; + ss << self; + return ss.str(); + }); + + auto cvb = + py::class_, + Acts::VolumeBounds>(m, "CylinderVolumeBounds") + .def(py::init(), + "rmin"_a, "rmax"_a, "halfz"_a, "halfphi"_a = M_PI, + "avgphi"_a = 0., "bevelMinZ"_a = 0., "bevelMaxZ"_a = 0.); + + py::enum_(cvb, "Face") + .value("PositiveDisc", CylinderVolumeBounds::Face::PositiveDisc) + .value("NegativeDisc", CylinderVolumeBounds::Face::NegativeDisc) + .value("OuterCylinder", CylinderVolumeBounds::Face::OuterCylinder) + .value("InnerCylinder", CylinderVolumeBounds::Face::InnerCylinder) + .value("NegativePhiPlane", CylinderVolumeBounds::Face::NegativePhiPlane) + .value("PositivePhiPlane", + CylinderVolumeBounds::Face::PositivePhiPlane); } { @@ -203,22 +233,72 @@ void addGeometry(Context& ctx) { })); } + py::class_(m, "ExtentEnvelope") + .def(py::init<>()) + .def(py::init()) + .def(py::init([](Envelope x, Envelope y, Envelope z, Envelope r, + Envelope phi, Envelope rPhi, Envelope h, Envelope eta, + Envelope mag) { + return ExtentEnvelope({.x = x, + .y = y, + .z = z, + .r = r, + .phi = phi, + .rPhi = rPhi, + .h = h, + .eta = eta, + .mag = mag}); + }), + py::arg("x") = zeroEnvelope, py::arg("y") = zeroEnvelope, + py::arg("z") = zeroEnvelope, py::arg("r") = zeroEnvelope, + py::arg("phi") = zeroEnvelope, py::arg("rPhi") = zeroEnvelope, + py::arg("h") = zeroEnvelope, py::arg("eta") = zeroEnvelope, + py::arg("mag") = zeroEnvelope) + .def_static("Zero", &ExtentEnvelope::Zero) + .def("__getitem__", [](ExtentEnvelope& self, + BinningValue bValue) { return self[bValue]; }) + .def("__setitem__", [](ExtentEnvelope& self, BinningValue bValue, + const Envelope& value) { self[bValue] = value; }) + .def("__str__", [](const ExtentEnvelope& self) { + std::array values; + + std::stringstream ss; + for (BinningValue val : allBinningValues()) { + ss << val << "=(" << self[val][0] << ", " << self[val][1] << ")"; + values.at(toUnderlying(val)) = ss.str(); + ss.str(""); + } + + ss.str(""); + ss << "ExtentEnvelope("; + ss << boost::algorithm::join(values, ", "); + ss << ")"; + return ss.str(); + }); + + py::class_(m, "Extent") + .def(py::init(), + py::arg("envelope") = ExtentEnvelope::Zero()) + .def("range", + [](const Acts::Extent& self, + Acts::BinningValue bval) -> std::array { + return {self.min(bval), self.max(bval)}; + }) + .def("__str__", &Extent::toString); + { - py::class_(m, "Extent") - .def(py::init( - [](const std::vector>>& - franges) { - Acts::Extent extent; - for (const auto& [bval, frange] : franges) { - extent.set(bval, frange[0], frange[1]); - } - return extent; - })) - .def("range", [](const Acts::Extent& self, Acts::BinningValue bval) { - return std::array{self.min(bval), - self.max(bval)}; - }); + auto cylStack = py::class_(m, "CylinderVolumeStack"); + + py::enum_(cylStack, + "AttachmentStrategy") + .value("Gap", CylinderVolumeStack::AttachmentStrategy::Gap) + .value("First", CylinderVolumeStack::AttachmentStrategy::First) + .value("Second", CylinderVolumeStack::AttachmentStrategy::Second) + .value("Midpoint", CylinderVolumeStack::AttachmentStrategy::Midpoint); + + py::enum_(cylStack, "ResizeStrategy") + .value("Gap", CylinderVolumeStack::ResizeStrategy::Gap) + .value("Expand", CylinderVolumeStack::ResizeStrategy::Expand); } } @@ -287,7 +367,7 @@ void addExperimentalGeometry(Context& ctx) { for (const auto& surface : smap) { auto gid = surface->geometryId(); // Exclusion criteria - if (sensitiveOnly and gid.sensitive() == 0) { + if (sensitiveOnly && gid.sensitive() == 0) { continue; }; surfaceVolumeLayerMap[gid.volume()][gid.layer()].push_back(surface); @@ -301,10 +381,15 @@ void addExperimentalGeometry(Context& ctx) { // Be able to construct a proto binning py::class_(m, "ProtoBinning") .def(py::init&, std::size_t>()) + const std::vector&, std::size_t>(), + "bValue"_a, "bType"_a, "e"_a, "exp"_a = 0u) .def(py::init()); + std::size_t>(), + "bValue"_a, "bType"_a, "minE"_a, "maxE"_a, "nbins"_a, "exp"_a = 0u) + .def(py::init(), + "bValue"_a, "bType"_a, "nbins"_a, "exp"_a = 0u); } { @@ -333,7 +418,7 @@ void addExperimentalGeometry(Context& ctx) { ACTS_PYTHON_MEMBER(surfacesProvider); ACTS_PYTHON_MEMBER(supports); ACTS_PYTHON_MEMBER(binnings); - ACTS_PYTHON_MEMBER(nSegments); + ACTS_PYTHON_MEMBER(quarterSegments); ACTS_PYTHON_MEMBER(auxiliary); ACTS_PYTHON_STRUCT_END(); diff --git a/Examples/Python/src/Input.cpp b/Examples/Python/src/Input.cpp index f75af4f4271..b3ad9e1f657 100644 --- a/Examples/Python/src/Input.cpp +++ b/Examples/Python/src/Input.cpp @@ -63,8 +63,8 @@ void addInput(Context& ctx) { ACTS_PYTHON_DECLARE_READER( ActsExamples::CsvMeasurementReader, mex, "CsvMeasurementReader", inputDir, - outputMeasurements, outputMeasurementSimHitsMap, outputSourceLinks, - outputClusters, outputMeasurementParticlesMap, inputSimHits); + outputMeasurements, outputMeasurementSimHitsMap, outputClusters, + outputMeasurementParticlesMap, inputSimHits); ACTS_PYTHON_DECLARE_READER(ActsExamples::CsvSimHitReader, mex, "CsvSimHitReader", inputDir, inputStem, @@ -93,7 +93,16 @@ void addInput(Context& ctx) { ACTS_PYTHON_DECLARE_READER( ActsExamples::RootAthenaDumpReader, mex, "RootAthenaDumpReader", treename, inputfile, outputMeasurements, outputPixelSpacePoints, - outputStripSpacePoints, outputSpacePoints, outputClusters); + outputStripSpacePoints, outputSpacePoints, outputClusters, + outputMeasurementParticlesMap, outputParticles, onlyPassedParticles, + skipOverlapSPsPhi, skipOverlapSPsEta, geometryIdMap, trackingGeometry, + absBoundaryTolerance); + +#ifdef WITH_GEOMODEL_PLUGIN + ACTS_PYTHON_DECLARE_READER(ActsExamples::RootAthenaDumpGeoIdCollector, mex, + "RootAthenaDumpGeoIdCollector", treename, + inputfile, trackingGeometry, geometryIdMap); +#endif ACTS_PYTHON_DECLARE_READER(ActsExamples::RootSimHitReader, mex, "RootSimHitReader", treeName, filePath, diff --git a/Examples/Python/src/Material.cpp b/Examples/Python/src/Material.cpp index bbc7199e237..5bc5aa6a1f3 100644 --- a/Examples/Python/src/Material.cpp +++ b/Examples/Python/src/Material.cpp @@ -9,6 +9,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Material/BinnedSurfaceMaterialAccumulater.hpp" +#include "Acts/Material/HomogeneousSurfaceMaterial.hpp" #include "Acts/Material/IMaterialDecorator.hpp" #include "Acts/Material/ISurfaceMaterial.hpp" #include "Acts/Material/IVolumeMaterial.hpp" @@ -16,6 +17,7 @@ #include "Acts/Material/MaterialMapper.hpp" #include "Acts/Material/MaterialValidater.hpp" #include "Acts/Material/PropagatorMaterialAssigner.hpp" +#include "Acts/Material/ProtoSurfaceMaterial.hpp" #include "Acts/Material/SurfaceMaterialMapper.hpp" #include "Acts/Material/VolumeMaterialMapper.hpp" #include "Acts/Plugins/Json/ActsJson.hpp" @@ -57,7 +59,20 @@ void addMaterial(Context& ctx) { { py::class_>( - m, "ISurfaceMaterial"); + m, "ISurfaceMaterial") + .def("toString", &Acts::ISurfaceMaterial::toString); + + py::class_>( + m, "ProtoGridSurfaceMaterial"); + + py::class_>(m, + "ProtoSurfaceMaterial"); + + py::class_>( + m, "HomogeneousSurfaceMaterial"); py::class_>( m, "IVolumeMaterial"); diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index 77348edaf1c..1a0e27db907 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -120,6 +120,7 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addAlgebra(ctx); addBinning(ctx); addEventData(ctx); + addOutput(ctx); addPropagation(ctx); addGeometryBuildingGen1(ctx); @@ -128,7 +129,6 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addMagneticField(ctx); addMaterial(ctx); - addOutput(ctx); addDetector(ctx); addExampleAlgorithms(ctx); addInput(ctx); diff --git a/Examples/Python/src/Obj.cpp b/Examples/Python/src/Obj.cpp index ce03e60a3fe..09acb0c53b6 100644 --- a/Examples/Python/src/Obj.cpp +++ b/Examples/Python/src/Obj.cpp @@ -6,6 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +#include "Acts/Visualization/IVisualization3D.hpp" #include #include #include @@ -19,6 +20,7 @@ #include #include +#include namespace py = pybind11; using namespace pybind11::literals; @@ -35,7 +37,7 @@ void addObj(Context& ctx) { /// @param surfaces is the collection of surfaces /// @param viewContext is the geometry context /// @param viewRgb is the color of the surfaces - /// @param viewSegements is the number of segments to approximate a full circle + /// @param viewSegments is the number of segments to approximate a quarter of a circle /// @param fileName is the path to the output file /// mex.def("writeSurfacesObj", @@ -87,5 +89,8 @@ void addObj(Context& ctx) { obj.write(fileName); }); } + + py::class_(m, "ObjVisualization3D") + .def(py::init<>()); } } // namespace Acts::Python diff --git a/Examples/Python/src/Output.cpp b/Examples/Python/src/Output.cpp index 328ae9ecd92..bda45b3cab7 100644 --- a/Examples/Python/src/Output.cpp +++ b/Examples/Python/src/Output.cpp @@ -10,6 +10,7 @@ #include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/Logger.hpp" +#include "Acts/Visualization/IVisualization3D.hpp" #include "Acts/Visualization/ViewConfig.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" @@ -25,16 +26,12 @@ #include "ActsExamples/Io/Csv/CsvTrackParameterWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackingGeometryWriter.hpp" -#include "ActsExamples/Io/NuclearInteractions/RootNuclearInteractionParametersWriter.hpp" -#include "ActsExamples/Io/Performance/CKFPerformanceWriter.hpp" -#include "ActsExamples/Io/Performance/SeedingPerformanceWriter.hpp" -#include "ActsExamples/Io/Performance/TrackFinderPerformanceWriter.hpp" -#include "ActsExamples/Io/Performance/TrackFitterPerformanceWriter.hpp" -#include "ActsExamples/Io/Performance/VertexPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/CKFPerformanceWriter.hpp" #include "ActsExamples/Io/Root/RootBFieldWriter.hpp" #include "ActsExamples/Io/Root/RootMaterialTrackWriter.hpp" #include "ActsExamples/Io/Root/RootMaterialWriter.hpp" #include "ActsExamples/Io/Root/RootMeasurementWriter.hpp" +#include "ActsExamples/Io/Root/RootNuclearInteractionParametersWriter.hpp" #include "ActsExamples/Io/Root/RootParticleWriter.hpp" #include "ActsExamples/Io/Root/RootPropagationStepsWriter.hpp" #include "ActsExamples/Io/Root/RootPropagationSummaryWriter.hpp" @@ -45,6 +42,10 @@ #include "ActsExamples/Io/Root/RootTrackStatesWriter.hpp" #include "ActsExamples/Io/Root/RootTrackSummaryWriter.hpp" #include "ActsExamples/Io/Root/RootVertexWriter.hpp" +#include "ActsExamples/Io/Root/SeedingPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/TrackFinderPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/TrackFitterPerformanceWriter.hpp" +#include "ActsExamples/Io/Root/VertexPerformanceWriter.hpp" #include "ActsExamples/MaterialMapping/IMaterialWriter.hpp" #include "ActsExamples/Plugins/Obj/ObjPropagationStepsWriter.hpp" #include "ActsExamples/Plugins/Obj/ObjTrackingGeometryWriter.hpp" @@ -57,6 +58,7 @@ #include #include +#include namespace Acts { class TrackingGeometry; @@ -119,7 +121,7 @@ void addOutput(Context& ctx) { ACTS_PYTHON_MEMBER(offset); ACTS_PYTHON_MEMBER(lineThickness); ACTS_PYTHON_MEMBER(surfaceThickness); - ACTS_PYTHON_MEMBER(nSegments); + ACTS_PYTHON_MEMBER(quarterSegments); ACTS_PYTHON_MEMBER(triangulate); ACTS_PYTHON_MEMBER(outputName); ACTS_PYTHON_STRUCT_END(); @@ -130,10 +132,14 @@ void addOutput(Context& ctx) { .def(py::init<>()) .def(py::init()) .def(py::init()) - .def(py::init()) + .def(py::init()) .def_readonly("rgb", &Color::rgb); } + py::class_(m, "IVisualization3D") + .def("write", py::overload_cast( + &IVisualization3D::write, py::const_)); + { using Writer = ActsExamples::ObjTrackingGeometryWriter; auto w = py::class_>( @@ -188,7 +194,7 @@ void addOutput(Context& ctx) { ACTS_PYTHON_DECLARE_WRITER(ActsExamples::RootParticleWriter, mex, "RootParticleWriter", inputParticles, - inputFinalParticles, filePath, fileMode, treeName); + inputParticlesFinal, filePath, fileMode, treeName); ACTS_PYTHON_DECLARE_WRITER(ActsExamples::RootVertexWriter, mex, "RootVertexWriter", inputVertices, filePath, diff --git a/Examples/Python/src/Svg.cpp b/Examples/Python/src/Svg.cpp index 2c0219d0466..301ceea1af3 100644 --- a/Examples/Python/src/Svg.cpp +++ b/Examples/Python/src/Svg.cpp @@ -225,7 +225,11 @@ void addSvg(Context& ctx) { ACTS_PYTHON_MEMBER(highlights); ACTS_PYTHON_MEMBER(strokeWidth); ACTS_PYTHON_MEMBER(strokeColor); - ACTS_PYTHON_MEMBER(nSegments); + ACTS_PYTHON_MEMBER(highlightStrokeWidth); + ACTS_PYTHON_MEMBER(highlightStrokeColor); + ACTS_PYTHON_MEMBER(fontSize); + ACTS_PYTHON_MEMBER(fontColor); + ACTS_PYTHON_MEMBER(quarterSegments); ACTS_PYTHON_STRUCT_END(); } @@ -297,6 +301,8 @@ void addSvg(Context& ctx) { svg.def("drawArrow", &actsvg::draw::arrow); svg.def("drawText", &actsvg::draw::text); + + svg.def("drawInfoBox", &Svg::infoBox); } // Draw Eta Lines @@ -328,6 +334,21 @@ void addSvg(Context& ctx) { }); } + { + auto gco = py::class_(svg, "GridOptions") + .def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(gco, Svg::GridConverter::Options); + ACTS_PYTHON_MEMBER(style); + ACTS_PYTHON_STRUCT_END(); + + auto isco = py::class_( + svg, "IndexedSurfacesOptions") + .def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(isco, Svg::IndexedSurfacesConverter::Options); + ACTS_PYTHON_MEMBER(gridOptions); + ACTS_PYTHON_STRUCT_END(); + } + // How detector volumes are drawn: Svg DetectorVolume options & drawning { auto c = py::class_( @@ -338,12 +359,16 @@ void addSvg(Context& ctx) { ACTS_PYTHON_MEMBER(portalIndices); ACTS_PYTHON_MEMBER(portalOptions); ACTS_PYTHON_MEMBER(surfaceOptions); + ACTS_PYTHON_MEMBER(indexedSurfacesOptions); ACTS_PYTHON_STRUCT_END(); // Define the proto volume & indexed surface grid py::class_(svg, "ProtoVolume"); py::class_(svg, "ProtoIndexedSurfaceGrid"); + // Define the proto grid + py::class_(svg, "ProtoGrid"); + // Convert an Acts::Experimental::DetectorVolume object into an // acts::svg::proto::volume svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert); @@ -352,6 +377,15 @@ void addSvg(Context& ctx) { svg.def("drawDetectorVolume", &drawDetectorVolume); } + // Draw the ProtoIndexedSurfaceGrid + { + svg.def("drawIndexedSurfaces", + [](const Svg::ProtoIndexedSurfaceGrid& pIndexedSurfaceGrid, + const std::string& identification) { + return Svg::View::xy(pIndexedSurfaceGrid, identification); + }); + } + // How a detector is drawn: Svg Detector options & drawning { svg.def("drawDetector", &drawDetector); } diff --git a/Examples/Python/src/TrackFinding.cpp b/Examples/Python/src/TrackFinding.cpp index 05ebcb3c7a7..ad3ad364ce7 100644 --- a/Examples/Python/src/TrackFinding.cpp +++ b/Examples/Python/src/TrackFinding.cpp @@ -58,10 +58,9 @@ namespace Acts::Python { void addTrackFinding(Context& ctx) { auto [m, mex] = ctx.get("main", "examples"); - ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::SpacePointMaker, mex, - "SpacePointMaker", inputSourceLinks, - inputMeasurements, outputSpacePoints, - trackingGeometry, geometrySelection); + ACTS_PYTHON_DECLARE_ALGORITHM( + ActsExamples::SpacePointMaker, mex, "SpacePointMaker", inputMeasurements, + outputSpacePoints, trackingGeometry, geometrySelection); { using Config = Acts::SeedFilterConfig; @@ -274,14 +273,14 @@ void addTrackFinding(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::GbtsSeedingAlgorithm, mex, "GbtsSeedingAlgorithm", inputSpacePoints, outputSeeds, seedFilterConfig, seedFinderConfig, - seedFinderOptions, layerMappingFile, geometrySelection, inputSourceLinks, - trackingGeometry, ActsGbtsMap, fill_module_csv, inputClusters); + seedFinderOptions, layerMappingFile, geometrySelection, trackingGeometry, + ActsGbtsMap, fill_module_csv, inputClusters); ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::HoughTransformSeeder, mex, "HoughTransformSeeder", - inputSpacePoints, outputProtoTracks, inputSourceLinks, trackingGeometry, - geometrySelection, inputMeasurements, subRegions, nLayers, xMin, xMax, - yMin, yMax, houghHistSize_x, houghHistSize_y, hitExtend_x, threshold, + inputSpacePoints, outputProtoTracks, trackingGeometry, geometrySelection, + inputMeasurements, subRegions, nLayers, xMin, xMax, yMin, yMax, + houghHistSize_x, houghHistSize_y, hitExtend_x, threshold, localMaxWindowSize, kA); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::MuonHoughSeeder, mex, @@ -323,7 +322,6 @@ void addTrackFinding(Context& ctx) { auto c = py::class_(alg, "Config").def(py::init<>()); ACTS_PYTHON_STRUCT_BEGIN(c, Config); ACTS_PYTHON_MEMBER(inputMeasurements); - ACTS_PYTHON_MEMBER(inputSourceLinks); ACTS_PYTHON_MEMBER(inputInitialTrackParameters); ACTS_PYTHON_MEMBER(inputSeeds); ACTS_PYTHON_MEMBER(outputTracks); @@ -337,11 +335,13 @@ void addTrackFinding(Context& ctx) { ACTS_PYTHON_MEMBER(reverseSearch); ACTS_PYTHON_MEMBER(seedDeduplication); ACTS_PYTHON_MEMBER(stayOnSeed); - ACTS_PYTHON_MEMBER(pixelVolumes); - ACTS_PYTHON_MEMBER(stripVolumes); + ACTS_PYTHON_MEMBER(pixelVolumeIds); + ACTS_PYTHON_MEMBER(stripVolumeIds); ACTS_PYTHON_MEMBER(maxPixelHoles); ACTS_PYTHON_MEMBER(maxStripHoles); ACTS_PYTHON_MEMBER(trimTracks); + ACTS_PYTHON_MEMBER(constrainToVolumeIds); + ACTS_PYTHON_MEMBER(endOfWorldVolumeIds); ACTS_PYTHON_STRUCT_END(); } diff --git a/Examples/Python/src/TrackFitting.cpp b/Examples/Python/src/TrackFitting.cpp index 20c6010c8ee..c8dc72f9eef 100644 --- a/Examples/Python/src/TrackFitting.cpp +++ b/Examples/Python/src/TrackFitting.cpp @@ -39,6 +39,7 @@ namespace py = pybind11; using namespace ActsExamples; using namespace Acts; +using namespace py::literals; namespace Acts::Python { @@ -106,12 +107,17 @@ void addTrackFitting(Context& ctx) { .value("KLDistance", MixtureReductionAlgorithm::KLDistance); py::class_(mex, "AtlasBetheHeitlerApprox") - .def_static("loadFromFiles", - &ActsExamples::BetheHeitlerApprox::loadFromFiles, - py::arg("lowParametersPath"), py::arg("highParametersPath"), - py::arg("lowLimit") = 0.1, py::arg("highLimit") = 0.2) - .def_static("makeDefault", - []() { return Acts::makeDefaultBetheHeitlerApprox(); }); + .def_static( + "loadFromFiles", &ActsExamples::BetheHeitlerApprox::loadFromFiles, + "lowParametersPath"_a, "highParametersPath"_a, "lowLimit"_a = 0.1, + "highLimit"_a = 0.2, "clampToRange"_a = false) + .def_static( + "makeDefault", + [](bool clampToRange) { + return Acts::makeDefaultBetheHeitlerApprox(clampToRange); + }, + "clampToRange"_a = false); + mex.def( "makeGsfFitterFunction", [](std::shared_ptr trackingGeometry, diff --git a/Examples/Python/src/TruthTracking.cpp b/Examples/Python/src/TruthTracking.cpp index 85d34698b7b..2c43b3cfc4d 100644 --- a/Examples/Python/src/TruthTracking.cpp +++ b/Examples/Python/src/TruthTracking.cpp @@ -108,8 +108,9 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_STRUCT_BEGIN(c, Config); ACTS_PYTHON_MEMBER(inputParticles); - ACTS_PYTHON_MEMBER(inputMeasurementParticlesMap); + ACTS_PYTHON_MEMBER(inputParticlesFinal); ACTS_PYTHON_MEMBER(outputParticles); + ACTS_PYTHON_MEMBER(outputParticlesFinal); ACTS_PYTHON_MEMBER(rhoMin); ACTS_PYTHON_MEMBER(rhoMax); ACTS_PYTHON_MEMBER(absZMin); diff --git a/Examples/Python/src/Utilities.cpp b/Examples/Python/src/Utilities.cpp index 7a68a6a3bdb..8993da1c46a 100644 --- a/Examples/Python/src/Utilities.cpp +++ b/Examples/Python/src/Utilities.cpp @@ -51,7 +51,7 @@ void addUtilities(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::MeasurementMapSelector, mex, "MeasurementMapSelector", - inputMeasurementParticleMap, inputSourceLinks, + inputMeasurements, inputMeasurementParticleMap, outputMeasurementParticleMap, geometrySelection); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::PrototracksToTracks, mex, diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index 368a7c45ef3..4374b827580 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -3,25 +3,25 @@ test_fatras__particles_simulation.root: bc970873fef0c2efd86ed5413623802353d2cd04 test_fatras__hits.root: 6e4beb045fa1712c4d14c280ba33c3fa13e4aff9de88d55c3e32f62ad226f724 test_geant4__particles_simulation.root: 3c9c6265183b04c9d62ed5f2d0709c6dd74e33fbb53ac0aeb3274f6600257fc1 test_geant4__hits.root: adf5dcdf000a580412dc5089e17460897d6535c978eafa021584ba4281d0a1ac -test_seeding__estimatedparams.root: ff9b876f07de7e352c71f11dccdbaed93b6ef0328bba40c4f6c9fdb2090f386e +test_seeding__estimatedparams.root: 69c0e268f9025a0991a212ea2a7f26f53112fecf614b475605bd1cb08415ba56 test_seeding__performance_seeding.root: 992f9c611d30dde0d3f3ab676bab19ada61ab6a4442828e27b65ec5e5b7a2880 test_seeding__particles.root: 7855b021f39ad238bca098e4282667be0666f2d1630e5bcb9d51d3b5ee39fa14 -test_seeding__particles_simulation.root: 87d9c6c82422ca381a17735096b36c547eacb5dda2f26d7377614bd97a70ab1a -test_hashing_seeding__estimatedparams.root: 77658e952bbceb5a61b0235c76f4dbb7bb5994a8ef32b769197ddcfe46ade241 -test_seeding_orthogonal__estimatedparams.root: 47718b57d59d9b9686bd7a7bb5958d668c0a953ad6b094601ca7b27f530db786 +test_seeding__particles_simulation.root: f937a4cc474e80cfbb6eac4384e42e9c5c7ac981fcd6870d624cc898d1a0c006 +test_hashing_seeding__estimatedparams.root: 1f43b760e80089b5674e106d00d962d74be564cbf33ae38222052ebb6f9cbf3a +test_seeding_orthogonal__estimatedparams.root: ca5896ec325daf5c8012291bc454269c61c32fe3d7e33bd1fa3b812826930299 test_seeding_orthogonal__performance_seeding.root: 60fbedcf5cb2b37cd8e526251940564432890d3a159d231ed819e915a904682c test_seeding_orthogonal__particles.root: 7855b021f39ad238bca098e4282667be0666f2d1630e5bcb9d51d3b5ee39fa14 -test_seeding_orthogonal__particles_simulation.root: 87d9c6c82422ca381a17735096b36c547eacb5dda2f26d7377614bd97a70ab1a -test_itk_seeding__estimatedparams.root: 1266e99f1885a3c74a58471a4d6e750ad8fd87c7372a9ab929e8b99603b42ac8 +test_seeding_orthogonal__particles_simulation.root: f937a4cc474e80cfbb6eac4384e42e9c5c7ac981fcd6870d624cc898d1a0c006 +test_itk_seeding__estimatedparams.root: 1cc05f9f2aefb5f71a85b31e97bc4e5845fedfcef6c53199495a6340c6b6210b test_itk_seeding__performance_seeding.root: 78ebda54cd0f026ba4b7f316724ffd946de56a932735914baf1b7bba9505c29d test_itk_seeding__particles.root: 0b6f4ad438010ac48803d48ed98e80b5e87d310bae6c2c02b16cd94d7a4d7d07 test_itk_seeding__particles_simulation.root: ef0246069aa697019f28a8b270a68de95312cae5f2f2c74848566c3ce4f70363 test_propagation__propagation_summary.root: 280c1a6fcfe71974ac39587b4afad27a31640bec42ca6537cc92e2d5e09d7ed6 test_material_recording__geant4_material_tracks.root: c022b9362249b29f57a07926b20644e3ab4ab8ebcf03f773fbf46c446fc1a0a1 -test_truth_tracking_gsf[generic]__trackstates_gsf.root: 974968d9c867a18e2477fb241288e8d8a4a5e7499495d3e1a7b9cafa26c6dda5 -test_truth_tracking_gsf[generic]__tracksummary_gsf.root: b698e3d21eacc34fc8b0ce1d3fbe07405a4b8b549e07f0160573e64c3b401f04 -test_truth_tracking_gsf[odd]__trackstates_gsf.root: a3ee2401c42e226d61dce0b47f497bd273d8eb5ef6bc0ed541e08180fbb549f3 -test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 2cfdc8002c36c2dabddce827d90ddfc865fef15f43faa15a98884f120542cbbd +test_truth_tracking_gsf[generic]__trackstates_gsf.root: 4df2c69d5dd7d5446a547651e4e962daf17924f5c8617165a93a3223c8ba18fd +test_truth_tracking_gsf[generic]__tracksummary_gsf.root: 8c01d139cb865afa1959c62dbca76f3a1fb8b684c57ea4c2968baa6ffedadb6f +test_truth_tracking_gsf[odd]__trackstates_gsf.root: 8e6559aaec4fada8b82cfcad5801f3a609c6c905c3172fc044473cef7de77870 +test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 4562341f12a61ea0d5e25872b6bf466b79a73781dc95fc18ef9c6515f0a47916 test_particle_gun__particles.root: 5fe7dda2933ee6b9615b064d192322fe07831133cd998e5ed99a3b992b713a10 test_material_mapping__material-map_tracks.root: 938b1a855369e9304401cb10d2751df3fd7acf32a2573f2057eb1691cd94edf3 test_material_mapping__propagation-material.root: 5eacd0cb804d381171c8fb65d7b2d36e1d57db9f4cb2b58c0f24703479218cbf @@ -33,22 +33,22 @@ test_digitization_example_input[smeared]__particles.root: 5fe7dda2933ee6b9615b06 test_digitization_example_input[smeared]__measurements.root: 243c2f69b7b0db9dbeaa7494d4ea0f3dd1691dc90f16e10df6c0491ff4dc7d62 test_digitization_example_input[geometric]__particles.root: 5fe7dda2933ee6b9615b064d192322fe07831133cd998e5ed99a3b992b713a10 test_digitization_example_input[geometric]__measurements.root: 63ec81635979058fb8976f94455bf490cf92b7b142c4a05cc39de6225f5de2fb -test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: e041089f5ddf060580f068a951621882082b4659d935965c118b76f6c3944fd2 -test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 4d2e26c352285aed77e46e09ee1977eb34cd82a29b0cb08f162a61ea76f71f17 +test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: 35249a79237804bce337d797986e1082c6987f0700e30877f6f217f9ac91d36a +test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: e6b9e539998ba007e9b7d2c8d9d022c47726a39e8ab9b1724c52b1d78234be03 test_ckf_tracks_example[generic-full_seeding]__performance_seeding_trees.root: 0e0676ffafdb27112fbda50d1cf627859fa745760f98073261dcf6db3f2f991e -test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: 71a48e6d22be7f01611394ed7bd5a4a498aade6e20deb7efb0d9bd67dd950970 -test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 5a973ec1c159681361ac069809e364cef4ec3e0efcafb05689f1175764f438b4 +test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: a8c5c6f6c1e6303b887d47b509b7f71a2ffa5f38638fe46ce5bce76fd20d64ca +test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 417f7326e1e1bb4519f1378145ac733bdda6653eb9871fd69e455e0269d996a6 test_ckf_tracks_example[generic-truth_estimated]__performance_seeding.root: 1facb05c066221f6361b61f015cdf0918e94d9f3fce2269ec7b6a4dffeb2bc7e -test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: b10f61d3b68ecc3d1910a17aeadd01a1e23b31b6418935809d1f12e95eac607a -test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 69733ede1fc08370b5c0d0535f274b59bb51217239bd6645ff62ec9dadaa1f41 -test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: 33a68b20005a92b88bf7d01179a4b4fe3c1476a2019176904356924c0ad68f4b -test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 749a53533fed3d550c2e37f504bac5a8ab5c5cdf60d5055dcd545aa44613601a +test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: edf0b06ce9ee0e4fcb153e41859af7b5153271de18f49a6842a23ad2d66b7e09 +test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 06d6ae1d05cb611b19df3c59531997c9b0108f5ef6027d76c4827bd2d9edb921 +test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: 6411378b31c1612120318773f8b807ce83a76e07c90a5f308ea86c6b34d02661 +test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: c2e029e462d4ca77df2c7f8963093da43be66c8279ca2cc9aee8c0bc35259eec test_ckf_tracks_example[odd-full_seeding]__performance_seeding_trees.root: 43c58577aafe07645e5660c4f43904efadf91d8cda45c5c04c248bbe0f59814f -test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 7750f58b970c79dc3c937482e2a6e4576bc5d495fd6e4576d63ac2882d8283d4 -test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: f5d5d5521e367dd2e26365b896b079279a5a8b97b7e11d38eb0eb317740ad4dd +test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 465bb9e982982eb2e79fc97cae9f513ff5937041da546081281f1f959d8173ea +test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 59e2c75e9524653a80a9fd62fe99e958f73f80aa09240dcbb4ea469372e4811d test_ckf_tracks_example[odd-truth_estimated]__performance_seeding.root: 1a36b7017e59f1c08602ef3c2cb0483c51df248f112e3780c66594110719c575 -test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 33398059bf968f7279d4cc706f3b914fcf6f010ae82c43df8875785bda6f7cbe -test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: 3d01335a51fb03c78a174c8b11d473b38ba9c4ed3d6cba201b3925f463411708 +test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 9cbf99353d71ee4e6779bffb231043b41db6219c9544a80fe5172f4a4fe60cbe +test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: 3e257de624674fa9a19dcc72598c78c29a52633821acaa56dc2aa39a1395f1b5 test_vertex_fitting_reading[Truth-False-100]__performance_vertexing.root: 76ef6084d758dfdfc0151ddec2170e12d73394424e3dac4ffe46f0f339ec8293 test_vertex_fitting_reading[Iterative-False-100]__performance_vertexing.root: 60372210c830a04f95ceb78c6c68a9b0de217746ff59e8e73053750c837b57eb test_vertex_fitting_reading[Iterative-True-100]__performance_vertexing.root: e34f217d524a5051dbb04a811d3407df3ebe2cc4bb7f54f6bda0847dbd7b52c3 @@ -74,23 +74,23 @@ test_exatrkx[cpu-torch]__performance_track_finding.root: 36b3045589c4c17c038dbc8 test_exatrkx[gpu-onnx]__performance_track_finding.root: 9090de10ffb1489d3f1993e2a3081a3038227e3e5c453e98a9a4f33ea3d6d817 test_exatrkx[gpu-torch]__performance_track_finding.root: 36b3045589c4c17c038dbc87943366f4af4440f7eea6887afb763871ac149b05 test_ML_Ambiguity_Solver__performance_ambiML.root: 284ff5c3a08c0b810938e4ac2f8ba8fe2babb17d4c202b624ed69fff731a9006 -test_refitting[odd]__trackstates_gsf_refit.root: 798a5b2e6d4b6d56e73ad107887f310c9463cc739ca1b69dad4a99d3419943ca -test_refitting[odd]__tracksummary_gsf_refit.root: 16951808df6363d2acb99e385aec35ad723b634403ca0724a552ae9d3a2ae237 -test_refitting[generic]__trackstates_gsf_refit.root: b044574ab5dec9781ca2a1d72095f2393270766365a0e165904ff628191c284a -test_refitting[generic]__tracksummary_gsf_refit.root: 5b9ae5be9e82b2a1fd27d54fae5b9d8e89574f7a04a68cd491d4e3761c8fb834 -test_truth_tracking_kalman[generic-False-0.0]__trackstates_kf.root: 673a2b6a6fe855e8fcc3f0005a8d598ad61fd696177f54b2fa1af36517ac8f44 -test_truth_tracking_kalman[generic-False-0.0]__tracksummary_kf.root: 5b9ae5be9e82b2a1fd27d54fae5b9d8e89574f7a04a68cd491d4e3761c8fb834 -test_truth_tracking_kalman[generic-False-1000.0]__trackstates_kf.root: e69d1aacacecc3d7d6619605cdd179d9fb0e19734f7c35345284f9a0e01f36d1 -test_truth_tracking_kalman[generic-False-1000.0]__tracksummary_kf.root: 8be4bcc0f05c90ffeb4ea7bbb517adee217550d59c2eed89a48ed3b4c5145c54 -test_truth_tracking_kalman[generic-True-0.0]__trackstates_kf.root: 673a2b6a6fe855e8fcc3f0005a8d598ad61fd696177f54b2fa1af36517ac8f44 -test_truth_tracking_kalman[generic-True-0.0]__tracksummary_kf.root: 5b9ae5be9e82b2a1fd27d54fae5b9d8e89574f7a04a68cd491d4e3761c8fb834 -test_truth_tracking_kalman[generic-True-1000.0]__trackstates_kf.root: e69d1aacacecc3d7d6619605cdd179d9fb0e19734f7c35345284f9a0e01f36d1 -test_truth_tracking_kalman[generic-True-1000.0]__tracksummary_kf.root: 8be4bcc0f05c90ffeb4ea7bbb517adee217550d59c2eed89a48ed3b4c5145c54 -test_truth_tracking_kalman[odd-False-0.0]__trackstates_kf.root: 22c9f39a8e9569499205c024f7075d4125aca111c0e580c0320d6d93696062d5 -test_truth_tracking_kalman[odd-False-0.0]__tracksummary_kf.root: a6da8b8ca2cd0f09fcbae5d08885fa2aee39990f2f329ef659ef3c260141643a -test_truth_tracking_kalman[odd-False-1000.0]__trackstates_kf.root: 214f3ded235a0da8d254e34f72253ee8d6dfa5b7f96040bccd760b51ee85a46f -test_truth_tracking_kalman[odd-False-1000.0]__tracksummary_kf.root: 87eaae3192ab29e2c2542c017071b6477c7237c5b8eaff107e84caed2a5e5b7a -test_truth_tracking_kalman[odd-True-0.0]__trackstates_kf.root: 22c9f39a8e9569499205c024f7075d4125aca111c0e580c0320d6d93696062d5 -test_truth_tracking_kalman[odd-True-0.0]__tracksummary_kf.root: a6da8b8ca2cd0f09fcbae5d08885fa2aee39990f2f329ef659ef3c260141643a -test_truth_tracking_kalman[odd-True-1000.0]__trackstates_kf.root: 214f3ded235a0da8d254e34f72253ee8d6dfa5b7f96040bccd760b51ee85a46f -test_truth_tracking_kalman[odd-True-1000.0]__tracksummary_kf.root: 87eaae3192ab29e2c2542c017071b6477c7237c5b8eaff107e84caed2a5e5b7a +test_refitting[odd]__trackstates_gsf_refit.root: e297749dc1e7eda3b8dea13defa0499986c584740d93e723a901b498b8e90c71 +test_refitting[odd]__tracksummary_gsf_refit.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 +test_refitting[generic]__trackstates_gsf_refit.root: 4424fdf2f27575db825c1a59f8e53a1595946211cbd5b2c8d3a2f71cdcc77ae9 +test_refitting[generic]__tracksummary_gsf_refit.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 +test_truth_tracking_kalman[generic-False-0.0]__trackstates_kf.root: 9f77962b92037cb760b1629a602b1dae61f45e659c45d9a87baa784f6190960e +test_truth_tracking_kalman[generic-False-0.0]__tracksummary_kf.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 +test_truth_tracking_kalman[generic-False-1000.0]__trackstates_kf.root: 56a1bd989b9c1316b9098c65fa75df9e6683e62e35ae68d8f72d27220be0fd7d +test_truth_tracking_kalman[generic-False-1000.0]__tracksummary_kf.root: 2d85be3a5dff01a1076e80f8c225aca32f65b30dc9c3551b610ac2f12d006a39 +test_truth_tracking_kalman[generic-True-0.0]__trackstates_kf.root: 9f77962b92037cb760b1629a602b1dae61f45e659c45d9a87baa784f6190960e +test_truth_tracking_kalman[generic-True-0.0]__tracksummary_kf.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 +test_truth_tracking_kalman[generic-True-1000.0]__trackstates_kf.root: 56a1bd989b9c1316b9098c65fa75df9e6683e62e35ae68d8f72d27220be0fd7d +test_truth_tracking_kalman[generic-True-1000.0]__tracksummary_kf.root: 2d85be3a5dff01a1076e80f8c225aca32f65b30dc9c3551b610ac2f12d006a39 +test_truth_tracking_kalman[odd-False-0.0]__trackstates_kf.root: 7e144571b19aaf00002aef4f5bec5d488b96fb9ed8e1b2904c3756b31be83513 +test_truth_tracking_kalman[odd-False-0.0]__tracksummary_kf.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 +test_truth_tracking_kalman[odd-False-1000.0]__trackstates_kf.root: efdf37f56fa3ef85265cda61853f4c0f989e3d4f4745b5e351c9bcca78cd93cc +test_truth_tracking_kalman[odd-False-1000.0]__tracksummary_kf.root: b84fabd6c1b71c360019cd719400746ccff1e565a983ca23d0621790b8843e44 +test_truth_tracking_kalman[odd-True-0.0]__trackstates_kf.root: 7e144571b19aaf00002aef4f5bec5d488b96fb9ed8e1b2904c3756b31be83513 +test_truth_tracking_kalman[odd-True-0.0]__tracksummary_kf.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 +test_truth_tracking_kalman[odd-True-1000.0]__trackstates_kf.root: efdf37f56fa3ef85265cda61853f4c0f989e3d4f4745b5e351c9bcca78cd93cc +test_truth_tracking_kalman[odd-True-1000.0]__tracksummary_kf.root: b84fabd6c1b71c360019cd719400746ccff1e565a983ca23d0621790b8843e44 diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index e396e17d0f7..9a2c45c7657 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -407,6 +407,7 @@ def test_itk_seeding(tmp_path, trk_geo, field, assert_root_hash): EtaConfig, MomentumConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) @@ -428,6 +429,12 @@ def test_itk_seeding(tmp_path, trk_geo, field, assert_root_hash): outputDirCsv=tmp_path / "csv", outputDirRoot=str(tmp_path), rnd=rnd, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + eta=(-4, 4), + measurements=(9, None), + removeNeutral=True, + ), ) srcdir = Path(__file__).resolve().parent.parent.parent.parent @@ -442,7 +449,6 @@ def test_itk_seeding(tmp_path, trk_geo, field, assert_root_hash): from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, ) from acts.examples.itk import itkSeedingAlgConfig, InputSpacePointsType @@ -450,7 +456,6 @@ def test_itk_seeding(tmp_path, trk_geo, field, assert_root_hash): seq, trk_geo, field, - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-4, 4), nHits=(9, None)), *itkSeedingAlgConfig(InputSpacePointsType.PixelSpacePoints), acts.logging.VERBOSE, geoSelectionConfigFile=srcdir @@ -699,6 +704,7 @@ def test_refitting(tmp_path, detector_config, assert_root_hash): runRefittingGsf( trackingGeometry=trackingGeometry, field=field, + digiConfigFile=detector_config.digiConfigFile, outputDir=tmp_path, s=seq, ).run() @@ -1000,6 +1006,40 @@ def test_digitization_example(trk_geo, tmp_path, assert_root_hash, digi_config_f assert_root_hash(root_file.name, root_file) +@pytest.mark.parametrize( + "digi_config_file", + [ + DIGI_SHARE_DIR / "default-smearing-config-generic.json", + DIGI_SHARE_DIR / "default-geometric-config-generic.json", + pytest.param( + ( + getOpenDataDetectorDirectory() + / "config" + / "odd-digi-smearing-config.json" + ), + marks=[ + pytest.mark.odd, + ], + ), + pytest.param( + ( + getOpenDataDetectorDirectory() + / "config" + / "odd-digi-geometric-config.json" + ), + marks=[ + pytest.mark.odd, + ], + ), + ], + ids=["smeared", "geometric", "odd-smeared", "odd-geometric"], +) +def test_digitization_example_input_parsing(digi_config_file): + from acts.examples import readDigiConfigFromJson + + acts.examples.readDigiConfigFromJson(str(digi_config_file)) + + @pytest.mark.parametrize( "digi_config_file", [ diff --git a/Examples/Python/tests/test_reader.py b/Examples/Python/tests/test_reader.py index f4e803776d5..b83b1317cc8 100644 --- a/Examples/Python/tests/test_reader.py +++ b/Examples/Python/tests/test_reader.py @@ -206,7 +206,6 @@ def test_csv_meas_reader(tmp_path, fatras, trk_geo, conf_const): level=acts.logging.WARNING, outputMeasurements="measurements", outputMeasurementSimHitsMap="simhitsmap", - outputSourceLinks="sourcelinks", outputMeasurementParticlesMap="meas_ptcl_map", inputSimHits=simAlg.config.outputSimHits, inputDir=str(out), @@ -215,7 +214,7 @@ def test_csv_meas_reader(tmp_path, fatras, trk_geo, conf_const): algs = [ AssertCollectionExistsAlg(k, f"check_alg_{k}", acts.logging.WARNING) - for k in ("measurements", "simhitsmap", "sourcelinks", "meas_ptcl_map") + for k in ("measurements", "simhitsmap", "meas_ptcl_map") ] for alg in algs: s.addAlgorithm(alg) @@ -291,8 +290,11 @@ def test_edm4hep_simhit_particle_reader(tmp_path): 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)) + p = multiprocessing.Process( + target=generate_input_test_edm4hep_simhit_reader, args=(odd_xml_file, tmp_file) + ) + p.start() + p.join() assert os.path.exists(tmp_file) @@ -363,14 +365,13 @@ def test_edm4hep_measurement_reader(tmp_path, fatras, conf_const): 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 k in ("measurements", "simhitsmap") ] for alg in algs: s.addAlgorithm(alg) diff --git a/Examples/Scripts/Optimization/ckf.py b/Examples/Scripts/Optimization/ckf.py index bf927850ecc..c1761141262 100755 --- a/Examples/Scripts/Optimization/ckf.py +++ b/Examples/Scripts/Optimization/ckf.py @@ -116,13 +116,13 @@ def runCKFTracks( EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, ParticleSmearingSigmas, SeedFinderConfigArg, SeedFinderOptionsArg, @@ -169,6 +169,11 @@ def runCKFTracks( trackingGeometry, field, rnd=rnd, + postSelectParticles=ParticleSelectorConfig( + pt=(0.5 * u.GeV, None), + measurements=(9, None), + removeNeutral=True, + ), ) addDigitization( @@ -183,7 +188,6 @@ def runCKFTracks( s, trackingGeometry, field, - TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), ParticleSmearingSigmas( # only used by SeedingAlgorithm.TruthSmeared # zero eveything so the CKF has a chance to find the measurements d0=0, diff --git a/Examples/Scripts/Python/ckf_tracks.py b/Examples/Scripts/Python/ckf_tracks.py index 9dc665eb835..e2b24436c0a 100755 --- a/Examples/Scripts/Python/ckf_tracks.py +++ b/Examples/Scripts/Python/ckf_tracks.py @@ -27,13 +27,13 @@ def runCKFTracks( EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, ParticleSmearingSigmas, SeedFinderConfigArg, SeedFinderOptionsArg, @@ -80,6 +80,11 @@ def runCKFTracks( trackingGeometry, field, rnd=rnd, + postSelectParticles=ParticleSelectorConfig( + pt=(0.5 * u.GeV, None), + measurements=(9, None), + removeNeutral=True, + ), ) addDigitization( @@ -94,7 +99,6 @@ def runCKFTracks( s, trackingGeometry, field, - TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), ParticleSmearingSigmas( # only used by SeedingAlgorithm.TruthSmeared # zero eveything so the CKF has a chance to find the measurements d0=0, diff --git a/Examples/Scripts/Python/full_chain_itk.py b/Examples/Scripts/Python/full_chain_itk.py index 6231127096c..51746c127a1 100755 --- a/Examples/Scripts/Python/full_chain_itk.py +++ b/Examples/Scripts/Python/full_chain_itk.py @@ -5,6 +5,7 @@ MomentumConfig, EtaConfig, ParticleConfig, + ParticleSelectorConfig, addPythia8, addFatras, ParticleSelectorConfig, @@ -13,7 +14,6 @@ from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, - TruthSeedRanges, addCKFTracks, CkfConfig, TrackSelectorConfig, @@ -72,6 +72,12 @@ if ttbar_pu200 else ParticleSelectorConfig() ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-4.0, 4.0), + measurements=(9, None), + removeNeutral=True, + ), outputDirRoot=outputDir, ) @@ -88,11 +94,6 @@ s, trackingGeometry, field, - ( - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-4.0, 4.0), nHits=(9, None)) - if ttbar_pu200 - else TruthSeedRanges() - ), seedingAlgorithm=SeedingAlgorithm.Default, *acts.examples.itk.itkSeedingAlgConfig( acts.examples.itk.InputSpacePointsType.PixelSpacePoints @@ -126,8 +127,8 @@ seedDeduplication=True, stayOnSeed=True, # ITk volumes from Noemi's plot - pixelVolumes={8, 9, 10, 13, 14, 15, 16, 18, 19, 20}, - stripVolumes={22, 23, 24}, + pixelVolumes=[8, 9, 10, 13, 14, 15, 16, 18, 19, 20], + stripVolumes=[22, 23, 24], maxPixelHoles=1, maxStripHoles=2, ), diff --git a/Examples/Scripts/Python/full_chain_itk_Gbts.py b/Examples/Scripts/Python/full_chain_itk_Gbts.py index 7a45b7e5a71..a87f5ca6656 100755 --- a/Examples/Scripts/Python/full_chain_itk_Gbts.py +++ b/Examples/Scripts/Python/full_chain_itk_Gbts.py @@ -5,6 +5,7 @@ MomentumConfig, EtaConfig, ParticleConfig, + ParticleSelectorConfig, addPythia8, addFatras, ParticleSelectorConfig, @@ -13,7 +14,6 @@ from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, - TruthSeedRanges, addCKFTracks, TrackSelectorConfig, ) @@ -67,6 +67,12 @@ if ttbar_pu200 else ParticleSelectorConfig() ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-4.0, 4.0), + measurements=(9, None), + removeNeutral=True, + ), outputDirRoot=outputDir, ) @@ -84,11 +90,6 @@ s, trackingGeometry, field, - ( - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-4.0, 4.0), nHits=(9, None)) - if ttbar_pu200 - else TruthSeedRanges() - ), seedingAlgorithm=SeedingAlgorithm.Gbts, *acts.examples.itk.itkSeedingAlgConfig( acts.examples.itk.InputSpacePointsType.PixelSpacePoints diff --git a/Examples/Scripts/Python/full_chain_odd.py b/Examples/Scripts/Python/full_chain_odd.py index e76111da7ac..fd98f759577 100755 --- a/Examples/Scripts/Python/full_chain_odd.py +++ b/Examples/Scripts/Python/full_chain_odd.py @@ -3,6 +3,7 @@ import os import argparse import pathlib +import math import acts import acts.examples @@ -12,6 +13,7 @@ EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addPythia8, addFatras, addGeant4, @@ -21,7 +23,6 @@ ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, CkfConfig, addCKFTracks, TrackSelectorConfig, @@ -270,6 +271,12 @@ pt=(150 * u.MeV, None), removeNeutral=True, ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(9, None), + removeNeutral=True, + ), outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, rnd=rnd, @@ -292,6 +299,12 @@ if args.ttbar else ParticleSelectorConfig() ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(9, None), + removeNeutral=True, + ), enableInteractions=True, outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, @@ -313,11 +326,6 @@ s, trackingGeometry, field, - ( - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), nHits=(9, None)) - if args.ttbar - else TruthSeedRanges() - ), initialSigmas=[ 1 * u.mm, 1 * u.mm, @@ -363,10 +371,27 @@ numMeasurementsCutOff=10, seedDeduplication=True, stayOnSeed=True, - pixelVolumes={16, 17, 18}, - stripVolumes={23, 24, 25}, + pixelVolumes=[16, 17, 18], + stripVolumes=[23, 24, 25], maxPixelHoles=1, maxStripHoles=2, + constrainToVolumes=[ + 2, # beam pipe + 32, + 4, # beam pip gap + 16, + 17, + 18, # pixel + 20, # PST + 23, + 24, + 25, # short strip + 26, + 8, # long strip gap + 28, + 29, + 30, # long strip + ], ), outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, @@ -395,8 +420,8 @@ maxSharedTracksPerMeasurement=2, pTMax=1400, pTMin=0.5, - phiMax=3.14, - phiMin=-3.14, + phiMax=math.pi, + phiMin=-math.pi, etaMax=4, etaMin=-4, useAmbiguityFunction=False, diff --git a/Examples/Scripts/Python/full_chain_odd_LRT.py b/Examples/Scripts/Python/full_chain_odd_LRT.py index a5fd294ceda..2188bb98d6b 100644 --- a/Examples/Scripts/Python/full_chain_odd_LRT.py +++ b/Examples/Scripts/Python/full_chain_odd_LRT.py @@ -21,7 +21,6 @@ ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, CkfConfig, addCKFTracks, TrackSelectorConfig, @@ -273,6 +272,12 @@ pt=(150 * u.MeV, None), removeNeutral=True, ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(9, None), + removeNeutral=True, + ), outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, rnd=rnd, @@ -295,6 +300,12 @@ if args.ttbar else ParticleSelectorConfig() ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(9, None), + removeNeutral=True, + ), enableInteractions=True, outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, @@ -316,11 +327,6 @@ s, trackingGeometry, field, - ( - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), nHits=(9, None)) - if args.ttbar - else TruthSeedRanges() - ), geoSelectionConfigFile=oddSeedingSel, outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, diff --git a/Examples/Scripts/Python/hashing_seeding.py b/Examples/Scripts/Python/hashing_seeding.py index a5608a0f329..3148a03837d 100755 --- a/Examples/Scripts/Python/hashing_seeding.py +++ b/Examples/Scripts/Python/hashing_seeding.py @@ -14,7 +14,6 @@ from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, addCKFTracks, TrackSelectorConfig, SeedingAlgorithm, @@ -210,7 +209,15 @@ def runHashingSeeding( trackingGeometry, field, preSelectParticles=ParticleSelectorConfig( - eta=(-eta, eta), pt=(150 * u.MeV, None), removeNeutral=True + eta=(-eta, eta), + pt=(150 * u.MeV, None), + removeNeutral=True, + ), + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-eta, eta), + measurements=(9, None), + removeNeutral=True, ), enableInteractions=True, # outputDirRoot=outputDir, # RootParticle ERROR when setting the outputDirRoot @@ -272,7 +279,6 @@ def runHashingSeeding( seedFinderOptionsArg, hashingTrainingConfigArg, hashingAlgorithmConfigArg, - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-eta, eta), nHits=(9, None)), seedingAlgorithm=seedingAlgorithm, geoSelectionConfigFile=geoSelectionConfigFile, initialSigmas=initialSigmas, @@ -338,10 +344,6 @@ def runHashingSeeding( phiBins=phiBins, ) - truthSeedRanges = TruthSeedRanges( - pt=(1.0 * u.GeV, None), eta=(-eta, eta), nHits=(9, None) - ) - doHashing = config.doHashing bucketSize = config.bucketSize npileup = config.mu diff --git a/Examples/Scripts/Python/material_mapping_core.py b/Examples/Scripts/Python/material_mapping_core.py index 9a9801b50af..0bd9a7415de 100644 --- a/Examples/Scripts/Python/material_mapping_core.py +++ b/Examples/Scripts/Python/material_mapping_core.py @@ -156,6 +156,29 @@ def runMaterialMapping(surfaces, inputFile, outputFile, outputMap, loglevel): help="Construct experimental geometry from GeoModel", ) + p.add_argument( + "--geomodel-name-list", + type=str, + nargs="+", + default=[], + help="List of Name List for the Surface Factory", + ) + + p.add_argument( + "--geomodel-material-list", + type=str, + nargs="+", + default=[], + help="List of Material List for the Surface Factory", + ) + + p.add_argument( + "--geomodel-convert-subvols", + help="Convert the children of the top level full phys vol", + action="store_true", + default=False, + ) + p.add_argument( "--geomodel-top-node", type=str, @@ -189,19 +212,16 @@ def runMaterialMapping(surfaces, inputFile, outputFile, outputMap, loglevel): # Read the geometry model from the database gmTree = acts.geomodel.readFromDb(args.geomodel_input) - gmFactoryConfig = gm.GeoModelDetectorSurfaceFactory.Config() - gmFactoryConfig.shapeConverters = [ - gm.GeoBoxConverter(), - gm.GeoTrdConverter(), - gm.GeoIntersectionAnnulusConverter(), - ] - - gmFactory = gm.GeoModelDetectorSurfaceFactory(gmFactoryConfig, logLevel) + gmFactoryConfig = gm.GeoModelDetectorObjectFactory.Config() + gmFactoryConfig.materialList = args.geomodel_material_list + gmFactoryConfig.nameList = args.geomodel_name_list + gmFactoryConfig.convertSubVolumes = args.geomodel_convert_subvols + gmFactory = gm.GeoModelDetectorObjectFactory(gmFactoryConfig, logLevel) # The options - gmFactoryOptions = gm.GeoModelDetectorSurfaceFactory.Options() + gmFactoryOptions = gm.GeoModelDetectorObjectFactory.Options() gmFactoryOptions.queries = args.geomodel_queries # The Cache & construct call - gmFactoryCache = gm.GeoModelDetectorSurfaceFactory.Cache() + gmFactoryCache = gm.GeoModelDetectorObjectFactory.Cache() gmFactory.construct(gmFactoryCache, gContext, gmTree, gmFactoryOptions) # All surfaces from GeoModel @@ -210,7 +230,10 @@ def runMaterialMapping(surfaces, inputFile, outputFile, outputMap, loglevel): # Construct the building hierarchy gmBlueprintConfig = gm.GeoModelBlueprintCreater.Config() gmBlueprintConfig.detectorSurfaces = gmSurfaces - gmBlueprintConfig.kdtBinning = [acts.Binning.z, acts.Binning.r] + gmBlueprintConfig.kdtBinning = [ + acts.BinningValue.binZ, + acts.BinningValue.binR, + ] gmBlueprintOptions = gm.GeoModelBlueprintCreater.Options() gmBlueprintOptions.table = args.geomodel_table_name diff --git a/Examples/Scripts/Python/material_recording.py b/Examples/Scripts/Python/material_recording.py index ee8910e8ef4..9de5914d45a 100755 --- a/Examples/Scripts/Python/material_recording.py +++ b/Examples/Scripts/Python/material_recording.py @@ -18,6 +18,12 @@ import acts.examples.geant4.dd4hep from acts.examples.odd import getOpenDataDetector +try: + import acts.examples.geant4.geomodel +except ImportError: + # geomodel is optional for this script + pass + u = acts.UnitConstants _material_recording_executed = False @@ -114,8 +120,6 @@ def main(): acts.examples.geant4.GdmlDetectorConstructionFactory(args.input) ) elif args.input.endswith(".sqlite") or args.input.endswith(".db"): - import acts.examples.geant4.geomodel - geoModelTree = acts.geomodel.readFromDb(args.input) detectorConstructionFactory = ( acts.examples.geant4.geomodel.GeoModelDetectorConstructionFactory( diff --git a/Examples/Scripts/Python/material_validation_core.py b/Examples/Scripts/Python/material_validation_core.py index e696f704a5f..7e2ae129dcf 100644 --- a/Examples/Scripts/Python/material_validation_core.py +++ b/Examples/Scripts/Python/material_validation_core.py @@ -92,6 +92,29 @@ def runMaterialValidation(s, ntracks, surfaces, outputFile, seed, loglevel): help="Construct experimental geometry from GeoModel", ) + p.add_argument( + "--geomodel-name-list", + type=str, + nargs="+", + default=[], + help="List of Name List for the Surface Factory", + ) + + p.add_argument( + "--geomodel-material-list", + type=str, + nargs="+", + default=[], + help="List of Material List for the Surface Factory", + ) + + p.add_argument( + "--geomodel-convert-subvols", + help="Convert the children of the top level full phys vol", + action="store_true", + default=False, + ) + p.add_argument( "--geomodel-top-node", type=str, @@ -129,19 +152,16 @@ def runMaterialValidation(s, ntracks, surfaces, outputFile, seed, loglevel): # Read the geometry model from the database gmTree = acts.geomodel.readFromDb(args.geomodel_input) - gmFactoryConfig = gm.GeoModelDetectorSurfaceFactory.Config() - gmFactoryConfig.shapeConverters = [ - gm.GeoBoxConverter(), - gm.GeoTrdConverter(), - gm.GeoIntersectionAnnulusConverter(), - ] - - gmFactory = gm.GeoModelDetectorSurfaceFactory(gmFactoryConfig, logLevel) + gmFactoryConfig = gm.GeoModelDetectorObjectFactory.Config() + gmFactoryConfig.materialList = args.geomodel_material_list + gmFactoryConfig.nameList = args.geomodel_name_list + gmFactoryConfig.convertSubVolumes = args.geomodel_convert_subvols + gmFactory = gm.GeoModelDetectorObjectFactory(gmFactoryConfig, logLevel) # The options - gmFactoryOptions = gm.GeoModelDetectorSurfaceFactory.Options() + gmFactoryOptions = gm.GeoModelDetectorObjectFactory.Options() gmFactoryOptions.queries = args.geomodel_queries # The Cache & construct call - gmFactoryCache = gm.GeoModelDetectorSurfaceFactory.Cache() + gmFactoryCache = gm.GeoModelDetectorObjectFactory.Cache() gmFactory.construct(gmFactoryCache, gContext, gmTree, gmFactoryOptions) # All surfaces from GeoModel @@ -150,7 +170,10 @@ def runMaterialValidation(s, ntracks, surfaces, outputFile, seed, loglevel): # Construct the building hierarchy gmBlueprintConfig = gm.GeoModelBlueprintCreater.Config() gmBlueprintConfig.detectorSurfaces = gmSurfaces - gmBlueprintConfig.kdtBinning = [acts.Binning.z, acts.Binning.r] + gmBlueprintConfig.kdtBinning = [ + acts.BinningValue.binZ, + acts.BinningValue.binR, + ] gmBlueprintOptions = gm.GeoModelBlueprintCreater.Options() gmBlueprintOptions.table = args.geomodel_table_name diff --git a/Examples/Scripts/Python/seeding.py b/Examples/Scripts/Python/seeding.py index 4c87ba9b82b..e50f9390c12 100755 --- a/Examples/Scripts/Python/seeding.py +++ b/Examples/Scripts/Python/seeding.py @@ -57,6 +57,7 @@ def runSeeding( EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) @@ -86,6 +87,12 @@ def runSeeding( outputDirRoot=outputDir, rnd=rnd, preSelectParticles=None, + postSelectParticles=ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-2.5, 2.5), + measurements=(9, None), + removeNeutral=True, + ), ) srcdir = Path(__file__).resolve().parent.parent.parent.parent @@ -99,7 +106,6 @@ def runSeeding( ) from acts.examples.reconstruction import ( addSeeding, - TruthSeedRanges, SeedFinderConfigArg, SeedFinderOptionsArg, ) @@ -108,7 +114,6 @@ def runSeeding( s, trackingGeometry, field, - TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-2.5, 2.5), nHits=(9, None)), SeedFinderConfigArg( r=(None, 200 * u.mm), # rMin=default, 33mm deltaR=(1 * u.mm, 60 * u.mm), diff --git a/Examples/Scripts/Python/truth_tracking_gsf.py b/Examples/Scripts/Python/truth_tracking_gsf.py index 82c99bb38aa..ae8aca06d6c 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf.py +++ b/Examples/Scripts/Python/truth_tracking_gsf.py @@ -24,13 +24,13 @@ def runTruthTrackingGsf( EtaConfig, PhiConfig, MomentumConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, - TruthSeedRanges, addTruthTrackingGsf, ) @@ -77,6 +77,12 @@ def runTruthTrackingGsf( field, rnd=rnd, enableInteractions=True, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + measurements=(7, None), + removeNeutral=True, + removeSecondaries=True, + ), ) addDigitization( @@ -95,9 +101,6 @@ def runTruthTrackingGsf( inputParticles="particles_input", seedingAlgorithm=SeedingAlgorithm.TruthSmeared, particleHypothesis=acts.ParticleHypothesis.electron, - truthSeedRanges=TruthSeedRanges( - nHits=(7, None), - ), ) addTruthTrackingGsf( @@ -122,7 +125,7 @@ def runTruthTrackingGsf( acts.examples.RootTrackStatesWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", inputSimHits="simhits", inputMeasurementSimHitsMap="measurement_simhits_map", @@ -134,7 +137,7 @@ def runTruthTrackingGsf( acts.examples.RootTrackSummaryWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "tracksummary_gsf.root"), writeGsfSpecific=True, @@ -145,7 +148,7 @@ def runTruthTrackingGsf( acts.examples.TrackFitterPerformanceWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "performance_gsf.root"), ) diff --git a/Examples/Scripts/Python/truth_tracking_gsf_refitting.py b/Examples/Scripts/Python/truth_tracking_gsf_refitting.py index 138540db2af..686f4af06c2 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf_refitting.py +++ b/Examples/Scripts/Python/truth_tracking_gsf_refitting.py @@ -11,25 +11,27 @@ def runRefittingGsf( - trackingGeometry, - field, - outputDir, + trackingGeometry: acts.TrackingGeometry, + field: acts.MagneticFieldProvider, + digiConfigFile: Path, + outputDir: Path, s: acts.examples.Sequencer = None, ): - srcdir = Path(__file__).resolve().parent.parent.parent.parent - s = runTruthTrackingKalman( trackingGeometry, field, - digiConfigFile=srcdir - / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json", - # "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json", + digiConfigFile=digiConfigFile, outputDir=outputDir, s=s, ) + # NOTE we specify clampToRange as True to silence warnings in the test about + # queries to the loss distribution outside the specified range, since no dedicated + # approximation for the ODD is done yet. + bha = acts.examples.AtlasBetheHeitlerApprox.makeDefault(clampToRange=True) + gsfOptions = { - "betheHeitlerApprox": acts.examples.AtlasBetheHeitlerApprox.makeDefault(), + "betheHeitlerApprox": bha, "maxComponents": 12, "componentMergeMethod": acts.examples.ComponentMergeMethod.maxWeight, "mixtureReductionAlgorithm": acts.examples.MixtureReductionAlgorithm.KLDistance, @@ -41,7 +43,7 @@ def runRefittingGsf( acts.examples.RefittingAlgorithm( acts.logging.INFO, inputTracks="kf_tracks", - outputTracks="gsf_tracks", + outputTracks="gsf_refit_tracks", fit=acts.examples.makeGsfFitterFunction( trackingGeometry, field, **gsfOptions ), @@ -51,8 +53,8 @@ def runRefittingGsf( s.addAlgorithm( acts.examples.TrackTruthMatcher( level=acts.logging.INFO, - inputTracks="gsf_tracks", - inputParticles="truth_seeds_selected", + inputTracks="gsf_refit_tracks", + inputParticles="particles_selected", inputMeasurementParticlesMap="measurement_particles_map", outputTrackParticleMatching="refit_track_particle_matching", outputParticleTrackMatching="refit_particle_track_matching", @@ -62,8 +64,8 @@ def runRefittingGsf( s.addWriter( acts.examples.RootTrackStatesWriter( level=acts.logging.INFO, - inputTracks="gsf_tracks", - inputParticles="truth_seeds_selected", + inputTracks="gsf_refit_tracks", + inputParticles="particles_selected", inputTrackParticleMatching="refit_track_particle_matching", inputSimHits="simhits", inputMeasurementSimHitsMap="measurement_simhits_map", @@ -75,7 +77,7 @@ def runRefittingGsf( acts.examples.RootTrackSummaryWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="refit_track_particle_matching", filePath=str(outputDir / "tracksummary_gsf_refit.root"), ) @@ -85,9 +87,9 @@ def runRefittingGsf( acts.examples.TrackFitterPerformanceWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", - filePath=str(outputDir / "performance_refitter.root"), + filePath=str(outputDir / "performance_gsf_refit.root"), ) ) @@ -95,10 +97,28 @@ def runRefittingGsf( if __name__ == "__main__": - outputDir = Path.cwd() + srcdir = Path(__file__).resolve().parent.parent.parent.parent + + # ODD + from acts.examples.odd import getOpenDataDetector + + detector, trackingGeometry, decorators = getOpenDataDetector() + digiConfigFile = ( + srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" + ) + + ## GenericDetector + # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # digiConfigFile = ( + # srcdir + # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" + # ) - # detector, trackingGeometry, decorators = getOpenDataDetector() - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) - runRefittingGsf(trackingGeometry, field, outputDir).run() + runRefittingGsf( + trackingGeometry=trackingGeometry, + field=field, + digiConfigFile=digiConfigFile, + outputDir=Path.cwd(), + ).run() diff --git a/Examples/Scripts/Python/truth_tracking_gx2f.py b/Examples/Scripts/Python/truth_tracking_gx2f.py index 46352ac8d21..8503dc982f4 100644 --- a/Examples/Scripts/Python/truth_tracking_gx2f.py +++ b/Examples/Scripts/Python/truth_tracking_gx2f.py @@ -23,13 +23,13 @@ def runTruthTrackingGx2f( EtaConfig, PhiConfig, MomentumConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, - TruthSeedRanges, addGx2fTracks, ) @@ -74,6 +74,12 @@ def runTruthTrackingGx2f( field, rnd=rnd, enableInteractions=True, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + measurements=(7, None), + removeNeutral=True, + removeSecondaries=True, + ), ) addDigitization( @@ -92,9 +98,6 @@ def runTruthTrackingGx2f( inputParticles="particles_input", seedingAlgorithm=SeedingAlgorithm.TruthSmeared, particleHypothesis=acts.ParticleHypothesis.muon, - truthSeedRanges=TruthSeedRanges( - nHits=(7, None), - ), ) addGx2fTracks( @@ -122,7 +125,7 @@ def runTruthTrackingGx2f( acts.examples.RootTrackStatesWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", inputSimHits="simhits", inputMeasurementSimHitsMap="measurement_simhits_map", @@ -134,7 +137,7 @@ def runTruthTrackingGx2f( acts.examples.RootTrackSummaryWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "tracksummary_gx2f.root"), writeGx2fSpecific=True, @@ -145,7 +148,7 @@ def runTruthTrackingGx2f( acts.examples.TrackFitterPerformanceWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "performance_gx2f.root"), ) diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index 276cb962c8f..91c18f1dd28 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -27,13 +27,13 @@ def runTruthTrackingKalman( EtaConfig, PhiConfig, MomentumConfig, + ParticleSelectorConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, - TruthSeedRanges, addKalmanTracks, ) @@ -82,6 +82,12 @@ def runTruthTrackingKalman( field, rnd=rnd, enableInteractions=True, + postSelectParticles=ParticleSelectorConfig( + pt=(0.9 * u.GeV, None), + measurements=(7, None), + removeNeutral=True, + removeSecondaries=True, + ), ) else: logger.info("Reading hits from %s", inputHitsPath.resolve()) @@ -110,9 +116,6 @@ def runTruthTrackingKalman( inputParticles="particles_input", seedingAlgorithm=SeedingAlgorithm.TruthSmeared, particleHypothesis=acts.ParticleHypothesis.muon, - truthSeedRanges=TruthSeedRanges( - nHits=(7, None), - ), ) addKalmanTracks( @@ -139,7 +142,7 @@ def runTruthTrackingKalman( acts.examples.RootTrackStatesWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", inputSimHits="simhits", inputMeasurementSimHitsMap="measurement_simhits_map", @@ -151,7 +154,7 @@ def runTruthTrackingKalman( acts.examples.RootTrackSummaryWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "tracksummary_kf.root"), ) @@ -161,7 +164,7 @@ def runTruthTrackingKalman( acts.examples.TrackFitterPerformanceWriter( level=acts.logging.INFO, inputTracks="tracks", - inputParticles="truth_seeds_selected", + inputParticles="particles_selected", inputTrackParticleMatching="track_particle_matching", filePath=str(outputDir / "performance_kf.root"), ) diff --git a/Examples/Scripts/Python/truth_tracking_kalman_refitting.py b/Examples/Scripts/Python/truth_tracking_kalman_refitting.py new file mode 100755 index 00000000000..46578423f33 --- /dev/null +++ b/Examples/Scripts/Python/truth_tracking_kalman_refitting.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 + +from pathlib import Path + +import acts +import acts.examples + +from truth_tracking_kalman import runTruthTrackingKalman + +u = acts.UnitConstants + + +def runRefittingKf( + trackingGeometry: acts.TrackingGeometry, + field: acts.MagneticFieldProvider, + digiConfigFile: Path, + outputDir: Path, + multipleScattering: bool = True, + energyLoss: bool = True, + reverseFilteringMomThreshold=0 * u.GeV, + s: acts.examples.Sequencer = None, +): + s = runTruthTrackingKalman( + trackingGeometry, + field, + digiConfigFile=digiConfigFile, + outputDir=outputDir, + s=s, + ) + + kalmanOptions = { + "multipleScattering": multipleScattering, + "energyLoss": energyLoss, + "reverseFilteringMomThreshold": reverseFilteringMomThreshold, + "freeToBoundCorrection": acts.examples.FreeToBoundCorrection(False), + "level": acts.logging.INFO, + } + + s.addAlgorithm( + acts.examples.RefittingAlgorithm( + level=acts.logging.INFO, + inputTracks="kf_tracks", + outputTracks="kf_refit_tracks", + fit=acts.examples.makeKalmanFitterFunction( + trackingGeometry, field, **kalmanOptions + ), + ) + ) + + s.addAlgorithm( + acts.examples.TrackTruthMatcher( + level=acts.logging.INFO, + inputTracks="kf_refit_tracks", + inputParticles="particles_selected", + inputMeasurementParticlesMap="measurement_particles_map", + outputTrackParticleMatching="refit_track_particle_matching", + outputParticleTrackMatching="refit_particle_track_matching", + ) + ) + + s.addWriter( + acts.examples.RootTrackStatesWriter( + level=acts.logging.INFO, + inputTracks="kf_refit_tracks", + inputParticles="particles_selected", + inputTrackParticleMatching="refit_track_particle_matching", + inputSimHits="simhits", + inputMeasurementSimHitsMap="measurement_simhits_map", + filePath=str(outputDir / "trackstates_kf_refit.root"), + ) + ) + + s.addWriter( + acts.examples.RootTrackSummaryWriter( + level=acts.logging.INFO, + inputTracks="tracks", + inputParticles="particles_selected", + inputTrackParticleMatching="refit_track_particle_matching", + filePath=str(outputDir / "tracksummary_kf_refit.root"), + ) + ) + + s.addWriter( + acts.examples.TrackFitterPerformanceWriter( + level=acts.logging.INFO, + inputTracks="tracks", + inputParticles="particles_selected", + inputTrackParticleMatching="track_particle_matching", + filePath=str(outputDir / "performance_kf_refit.root"), + ) + ) + + return s + + +if __name__ == "__main__": + srcdir = Path(__file__).resolve().parent.parent.parent.parent + outputDir = Path.cwd() + + # ODD + from acts.examples.odd import getOpenDataDetector + + detector, trackingGeometry, decorators = getOpenDataDetector() + digiConfigFile = ( + srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" + ) + + ## GenericDetector + # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # digiConfigFile = ( + # srcdir + # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" + # ) + + field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) + + runRefittingKf( + trackingGeometry=trackingGeometry, + field=field, + digiConfigFile=digiConfigFile, + outputDir=Path.cwd(), + ).run() diff --git a/Fatras/include/ActsFatras/Kernel/Simulation.hpp b/Fatras/include/ActsFatras/Kernel/Simulation.hpp index cbc1115225a..2d8280737f0 100644 --- a/Fatras/include/ActsFatras/Kernel/Simulation.hpp +++ b/Fatras/include/ActsFatras/Kernel/Simulation.hpp @@ -245,6 +245,9 @@ struct Simulation { continue; } + assert(result->particle.particleId() == initialParticle.particleId() && + "Particle id must not change during simulation"); + copyOutputs(result.value(), simulatedParticlesInitial, simulatedParticlesFinal, hits); // since physics processes are independent, there can be particle id @@ -256,6 +259,10 @@ struct Simulation { } } + assert( + (simulatedParticlesInitial.size() == simulatedParticlesFinal.size()) && + "Inconsistent final sizes of the simulated particle containers"); + // the overall function call succeeded, i.e. no fatal errors occurred. // yet, there might have been some particle for which the propagation // failed. thus, the successful result contains a list of failed particles. @@ -284,12 +291,13 @@ struct Simulation { // initial particle state was already pushed to the container before // store final particle state at the end of the simulation particlesFinal.push_back(result.particle); + std::copy(result.hits.begin(), result.hits.end(), std::back_inserter(hits)); + // move generated secondaries that should be simulated to the output std::copy_if( result.generatedParticles.begin(), result.generatedParticles.end(), std::back_inserter(particlesInitial), [this](const Particle &particle) { return selectParticle(particle); }); - std::copy(result.hits.begin(), result.hits.end(), std::back_inserter(hits)); } /// Renumber particle ids in the tail of the container. diff --git a/Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp b/Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp index cc6d7200868..acbaaaa713d 100644 --- a/Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp +++ b/Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp @@ -69,7 +69,17 @@ struct SimulationActor { void act(propagator_state_t &state, stepper_t &stepper, navigator_t &navigator, result_type &result, const Acts::Logger &logger) const { - assert(generator && "The generator pointer must be valid"); + assert(generator != nullptr && "The generator pointer must be valid"); + + if (state.stage == Acts::PropagatorStage::prePropagation) { + // first step is special: there is no previous state and we need to arm + // the decay simulation for all future steps. + result.particle = + makeParticle(initialParticle, state, stepper, navigator); + result.properTimeLimit = + decay.generateProperTimeLimit(*generator, initialParticle); + return; + } // actors are called once more after the propagation terminated if (!result.isAlive) { @@ -82,28 +92,11 @@ struct SimulationActor { return; } - // check if we are still on the start surface and skip if so - if ((navigator.startSurface(state.navigation) != nullptr) && - (navigator.startSurface(state.navigation) == - navigator.currentSurface(state.navigation))) { - return; - } - // update the particle state first. this also computes the proper time which // needs the particle state from the previous step for reference. that means // this must happen for every step (not just on surface) and before // everything, e.g. any interactions that could modify the state. - if (std::isnan(result.properTimeLimit)) { - // first step is special: there is no previous state and we need to arm - // the decay simulation for all future steps. - result.particle = - makeParticle(initialParticle, state, stepper, navigator); - result.properTimeLimit = - decay.generateProperTimeLimit(*generator, initialParticle); - } else { - result.particle = - makeParticle(result.particle, state, stepper, navigator); - } + result.particle = makeParticle(result.particle, state, stepper, navigator); // decay check. needs to happen at every step, not just on surfaces. if (std::isfinite(result.properTimeLimit) && diff --git a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp index 6ac84da77ee..f27db472439 100644 --- a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp +++ b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/DetectorVolumeSvgConverter.hpp @@ -41,6 +41,8 @@ struct Options { PortalConverter::Options portalOptions; /// The Surface converter options SurfaceConverter::Options surfaceOptions; + /// The Grid converter options + IndexedSurfacesConverter::Options indexedSurfacesOptions; }; /// Write/create the detector volume diff --git a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp index a6faa5ed42c..38189871efb 100644 --- a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp +++ b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp @@ -19,6 +19,7 @@ namespace Acts::Svg { +/// @brief Style struct struct Style { // Fill parameters std::array fillColor = {255, 255, 255}; @@ -37,8 +38,10 @@ struct Style { std::vector strokeDasharray = {}; unsigned int fontSize = 14u; + std::array fontColor = {0}; - unsigned int nSegments = 72u; + /// Number of segments to approximate a quarter of a circle + unsigned int quarterSegments = 72u; /// Conversion to fill and stroke object from the base library /// @return a tuple of actsvg digestable objects @@ -58,6 +61,19 @@ struct Style { return std::tie(fll, str); } + + /// Conversion to fill, stroke and font + /// @return a tuple of actsvg digestable objects + std::tuple + fillStrokeFont() const { + auto [fll, str] = fillAndStroke(); + + actsvg::style::font fnt; + fnt._size = fontSize; + fnt._fc._rgb = fontColor; + + return std::tie(fll, str, fnt); + } }; /// Create a group @@ -131,31 +147,27 @@ inline static actsvg::svg::object axesXY(ActsScalar xMin, ActsScalar xMax, /// @param xPos the minimum x value /// @param yPos the maximum x value /// @param title the title of the info box +/// @param titleStyle the title of the info box /// @param info the text of the info box -/// @param infoBoxStyle the style of the info box +/// @param infoStyle the style of the info box (body) /// @param object the connected object /// /// @return an svg object -inline static actsvg::svg::object infoBox(ActsScalar xPos, ActsScalar yPos, - const std::string& title, - const std::vector& info, - const Style& infoBoxStyle, - const actsvg::svg::object& object) { - auto [fill, stroke] = infoBoxStyle.fillAndStroke(); - - actsvg::style::font titleFont; - titleFont._fc = actsvg::style::color{{255, 255, 255}}; - titleFont._size = infoBoxStyle.fontSize; - - actsvg::style::fill infoFill = fill; - infoFill._fc._opacity = 0.4; - actsvg::style::font infoFont; - infoFont._size = infoBoxStyle.fontSize; +inline static actsvg::svg::object infoBox( + ActsScalar xPos, ActsScalar yPos, const std::string& title, + const Style& titleStyle, const std::vector& info, + const Style& infoStyle, actsvg::svg::object& object, + const std::vector& highlights = {"mouseover", "mouseout"}) { + auto [titleFill, titleStroke, titleFont] = titleStyle.fillStrokeFont(); + auto [infoFill, infoStroke, infoFont] = infoStyle.fillStrokeFont(); + + actsvg::style::stroke stroke; return actsvg::draw::connected_info_box( object._id + "_infoBox", {static_cast(xPos), static_cast(yPos)}, - title, fill, titleFont, info, infoFill, infoFont, stroke, object); + title, titleFill, titleFont, info, infoFill, infoFont, stroke, object, + highlights); } /// Helper method to write to file diff --git a/Plugins/ActSVG/src/DetectorVolumeSvgConverter.cpp b/Plugins/ActSVG/src/DetectorVolumeSvgConverter.cpp index c187e69569b..93527488538 100644 --- a/Plugins/ActSVG/src/DetectorVolumeSvgConverter.cpp +++ b/Plugins/ActSVG/src/DetectorVolumeSvgConverter.cpp @@ -68,7 +68,8 @@ Acts::Svg::DetectorVolumeConverter::convert( // Make dedicated surface grid sheets const auto& internalNavigationDelegate = dVolume.internalNavigation(); - IndexedSurfacesConverter::Options isOptions; + IndexedSurfacesConverter::Options isOptions = + volumeOptions.indexedSurfacesOptions; // Use or transfer the surface style if (isOptions.surfaceStyles.empty()) { std::pair style{ diff --git a/Plugins/ActSVG/src/SurfaceArraySvgConverter.cpp b/Plugins/ActSVG/src/SurfaceArraySvgConverter.cpp index f2acf870864..794bb1d43b5 100644 --- a/Plugins/ActSVG/src/SurfaceArraySvgConverter.cpp +++ b/Plugins/ActSVG/src/SurfaceArraySvgConverter.cpp @@ -13,6 +13,8 @@ #include "Acts/Surfaces/SurfaceArray.hpp" #include "Acts/Surfaces/SurfaceBounds.hpp" +#include + std::tuple, Acts::Svg::ProtoGrid, std::vector > Acts::Svg::SurfaceArrayConverter::convert( @@ -93,11 +95,9 @@ Acts::Svg::SurfaceArrayConverter::convert( auto sameBounds = [&](const SurfaceBounds* test) { return ((*test) == sBounds); }; - // Check if you have this template object already - auto tBounds = - std::find_if(templateBounds.begin(), templateBounds.end(), sameBounds); - // New reference bounds and new reference object - if (tBounds == templateBounds.end()) { + // Check if you have this template object already before creating new + // reference bounds and new reference object + if (std::ranges::none_of(templateBounds, sameBounds)) { // Let's get the right style SurfaceConverter::Options sOptions; sOptions.templateSurface = true; @@ -107,7 +107,7 @@ Acts::Svg::SurfaceArrayConverter::convert( sOptions.style = *sfStyle; } - // Create a referese surface and reference object from it + // Create a reference surface and reference object from it auto referenceSurface = SurfaceConverter::convert(gctx, *sf, sOptions); auto referenceObject = View::xy(referenceSurface, @@ -148,8 +148,7 @@ Acts::Svg::SurfaceArrayConverter::convert( return ((*test) == sBounds); }; // Check if you have this template object already - auto tBounds = std::find_if(templateBounds.begin(), templateBounds.end(), - sameBounds); + auto tBounds = std::ranges::find_if(templateBounds, sameBounds); // New reference bounds and new reference object if (tBounds != templateBounds.end()) { std::size_t tObject = std::distance(templateBounds.begin(), tBounds); @@ -196,7 +195,7 @@ Acts::Svg::SurfaceArrayConverter::convert( auto bSurfaces = surfaceArray.neighbors(bCenter); std::vector binnAssoc; for (const auto& bs : bSurfaces) { - auto candidate = std::find(surfaces.begin(), surfaces.end(), bs); + auto candidate = std::ranges::find(surfaces, bs); if (candidate != surfaces.end()) { binnAssoc.push_back(std::distance(surfaces.begin(), candidate)); } diff --git a/Plugins/ActSVG/src/SurfaceSvgConverter.cpp b/Plugins/ActSVG/src/SurfaceSvgConverter.cpp index c5bf2bba429..8cc7a78d2df 100644 --- a/Plugins/ActSVG/src/SurfaceSvgConverter.cpp +++ b/Plugins/ActSVG/src/SurfaceSvgConverter.cpp @@ -21,7 +21,7 @@ Acts::Svg::ProtoSurface Acts::Svg::SurfaceConverter::convert( if (!cOptions.templateSurface) { // Polyhedron surface for vertices needed anyway Polyhedron surfaceHedron = - surface.polyhedronRepresentation(gctx, cOptions.style.nSegments); + surface.polyhedronRepresentation(gctx, cOptions.style.quarterSegments); auto vertices3D = surfaceHedron.vertices; pSurface._vertices = vertices3D; } else { @@ -30,7 +30,7 @@ Acts::Svg::ProtoSurface Acts::Svg::SurfaceConverter::convert( auto planarBounds = dynamic_cast(&(surface.bounds())); if (planarBounds != nullptr) { - auto vertices2D = planarBounds->vertices(cOptions.style.nSegments); + auto vertices2D = planarBounds->vertices(cOptions.style.quarterSegments); pSurface._vertices.reserve(vertices2D.size()); for (const auto& v2 : vertices2D) { pSurface._vertices.push_back({v2[0], v2[1], 0.}); @@ -40,7 +40,8 @@ Acts::Svg::ProtoSurface Acts::Svg::SurfaceConverter::convert( auto annulusBounds = dynamic_cast(&(surface.bounds())); if (annulusBounds != nullptr) { - auto vertices2D = annulusBounds->vertices(cOptions.style.nSegments); + auto vertices2D = + annulusBounds->vertices(cOptions.style.quarterSegments); pSurface._vertices.reserve(vertices2D.size()); for (const auto& v2 : vertices2D) { pSurface._vertices.push_back({v2[0], v2[1], 0.}); @@ -129,18 +130,9 @@ Acts::Svg::ProtoSurface Acts::Svg::SurfaceConverter::convert( geoId._id = std::to_string(surface.geometryId().value()); pSurface._decorations["geo_id"] = geoId; - // Attach the style - pSurface._fill._fc = { - cOptions.style.fillColor, - static_cast(cOptions.style.fillOpacity)}; - - // Fill style - pSurface._fill._fc._hl_rgb = cOptions.style.highlightColor; - pSurface._fill._fc._highlight = cOptions.style.highlights; - - // Stroke style - pSurface._stroke._sc = actsvg::style::color{cOptions.style.strokeColor}; - pSurface._stroke._width = cOptions.style.strokeWidth; + auto [surfaceFill, surfaceStroke] = cOptions.style.fillAndStroke(); + pSurface._fill = surfaceFill; + pSurface._stroke = surfaceStroke; return pSurface; } diff --git a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorSurfaceFactory.hpp b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorSurfaceFactory.hpp index 0a283433ab4..d6855443af2 100644 --- a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorSurfaceFactory.hpp +++ b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorSurfaceFactory.hpp @@ -71,8 +71,8 @@ class DD4hepDetectorSurfaceFactory { std::optional pExtent = std::nullopt; /// Optionally provide an Extent constraints to measure the layers std::vector extentConstraints = {}; - /// The approximination for extent measuring - std::size_t nExtentSegments = 1u; + /// The approximination of a circle quarter for extent measuring + std::size_t nExtentQSegments = 1u; }; /// Nested options struct to steer the conversion diff --git a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepLayerStructure.hpp b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepLayerStructure.hpp index bdf4780fb2f..a44e4341feb 100644 --- a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepLayerStructure.hpp +++ b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepLayerStructure.hpp @@ -65,8 +65,8 @@ class DD4hepLayerStructure { std::optional extent = std::nullopt; /// The extent constraints - optionally std::vector extentConstraints = {}; - /// Approximation for the polyhedron binning nSegments - unsigned int nSegments = 1u; + /// Approximation for the polyhedron binning + unsigned int quarterSegments = 1u; /// Patch the binning with the extent if possible bool patchBinningWithExtent = true; /// Conversion options diff --git a/Plugins/DD4hep/src/DD4hepDetectorSurfaceFactory.cpp b/Plugins/DD4hep/src/DD4hepDetectorSurfaceFactory.cpp index f3059fbeed5..d628ff76379 100644 --- a/Plugins/DD4hep/src/DD4hepDetectorSurfaceFactory.cpp +++ b/Plugins/DD4hep/src/DD4hepDetectorSurfaceFactory.cpp @@ -108,7 +108,7 @@ Acts::DD4hepDetectorSurfaceFactory::constructSensitiveComponents( // Measure if configured to do so if (cache.sExtent.has_value()) { auto sExtent = - sSurface->polyhedronRepresentation(gctx, cache.nExtentSegments) + sSurface->polyhedronRepresentation(gctx, cache.nExtentQSegments) .extent(); cache.sExtent.value().extend(sExtent, cache.extentConstraints); } @@ -137,7 +137,7 @@ Acts::DD4hepDetectorSurfaceFactory::constructPassiveComponents( // Measure if configured to do so if (cache.pExtent.has_value()) { auto sExtent = - pSurface->polyhedronRepresentation(gctx, cache.nExtentSegments) + pSurface->polyhedronRepresentation(gctx, cache.nExtentQSegments) .extent(); cache.pExtent.value().extend(sExtent, cache.extentConstraints); } diff --git a/Plugins/DD4hep/src/DD4hepLayerStructure.cpp b/Plugins/DD4hep/src/DD4hepLayerStructure.cpp index 64b6e8cf32e..10a4e6eb9f0 100644 --- a/Plugins/DD4hep/src/DD4hepLayerStructure.cpp +++ b/Plugins/DD4hep/src/DD4hepLayerStructure.cpp @@ -38,7 +38,7 @@ Acts::Experimental::DD4hepLayerStructure::builder( fCache.sExtent = options.extent; fCache.pExtent = options.extent; fCache.extentConstraints = options.extentConstraints; - fCache.nExtentSegments = options.nSegments; + fCache.nExtentQSegments = options.quarterSegments; m_surfaceFactory->construct(fCache, gctx, dd4hepElement, options.conversionOptions); diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp index 201aeb2807d..2f827a08bea 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp @@ -20,6 +20,10 @@ namespace Acts { +namespace Experimental { +class DetectorVolume; +} + using DetrayHostDetector = detray::detector; namespace DetrayConversionUtils { @@ -28,12 +32,37 @@ namespace DetrayConversionUtils { /// /// This object is used to synchronize link information between the /// different converters (geometry, material, surface grids) -struct GeometryIdCache { - /// This is a multimap to pass volume local surface link information - /// The portal splitting requires a multimap implementation here - std::multimap localSurfaceLinks; +struct Cache { + /// Explicit constructor with detector volumes + /// + /// @param detectorVolumes the number of detector volumes + Cache(const std::vector& dVolumes) + : detectorVolumes(dVolumes) {} + + /// The volumes of the detector for index lookup + std::vector detectorVolumes; /// This is a map to pass on volume link information std::map volumeLinks; + /// This is a multimap to pass volume local surface link information + /// The portal splitting requires a multimap implementation here + /// + /// These are volume local, hence indexed per volumes + std::map> + localSurfaceLinks; + + /// Find the position of the volume to point to + /// + /// @param volume the volume to find + /// + /// @note throws exception if volume is not found + std::size_t volumeIndex( + const Acts::Experimental::DetectorVolume* volume) const { + auto candidate = std::ranges::find(detectorVolumes, volume); + if (candidate != detectorVolumes.end()) { + return std::distance(detectorVolumes.begin(), candidate); + } + throw std::invalid_argument("Volume not found in the cache"); + } }; /// Convert the binning option diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp index 2c8ca2c7fac..441b8f121f0 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp @@ -54,7 +54,7 @@ class DetrayConverter { const Experimental::Detector& detector, vecmem::memory_resource& mr, const Options& options) { // The building cache object - DetrayConversionUtils::GeometryIdCache geoIdCache; + DetrayConversionUtils::Cache cCache(detector.volumes()); typename detector_t::name_map names = {{0u, detector.name()}}; @@ -62,7 +62,7 @@ class DetrayConverter { detray::detector_builder detectorBuilder{}; // (1) geometry detray::io::detector_payload detectorPayload = - DetrayGeometryConverter::convertDetector(geoIdCache, gctx, detector, + DetrayGeometryConverter::convertDetector(cCache, gctx, detector, logger()); detray::io::geometry_reader::convert(detectorBuilder, names, detectorPayload); @@ -72,7 +72,7 @@ class DetrayConverter { if (options.convertMaterial) { detray::io::detector_homogeneous_material_payload materialSlabsPayload = DetrayMaterialConverter::convertHomogeneousSurfaceMaterial( - geoIdCache, detector, logger()); + cCache, detector, logger()); detray::io::homogeneous_material_reader::convert( detectorBuilder, names, std::move(materialSlabsPayload)); } @@ -85,7 +85,7 @@ class DetrayConverter { detray::io::material_id> materialGridsPayload = DetrayMaterialConverter::convertGridSurfaceMaterial( - geoIdCache, detector, logger()); + cCache, detector, logger()); detray::io::material_map_reader>::convert(detectorBuilder, names, std::move( diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayGeometryConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayGeometryConverter.hpp index 2df31096746..b6ea1373125 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayGeometryConverter.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayGeometryConverter.hpp @@ -64,25 +64,25 @@ detray::io::surface_payload convertSurface(const GeometryContext& gctx, /// Conversion method for Portal object to detray::portal payloads /// +/// @param cCache [in, out] object /// @param gctx the geometry context /// @param portal the portal to be converted /// @param ip the portal index /// @param volume the volume to which the portal belongs /// @param orientedSurfaces the oriented surfaces of the portal -/// @param detectorVolumes the detector volumes for the link lookup /// /// @note due to portal splitting this can add up in N portals for one initial one /// /// @brief convert the acts portal to detray surface payload and populate the payload std::vector convertPortal( - const GeometryContext& gctx, const Experimental::Portal& portal, - std::size_t ip, const Experimental::DetectorVolume& volume, - const std::vector& orientedSurfaces, - const std::vector& detectorVolumes); + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, + const Experimental::Portal& portal, std::size_t ip, + const Experimental::DetectorVolume& volume, + const std::vector& orientedSurfaces); /// Conversion method for volume objects to detray::volume payloads /// -/// @param geoIdCache [in, out] object +/// @param cCache [in, out] object /// @param gctx the geometry context /// @param volume the volume to be converted /// @param detectorVolumes the detector volumes for the link lookup @@ -90,23 +90,20 @@ std::vector convertPortal( /// /// @return the volume_payload for portals and volumes by @param volume acts object detray::io::volume_payload convertVolume( - DetrayConversionUtils::GeometryIdCache& geoIdCache, - const GeometryContext& gctx, const Experimental::DetectorVolume& volume, - const std::vector& detectorVolumes, - const Acts::Logger& logger); + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, + const Experimental::DetectorVolume& volume, const Acts::Logger& logger); /// Conversion method for detector objects to detray::detector payload /// -/// @param geoIdCache [in, out] object +/// @param cCache [in, out] object /// @param gctx the geometry context /// @param detector the detector to be converted /// @param logger the logger object for screen output /// /// @return the detector_payload for portals and volumes by @param detector acts object detray::io::detector_payload convertDetector( - DetrayConversionUtils::GeometryIdCache& geoIdCache, - const GeometryContext& gctx, const Experimental::Detector& detector, - const Acts::Logger& logger); + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, + const Experimental::Detector& detector, const Acts::Logger& logger); } // namespace DetrayGeometryConverter } // namespace Acts diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp index d3be849e362..70a2c8cec73 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp @@ -35,15 +35,15 @@ detray::io::material_slab_payload convertMaterialSlab( /// Conversion method for homogeneous material /// -/// @param geoIdCache object to have the link association from the geometry building +/// @param cCache object to have the link association from the geometry building /// @param detector the detector object /// @param logger the logger object for screen output /// /// @return the volume_payload for portals and volumes by @param volume acts object detray::io::detector_homogeneous_material_payload -convertHomogeneousSurfaceMaterial( - const DetrayConversionUtils::GeometryIdCache& geoIdCache, - const Experimental::Detector& detector, const Logger& logger); +convertHomogeneousSurfaceMaterial(const DetrayConversionUtils::Cache& cCache, + const Experimental::Detector& detector, + const Logger& logger); /// Conversion method for grid based surface material /// @@ -58,16 +58,16 @@ convertGridSurfaceMaterial(const ISurfaceMaterial& material, /// Conversion method for material grids /// -/// @param geoIdCache object to have the link association from the geometry building +/// @param cCache object to have the link association from the geometry building /// @param detector the detector object /// @param logger the logger object for screen output /// /// @return the volume_payload for portals and volumes by @param volume acts object detray::io::detector_grids_payload -convertGridSurfaceMaterial( - const DetrayConversionUtils::GeometryIdCache& geoIdCache, - const Experimental::Detector& detector, const Logger& logger); +convertGridSurfaceMaterial(const DetrayConversionUtils::Cache& cCache, + const Experimental::Detector& detector, + const Logger& logger); } // namespace DetrayMaterialConverter diff --git a/Plugins/Detray/src/DetrayGeometryConverter.cpp b/Plugins/Detray/src/DetrayGeometryConverter.cpp index da57d91c2bb..23eb56a0f8c 100644 --- a/Plugins/Detray/src/DetrayGeometryConverter.cpp +++ b/Plugins/Detray/src/DetrayGeometryConverter.cpp @@ -21,29 +21,12 @@ #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/SurfaceBounds.hpp" +#include + #include using namespace detray; -namespace { - -/// Find the position of the volume to point to -/// -/// @param volume the volume to find -/// @param the collection of volumes -/// -/// @note return -1 if not found, to be interpreted by the caller -int findVolume( - const Acts::Experimental::DetectorVolume* volume, - const std::vector& volumes) { - auto candidate = std::find(volumes.begin(), volumes.end(), volume); - if (candidate != volumes.end()) { - return std::distance(volumes.begin(), candidate); - } - return -1; -} -} // namespace - detray::io::transform_payload Acts::DetrayGeometryConverter::convertTransform( const Transform3& t) { detray::io::transform_payload tfPayload; @@ -85,10 +68,10 @@ detray::io::surface_payload Acts::DetrayGeometryConverter::convertSurface( std::vector Acts::DetrayGeometryConverter::convertPortal( - const GeometryContext& gctx, const Experimental::Portal& portal, - std::size_t ip, const Experimental::DetectorVolume& volume, - const std::vector& orientedSurfaces, - const std::vector& detectorVolumes) { + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, + const Experimental::Portal& portal, std::size_t ip, + const Experimental::DetectorVolume& volume, + const std::vector& orientedSurfaces) { std::vector portals{}; const RegularSurface& surface = portal.surface(); @@ -133,7 +116,7 @@ Acts::DetrayGeometryConverter::convertPortal( // in order to make sure the size is adjusted if (singleLink != nullptr) { // Single link can be written out - std::size_t vLink = findVolume(singleLink->object(), detectorVolumes); + std::size_t vLink = cCache.volumeIndex(singleLink->object()); auto portalPayload = convertSurface(gctx, *surfaceAdjusted, true); portalPayload.mask.volume_link.link = vLink; portals.push_back(portalPayload); @@ -159,10 +142,10 @@ Acts::DetrayGeometryConverter::convertPortal( auto surfaceType = surfaceAdjusted->type(); std::vector vIndices = {}; for (const auto& v : volumes) { - vIndices.push_back(findVolume(v, detectorVolumes)); + vIndices.push_back(cCache.volumeIndex(v)); } - // Pick the surface dimension - via poly + // Pick the surface dimension std::array clipRange = {0., 0.}; std::vector boundValues = surfaceAdjusted->bounds().values(); if (surfaceType == Surface::SurfaceType::Cylinder && @@ -225,6 +208,7 @@ Acts::DetrayGeometryConverter::convertPortal( 0., 0., clippedBoundaries[ib - 1u] + subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ])); + auto subSurface = Surface::makeShared(subTransform, subBounds); subSurface->assignGeometryId(surface.geometryId()); @@ -249,6 +233,7 @@ Acts::DetrayGeometryConverter::convertPortal( auto subBounds = std::make_shared(subBoundValues); auto subSurface = Surface::makeShared( portal.surface().transform(gctx), subBounds); + subSurface->assignGeometryId(surface.geometryId()); auto portalPayload = convertSurface(gctx, *subSurface, true); portalPayload.mask.volume_link.link = clippedIndices[ib - 1u]; @@ -258,7 +243,6 @@ Acts::DetrayGeometryConverter::convertPortal( } } else { - // End of world portal // Write surface with invalid link auto portalPayload = convertSurface(gctx, *surfaceAdjusted, true); using NavigationLink = @@ -269,27 +253,27 @@ Acts::DetrayGeometryConverter::convertPortal( portals.push_back(portalPayload); } } - return portals; } detray::io::volume_payload Acts::DetrayGeometryConverter::convertVolume( - DetrayConversionUtils::GeometryIdCache& geoIdCache, - const GeometryContext& gctx, + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, const Acts::Experimental::DetectorVolume& volume, - const std::vector& detectorVolumes, const Acts::Logger& logger) { ACTS_DEBUG("DetrayGeometryConverter: converting volume " << volume.name() << " with " << volume.surfaces().size() << " surfaces and " << volume.portals().size() << " portals"); detray::io::volume_payload volumePayload; + std::size_t volumeIndex = cCache.volumeIndex(&volume); volumePayload.name = volume.name(); - volumePayload.index.link = findVolume(&volume, detectorVolumes); + volumePayload.index.link = volumeIndex; volumePayload.transform = convertTransform(volume.transform(gctx)); // Remember the link - geoIdCache.volumeLinks[volume.geometryId()] = volumePayload.index.link; + cCache.volumeLinks[volume.geometryId()] = volumePayload.index.link; + + std::multimap localSurfaceLinks; // iterate over surfaces and portals keeping the same surf_pd.index_in_coll std::size_t sIndex = 0; @@ -297,7 +281,7 @@ detray::io::volume_payload Acts::DetrayGeometryConverter::convertVolume( io::surface_payload surfacePayload = convertSurface(gctx, *surface, false); // Set the index in the collection & remember it in the cache surfacePayload.index_in_coll = sIndex++; - geoIdCache.localSurfaceLinks.insert( + localSurfaceLinks.insert( {surface->geometryId(), surfacePayload.index_in_coll.value()}); // Set mask to volume link surfacePayload.mask.volume_link.link = @@ -312,30 +296,32 @@ detray::io::volume_payload Acts::DetrayGeometryConverter::convertVolume( int portalCounter = 0; for (const auto& [ip, p] : enumerate(volume.portals())) { auto portals = - convertPortal(gctx, *p, ip, volume, orientedSurfaces, detectorVolumes); - + convertPortal(cCache, gctx, *p, ip, volume, orientedSurfaces); + ACTS_VERBOSE(" > portal " << ip << " split into " << portals.size() + << " surfaces"); GeometryIdentifier geoID = p->surface().geometryId(); std::for_each(portals.begin(), portals.end(), [&](auto& portalPayload) { // Set the index in the collection & remember it in the cache portalPayload.index_in_coll = sIndex++; - geoIdCache.localSurfaceLinks.insert( - {geoID, portalPayload.index_in_coll.value()}); + localSurfaceLinks.insert({geoID, portalPayload.index_in_coll.value()}); // Add it to the surfaces volumePayload.surfaces.push_back(portalPayload); portalCounter++; }); } - ACTS_VERBOSE(" > " << volume.portals().size() - << " initial ACTS portals split into final " - << portalCounter << " detray portals"); + cCache.localSurfaceLinks[volumeIndex] = localSurfaceLinks; + ACTS_DEBUG(" > " << volume.portals().size() + << " initial ACTS portals split into final " << portalCounter + << " detray portals"); + ACTS_VERBOSE(" > Local surface link cache has " << localSurfaceLinks.size() + << " entries"); return volumePayload; } detray::io::detector_payload Acts::DetrayGeometryConverter::convertDetector( - DetrayConversionUtils::GeometryIdCache& geoIdCache, - const GeometryContext& gctx, const Acts::Experimental::Detector& detector, - const Acts::Logger& logger) { + DetrayConversionUtils::Cache& cCache, const GeometryContext& gctx, + const Acts::Experimental::Detector& detector, const Acts::Logger& logger) { ACTS_DEBUG("DetrayGeometryConverter: converting detector" << detector.name() << " with " << detector.volumes().size() << " volumes."); @@ -345,7 +331,7 @@ detray::io::detector_payload Acts::DetrayGeometryConverter::convertDetector( for (const auto volume : detector.volumes()) { detectorPayload.volumes.push_back( - convertVolume(geoIdCache, gctx, *volume, detector.volumes(), logger)); + convertVolume(cCache, gctx, *volume, logger)); } return detectorPayload; diff --git a/Plugins/Detray/src/DetrayMaterialConverter.cpp b/Plugins/Detray/src/DetrayMaterialConverter.cpp index dd82b0d1b93..c08bdc447bf 100644 --- a/Plugins/Detray/src/DetrayMaterialConverter.cpp +++ b/Plugins/Detray/src/DetrayMaterialConverter.cpp @@ -52,13 +52,13 @@ Acts::DetrayMaterialConverter::convertMaterialSlab( detray::io::detector_homogeneous_material_payload Acts::DetrayMaterialConverter::convertHomogeneousSurfaceMaterial( - const DetrayConversionUtils::GeometryIdCache& geoIdCache, + const DetrayConversionUtils::Cache& cCache, const Experimental::Detector& detector, const Logger& logger) { detray::io::detector_homogeneous_material_payload materialPayload; for (const auto volume : detector.volumes()) { - auto volumeIndex = geoIdCache.volumeLinks.find(volume->geometryId()); - if (volumeIndex != geoIdCache.volumeLinks.end()) { + auto volumeIndex = cCache.volumeLinks.find(volume->geometryId()); + if (volumeIndex != cCache.volumeLinks.end()) { // The volume material payload & its link detray::io::material_volume_payload volumePayload; detray::io::single_link_payload volumeLink; @@ -80,18 +80,24 @@ Acts::DetrayMaterialConverter::convertHomogeneousSurfaceMaterial( auto materialSlab = homogeneousMaterial->materialSlab(); detray::io::material_slab_payload slabPayload = convertMaterialSlab(materialSlab); - // Find the surfaces and assign - auto surfaceIndices = - geoIdCache.localSurfaceLinks.equal_range(surface->geometryId()); - // Loop over the equal range and fill one grid each, this is needed - // as the initial portal could be split into multiple surfaces - for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; - ++itr) { - // Make an identified link copy for every matching surface - detray::io::single_link_payload surfaceLink; - surfaceLink.link = itr->second; - slabPayload.surface = surfaceLink; - volumePayload.mat_slabs.push_back(slabPayload); + // Find the surfaces to assign + auto vIndex = cCache.volumeIndex(volume); + auto localSurfaceLinks = cCache.localSurfaceLinks.find(vIndex); + if (localSurfaceLinks != cCache.localSurfaceLinks.end()) { + // Find the surface link + auto surfaceIndices = + localSurfaceLinks->second.equal_range(surface->geometryId()); + // Loop over the equal range and fill one grid each, this is needed + // as the initial portal could be split into multiple surfaces + for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; + ++itr) { + // Make an identified link copy for every matching surface + slabPayload.surface.link = itr->second; + volumePayload.mat_slabs.push_back(slabPayload); + } + } else { + ACTS_WARNING( + "DetrayMaterialConverter: no local surface links found"); } } } @@ -232,7 +238,7 @@ Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( detray::io::detector_grids_payload Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( - const DetrayConversionUtils::GeometryIdCache& geoIdCache, + const DetrayConversionUtils::Cache& cCache, const Experimental::Detector& detector, const Logger& logger) { // The material grid payload detray::io::detector_grids_payloadvisitSurfaces(selector); - ACTS_DEBUG("DetrayMaterialConverter: found " - << selector.surfaces.size() - << " surfaces/portals with material in volume " - << volume->name()); + ACTS_VERBOSE("DetrayMaterialConverter: found " + << selector.surfaces.size() + << " surfaces/portals with material in volume " + << volume->name()); // Find the voluem index first - auto volumeIndex = geoIdCache.volumeLinks.find(volume->geometryId()); - if (volumeIndex != geoIdCache.volumeLinks.end()) { + auto volumeIndex = cCache.volumeLinks.find(volume->geometryId()); + if (volumeIndex != cCache.volumeLinks.end()) { std::vector volumeMaterialGrids = {}; // Now convert the surfaces for (const auto& surface : selector.surfaces) { - // Find the surface index - auto surfaceIndices = - geoIdCache.localSurfaceLinks.equal_range(surface->geometryId()); - DetrayMaterialGrid materialGrid = - convertGridSurfaceMaterial(*surface->surfaceMaterial(), logger); - // Ignore if an empty payload is returned - if (materialGrid.axes.empty() && materialGrid.bins.empty()) { - continue; - } - // Loop over the equal range and fill one grid each, this is needed - // as the initial portal could be split into multiple surfaces - for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; - ++itr) { - // Fill the surface index - materialGrid.owner_link = - detray::io::single_link_payload{itr->second}; - // Fill the grid - volumeMaterialGrids.push_back(materialGrid); + // Find the surfaces to assign + auto vIndex = cCache.volumeIndex(volume); + auto localSurfaceLinks = cCache.localSurfaceLinks.find(vIndex); + if (localSurfaceLinks != cCache.localSurfaceLinks.end()) { + // Find the surface link + auto surfaceIndices = + localSurfaceLinks->second.equal_range(surface->geometryId()); + + ACTS_VERBOSE( + "DetrayMaterialConverter: assigning to " + << std::distance(surfaceIndices.first, surfaceIndices.second) + << " surfaces with material in volume " << volume->name()); + DetrayMaterialGrid materialGrid = + convertGridSurfaceMaterial(*surface->surfaceMaterial(), logger); + // Ignore if an empty payload is returned + if (materialGrid.axes.empty() || materialGrid.bins.empty()) { + continue; + } + + // Loop over the equal range and fill one grid each, this is needed + // as the initial portal could be split into multiple surfaces + for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; + ++itr) { + // Fill the surface index + materialGrid.owner_link = + detray::io::single_link_payload{itr->second}; + // Fill the grid + volumeMaterialGrids.push_back(materialGrid); + } } } // Register the grids of this volume materialGrids.grids.insert({volumeIndex->second, volumeMaterialGrids}); - } else { ACTS_WARNING( "DetrayMaterialConverter: volume not found in cache, should not " diff --git a/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp b/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp index a3d6dfe1148..d53de1d3071 100644 --- a/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp +++ b/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp @@ -8,6 +8,8 @@ #include "Acts/Plugins/ExaTrkX/ExaTrkXPipeline.hpp" +#include "Acts/Utilities/Helpers.hpp" + #include namespace Acts { @@ -27,9 +29,8 @@ ExaTrkXPipeline::ExaTrkXPipeline( if (!m_trackBuilder) { throw std::invalid_argument("Missing track building module"); } - if (m_edgeClassifiers.empty() or - not std::all_of(m_edgeClassifiers.begin(), m_edgeClassifiers.end(), - [](const auto &a) { return static_cast(a); })) { + if (m_edgeClassifiers.empty() || + rangeContainsValue(m_edgeClassifiers, nullptr)) { throw std::invalid_argument("Missing graph construction module"); } } diff --git a/Plugins/FpeMonitoring/CMakeLists.txt b/Plugins/FpeMonitoring/CMakeLists.txt index 05a6126e2f3..ccd7c7f4ceb 100644 --- a/Plugins/FpeMonitoring/CMakeLists.txt +++ b/Plugins/FpeMonitoring/CMakeLists.txt @@ -24,6 +24,11 @@ else() set(_backtrace_setup_complete FALSE) + find_path( + boost_stacktrace_include + NAMES "boost/stacktrace.hpp" + REQUIRED + ) if(Backtrace_FOUND) # check if we need to link against bracktrace or not set(backtrace_include "") @@ -44,6 +49,7 @@ else() "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/backtrace.cpp" LINK_LIBRARIES ${dl_LIBRARY} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${boost_stacktrace_include}" COMPILE_DEFINITIONS -DBOOST_STACKTRACE_USE_BACKTRACE OUTPUT_VARIABLE __OUTPUT ) @@ -54,9 +60,9 @@ else() message(CHECK_FAIL "no") file(GLOB hints "/usr/lib/gcc/*/*/include") - find_file(backtrace_header "backtrace.h" HINTS ${hints}) + find_file(backtrace_header NAMES "backtrace.h" HINTS ${hints}) - if(${backtrace_header} STREQUAL "backtrcae_header-NOTFOUND") + if(${backtrace_header} STREQUAL "backtrace_header-NOTFOUND") message(STATUS "Could not find backtrace header file") else() set(backtrace_include @@ -82,6 +88,8 @@ else() "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/backtrace.cpp" LINK_LIBRARIES ${dl_LIBRARY} + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${boost_stacktrace_include}" COMPILE_DEFINITIONS -DBOOST_STACKTRACE_USE_BACKTRACE ${backtrace_include} @@ -111,6 +119,7 @@ else() "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/backtrace.cpp" LINK_LIBRARIES ${dl_LIBRARY} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${boost_stacktrace_include}" COMPILE_DEFINITIONS -DBOOST_STACKTRACE_USE_BACKTRACE ${backtrace_include} @@ -137,6 +146,8 @@ else() "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/backtrace.cpp" LINK_LIBRARIES backtrace ${dl_LIBRARY} + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${boost_stacktrace_include}" COMPILE_DEFINITIONS -DBOOST_STACKTRACE_USE_BACKTRACE ${backtrace_include} @@ -158,23 +169,6 @@ else() endif() endif() - if(NOT _backtrace_setup_complete) - message(CHECK_START "Is addr2line available") - if(addr2line_EXECUTABLE) - list(APPEND _fpe_options -DBOOST_STACKTRACE_USE_ADDR2LINE) - list( - APPEND - _fpe_options - -DBOOST_STACKTRACE_ADDR2LINE_LOCATION=${addr2line_EXECUTABLE} - ) - message(CHECK_PASS "yes") - - set(_backtrace_setup_complete TRUE) - else() - message(CHECK_FAIL "no") - endif() - endif() - if(NOT _backtrace_setup_complete) message(STATUS "Unable to set up stacktrace setup: use noop") list(APPEND _fpe_options -BOOST_STACKTRACE_USE_NOOP) diff --git a/Plugins/FpeMonitoring/include/Acts/Plugins/FpeMonitoring/FpeMonitor.hpp b/Plugins/FpeMonitoring/include/Acts/Plugins/FpeMonitoring/FpeMonitor.hpp index 99e39da96d0..022a07a3426 100644 --- a/Plugins/FpeMonitoring/include/Acts/Plugins/FpeMonitoring/FpeMonitor.hpp +++ b/Plugins/FpeMonitoring/include/Acts/Plugins/FpeMonitoring/FpeMonitor.hpp @@ -40,7 +40,7 @@ std::ostream &operator<<(std::ostream &os, FpeType type); class FpeMonitor { public: struct Buffer { - Buffer(std::size_t bufferSize) + explicit Buffer(std::size_t bufferSize) : m_data{std::make_unique(bufferSize)}, m_size{bufferSize} {} @@ -105,12 +105,12 @@ class FpeMonitor { Result() = default; - operator bool() const { return !m_stracktraces.empty(); } + bool hasStackTraces() const { return !m_stackTraces.empty(); } void add(Acts::FpeType type, void *stackPtr, std::size_t bufferSize); private: - std::vector m_stracktraces; + std::vector m_stackTraces; std::array m_counts{}; friend FpeMonitor; @@ -131,6 +131,8 @@ class FpeMonitor { std::size_t depth); static std::string getSourceLocation(const boost::stacktrace::frame &frame); + static bool canSymbolize(); + private: void enable(); void disable(); diff --git a/Plugins/FpeMonitoring/src/FpeMonitor.cpp b/Plugins/FpeMonitoring/src/FpeMonitor.cpp index 3b08145668e..09dce1f4b0a 100644 --- a/Plugins/FpeMonitoring/src/FpeMonitor.cpp +++ b/Plugins/FpeMonitoring/src/FpeMonitor.cpp @@ -61,10 +61,10 @@ FpeMonitor::Result FpeMonitor::Result::merged(const Result &with) const { result.m_counts[i] = m_counts[i] + with.m_counts[i]; } - std::copy(with.m_stracktraces.begin(), with.m_stracktraces.end(), - std::back_inserter(result.m_stracktraces)); - std::copy(m_stracktraces.begin(), m_stracktraces.end(), - std::back_inserter(result.m_stracktraces)); + std::copy(with.m_stackTraces.begin(), with.m_stackTraces.end(), + std::back_inserter(result.m_stackTraces)); + std::copy(m_stackTraces.begin(), m_stackTraces.end(), + std::back_inserter(result.m_stackTraces)); result.deduplicate(); @@ -76,8 +76,8 @@ void FpeMonitor::Result::merge(const Result &with) { m_counts[i] = m_counts[i] + with.m_counts[i]; } - std::copy(with.m_stracktraces.begin(), with.m_stracktraces.end(), - std::back_inserter(m_stracktraces)); + std::copy(with.m_stackTraces.begin(), with.m_stackTraces.end(), + std::back_inserter(m_stackTraces)); deduplicate(); } @@ -87,24 +87,22 @@ void FpeMonitor::Result::add(FpeType type, void *stackPtr, auto st = std::make_unique( boost::stacktrace::stacktrace::from_dump(stackPtr, bufferSize)); - auto it = std::find_if( - m_stracktraces.begin(), m_stracktraces.end(), [&](const FpeInfo &el) { - return areFpesEquivalent({el.type, *el.st}, {type, *st}); - }); + auto it = std::ranges::find_if(m_stackTraces, [&](const FpeInfo &el) { + return areFpesEquivalent({el.type, *el.st}, {type, *st}); + }); - if (it != m_stracktraces.end()) { + if (it != m_stackTraces.end()) { it->count += 1; } else { - m_stracktraces.push_back({1, type, std::move(st)}); + m_stackTraces.push_back({1, type, std::move(st)}); } } bool FpeMonitor::Result::contains( FpeType type, const boost::stacktrace::stacktrace &st) const { - return std::find_if(m_stracktraces.begin(), m_stracktraces.end(), - [&](const FpeInfo &el) { - return areFpesEquivalent({el.type, *el.st}, {type, st}); - }) != m_stracktraces.end(); + return std::ranges::any_of(m_stackTraces, [&](const FpeInfo &el) { + return areFpesEquivalent({el.type, *el.st}, {type, st}); + }); } FpeMonitor::Result &FpeMonitor::result() { @@ -130,12 +128,12 @@ unsigned int FpeMonitor::Result::count(FpeType type) const { } unsigned int FpeMonitor::Result::numStackTraces() const { - return m_stracktraces.size(); + return m_stackTraces.size(); } const std::vector & FpeMonitor::Result::stackTraces() const { - return m_stracktraces; + return m_stackTraces; } bool FpeMonitor::Result::encountered(FpeType type) const { @@ -163,20 +161,18 @@ void FpeMonitor::Result::summary(std::ostream &os, std::size_t depth) const { void FpeMonitor::Result::deduplicate() { std::vector copy{}; - copy = std::move(m_stracktraces); - m_stracktraces.clear(); + copy = std::move(m_stackTraces); + m_stackTraces.clear(); for (auto &info : copy) { - auto it = std::find_if(m_stracktraces.begin(), m_stracktraces.end(), - [&info](const FpeInfo &el) { - return areFpesEquivalent({el.type, *el.st}, - {info.type, *info.st}); - }); - if (it != m_stracktraces.end()) { + auto it = std::ranges::find_if(m_stackTraces, [&info](const FpeInfo &el) { + return areFpesEquivalent({el.type, *el.st}, {info.type, *info.st}); + }); + if (it != m_stackTraces.end()) { it->count += info.count; continue; } - m_stracktraces.push_back({info.count, info.type, std::move(info.st)}); + m_stackTraces.push_back({info.count, info.type, std::move(info.st)}); } } @@ -336,4 +332,12 @@ std::string FpeMonitor::getSourceLocation( return frame.source_file() + ":" + std::to_string(frame.source_line()); } +bool FpeMonitor::canSymbolize() { +#if defined(BOOST_STACKTRACE_USE_NOOP) + return false; +#else + return true; +#endif +} + } // namespace Acts diff --git a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp index fae8fc20fea..d06c77412e4 100644 --- a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp +++ b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp @@ -67,7 +67,7 @@ class Geant4DetectorSurfaceFactory { /// Convert the length scale ActsScalar scaleConversion = 1.; /// Convert the material - bool convertMaterial = true; + bool convertMaterial = false; /// Converted material thickness (< 0 indicates keeping original thickness) ActsScalar convertedMaterialThickness = -1; /// A selector for sensitive surfaces diff --git a/Plugins/GeoModel/CMakeLists.txt b/Plugins/GeoModel/CMakeLists.txt index 05c4c9e8148..90a672a012b 100644 --- a/Plugins/GeoModel/CMakeLists.txt +++ b/Plugins/GeoModel/CMakeLists.txt @@ -20,6 +20,7 @@ add_library( src/detail/GeoModelBinningHelper.cpp src/detail/GeoModelExtentHelper.cpp src/detail/GeoUnionDoubleTrdConverter.cpp + src/GeoModelDetectorElementITk.cpp ) target_include_directories( ActsPluginGeoModel diff --git a/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp index f382a49543a..c6358bdbec2 100644 --- a/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp +++ b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp @@ -40,8 +40,8 @@ class GeoModelBlueprintCreater { std::vector> detectorSurfaces = {}; /// The binning values for the KDTree sorting std::vector kdtBinning = {}; - /// Polyhedron approximations - unsigned int nSegments = 1u; + /// Polyhedron approximation: number of segments per circlequarter + unsigned int quarterSegments = 1u; }; /// The cache struct @@ -58,6 +58,8 @@ class GeoModelBlueprintCreater { std::string topEntry; /// Optionally override the top node bounds std::string topBoundsOverride = ""; + /// Export dot graph + std::string dotGraph = ""; }; /// The Blueprint return object diff --git a/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElement.hpp b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElement.hpp index eed4d2f62a3..ab1cb025a4a 100644 --- a/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElement.hpp +++ b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElement.hpp @@ -87,7 +87,18 @@ class GeoModelDetectorElement : public DetectorElementBase { /// @return to the Geant4 physical volume PVConstLink physicalVolume() const; - private: + /// Get the name of the logical volume + const std::string& logVolName() const; + + /// Get the string identifier of the corresponding database entry + /// Note: This is not by defnitition a unique identifier, there can be + /// several detector elements created from a single database entry. + const std::string& databaseEntryName() const { return m_entryName; }; + + /// Set the corresponding database entry string + void setDatabaseEntryName(const std::string& n) { m_entryName = n; }; + + protected: /// Attach a surface /// /// @param surface The surface to attach @@ -95,6 +106,9 @@ class GeoModelDetectorElement : public DetectorElementBase { m_surface = std::move(surface); } + private: + std::string m_entryName; + /// The GeoModel full physical volume PVConstLink m_geoPhysVol{nullptr}; /// The surface diff --git a/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElementITk.hpp b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElementITk.hpp new file mode 100644 index 00000000000..31cce3360e9 --- /dev/null +++ b/Plugins/GeoModel/include/Acts/Plugins/GeoModel/GeoModelDetectorElementITk.hpp @@ -0,0 +1,87 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/Plugins/GeoModel/GeoModelDetectorElement.hpp" +#include "Acts/Utilities/MultiIndex.hpp" + +namespace Acts { + +class ITkIdentifier { + Acts::MultiIndex m_identifier{}; + + public: + ITkIdentifier(int hardware, int barrelEndcap, int layerWheel, int etaModule, + int phiModule, int side); + + /// Access the hardware specifier (pixel=0, strip=1) + int hardware() const; + + /// Access the barrel-endcap specifier (-2,0,2) + int barrelEndcap() const; + + /// Access the layer specifier + int layerWheel() const; + + /// Access the phi module specifier + int phiModule() const; + + /// Access the eta module specifier + int etaModule() const; + + /// Access the side (for double sided strip modules) + int side() const; + + /// A unique identifier that represents the combination of specifiers + std::size_t value() const; +}; + +std::ostream& operator<<(std::ostream& os, const ITkIdentifier& id); + +/// Specialization of the GeoModelDetectorElement for the ITk. This allows +/// mapping of Acts::GeometryIdentifiers to ITk modules in a straight-forward +/// way. +class GeoModelDetectorElementITk : public GeoModelDetectorElement { + public: + GeoModelDetectorElementITk(const PVConstLink& geoPhysVol, + std::shared_ptr surface, + const Transform3& sfTransform, + ActsScalar thickness, int hardware, + int barrelEndcap, int layerWheel, int etaModule, + int phiModule, int side) + : GeoModelDetectorElement(geoPhysVol, std::move(surface), sfTransform, + thickness), + m_identifier(hardware, barrelEndcap, layerWheel, etaModule, phiModule, + side) {} + + ITkIdentifier identifier() const { return m_identifier; } + + /// Convert a GeoModelDetectorElement to a GeoModelDetectorElementITk + /// A new surface is constructed. + /// @todo Remove redundancy in signature once plugin is refactored + static std::tuple, + std::shared_ptr> + convertFromGeomodel(std::shared_ptr detEl, + std::shared_ptr srf, const GeometryContext& gctx, + int hardware, int barrelEndcap, int layerWheel, + int etaModule, int phiModule, int side); + + private: + ITkIdentifier m_identifier; +}; + +} // namespace Acts diff --git a/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp b/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp index 9d31eeaaffc..0b3d2f66e42 100644 --- a/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp +++ b/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp @@ -10,6 +10,7 @@ #include "Acts/Detector/GeometryIdGenerator.hpp" #include "Acts/Detector/LayerStructureBuilder.hpp" +#include "Acts/Detector/detail/BlueprintDrawer.hpp" #include "Acts/Detector/detail/BlueprintHelper.hpp" #include "Acts/Detector/interface/IGeometryIdGenerator.hpp" #include "Acts/Plugins/GeoModel/GeoModelTree.hpp" @@ -20,6 +21,8 @@ #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/RangeXD.hpp" +#include + #include using namespace Acts::detail; @@ -128,6 +131,14 @@ Acts::GeoModelBlueprintCreater::create(const GeometryContext& gctx, blueprint.topNode = createNode(cache, gctx, topEntry->second, blueprintTableMap, Extent()); + // Export to dot graph if configured + if (!options.dotGraph.empty()) { + std::ofstream dotFile(options.dotGraph); + Experimental::detail::BlueprintDrawer::dotStream(dotFile, + *blueprint.topNode); + dotFile.close(); + } + // Return the ready-to-use blueprint return blueprint; } @@ -354,7 +365,7 @@ Acts::GeoModelBlueprintCreater::createInternalStructureBuilder( // Loop over surfaces and create an internal extent for (auto& sf : surfaces) { auto sfExtent = - sf->polyhedronRepresentation(gctx, m_cfg.nSegments).extent(); + sf->polyhedronRepresentation(gctx, m_cfg.quarterSegments).extent(); internalExtent.extend(sfExtent, internalConstraints); } ACTS_VERBOSE("Found " << surfaces.size() << " surfaces in range " diff --git a/Plugins/GeoModel/src/GeoModelDetectorElement.cpp b/Plugins/GeoModel/src/GeoModelDetectorElement.cpp index 81526fc7693..279a7b802b5 100644 --- a/Plugins/GeoModel/src/GeoModelDetectorElement.cpp +++ b/Plugins/GeoModel/src/GeoModelDetectorElement.cpp @@ -12,6 +12,8 @@ #include +#include + Acts::GeoModelDetectorElement::GeoModelDetectorElement( PVConstLink geoPhysVol, std::shared_ptr surface, const Transform3& sfTransform, ActsScalar thickness) @@ -40,3 +42,7 @@ Acts::ActsScalar Acts::GeoModelDetectorElement::thickness() const { PVConstLink Acts::GeoModelDetectorElement::physicalVolume() const { return m_geoPhysVol; } + +const std::string& Acts::GeoModelDetectorElement::logVolName() const { + return m_geoPhysVol->getLogVol()->getName(); +} diff --git a/Plugins/GeoModel/src/GeoModelDetectorElementITk.cpp b/Plugins/GeoModel/src/GeoModelDetectorElementITk.cpp new file mode 100644 index 00000000000..f671386dec6 --- /dev/null +++ b/Plugins/GeoModel/src/GeoModelDetectorElementITk.cpp @@ -0,0 +1,131 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/GeoModel/GeoModelDetectorElementITk.hpp" + +#include "Acts/Surfaces/AnnulusBounds.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" + +#include + +namespace Acts { + +// Mapping between the barrel-endcap identifier and its unsigned representation +constexpr static std::array, 3> s_barrelEndcapMap{ + {{0, 0}, {1, 2}, {2, -2}}}; + +Acts::ITkIdentifier::ITkIdentifier(int hardware, int barrelEndcap, + int layerWheel, int etaModule, int phiModule, + int side) { + assert((hardware == 0) || (hardware == 1)); + assert((barrelEndcap == 2) || (barrelEndcap == -2) || (barrelEndcap == 0)); + assert(layerWheel >= 0); + assert(phiModule >= 0); + assert((side == 0) || (side == 1)); + + m_identifier.set(0, hardware); + + auto found = std::ranges::find(s_barrelEndcapMap, barrelEndcap, + &std::pair::second); + if (found == s_barrelEndcapMap.end()) { + throw std::invalid_argument("Invalid barrel-endcap specifier"); + } + m_identifier.set(1, found->first); + m_identifier.set(2, layerWheel); + m_identifier.set(3, static_cast(etaModule < 0)); + m_identifier.set(4, std::abs(etaModule)); + m_identifier.set(5, phiModule); + m_identifier.set(6, side); +} + +int Acts::ITkIdentifier::hardware() const { + return m_identifier.level(0); +} + +int Acts::ITkIdentifier::barrelEndcap() const { + auto found = std::ranges::find(s_barrelEndcapMap, m_identifier.level(1), + &std::pair::first); + if (found == s_barrelEndcapMap.end()) { + throw std::invalid_argument("Invalid barrel-endcap specifier"); + } + return found->second; +} + +int Acts::ITkIdentifier::layerWheel() const { + return m_identifier.level(2); +} + +int Acts::ITkIdentifier::etaModule() const { + int sign = (m_identifier.level(3) == 0) ? 1 : -1; + return sign * m_identifier.level(4); +} + +int Acts::ITkIdentifier::phiModule() const { + return m_identifier.level(5); +} + +int Acts::ITkIdentifier::side() const { + return m_identifier.level(6); +} + +std::size_t Acts::ITkIdentifier::value() const { + return m_identifier.value(); +} + +std::ostream &operator<<(std::ostream &os, const ITkIdentifier &id) { + os << "(hw: " << id.hardware() << ", be: " << id.barrelEndcap() + << ", lw: " << id.layerWheel() << ", em: " << id.etaModule() + << ", pm: " << id.phiModule() << ", sd: " << id.side() << ")"; + return os; +} + +std::tuple, + std::shared_ptr> +Acts::GeoModelDetectorElementITk::convertFromGeomodel( + std::shared_ptr detEl, + std::shared_ptr srf, const GeometryContext &gctx, int hardware, + int barrelEndcap, int layerWheel, int etaModule, int phiModule, int side) { + auto helper = [&]() { + auto bounds = std::make_shared( + dynamic_cast(srf->bounds())); + + auto itkEl = std::make_shared( + detEl->physicalVolume(), nullptr, detEl->transform(gctx), + detEl->thickness(), hardware, barrelEndcap, layerWheel, etaModule, + phiModule, side); + auto surface = Surface::makeShared(bounds, *itkEl.get()); + + itkEl->attachSurface(surface); + itkEl->setDatabaseEntryName(detEl->databaseEntryName()); + return std::pair{itkEl, surface}; + }; + + if (srf->type() == Acts::Surface::Plane && + srf->bounds().type() == Acts::SurfaceBounds::eRectangle) { + return helper.operator()(); + } + if (srf->type() == Acts::Surface::Disc && + srf->bounds().type() == Acts::SurfaceBounds::eAnnulus) { + return helper.operator()(); + } + + throw std::runtime_error( + "Only Plane+Rectangle and Disc+Annulus are converted for the ITk"); +} + +} // namespace Acts diff --git a/Plugins/GeoModel/src/GeoModelDetectorObjectFactory.cpp b/Plugins/GeoModel/src/GeoModelDetectorObjectFactory.cpp index 91580a7bfda..b68bb585358 100644 --- a/Plugins/GeoModel/src/GeoModelDetectorObjectFactory.cpp +++ b/Plugins/GeoModel/src/GeoModelDetectorObjectFactory.cpp @@ -19,6 +19,7 @@ #include "Acts/Plugins/GeoModel/GeoModelConverters.hpp" #include "Acts/Plugins/GeoModel/IGeoShapeConverter.hpp" +#include #include #include @@ -141,15 +142,16 @@ Acts::GeoModelDetectorObjectFactory::findAllSubVolumes(const PVConstLink &vol) { } bool Acts::GeoModelDetectorObjectFactory::convertBox(std::string name) { - auto convB = std::any_of( - m_cfg.convertBox.begin(), m_cfg.convertBox.end(), - [&](const auto &n) { return name.find(n) != std::string::npos; }); + auto convB = std::ranges::any_of(m_cfg.convertBox, [&](const auto &n) { + return name.find(n) != std::string::npos; + }); return convB; } void Acts::GeoModelDetectorObjectFactory::convertFpv( const std::string &name, GeoFullPhysVol *fpv, Cache &cache, const GeometryContext &gctx) { + const auto prevSize = cache.sensitiveSurfaces.size(); PVConstLink physVol{fpv}; // get children @@ -184,6 +186,12 @@ void Acts::GeoModelDetectorObjectFactory::convertFpv( const Transform3 &transform = fpv->getAbsoluteTransform(); convertSensitive(fpv, transform, cache.sensitiveSurfaces); } + + // Set the corresponding database entry name to all sensitive surfaces + for (auto i = prevSize; i < cache.sensitiveSurfaces.size(); ++i) { + auto &[detEl, _] = cache.sensitiveSurfaces[i]; + detEl->setDatabaseEntryName(name); + } } // function to determine if object fits query bool Acts::GeoModelDetectorObjectFactory::matches(const std::string &name, @@ -192,14 +200,14 @@ bool Acts::GeoModelDetectorObjectFactory::matches(const std::string &name, return true; } - auto matchName = std::any_of( - m_cfg.nameList.begin(), m_cfg.nameList.end(), - [&](const auto &n) { return name.find(n) != std::string::npos; }); + auto matchName = std::ranges::any_of(m_cfg.nameList, [&](const auto &n) { + return name.find(n) != std::string::npos; + }); std::string matStr = physvol->getLogVol()->getMaterial()->getName(); - auto matchMaterial = std::any_of( - m_cfg.materialList.begin(), m_cfg.materialList.end(), + auto matchMaterial = std::ranges::any_of( + m_cfg.materialList, [&](const auto &m) { return matStr.find(m) != std::string::npos; }); bool match = matchMaterial && matchName; diff --git a/Plugins/GeoModel/src/GeoModelToDetectorVolume.cpp b/Plugins/GeoModel/src/GeoModelToDetectorVolume.cpp index 6303dec2c26..f784a12a990 100644 --- a/Plugins/GeoModel/src/GeoModelToDetectorVolume.cpp +++ b/Plugins/GeoModel/src/GeoModelToDetectorVolume.cpp @@ -46,6 +46,13 @@ Volume convertVolume(const Transform3& trf, const GeoShape& shape) { const GeoBox* box = dynamic_cast(&shape); bounds = std::make_shared( box->getXHalfLength(), box->getYHalfLength(), box->getZHalfLength()); + } else if (shape.typeID() == GeoSimplePolygonBrep::getClassTypeID()) { + const GeoSimplePolygonBrep* brep = + dynamic_cast(&shape); + double xmin{0}, xmax{0}, ymin{0}, ymax{0}, zmin{0}, zmax{0}; + brep->extent(xmin, ymin, zmin, xmax, ymax, zmax); + bounds = std::make_shared( + (xmax - xmin) / 2, (ymax - ymin) / 2, (zmax - zmin) / 2); } else if (shape.typeID() == GeoTrd::getClassTypeID()) { const GeoTrd* trd = dynamic_cast(&shape); double x1 = trd->getXHalfLength1(); @@ -117,9 +124,10 @@ Volume convertVolume(const Transform3& trf, const GeoShape& shape) { dynamic_cast(&shape); const GeoShape* shapeOp = shiftShape->getOp(); newTrf = trf * shiftShape->getX(); - return convertVolume(trf, *shapeOp); + return convertVolume(newTrf, *shapeOp); } else { - throw std::runtime_error("FATAL: Unsupported GeoModel shape"); + throw std::runtime_error("FATAL: Unsupported GeoModel shape: " + + shape.type()); } return Volume(newTrf, bounds); } diff --git a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp index 20065d488bb..3215bd66eae 100644 --- a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp +++ b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Plugins/Json/ActsJson.hpp" +#include "Acts/Plugins/Json/TrackParametersJsonConverter.hpp" #include "Acts/Utilities/AxisFwd.hpp" #include "Acts/Utilities/GridAccessHelpers.hpp" #include "Acts/Utilities/IAxis.hpp" @@ -266,15 +267,17 @@ auto fromJson(const nlohmann::json& jGrid, if constexpr (GridType::DIM == 1u) { for (const auto& jd : jData) { std::array lbin = jd[0u]; - value_type values = jd[1u]; - grid.atLocalBins(lbin) = values; + if (!jd[1u].is_null()) { + grid.atLocalBins(lbin) = jd[1u].get(); + } } } if constexpr (GridType::DIM == 2u) { for (const auto& jd : jData) { std::array lbin = jd[0u]; - value_type values = jd[1u]; - grid.atLocalBins(lbin) = values; + if (!jd[1u].is_null()) { + grid.atLocalBins(lbin) = jd[1u].get(); + } } } return grid; diff --git a/Plugins/Json/include/Acts/Plugins/Json/TrackParametersJsonConverter.hpp b/Plugins/Json/include/Acts/Plugins/Json/TrackParametersJsonConverter.hpp new file mode 100644 index 00000000000..ebf7d5c6054 --- /dev/null +++ b/Plugins/Json/include/Acts/Plugins/Json/TrackParametersJsonConverter.hpp @@ -0,0 +1,221 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/Plugins/Json/ActsJson.hpp" +#include "Acts/Plugins/Json/SurfaceJsonConverter.hpp" + +#include + +namespace { + +// Alias to bound adl_serializer specialization +// only to track parameters +template +concept TrackParameters = Acts::FreeTrackParametersConcept || + Acts::BoundTrackParametersConcept; + +// Shorthand for bound track parameters +template +concept IsGenericBound = + std::same_as>; + +} // namespace + +namespace Acts { +NLOHMANN_JSON_SERIALIZE_ENUM(Acts::PdgParticle, + + {{Acts::PdgParticle::eInvalid, "Invalid"}, + {Acts::PdgParticle::eElectron, "Electron"}, + {Acts::PdgParticle::eAntiElectron, + "AntiElectron"}, + {Acts::PdgParticle::ePositron, "Positron"}, + {Acts::PdgParticle::eMuon, "Muon"}, + {Acts::PdgParticle::eAntiMuon, "AntiMuon"}, + {Acts::PdgParticle::eTau, "Tau"}, + {Acts::PdgParticle::eAntiTau, "AntiTau"}, + {Acts::PdgParticle::eGamma, "Gamma"}, + {Acts::PdgParticle::ePionZero, "PionZero"}, + {Acts::PdgParticle::ePionPlus, "PionPlus"}, + {Acts::PdgParticle::ePionMinus, "PionMinus"}, + {Acts::PdgParticle::eKaonPlus, "KaonPlus"}, + {Acts::PdgParticle::eKaonMinus, "KaonMinus"}, + {Acts::PdgParticle::eNeutron, "Neutron"}, + {Acts::PdgParticle::eAntiNeutron, "AntiNeutron"}, + {Acts::PdgParticle::eProton, "Proton"}, + {Acts::PdgParticle::eAntiProton, "AntiProton"}, + {Acts::PdgParticle::eLead, "Lead"}} + +) +} + +namespace nlohmann { + +/// @brief Serialize a track parameters object to json +/// +/// nlohmann::json serializer specialized for track parameters +/// as they are not default constructible. Is able to serialize +/// either bound or free track parameters given that the constructor +/// convention is followed. +/// +/// @tparam parameters_t The track parameters type +template +struct adl_serializer { + /// Covariance matrix type attached to the parameters + using CovarianceMatrix = typename parameters_t::CovarianceMatrix; + + /// @brief Serialize track parameters object to json + /// + /// @param j Json object to write to + /// @param t Track parameters object to serialize + static void to_json(nlohmann::json& j, const parameters_t& t) { + // Serialize parameters + // common to all track parameters + j["direction"] = t.direction(); + j["qOverP"] = t.qOverP(); + j["particleHypothesis"] = t.particleHypothesis().absolutePdg(); + + // Covariance is optional + j["covariance"]; + if (t.covariance().has_value()) { + // Extract covariance matrix + // parameters and serialize + auto cov = t.covariance().value(); + constexpr unsigned int size = cov.rows(); + std::array covData{}; + for (std::size_t n = 0; n < size; ++n) { + for (std::size_t m = 0; m < size; ++m) { + covData[n * size + m] = cov(n, m); + } + } + j["covariance"] = covData; + } + // Bound track parameters have + // reference surface attached + // and position takes a geometry context + if constexpr (IsGenericBound) { + Acts::GeometryContext gctx; + j["position"] = t.fourPosition(gctx); + + j["referenceSurface"] = + Acts::SurfaceJsonConverter::toJson(gctx, t.referenceSurface()); + } else { + j["position"] = t.fourPosition(); + } + } + + /// @brief Deserialize track parameters object from json + /// + /// @param j Json object to read from + /// @return Track parameters object + static parameters_t from_json(const nlohmann::json& j) { + // Extract common parameters + std::array posData = j.at("position"); + Acts::Vector4 position(posData[0], posData[1], posData[2], posData[3]); + + std::array dirData = j.at("direction"); + Acts::Vector3 direction(dirData[0], dirData[1], dirData[2]); + + Acts::ActsScalar qOverP = j.at("qOverP"); + Acts::PdgParticle absPdg = j.at("particleHypothesis"); + + // Covariance is optional + std::optional cov; + if (j.at("covariance").is_null()) { + cov = std::nullopt; + } else { + // Extract covariance matrix + // parameters and deserialize + CovarianceMatrix mat; + constexpr unsigned int size = mat.rows(); + std::array covData = j.at("covariance"); + for (std::size_t n = 0; n < size; ++n) { + for (std::size_t m = 0; m < size; ++m) { + mat(n, m) = covData[n * size + m]; + } + } + cov.emplace(std::move(mat)); + } + + // Create particle hypothesis + typename parameters_t::ParticleHypothesis particle(absPdg); + + // Bound track parameters have + // reference surface attached + // and constructor is hidden + // behind a factory method + if constexpr (IsGenericBound) { + Acts::GeometryContext gctx; + auto referenceSurface = + Acts::SurfaceJsonConverter::fromJson(j.at("referenceSurface")); + + auto res = parameters_t::create(referenceSurface, gctx, position, + direction, qOverP, cov, particle); + + if (!res.ok()) { + throw std::invalid_argument("Invalid bound track parameters"); + } + return res.value(); + } else { + return parameters_t(position, direction, qOverP, cov, particle); + } + } +}; + +/// @brief Serialize a shared pointer to track parameters object to json +/// +/// nlohmann::json serializer specialized for shared pointers to track +/// parameters as they are not default constructible. Is able to serialize +/// either bound or free track parameters given that the constructor +/// convention is followed. +/// +/// @tparam parameters_t The track parameters type +template +struct adl_serializer> { + using CovarianceMatrix = typename parameters_t::CovarianceMatrix; + static void to_json(nlohmann::json& j, + const std::shared_ptr& t) { + if (t == nullptr) { + return; + } + j = *t; + } + + static std::shared_ptr from_json(const nlohmann::json& j) { + return std::make_shared(j.get()); + } +}; + +/// @brief Serialize a unique pointer to track parameters object to json +/// +/// nlohmann::json serializer specialized for unique pointers to track +/// parameters as they are not default constructible. Is able to serialize +/// either bound or free track parameters given that the constructor +/// convention is followed. +/// +/// @tparam parameters_t The track parameters type +template +struct adl_serializer> { + using CovarianceMatrix = typename parameters_t::CovarianceMatrix; + static void to_json(nlohmann::json& j, + const std::unique_ptr& t) { + if (t == nullptr) { + return; + } + j = *t; + } + + static std::unique_ptr from_json(const nlohmann::json& j) { + return std::make_unique(j.get()); + } +}; + +} // namespace nlohmann diff --git a/Plugins/Json/src/DetectorVolumeJsonConverter.cpp b/Plugins/Json/src/DetectorVolumeJsonConverter.cpp index 19628c078ab..3d5c03c020d 100644 --- a/Plugins/Json/src/DetectorVolumeJsonConverter.cpp +++ b/Plugins/Json/src/DetectorVolumeJsonConverter.cpp @@ -21,6 +21,7 @@ #include "Acts/Plugins/Json/VolumeBoundsJsonConverter.hpp" #include "Acts/Utilities/Enumerate.hpp" +#include #include nlohmann::json Acts::DetectorVolumeJsonConverter::toJson( @@ -59,7 +60,7 @@ nlohmann::json Acts::DetectorVolumeJsonConverter::toJson( nlohmann::json jPortals; if (!portals.empty()) { for (const auto* p : volume.portals()) { - auto it = std::find(portals.begin(), portals.end(), p); + auto it = std::ranges::find(portals, p); if (it != portals.end()) { jPortals.push_back(std::distance(portals.begin(), it)); } else { diff --git a/Plugins/Json/src/MaterialMapJsonConverter.cpp b/Plugins/Json/src/MaterialMapJsonConverter.cpp index e27c069157c..76fc01f8569 100644 --- a/Plugins/Json/src/MaterialMapJsonConverter.cpp +++ b/Plugins/Json/src/MaterialMapJsonConverter.cpp @@ -336,12 +336,10 @@ void Acts::MaterialMapJsonConverter::convertToHierarchy( std::pair>& surfaceHierarchy, const Acts::TrackingVolume* tVolume) { - auto sameId = - [tVolume]( - const std::pair& - pair) { return (tVolume->geometryId() == pair.first); }; - if (std::find_if(volumeHierarchy.begin(), volumeHierarchy.end(), sameId) != - volumeHierarchy.end()) { + auto sameId = [tVolume](const auto& pair) { + return (tVolume->geometryId() == pair.first); + }; + if (std::ranges::any_of(volumeHierarchy, sameId)) { // this volume was already visited return; } diff --git a/Plugins/Json/src/PortalJsonConverter.cpp b/Plugins/Json/src/PortalJsonConverter.cpp index a7044f7b6dd..e39b6efca17 100644 --- a/Plugins/Json/src/PortalJsonConverter.cpp +++ b/Plugins/Json/src/PortalJsonConverter.cpp @@ -40,7 +40,7 @@ namespace { int findVolume( const Acts::Experimental::DetectorVolume* volume, const std::vector& volumes) { - auto candidate = std::find(volumes.begin(), volumes.end(), volume); + auto candidate = std::ranges::find(volumes, volume); if (candidate != volumes.end()) { return std::distance(volumes.begin(), candidate); } diff --git a/Tests/Benchmarks/CMakeLists.txt b/Tests/Benchmarks/CMakeLists.txt index 8906375caea..b19e67fed59 100644 --- a/Tests/Benchmarks/CMakeLists.txt +++ b/Tests/Benchmarks/CMakeLists.txt @@ -29,7 +29,6 @@ add_benchmark(SurfaceIntersection SurfaceIntersectionBenchmark.cpp) add_benchmark(RayFrustum RayFrustumBenchmark.cpp) add_benchmark(AnnulusBounds AnnulusBoundsBenchmark.cpp) add_benchmark(StraightLineStepper StraightLineStepperBenchmark.cpp) -add_benchmark(QuickMath QuickMathBenchmark.cpp) add_benchmark(SympyStepper SympyStepperBenchmark.cpp) add_benchmark(Stepper StepperBenchmark.cpp) add_benchmark(SourceLink SourceLinkBenchmark.cpp) diff --git a/Tests/Benchmarks/QuickMathBenchmark.cpp b/Tests/Benchmarks/QuickMathBenchmark.cpp deleted file mode 100644 index 0cbec176b94..00000000000 --- a/Tests/Benchmarks/QuickMathBenchmark.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -#include -#include - -#include "Acts/Tests/CommonHelpers/BenchmarkTools.hpp" -#include "Acts/Utilities/QuickMath.hpp" - -#include -#include - -namespace bdata = boost::unit_test::data; - -namespace Acts::Test { - -/// @brief Another fast power function @see `fastPow` -// Taken from -// https://martin.ankerl.com/2007/02/11/optimized-exponential-functions-for-java -/// @param a the base -/// @param b the exponent -constexpr double fastPowAnother(double a, double b) { - // enable only on IEEE 754 - static_assert(std::numeric_limits::is_iec559); - - union { - double f; - std::int64_t i; - } u = {}; - - u.i = static_cast( - 9076650 * (a - 1) / (a + 1 + 4 * std::sqrt(a)) * b + 1072632447); - u.i <<= 32; - - // result seems broken? - return u.f; -} - -// Some randomness & number crunching -const unsigned int nTests = 10; -const unsigned int nReps = 10000; - -BOOST_DATA_TEST_CASE( - benchmark_pow_25, - bdata::random( - (bdata::engine = std::mt19937(), bdata::seed = 21, - bdata::distribution = std::uniform_real_distribution(-4, 4))) ^ - bdata::xrange(nTests), - baseExp, index) { - (void)index; - - const double base = std::pow(10, baseExp); - const double exp = 0.25; - - std::cout << std::endl - << "Benchmarking base=" << base << ", exp=" << exp << "..." - << std::endl; - std::cout << "- void: " - << Acts::Test::microBenchmark([&] { return 0; }, nReps) - << std::endl; - std::cout << "- std::pow: " - << Acts::Test::microBenchmark([&] { return std::pow(base, exp); }, - nReps) - << std::endl; - std::cout << "- std::exp: " - << Acts::Test::microBenchmark( - [&] { return std::exp(std::log(base) * exp); }, nReps) - << std::endl; - std::cout << "- std::sqrt: " - << Acts::Test::microBenchmark( - [&] { return std::sqrt(std::sqrt(base)); }, nReps) - << std::endl; - std::cout << "- fastPow: " - << Acts::Test::microBenchmark([&] { return fastPow(base, exp); }, - nReps) - << std::endl; - std::cout << "- fastPowMorePrecise: " - << Acts::Test::microBenchmark( - [&] { return fastPowMorePrecise(base, exp); }, nReps) - << std::endl; - std::cout << "- fastPowAnother: " - << Acts::Test::microBenchmark( - [&] { return fastPowAnother(base, exp); }, nReps) - << std::endl; -} - -} // namespace Acts::Test diff --git a/Tests/Benchmarks/RayFrustumBenchmark.cpp b/Tests/Benchmarks/RayFrustumBenchmark.cpp index 2d0069da1b1..e4f11e60991 100644 --- a/Tests/Benchmarks/RayFrustumBenchmark.cpp +++ b/Tests/Benchmarks/RayFrustumBenchmark.cpp @@ -187,10 +187,10 @@ int main(int /*argc*/, char** /*argv[]*/) { return {name, func(testBox, ray)}; }); - bool all = std::all_of(results.begin(), results.end(), - [](const auto& r) { return r.second; }); - bool none = std::none_of(results.begin(), results.end(), - [](const auto& r) { return r.second; }); + bool all = + std::ranges::all_of(results, [](const auto& r) { return r.second; }); + bool none = + std::ranges::none_of(results, [](const auto& r) { return r.second; }); if (!all && !none) { std::cerr << "Discrepancy: " << std::endl; @@ -369,10 +369,10 @@ int main(int /*argc*/, char** /*argv[]*/) { return {name, func(testBox, fr)}; }); - bool all = std::all_of(results.begin(), results.end(), - [](const auto& r) { return r.second; }); - bool none = std::none_of(results.begin(), results.end(), - [](const auto& r) { return r.second; }); + bool all = + std::ranges::all_of(results, [](const auto& r) { return r.second; }); + bool none = + std::ranges::none_of(results, [](const auto& r) { return r.second; }); if (!all && !none) { std::cerr << "Discrepancy: " << std::endl; diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/Assertions.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/Assertions.hpp index 6bc19dc0431..3fee5af18ae 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/Assertions.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/Assertions.hpp @@ -10,15 +10,15 @@ #include +#include #include -#define CHECK_NE_COLLECTIONS(col1, col2) \ - do { \ - BOOST_CHECK_EQUAL(col1.size(), col2.size()); \ - std::vector result; \ - for (std::size_t i = 0; i < col1.size(); i++) { \ - result.push_back(col1[i] == col2[i]); \ - } \ - BOOST_CHECK( \ - !std::all_of(result.begin(), result.end(), [](bool r) { return r; })); \ +#define CHECK_NE_COLLECTIONS(col1, col2) \ + do { \ + BOOST_CHECK_EQUAL(col1.size(), col2.size()); \ + std::vector result; \ + for (std::size_t i = 0; i < col1.size(); i++) { \ + result.push_back(col1[i] == col2[i]); \ + } \ + BOOST_CHECK(!std::ranges::all_of(result, [](bool r) { return r; })); \ } while (0) diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/LineSurfaceStub.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/LineSurfaceStub.hpp index 848e02cab01..662969bc450 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/LineSurfaceStub.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/LineSurfaceStub.hpp @@ -52,11 +52,11 @@ class LineSurfaceStub : public LineSurface { /// Return a Polyhedron for the surfaces /// /// @param gctx The current geometry context object, e.g. alignment - /// @param lseg is ignored for a perigee @note ignored + /// @param ingoredSegmeent is ignored for the srub /// /// @return A list of vertices and a face/facett description of it Polyhedron polyhedronRepresentation(const GeometryContext& /*gctx*/, - std::size_t /*lseg*/) const final { + unsigned int /*lseg*/) const final { return Polyhedron({}, {}, {}); } }; diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp index f40f658e63e..85a760bd6af 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp @@ -15,6 +15,7 @@ #include namespace Acts::Test { + struct DummySequenceElement : public ActsExamples::SequenceElement { ActsExamples::ProcessCode initialize() override { return {}; }; ActsExamples::ProcessCode finalize() override { return {}; }; @@ -102,4 +103,5 @@ struct GenericReadWriteTool { return get(get, std::tuple<>{}, std::integral_constant{}); } }; + } // namespace Acts::Test diff --git a/Tests/DownstreamProject/CMakeLists.txt b/Tests/DownstreamProject/CMakeLists.txt index d41277017a5..0cb5ca1f9ea 100644 --- a/Tests/DownstreamProject/CMakeLists.txt +++ b/Tests/DownstreamProject/CMakeLists.txt @@ -53,3 +53,10 @@ if(EDM4HEP) find_package(Acts CONFIG REQUIRED COMPONENTS PluginEDM4hep) target_link_libraries(ShowActsVersion PRIVATE ActsPluginEDM4hep) endif() + +option(GEOMODEL "Build with GeoModel" ON) +if(GEOMODEL) + message(STATUS "Adding GeoModel plugin") + find_package(Acts CONFIG REQUIRED COMPONENTS PluginGeoModel) + target_link_libraries(ShowActsVersion PRIVATE ActsPluginGeoModel) +endif() diff --git a/Tests/UnitTests/Core/Clusterization/CMakeLists.txt b/Tests/UnitTests/Core/Clusterization/CMakeLists.txt index 38dffefd462..a6c4844bdb9 100644 --- a/Tests/UnitTests/Core/Clusterization/CMakeLists.txt +++ b/Tests/UnitTests/Core/Clusterization/CMakeLists.txt @@ -1,2 +1,3 @@ add_unittest(Clusterization1D ClusterizationTests1D.cpp) add_unittest(Clusterization2D ClusterizationTests2D.cpp) +add_unittest(TimedClusterization TimedClusterizationTests.cpp) diff --git a/Tests/UnitTests/Core/Clusterization/TimedClusterizationTests.cpp b/Tests/UnitTests/Core/Clusterization/TimedClusterizationTests.cpp new file mode 100644 index 00000000000..ac60393d9b6 --- /dev/null +++ b/Tests/UnitTests/Core/Clusterization/TimedClusterizationTests.cpp @@ -0,0 +1,260 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Clusterization/TimedClusterization.hpp" + +namespace Acts::Test { + +// Define objects +using Identifier = std::size_t; +struct Cell { + Cell(Identifier identifier, int c, int r, double t) + : id(identifier), column(c), row(r), time(t) {} + + Identifier id{}; + int column{0}; + int row{0}; + int label{-1}; + double time{0.}; +}; + +struct Cluster { + std::vector ids{}; +}; + +using CellCollection = std::vector; +using ClusterCollection = std::vector; + +// Define functions +static inline int getCellRow(const Cell& cell) { + return cell.row; +} + +static inline int getCellColumn(const Cell& cell) { + return cell.column; +} + +static inline int& getCellLabel(Cell& cell) { + return cell.label; +} + +static inline double getCellTime(const Cell& cell) { + return cell.time; +} + +static void clusterAddCell(Cluster& cl, const Cell& cell) { + cl.ids.push_back(cell.id); +} + +BOOST_AUTO_TEST_CASE(TimedGrid_1D_withtime) { + // 1x10 matrix + /* + X X X Y O X Y Y X X + */ + // 6 + 3 cells -> 3 + 2 clusters in total + + std::vector cells; + // X + cells.emplace_back(0ul, 0, -1, 0); + cells.emplace_back(1ul, 1, -1, 0); + cells.emplace_back(2ul, 2, -1, 0); + cells.emplace_back(3ul, 5, -1, 0); + cells.emplace_back(4ul, 8, -1, 0); + cells.emplace_back(5ul, 9, -1, 0); + // Y + cells.emplace_back(6ul, 3, 0, 1); + cells.emplace_back(7ul, 6, 1, 1); + cells.emplace_back(8ul, 7, 1, 1); + + std::vector> expectedResults; + expectedResults.push_back({0ul, 1ul, 2ul}); + expectedResults.push_back({6ul}); + expectedResults.push_back({3ul}); + expectedResults.push_back({7ul, 8ul}); + expectedResults.push_back({4ul, 5ul}); + + ClusterCollection clusters = + Acts::Ccl::createClusters( + cells, Acts::Ccl::TimedConnect(0.5)); + + BOOST_CHECK_EQUAL(5ul, clusters.size()); + + for (std::size_t i(0); i < clusters.size(); ++i) { + std::vector& timedIds = clusters[i].ids; + const std::vector& expected = expectedResults[i]; + std::sort(timedIds.begin(), timedIds.end()); + BOOST_CHECK_EQUAL(timedIds.size(), expected.size()); + + for (std::size_t j(0); j < timedIds.size(); ++j) { + BOOST_CHECK_EQUAL(timedIds[j], expected[j]); + } + } +} + +BOOST_AUTO_TEST_CASE(TimedGrid_2D_notime) { + // 4x4 matrix + /* + X O O X + O O O X + X X O O + X O O X + */ + // 7 cells -> 4 clusters in total + + std::vector cells; + cells.emplace_back(0ul, 0, 0, 0); + cells.emplace_back(1ul, 3, 0, 0); + cells.emplace_back(2ul, 3, 1, 0); + cells.emplace_back(3ul, 0, 2, 0); + cells.emplace_back(4ul, 1, 2, 0); + cells.emplace_back(5ul, 0, 3, 0); + cells.emplace_back(6ul, 3, 3, 0); + + std::vector> expectedResults; + expectedResults.push_back({0ul}); + expectedResults.push_back({3ul, 4ul, 5ul}); + expectedResults.push_back({1ul, 2ul}); + expectedResults.push_back({6ul}); + + ClusterCollection clusters = + Acts::Ccl::createClusters( + cells, + Acts::Ccl::TimedConnect(std::numeric_limits::max())); + + BOOST_CHECK_EQUAL(4ul, clusters.size()); + + // Compare against default connect (only space) + ClusterCollection defaultClusters = + Acts::Ccl::createClusters( + cells, Acts::Ccl::DefaultConnect()); + + BOOST_CHECK_EQUAL(4ul, defaultClusters.size()); + BOOST_CHECK_EQUAL(defaultClusters.size(), expectedResults.size()); + + std::vector sizes{1, 3, 2, 1}; + for (std::size_t i(0); i < clusters.size(); ++i) { + std::vector& timedIds = clusters[i].ids; + std::vector& defaultIds = defaultClusters[i].ids; + const std::vector& expected = expectedResults[i]; + BOOST_CHECK_EQUAL(timedIds.size(), defaultIds.size()); + BOOST_CHECK_EQUAL(timedIds.size(), sizes[i]); + BOOST_CHECK_EQUAL(timedIds.size(), expected.size()); + + std::sort(timedIds.begin(), timedIds.end()); + std::sort(defaultIds.begin(), defaultIds.end()); + for (std::size_t j(0); j < timedIds.size(); ++j) { + BOOST_CHECK_EQUAL(timedIds[j], defaultIds[j]); + BOOST_CHECK_EQUAL(timedIds[j], expected[j]); + } + } +} + +BOOST_AUTO_TEST_CASE(TimedGrid_2D_withtime) { + // 4x4 matrix + /* + X Y O X + O Y Y X + X X Z Z + X O O X + */ + // 7 + 3 + 2 cells -> 4 + 1 + 1 clusters in total + + std::vector cells; + // X + cells.emplace_back(0ul, 0, 0, 0); + cells.emplace_back(1ul, 3, 0, 0); + cells.emplace_back(2ul, 3, 1, 0); + cells.emplace_back(3ul, 0, 2, 0); + cells.emplace_back(4ul, 1, 2, 0); + cells.emplace_back(5ul, 0, 3, 0); + cells.emplace_back(6ul, 3, 3, 0); + // Y + cells.emplace_back(7ul, 1, 0, 1); + cells.emplace_back(8ul, 1, 1, 1); + cells.emplace_back(9ul, 2, 1, 1); + // Z + cells.emplace_back(10ul, 2, 2, 2); + cells.emplace_back(11ul, 3, 2, 2); + + std::vector> expectedResults; + expectedResults.push_back({0ul}); + expectedResults.push_back({3ul, 4ul, 5ul}); + expectedResults.push_back({7ul, 8ul, 9ul}); + expectedResults.push_back({10ul, 11ul}); + expectedResults.push_back({1ul, 2ul}); + expectedResults.push_back({6ul}); + + ClusterCollection clusters = + Acts::Ccl::createClusters( + cells, Acts::Ccl::TimedConnect(0.5)); + + BOOST_CHECK_EQUAL(6ul, clusters.size()); + + std::vector sizes{1, 3, 3, 2, 2, 1}; + for (std::size_t i(0); i < clusters.size(); ++i) { + std::vector& timedIds = clusters[i].ids; + BOOST_CHECK_EQUAL(timedIds.size(), sizes[i]); + std::sort(timedIds.begin(), timedIds.end()); + + const std::vector& expected = expectedResults[i]; + BOOST_CHECK_EQUAL(timedIds.size(), expected.size()); + + for (std::size_t j(0); j < timedIds.size(); ++j) { + BOOST_CHECK_EQUAL(timedIds[j], expected[j]); + } + } +} + +BOOST_AUTO_TEST_CASE(TimedGrid_2D_noTollerance) { + // 4x4 matrix + /* + X O O X + O O O X + X X O O + X O O X + */ + // 7 cells -> 7 clusters in total + // since time requirement will never be satisfied + + std::vector cells; + cells.emplace_back(0ul, 0, 0, 0); + cells.emplace_back(1ul, 3, 0, 0); + cells.emplace_back(2ul, 3, 1, 0); + cells.emplace_back(3ul, 0, 2, 0); + cells.emplace_back(4ul, 1, 2, 0); + cells.emplace_back(5ul, 0, 3, 0); + cells.emplace_back(6ul, 3, 3, 0); + + std::vector> expectedResults; + expectedResults.push_back({0ul}); + expectedResults.push_back({3ul}); + expectedResults.push_back({5ul}); + expectedResults.push_back({4ul}); + expectedResults.push_back({1ul}); + expectedResults.push_back({2ul}); + expectedResults.push_back({6ul}); + + ClusterCollection clusters = + Acts::Ccl::createClusters( + cells, Acts::Ccl::TimedConnect(0.)); + + BOOST_CHECK_EQUAL(7ul, clusters.size()); + + for (std::size_t i(0); i < clusters.size(); ++i) { + std::vector& timedIds = clusters[i].ids; + const std::vector& expected = expectedResults[i]; + + BOOST_CHECK_EQUAL(timedIds.size(), 1); + BOOST_CHECK_EQUAL(timedIds.size(), expected.size()); + BOOST_CHECK_EQUAL(timedIds[0], expected[0]); + } +} + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp index a05d273f7a1..9daf24c8a79 100644 --- a/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp @@ -76,6 +76,14 @@ void checkParameters(const BoundTrackParameters& params, double l0, double l1, eps); CHECK_CLOSE_OR_SMALL(params.momentum(), p * unitDir, eps, eps); BOOST_CHECK_EQUAL(params.charge(), q); + + // reflection + BoundTrackParameters reflectedParams = params; + reflectedParams.reflectInPlace(); + CHECK_CLOSE_OR_SMALL(params.reflect().parameters(), + reflectedParams.parameters(), eps, eps); + CHECK_CLOSE_OR_SMALL(reflectedParams.reflect().parameters(), + params.parameters(), eps, eps); } void runTest(const std::shared_ptr& surface, double l0, diff --git a/Tests/UnitTests/Core/EventData/CMakeLists.txt b/Tests/UnitTests/Core/EventData/CMakeLists.txt index 5699dc4527f..61b5079da8e 100644 --- a/Tests/UnitTests/Core/EventData/CMakeLists.txt +++ b/Tests/UnitTests/Core/EventData/CMakeLists.txt @@ -15,5 +15,6 @@ add_unittest(ParticleHypothesis ParticleHypothesisTests.cpp) add_unittest(MultiTrajectoryHelpers MultiTrajectoryHelpersTests.cpp) add_unittest(SubspaceHelpers SubspaceHelpersTests.cpp) add_unittest(SeedEdm SeedEdmTests.cpp) +add_unittest(SpacePointContainerEdm SpacePointContainerEdmTests.cpp) add_non_compile_test(MultiTrajectory TrackContainerComplianceTests.cpp) diff --git a/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp index 122d4b075d0..143515148d4 100644 --- a/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp @@ -72,6 +72,15 @@ void checkParameters(const CurvilinearTrackParameters& params, double phi, // curvilinear reference surface CHECK_CLOSE_OR_SMALL(referenceSurface->center(geoCtx), pos, eps, eps); CHECK_CLOSE_OR_SMALL(referenceSurface->normal(geoCtx), unitDir, eps, eps); + + // reflection + CurvilinearTrackParameters reflectedParams = params; + reflectedParams.reflectInPlace(); + CHECK_CLOSE_OR_SMALL(params.reflect().parameters(), + reflectedParams.parameters(), eps, eps); + CHECK_CLOSE_OR_SMALL(reflectedParams.reflect().parameters(), + params.parameters(), eps, eps); + // TODO verify reference frame } diff --git a/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp index 71b0dc0a20e..fb7a11f3f68 100644 --- a/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp @@ -67,6 +67,14 @@ void checkParameters(const FreeTrackParameters& params, const Vector4& pos4, eps); CHECK_CLOSE_OR_SMALL(params.time(), params.template get(), eps, eps); + + // reflection + FreeTrackParameters reflectedParams = params; + reflectedParams.reflectInPlace(); + CHECK_CLOSE_OR_SMALL(params.reflect().parameters(), + reflectedParams.parameters(), eps, eps); + CHECK_CLOSE_OR_SMALL(reflectedParams.reflect().parameters(), + params.parameters(), eps, eps); } } // namespace diff --git a/Tests/UnitTests/Core/EventData/SpacePointContainerEdmTests.cpp b/Tests/UnitTests/Core/EventData/SpacePointContainerEdmTests.cpp new file mode 100644 index 00000000000..7ca20985499 --- /dev/null +++ b/Tests/UnitTests/Core/EventData/SpacePointContainerEdmTests.cpp @@ -0,0 +1,203 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/EventData/SpacePointContainer.hpp" +#include "Acts/Utilities/HashedString.hpp" +#include "Acts/Utilities/Holders.hpp" + +#include +#include +#include +#include +#include + +namespace Acts::Test { + +struct SpacePoint { + SpacePoint() = default; + SpacePoint(float ix, float iy, float iz, float ivarR, float ivarZ) + : x(ix), y(iy), z(iz), varR(ivarR), varZ(ivarZ) {} + float x{}; + float y{}; + float z{}; + float varR{}; + float varZ{}; +}; +using SpacePointCollection = std::vector; + +class Adapter { + public: + friend Acts::SpacePointContainer; + friend Acts::SpacePointContainer; + using value_type = SpacePoint; + using ValueType = value_type; + + Adapter(SpacePointCollection&&) = delete; + Adapter(const SpacePointCollection& externalCollection) + : m_storage(&externalCollection) {} + + private: + std::size_t size_impl() const { return storage().size(); } + + float x_impl(std::size_t idx) const { return storage()[idx].x; }; + float y_impl(std::size_t idx) const { return storage()[idx].y; }; + float z_impl(std::size_t idx) const { return storage()[idx].z; }; + float varianceR_impl(std::size_t idx) const { return storage()[idx].varR; } + float varianceZ_impl(std::size_t idx) const { return storage()[idx].varZ; } + + const SpacePoint& get_impl(std::size_t idx) const { return storage()[idx]; } + + std::any component_impl(Acts::HashedString key, std::size_t /*n*/) const { + using namespace Acts::HashedStringLiteral; + switch (key) { + case "TopStripVector"_hash: + case "BottomStripVector"_hash: + case "StripCenterDistance"_hash: + case "TopStripCenterPosition"_hash: + return Acts::Vector3(0., 0., 0.); + default: + throw std::runtime_error("no such component " + std::to_string(key)); + } + } + + const SpacePointCollection& storage() const { return *m_storage; } + + private: + const SpacePointCollection* m_storage{}; +}; + +BOOST_AUTO_TEST_CASE(spacepoint_container_edm_traits) { + using adapter_t = Acts::Test::Adapter; + using container_t = + Acts::SpacePointContainer; + using proxy_t = Acts::SpacePointProxy; + using iterator_t = Acts::ContainerIndexIterator; + + static_assert(std::ranges::range); + static_assert(std::same_as); + static_assert( + std::same_as::iterator_category, + std::random_access_iterator_tag>); +} + +BOOST_AUTO_TEST_CASE(spacepoint_container_edm_constructors) { + std::size_t nExternalPoints = 10; + SpacePointCollection externalCollection(nExternalPoints); + + Acts::SpacePointContainerConfig spConfig; + Acts::SpacePointContainerOptions spOptions; + + Acts::Test::Adapter adapterForRef(externalCollection); + Acts::SpacePointContainer + spContainerRef(spConfig, spOptions, adapterForRef); + + Acts::SpacePointContainer + spContainerVal(spConfig, spOptions, + Acts::Test::Adapter(externalCollection)); + + BOOST_CHECK_EQUAL(spContainerRef.size(), nExternalPoints); + BOOST_CHECK_EQUAL(spContainerVal.size(), nExternalPoints); +} + +BOOST_AUTO_TEST_CASE(spacepoint_container_edm_functionalities) { + std::size_t nExternalPoints = 100; + SpacePointCollection externalCollection; + externalCollection.reserve(nExternalPoints); + for (std::size_t i = 0; i < nExternalPoints; ++i) { + externalCollection.emplace_back(1.f * i, 1.5f * i, 2.f * i, 2.5f * i, + 3.f * i); + } + + Acts::SpacePointContainerConfig spConfig; + spConfig.useDetailedDoubleMeasurementInfo = true; + Acts::SpacePointContainerOptions spOptions; + + Acts::Test::Adapter adapter(externalCollection); + Acts::SpacePointContainer + spContainer(spConfig, spOptions, adapter); + + BOOST_CHECK_EQUAL(spContainer.size(), nExternalPoints); + BOOST_CHECK_EQUAL(spContainer.size(), externalCollection.size()); + BOOST_CHECK_EQUAL(spContainer.end() - spContainer.begin(), nExternalPoints); + BOOST_CHECK_EQUAL(std::distance(spContainer.begin(), spContainer.end()), + nExternalPoints); + BOOST_CHECK_EQUAL(std::distance(std::ranges::begin(spContainer), + std::ranges::end(spContainer)), + nExternalPoints); + + using proxy_t = Acts::SpacePointProxy< + Acts::SpacePointContainer>; + static_assert(std::same_as); + static_assert( + std::same_as); + static_assert( + std::same_as); + static_assert( + std::same_as); + static_assert( + std::same_as); + + using iterator_t = + Acts::ContainerIndexIterator; + using const_iterator_t = + Acts::ContainerIndexIterator; + static_assert( + std::same_as); + static_assert(std::same_as); + static_assert( + std::same_as); + static_assert(std::same_as); + + std::size_t n = 0ul; + for (const proxy_t& proxy : spContainer) { + float refX = 1.f * n; + float refY = 1.5f * n; + float refZ = 2.f * n; + float refCovR = 2.5f * n; + float refCovZ = 3.f * n; + float refRadius = std::hypot(refX, refY); + float refPhi = std::atan2(refY, refX); + + BOOST_CHECK_EQUAL(proxy.index(), n); + BOOST_CHECK_EQUAL(proxy.x(), refX); + BOOST_CHECK_EQUAL(proxy.y(), refY); + BOOST_CHECK_EQUAL(proxy.z(), refZ); + BOOST_CHECK_EQUAL(proxy.radius(), refRadius); + BOOST_CHECK_EQUAL(proxy.phi(), refPhi); + BOOST_CHECK_EQUAL(proxy.varianceR(), refCovR); + BOOST_CHECK_EQUAL(proxy.varianceZ(), refCovZ); + + const Acts::Vector3& topStripVector = proxy.topStripVector(); + const Acts::Vector3& bottomStripVector = proxy.bottomStripVector(); + const Acts::Vector3& stripCenterDistance = proxy.stripCenterDistance(); + const Acts::Vector3& topStripCenterPosition = + proxy.topStripCenterPosition(); + + for (std::size_t i = 0; i < 3; ++i) { + BOOST_CHECK_EQUAL(topStripVector[i], 0.); + BOOST_CHECK_EQUAL(bottomStripVector[i], 0.); + BOOST_CHECK_EQUAL(stripCenterDistance[i], 0.); + BOOST_CHECK_EQUAL(topStripCenterPosition[i], 0.); + } + + const Acts::Test::SpacePoint& sp = proxy.externalSpacePoint(); + BOOST_CHECK_EQUAL(&sp, &externalCollection[n]); + + ++n; + } + BOOST_CHECK_EQUAL(n, nExternalPoints); +} + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Core/EventData/TrackTests.cpp b/Tests/UnitTests/Core/EventData/TrackTests.cpp index 013132a60b9..432efad6447 100644 --- a/Tests/UnitTests/Core/EventData/TrackTests.cpp +++ b/Tests/UnitTests/Core/EventData/TrackTests.cpp @@ -228,9 +228,9 @@ BOOST_AUTO_TEST_CASE(IteratorConcept) { BOOST_CHECK(*it == tc.getTrack(0)); std::advance(it, 4); BOOST_CHECK(*it == tc.getTrack(4)); - BOOST_CHECK(*(it[-1]) == tc.getTrack(3)); - BOOST_CHECK(*(it[0]) == tc.getTrack(4)); - BOOST_CHECK(*(it[1]) == tc.getTrack(5)); + BOOST_CHECK(*(it + (-1)) == tc.getTrack(3)); + BOOST_CHECK(*(it + 0) == tc.getTrack(4)); + BOOST_CHECK(*(it + 1) == tc.getTrack(5)); BOOST_CHECK(*(it - 2) == tc.getTrack(2)); } diff --git a/Tests/UnitTests/Core/EventData/TrackTestsExtra.cpp b/Tests/UnitTests/Core/EventData/TrackTestsExtra.cpp index 2c16463ebe1..ca444466216 100644 --- a/Tests/UnitTests/Core/EventData/TrackTestsExtra.cpp +++ b/Tests/UnitTests/Core/EventData/TrackTestsExtra.cpp @@ -447,4 +447,24 @@ BOOST_AUTO_TEST_CASE(ReverseTrackStates) { } } +BOOST_AUTO_TEST_CASE(CopyTrackProxyCalibrated) { + VectorTrackContainer vtc{}; + VectorMultiTrajectory mtj{}; + TrackContainer tc{vtc, mtj}; + + constexpr static std::size_t kMeasurementSize = 3; + + auto track1 = tc.makeTrack(); + auto ts = track1.appendTrackState(TrackStatePropMask::Calibrated); + ts.allocateCalibrated(kMeasurementSize); + ts.calibrated() = Vector3::Ones(); + ts.calibratedCovariance() = SquareMatrix3::Identity(); + ts.setSubspaceIndices(BoundSubspaceIndices{}); + + auto tsCopy = track1.appendTrackState(TrackStatePropMask::Calibrated); + tsCopy.copyFrom(ts, TrackStatePropMask::Calibrated, false); + + BOOST_CHECK_EQUAL(ts.calibratedSize(), tsCopy.calibratedSize()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Geometry/CMakeLists.txt b/Tests/UnitTests/Core/Geometry/CMakeLists.txt index 1fa422bb0a6..0226e46d7d0 100644 --- a/Tests/UnitTests/Core/Geometry/CMakeLists.txt +++ b/Tests/UnitTests/Core/Geometry/CMakeLists.txt @@ -34,3 +34,4 @@ add_unittest(Volume VolumeTests.cpp) add_unittest(CylinderVolumeStack CylinderVolumeStackTests.cpp) add_unittest(PortalLink PortalLinkTests.cpp) add_unittest(Portal PortalTests.cpp) +add_unittest(PortalShell PortalShellTests.cpp) diff --git a/Tests/UnitTests/Core/Geometry/CylinderVolumeStackTests.cpp b/Tests/UnitTests/Core/Geometry/CylinderVolumeStackTests.cpp index ab3c23a3dfc..19850be4bbe 100644 --- a/Tests/UnitTests/Core/Geometry/CylinderVolumeStackTests.cpp +++ b/Tests/UnitTests/Core/Geometry/CylinderVolumeStackTests.cpp @@ -813,6 +813,165 @@ BOOST_DATA_TEST_CASE( } } +BOOST_AUTO_TEST_CASE(ResizeReproduction1) { + Transform3 trf1 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * -2000}; + auto bounds1 = std::make_shared(70, 100, 100.0); + Volume vol1{trf1, bounds1}; + + std::vector volumes = {&vol1}; + CylinderVolumeStack stack(volumes, BinningValue::binZ, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, *logger); + + Transform3 trf2 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * -1500}; + stack.update(std::make_shared(30.0, 100, 600), trf2, + *logger); + + std::cout << stack.volumeBounds() << std::endl; + std::cout << stack.transform().matrix() << std::endl; + + Transform3 trf3 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * -1600}; + stack.update(std::make_shared(30.0, 100, 700), trf3, + *logger); +} + +BOOST_AUTO_TEST_CASE(ResizeReproduction2) { + // The numbers are tuned a bit to reproduce the faulty behavior + Transform3 trf1 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * 263}; + auto bounds1 = std::make_shared(30, 100, 4.075); + Volume vol1{trf1, bounds1}; + + std::vector volumes = {&vol1}; + CylinderVolumeStack stack(volumes, BinningValue::binZ, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, *logger); + + Transform3 trf2 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * 260.843}; + stack.update(std::make_shared(30.0, 100, 6.232), trf2, + *logger); + + std::cout << stack.volumeBounds() << std::endl; + std::cout << stack.transform().matrix() << std::endl; + + Transform3 trf3 = + Transform3::Identity() * Translation3{Vector3::UnitZ() * 1627.31}; + stack.update(std::make_shared(30.0, 100, 1372.699), + trf3, *logger); +} + +// original size +// <---------------> +// +---------------+ +// | | +// | | +// | Volume 1 | +// | | +// | | +// +---------------+ +// first resize +// <--------------------------> +// +---------------+----------+ +// | | | +// | | | +// | Volume 1 | Gap | +// | | | Gap is +// | | | reused!--+ +// +---------------+----------+ | +// second resize | +// <-----------------------------------> | +// +---------------+-------------------+ | +// | | | | +// | | | | +// | Volume 1 | Gap |<-----+ +// | | | +// | | | +// +---------------+-------------------+ +// +BOOST_AUTO_TEST_CASE(ResizeGapMultiple) { + Transform3 trf = Transform3::Identity(); + auto bounds = std::make_shared(70, 100, 100.0); + Volume vol{trf, bounds}; + + BOOST_TEST_CONTEXT("Positive") { + std::vector volumes = {&vol}; + CylinderVolumeStack stack(volumes, BinningValue::binZ, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 1); + BOOST_CHECK(stack.gaps().empty()); + + stack.update(std::make_shared(30.0, 100, 200), + trf * Translation3{Vector3::UnitZ() * 100}, *logger); + BOOST_CHECK_EQUAL(volumes.size(), 2); + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + BOOST_CHECK_EQUAL(stack.gaps().front()->center()[eZ], 200.0); + const auto* cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), + 100.0); + + stack.update(std::make_shared(30.0, 100, 300), + trf * Translation3{Vector3::UnitZ() * 200}, *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 2); + // No additional gap volume was added! + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + BOOST_CHECK_EQUAL(stack.gaps().front()->center()[eZ], 300.0); + cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), + 200.0); + } + + BOOST_TEST_CONTEXT("Negative") { + std::vector volumes = {&vol}; + CylinderVolumeStack stack(volumes, BinningValue::binZ, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 1); + BOOST_CHECK(stack.gaps().empty()); + + stack.update(std::make_shared(30.0, 100, 200), + trf * Translation3{Vector3::UnitZ() * -100}, *logger); + BOOST_CHECK_EQUAL(volumes.size(), 2); + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + BOOST_CHECK_EQUAL(stack.gaps().front()->center()[eZ], -200.0); + const auto* cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), + 100.0); + + stack.update(std::make_shared(30.0, 100, 300), + trf * Translation3{Vector3::UnitZ() * -200}, *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 2); + // No additional gap volume was added! + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + BOOST_CHECK_EQUAL(stack.gaps().front()->center()[eZ], -300.0); + cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), + 200.0); + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(RDirection) @@ -1503,6 +1662,82 @@ BOOST_DATA_TEST_CASE( } } +BOOST_AUTO_TEST_CASE(ResizeGapMultiple) { + Transform3 trf = Transform3::Identity(); + auto bounds = std::make_shared(100, 200, 100); + Volume vol{trf, bounds}; + + BOOST_TEST_CONTEXT("Outer") { + std::vector volumes = {&vol}; + CylinderVolumeStack stack(volumes, BinningValue::binR, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 1); + BOOST_CHECK(stack.gaps().empty()); + + stack.update(std::make_shared(100, 250, 100), trf, + *logger); + BOOST_CHECK_EQUAL(volumes.size(), 2); + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + const auto* cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 200); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 250); + + stack.update(std::make_shared(100, 300, 100), trf, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 2); + // No additional gap volume was added! + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 200); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 300); + } + + BOOST_TEST_CONTEXT("Inner") { + std::vector volumes = {&vol}; + CylinderVolumeStack stack(volumes, BinningValue::binR, + CylinderVolumeStack::AttachmentStrategy::Gap, + CylinderVolumeStack::ResizeStrategy::Gap, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 1); + BOOST_CHECK(stack.gaps().empty()); + + stack.update(std::make_shared(50, 200, 100), trf, + *logger); + BOOST_CHECK_EQUAL(volumes.size(), 2); + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + const auto* cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 100); + + stack.update(std::make_shared(0, 200, 100), trf, + *logger); + + BOOST_CHECK_EQUAL(volumes.size(), 2); + // No additional gap volume was added! + BOOST_CHECK_EQUAL(stack.gaps().size(), 1); + + cylBounds = dynamic_cast( + &stack.gaps().front()->volumeBounds()); + BOOST_REQUIRE_NE(cylBounds, nullptr); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 0); + BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 100); + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Common) diff --git a/Tests/UnitTests/Core/Geometry/ExtentTests.cpp b/Tests/UnitTests/Core/Geometry/ExtentTests.cpp index 80513a1d80b..e0913e273eb 100644 --- a/Tests/UnitTests/Core/Geometry/ExtentTests.cpp +++ b/Tests/UnitTests/Core/Geometry/ExtentTests.cpp @@ -176,6 +176,17 @@ BOOST_AUTO_TEST_CASE(ProtoSupportCaseTests) { BOOST_CHECK(volumeExtent.constrains(BinningValue::binR)); } +BOOST_AUTO_TEST_CASE(DesignatedInitializers) { + using enum BinningValue; + ExtentEnvelope exp; + exp[binX] = {1., 2.}; + exp[binEta] = {-1., 1.}; + + ExtentEnvelope act{{.x = {1., 2.}, .eta = {-1., 1.}}}; + + BOOST_CHECK(exp == act); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Geometry/PortalLinkTests.cpp b/Tests/UnitTests/Core/Geometry/PortalLinkTests.cpp index dc032060a5b..e5345268e84 100644 --- a/Tests/UnitTests/Core/Geometry/PortalLinkTests.cpp +++ b/Tests/UnitTests/Core/Geometry/PortalLinkTests.cpp @@ -6,6 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +#include #include #include #include @@ -2135,7 +2136,161 @@ BOOST_AUTO_TEST_CASE(CompositeConstruction) { BOOST_AUTO_TEST_SUITE(PortalMerging) -BOOST_AUTO_TEST_CASE(TrivialTrivial) {} +BOOST_DATA_TEST_CASE(TrivialTrivial, + (boost::unit_test::data::make(0, -135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm})), + angle, offset) { + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + BOOST_TEST_CONTEXT("RDirection") { + auto vol1 = std::make_shared( + base, std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol2 = std::make_shared( + base, std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol3 = std::make_shared( + base, std::make_shared(40_mm, 50_mm, 100_mm)); + + auto disc1 = + Surface::makeShared(Transform3::Identity(), 30_mm, 60_mm); + + auto disc2 = + Surface::makeShared(Transform3::Identity(), 60_mm, 90_mm); + + auto disc3 = + Surface::makeShared(Transform3::Identity(), 90_mm, 120_mm); + + auto trivial1 = std::make_unique(disc1, *vol1); + BOOST_REQUIRE(trivial1); + auto trivial2 = std::make_unique(disc2, *vol2); + BOOST_REQUIRE(trivial2); + auto trivial3 = std::make_unique(disc3, *vol3); + BOOST_REQUIRE(trivial3); + + auto grid1 = trivial1->makeGrid(BinningValue::binR); + auto compGridTrivial = PortalLinkBase::merge( + std::move(grid1), std::make_unique(*trivial2), + BinningValue::binR, *logger); + BOOST_REQUIRE(compGridTrivial); + BOOST_CHECK_EQUAL(dynamic_cast(*compGridTrivial) + .makeGrid(gctx, *logger), + nullptr); + + auto composite = PortalLinkBase::merge( + std::move(trivial1), std::move(trivial2), BinningValue::binR, *logger); + BOOST_REQUIRE(composite); + + auto grid12 = + dynamic_cast(*composite).makeGrid(gctx, *logger); + BOOST_REQUIRE(grid12); + + BOOST_CHECK_EQUAL( + grid12->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(), + vol1.get()); + + BOOST_CHECK_EQUAL( + grid12->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(), + vol2.get()); + + composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3), + BinningValue::binR, *logger); + BOOST_REQUIRE(composite); + + auto grid123 = + dynamic_cast(*composite).makeGrid(gctx, *logger); + BOOST_REQUIRE(grid123); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(), + vol1.get()); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(), + vol2.get()); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(), + vol3.get()); + } + + BOOST_TEST_CONTEXT("ZDirection") { + auto vol1 = std::make_shared( + base, std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol2 = std::make_shared( + base * Translation3{Vector3::UnitZ() * 200}, + std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol3 = std::make_shared( + base * Translation3{Vector3::UnitZ() * 400}, + std::make_shared(30_mm, 40_mm, 100_mm)); + + auto cyl1 = Surface::makeShared(base, 40_mm, 100_mm); + + auto cyl2 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200}, 40_mm, 100_mm); + + auto cyl3 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 400}, 40_mm, 100_mm); + + auto trivial1 = std::make_unique(cyl1, *vol1); + BOOST_REQUIRE(trivial1); + auto trivial2 = std::make_unique(cyl2, *vol2); + BOOST_REQUIRE(trivial2); + auto trivial3 = std::make_unique(cyl3, *vol3); + BOOST_REQUIRE(trivial3); + + auto grid1 = trivial1->makeGrid(BinningValue::binZ); + auto compGridTrivial = PortalLinkBase::merge( + std::move(grid1), std::make_unique(*trivial2), + BinningValue::binZ, *logger); + BOOST_REQUIRE(compGridTrivial); + BOOST_CHECK_EQUAL(dynamic_cast(*compGridTrivial) + .makeGrid(gctx, *logger), + nullptr); + + auto composite = PortalLinkBase::merge( + std::move(trivial1), std::move(trivial2), BinningValue::binZ, *logger); + BOOST_REQUIRE(composite); + + auto grid12 = + dynamic_cast(*composite).makeGrid(gctx, *logger); + BOOST_REQUIRE(grid12); + + BOOST_CHECK_EQUAL( + grid12->resolveVolume(gctx, Vector2{40_mm, -40_mm}).value(), + vol1.get()); + + BOOST_CHECK_EQUAL( + grid12->resolveVolume(gctx, Vector2{40_mm, 40_mm}).value(), vol2.get()); + + composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3), + BinningValue::binZ, *logger); + BOOST_REQUIRE(composite); + + auto grid123 = + dynamic_cast(*composite).makeGrid(gctx, *logger); + BOOST_REQUIRE(grid123); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{40_mm, -110_mm}).value(), + vol1.get()); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{40_mm, -10_mm}).value(), + vol2.get()); + + BOOST_CHECK_EQUAL( + grid123->resolveVolume(gctx, Vector2{40_mm, 190_mm}).value(), + vol3.get()); + } +} BOOST_AUTO_TEST_CASE(TrivialGridR) { auto vol1 = std::make_shared( @@ -2167,32 +2322,14 @@ BOOST_AUTO_TEST_CASE(TrivialGridR) { auto merged = PortalLinkBase::merge(copy(trivial), copy(gridR), BinningValue::binR, *logger); BOOST_REQUIRE(merged); - - auto* mergedGrid = dynamic_cast(merged.get()); - BOOST_REQUIRE(mergedGrid); - - mergedGrid->printContents(std::cout); - - BOOST_CHECK_EQUAL(mergedGrid->grid().axes().size(), 1); - Axis axisExpected{AxisBound, {30_mm, 45_mm, 60_mm, 90_mm}}; - BOOST_CHECK_EQUAL(*mergedGrid->grid().axes().front(), axisExpected); + BOOST_CHECK_NE(dynamic_cast(merged.get()), nullptr); } BOOST_TEST_CONTEXT("Orthogonal") { auto merged = PortalLinkBase::merge(copy(gridPhi), copy(trivial), BinningValue::binR, *logger); BOOST_REQUIRE(merged); - - auto* mergedGrid = dynamic_cast(merged.get()); - BOOST_REQUIRE(mergedGrid); - - mergedGrid->printContents(std::cout); - - BOOST_CHECK_EQUAL(mergedGrid->grid().axes().size(), 2); - Axis axis1Expected{AxisBound, 30_mm, 90_mm, 2}; - Axis axis2Expected{AxisClosed, -M_PI, M_PI, 2}; - BOOST_CHECK_EQUAL(*mergedGrid->grid().axes().front(), axis1Expected); - BOOST_CHECK_EQUAL(*mergedGrid->grid().axes().back(), axis2Expected); + BOOST_CHECK_NE(dynamic_cast(merged.get()), nullptr); } } @@ -2227,38 +2364,14 @@ BOOST_AUTO_TEST_CASE(TrivialGridPhi) { auto merged = PortalLinkBase::merge(copy(trivial), copy(gridPhi), BinningValue::binPhi, *logger); BOOST_REQUIRE(merged); - - auto* mergedGrid = dynamic_cast(merged.get()); - BOOST_REQUIRE(mergedGrid); - - mergedGrid->printContents(std::cout); - - BOOST_CHECK_EQUAL(mergedGrid->grid().axes().size(), 1); - const auto& axis = *mergedGrid->grid().axes().front(); - BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable); - BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound); - std::vector expectedBins{-90_degree, 30_degree, 60_degree, - 90_degree}; - CHECK_CLOSE_REL(axis.getBinEdges(), expectedBins, 1e-6); + BOOST_CHECK_NE(dynamic_cast(merged.get()), nullptr); } BOOST_TEST_CONTEXT("Orthogonal") { auto merged = PortalLinkBase::merge(copy(gridR), copy(trivial), BinningValue::binPhi, *logger); BOOST_REQUIRE(merged); - - auto* mergedGrid = dynamic_cast(merged.get()); - BOOST_REQUIRE(mergedGrid); - - mergedGrid->printContents(std::cout); - - BOOST_CHECK_EQUAL(mergedGrid->grid().axes().size(), 2); - const auto& axis1 = *mergedGrid->grid().axes().front(); - const auto& axis2 = *mergedGrid->grid().axes().back(); - Axis axis1Expected{AxisBound, 30_mm, 100_mm, 2}; - BOOST_CHECK_EQUAL(axis1, axis1Expected); - std::vector expectedBins{-90_degree, 30_degree, 90_degree}; - CHECK_CLOSE_REL(axis2.getBinEdges(), expectedBins, 1e-6); + BOOST_CHECK_NE(dynamic_cast(merged.get()), nullptr); } } diff --git a/Tests/UnitTests/Core/Geometry/PortalShellTests.cpp b/Tests/UnitTests/Core/Geometry/PortalShellTests.cpp new file mode 100644 index 00000000000..15c5a9c7953 --- /dev/null +++ b/Tests/UnitTests/Core/Geometry/PortalShellTests.cpp @@ -0,0 +1,768 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/GridPortalLink.hpp" +#include "Acts/Geometry/Portal.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Geometry/TrivialPortalLink.hpp" +#include "Acts/Surfaces/SurfaceMergingException.hpp" + +#include + +using namespace Acts::UnitLiterals; + +namespace Acts::Test { +GeometryContext gctx; + +std::size_t getVolumeIndex() { + static std::size_t i = 1; + return i++; +} + +auto makeVolume(auto&&... pars) { + TrackingVolume vol(Transform3::Identity(), + std::make_shared( + std::forward(pars)...)); + vol.setVolumeName("cyl" + std::to_string(getVolumeIndex())); + return vol; +}; + +auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE); + +BOOST_AUTO_TEST_SUITE(PortalShellTests) + +BOOST_AUTO_TEST_CASE(ConstructionFromVolume) { + // - Cylinder + // | | no phi | phi | + // | --------- | ------ | --- | + // | rMin > 0 | 1 | 3 | + // | rMin == 0 | 2 | 4 | + + auto cyl1 = makeVolume(30_mm, 40_mm, 100_mm); + auto cyl2 = makeVolume(0_mm, 40_mm, 100_mm); + auto cyl3 = makeVolume(30_mm, 40_mm, 100_mm, 45_degree); + auto cyl4 = makeVolume(0_mm, 40_mm, 100_mm, 45_degree); + + TrackingVolume boxVolume( + Transform3::Identity(), + std::make_shared(10_mm, 10_mm, 10_mm)); + + BOOST_CHECK_THROW(SingleCylinderPortalShell{boxVolume}, + std::invalid_argument); + + SingleCylinderPortalShell shell1{cyl1}; + BOOST_CHECK_EQUAL(shell1.size(), 4); + + using enum CylinderVolumeBounds::Face; + + const auto* pDisc = shell1.portal(PositiveDisc); + BOOST_REQUIRE_NE(pDisc, nullptr); + BOOST_CHECK_EQUAL( + pDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ()) + .value(), + &cyl1); + BOOST_CHECK_EQUAL( + pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ()) + .value(), + nullptr); + + const auto* nDisc = shell1.portal(NegativeDisc); + BOOST_REQUIRE_NE(nDisc, nullptr); + BOOST_CHECK_EQUAL(nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, + -Vector3::UnitZ()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ()) + .value(), + &cyl1); + + const auto* oCyl = shell1.portal(OuterCylinder); + BOOST_REQUIRE_NE(oCyl, nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + &cyl1); + + const auto* iCyl = shell1.portal(InnerCylinder); + BOOST_REQUIRE_NE(iCyl, nullptr); + BOOST_CHECK_EQUAL( + iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + &cyl1); + BOOST_CHECK_EQUAL( + iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + nullptr); + + SingleCylinderPortalShell shell2{cyl2}; + BOOST_CHECK_EQUAL(shell2.size(), 3); + + pDisc = shell2.portal(PositiveDisc); + BOOST_REQUIRE_NE(pDisc, nullptr); + BOOST_CHECK_EQUAL( + pDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ()) + .value(), + &cyl2); + BOOST_CHECK_EQUAL( + pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ()) + .value(), + nullptr); + + nDisc = shell2.portal(NegativeDisc); + BOOST_REQUIRE_NE(nDisc, nullptr); + BOOST_CHECK_EQUAL(nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, + -Vector3::UnitZ()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ()) + .value(), + &cyl2); + + oCyl = shell2.portal(OuterCylinder); + BOOST_REQUIRE_NE(oCyl, nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + &cyl2); + + iCyl = shell2.portal(InnerCylinder); + BOOST_CHECK_EQUAL(iCyl, nullptr); + + SingleCylinderPortalShell shell3{cyl3}; + BOOST_CHECK_EQUAL(shell3.size(), 6); + + pDisc = shell3.portal(PositiveDisc); + BOOST_REQUIRE_NE(pDisc, nullptr); + BOOST_CHECK_EQUAL( + pDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ()) + .value(), + &cyl3); + BOOST_CHECK_EQUAL( + pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ()) + .value(), + nullptr); + + nDisc = shell3.portal(NegativeDisc); + BOOST_REQUIRE_NE(nDisc, nullptr); + BOOST_CHECK_EQUAL(nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, + -Vector3::UnitZ()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ()) + .value(), + &cyl3); + + oCyl = shell3.portal(OuterCylinder); + BOOST_REQUIRE_NE(oCyl, nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + &cyl3); + + iCyl = shell3.portal(InnerCylinder); + BOOST_REQUIRE_NE(iCyl, nullptr); + BOOST_CHECK_EQUAL( + iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + &cyl3); + BOOST_CHECK_EQUAL( + iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + nullptr); + + auto anglePoint = [](double angle, double r, double z) { + return Vector3{r * std::cos(angle), r * std::sin(angle), z}; + }; + + const auto* nPhi = shell3.portal(NegativePhiPlane); + BOOST_REQUIRE_NE(nPhi, nullptr); + Vector3 point = anglePoint(-45_degree, 35_mm, 10_mm); + Vector3 dir = Vector3::UnitZ().cross(point).normalized(); + Vector3 idir = (-Vector3::UnitZ()).cross(point).normalized(); + BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, dir).value(), nullptr); + BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, idir).value(), &cyl3); + + const auto* pPhi = shell3.portal(PositivePhiPlane); + BOOST_REQUIRE_NE(pPhi, nullptr); + point = anglePoint(45_degree, 35_mm, 10_mm); + dir = Vector3::UnitZ().cross(point).normalized(); + idir = (-Vector3::UnitZ()).cross(point).normalized(); + BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, dir).value(), nullptr); + BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, idir).value(), &cyl3); + + SingleCylinderPortalShell shell4{cyl4}; + BOOST_CHECK_EQUAL(shell4.size(), 5); + + pDisc = shell4.portal(PositiveDisc); + BOOST_REQUIRE_NE(pDisc, nullptr); + BOOST_CHECK_EQUAL( + pDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ()) + .value(), + &cyl4); + BOOST_CHECK_EQUAL( + pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ()) + .value(), + nullptr); + + nDisc = shell4.portal(NegativeDisc); + BOOST_REQUIRE_NE(nDisc, nullptr); + BOOST_CHECK_EQUAL(nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, + -Vector3::UnitZ()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + nDisc + ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ()) + .value(), + &cyl4); + + oCyl = shell4.portal(OuterCylinder); + BOOST_REQUIRE_NE(oCyl, nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX()) + .value(), + nullptr); + BOOST_CHECK_EQUAL( + oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX()) + .value(), + &cyl4); + + iCyl = shell4.portal(InnerCylinder); + BOOST_CHECK_EQUAL(iCyl, nullptr); + + nPhi = shell4.portal(NegativePhiPlane); + BOOST_REQUIRE_NE(nPhi, nullptr); + point = anglePoint(-45_degree, 35_mm, 10_mm); + dir = Vector3::UnitZ().cross(point).normalized(); + idir = (-Vector3::UnitZ()).cross(point).normalized(); + BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, dir).value(), nullptr); + BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, idir).value(), &cyl4); + + pPhi = shell4.portal(PositivePhiPlane); + BOOST_REQUIRE_NE(pPhi, nullptr); + point = anglePoint(45_degree, 35_mm, 10_mm); + dir = Vector3::UnitZ().cross(point).normalized(); + idir = (-Vector3::UnitZ()).cross(point).normalized(); + BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, dir).value(), nullptr); + BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, idir).value(), &cyl4); +} + +// outer cylinder +// +----------+----------+ +// | | | +// negative | v | positive +// disc +--> <--+ disc +// | ^ | +// | | | +// +----------+----------+ +// inner cylinder + +BOOST_AUTO_TEST_CASE(PortalAssignment) { + using enum CylinderVolumeBounds::Face; + TrackingVolume vol( + Transform3::Identity(), + std::make_shared(30_mm, 100_mm, 100_mm)); + + SingleCylinderPortalShell shell{vol}; + + const auto* iCyl = shell.portal(InnerCylinder); + const auto* pDisc = shell.portal(PositiveDisc); + auto* oCyl = shell.portal(OuterCylinder); + auto* nDisc = shell.portal(NegativeDisc); + + // Setting new outer cylinder + BOOST_REQUIRE_NE(oCyl, nullptr); + auto* oCylLink = dynamic_cast( + oCyl->getLink(Direction::OppositeNormal)); + BOOST_REQUIRE_NE(oCylLink, nullptr); + + auto grid = oCylLink->makeGrid(BinningValue::binZ); + + auto portal2 = + std::make_shared(Direction::OppositeNormal, std::move(grid)); + shell.setPortal(portal2, OuterCylinder); + BOOST_CHECK_EQUAL(shell.portal(OuterCylinder), portal2.get()); + + // Other portals should stay the same + BOOST_CHECK_EQUAL(shell.portal(InnerCylinder), iCyl); + BOOST_CHECK_EQUAL(shell.portal(PositiveDisc), pDisc); + BOOST_CHECK_EQUAL(shell.portal(NegativeDisc), nDisc); + + // Setting new negative disc + BOOST_REQUIRE_NE(nDisc, nullptr); + auto* nDiscLink = dynamic_cast( + nDisc->getLink(Direction::AlongNormal)); + BOOST_REQUIRE_NE(nDiscLink, nullptr); + + grid = nDiscLink->makeGrid(BinningValue::binR); + + auto portal3 = + std::make_shared(Direction::AlongNormal, std::move(grid)); + shell.setPortal(portal3, NegativeDisc); + BOOST_CHECK_EQUAL(shell.portal(NegativeDisc), portal3.get()); + + // Other portals should stay the same + BOOST_CHECK_EQUAL(shell.portal(OuterCylinder), portal2.get()); + BOOST_CHECK_EQUAL(shell.portal(InnerCylinder), iCyl); + BOOST_CHECK_EQUAL(shell.portal(PositiveDisc), pDisc); +} + +BOOST_AUTO_TEST_SUITE(CylinderStack) +BOOST_AUTO_TEST_CASE(ZDirection) { + using enum CylinderVolumeBounds::Face; + BOOST_TEST_CONTEXT("rMin>0") { + TrackingVolume vol1( + Transform3{Translation3{Vector3::UnitZ() * -100_mm}}, + std::make_shared(30_mm, 100_mm, 100_mm)); + + TrackingVolume vol2( + Transform3{Translation3{Vector3::UnitZ() * 100_mm}}, + std::make_shared(30_mm, 100_mm, 100_mm)); + + SingleCylinderPortalShell shell1{vol1}; + SingleCylinderPortalShell shell2{vol2}; + + BOOST_CHECK_NE(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc)); + + CylinderStackPortalShell stack{ + gctx, {&shell1, &shell2}, BinningValue::binZ}; + BOOST_CHECK_EQUAL(stack.size(), 4); + + const auto* iCyl = stack.portal(InnerCylinder); + BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), iCyl); + BOOST_CHECK_EQUAL(shell2.portal(InnerCylinder), iCyl); + + const auto* oCyl = stack.portal(OuterCylinder); + BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), oCyl); + BOOST_CHECK_EQUAL(shell2.portal(OuterCylinder), oCyl); + + BOOST_CHECK_EQUAL(stack.portal(PositiveDisc), shell2.portal(PositiveDisc)); + BOOST_CHECK_EQUAL(stack.portal(NegativeDisc), shell1.portal(NegativeDisc)); + + BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr); + + BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr); + + // Disc portals have been fused + BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc)); + + shell1 = SingleCylinderPortalShell{vol1}; + shell2 = SingleCylinderPortalShell{vol2}; + + BOOST_CHECK_THROW( + CylinderStackPortalShell(gctx, {&shell1, &shell2}, BinningValue::binR), + SurfaceMergingException); + } + + BOOST_TEST_CONTEXT("rMin==0") { + TrackingVolume vol1( + Transform3{Translation3{Vector3::UnitZ() * -100_mm}}, + std::make_shared(0_mm, 100_mm, 100_mm)); + + TrackingVolume vol2( + Transform3{Translation3{Vector3::UnitZ() * 100_mm}}, + std::make_shared(0_mm, 100_mm, 100_mm)); + + SingleCylinderPortalShell shell1{vol1}; + SingleCylinderPortalShell shell2{vol2}; + + BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), nullptr); + BOOST_CHECK_EQUAL(shell2.portal(InnerCylinder), nullptr); + + BOOST_CHECK_NE(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc)); + + CylinderStackPortalShell stack{ + gctx, {&shell1, &shell2}, BinningValue::binZ}; + BOOST_CHECK_EQUAL(stack.size(), 3); + + // Disc portals have been fused + BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc)); + + const auto* iCyl = stack.portal(InnerCylinder); + BOOST_CHECK_EQUAL(iCyl, nullptr); + + const auto* oCyl = stack.portal(OuterCylinder); + BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), oCyl); + BOOST_CHECK_EQUAL(shell2.portal(OuterCylinder), oCyl); + + BOOST_CHECK_EQUAL(stack.portal(PositiveDisc), shell2.portal(PositiveDisc)); + BOOST_CHECK_EQUAL(stack.portal(NegativeDisc), shell1.portal(NegativeDisc)); + + BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr); + + BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr); + + shell1 = SingleCylinderPortalShell{vol1}; + shell2 = SingleCylinderPortalShell{vol2}; + + BOOST_CHECK_THROW( + CylinderStackPortalShell(gctx, {&shell1, &shell2}, BinningValue::binR), + SurfaceMergingException); + } +} + +BOOST_AUTO_TEST_CASE(RDirection) { + using enum CylinderVolumeBounds::Face; + BOOST_TEST_CONTEXT("rMin>0") { + TrackingVolume vol1( + Transform3::Identity(), + std::make_shared(30_mm, 100_mm, 100_mm)); + + TrackingVolume vol2( + Transform3::Identity(), + std::make_shared(100_mm, 150_mm, 100_mm)); + + SingleCylinderPortalShell shell1{vol1}; + SingleCylinderPortalShell shell2{vol2}; + + BOOST_CHECK_NE(shell1.portal(OuterCylinder), shell2.portal(InnerCylinder)); + + CylinderStackPortalShell stack{ + gctx, {&shell1, &shell2}, BinningValue::binR}; + BOOST_CHECK_EQUAL(stack.size(), 4); + + // Internal cylinder portals have been fused + BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), + shell2.portal(InnerCylinder)); + + const auto* nDisc = stack.portal(NegativeDisc); + const auto* pDisc = stack.portal(PositiveDisc); + + BOOST_CHECK_EQUAL(shell1.portal(NegativeDisc), nDisc); + BOOST_CHECK_EQUAL(shell2.portal(NegativeDisc), nDisc); + BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), pDisc); + BOOST_CHECK_EQUAL(shell2.portal(PositiveDisc), pDisc); + + BOOST_CHECK_EQUAL(stack.portal(InnerCylinder), + shell1.portal(InnerCylinder)); + BOOST_CHECK_EQUAL(stack.portal(OuterCylinder), + shell2.portal(OuterCylinder)); + + BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr); + + BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr); + + shell1 = SingleCylinderPortalShell{vol1}; + shell2 = SingleCylinderPortalShell{vol2}; + + BOOST_CHECK_THROW( + CylinderStackPortalShell(gctx, {&shell1, &shell2}, BinningValue::binZ), + SurfaceMergingException); + } + + BOOST_TEST_CONTEXT("rMin==0") { + TrackingVolume vol1( + Transform3::Identity(), + std::make_shared(0_mm, 100_mm, 100_mm)); + + TrackingVolume vol2( + Transform3::Identity(), + std::make_shared(100_mm, 150_mm, 100_mm)); + + SingleCylinderPortalShell shell1{vol1}; + SingleCylinderPortalShell shell2{vol2}; + + BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), nullptr); + BOOST_CHECK_NE(shell1.portal(OuterCylinder), shell2.portal(InnerCylinder)); + + CylinderStackPortalShell stack{ + gctx, {&shell1, &shell2}, BinningValue::binR}; + BOOST_CHECK_EQUAL(stack.size(), 4); + + // Internal cylinder portals have been fused + BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), + shell2.portal(InnerCylinder)); + + const auto* nDisc = stack.portal(NegativeDisc); + const auto* pDisc = stack.portal(PositiveDisc); + + BOOST_CHECK_EQUAL(shell1.portal(NegativeDisc), nDisc); + BOOST_CHECK_EQUAL(shell2.portal(NegativeDisc), nDisc); + BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), pDisc); + BOOST_CHECK_EQUAL(shell2.portal(PositiveDisc), pDisc); + + BOOST_CHECK_EQUAL(stack.portal(InnerCylinder), nullptr); + BOOST_CHECK_EQUAL(stack.portal(OuterCylinder), + shell2.portal(OuterCylinder)); + + BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr); + + BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr); + BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr); + + shell1 = SingleCylinderPortalShell{vol1}; + shell2 = SingleCylinderPortalShell{vol2}; + + BOOST_CHECK_THROW( + CylinderStackPortalShell(gctx, {&shell1, &shell2}, BinningValue::binZ), + std::invalid_argument); + } +} + +BOOST_AUTO_TEST_CASE(NestedStacks) { + // ^ + // r | +---------------------------------+---------+ + // | | | | + // | | | | + // | | vol2 | | + // | | | | + // | | | | + // | +---------------------------------+ | + // | | | | + // | | | | + // | | gap | vol3 | + // | | | | + // | | | | + // | +---------------------------------+ | + // | | | | + // | | | | + // | | vol1 | | + // | | | | + // | | | | + // | +---------------------------------+---------+ + // | + // +--------------------------------------------------> + // z + + Transform3 base = Transform3::Identity(); + + TrackingVolume vol1( + base, std::make_shared(23_mm, 48_mm, 200_mm), + "PixelLayer0"); + + TrackingVolume gap( + base, std::make_shared(48_mm, 250_mm, 200_mm), + "Gap"); + + TrackingVolume vol2( + base, std::make_shared(250_mm, 400_mm, 200_mm), + "PixelLayer3"); + + TrackingVolume vol3( + base * Translation3{Vector3::UnitZ() * 300_mm}, + std::make_shared(23_mm, 400_mm, 100_mm), + "PixelEcPos"); + + SingleCylinderPortalShell shell1{vol1}; + BOOST_CHECK(shell1.isValid()); + SingleCylinderPortalShell gapShell{gap}; + BOOST_CHECK(gapShell.isValid()); + SingleCylinderPortalShell shell2{vol2}; + BOOST_CHECK(shell2.isValid()); + + CylinderStackPortalShell stack{ + gctx, {&shell1, &gapShell, &shell2}, BinningValue::binR}; + + BOOST_CHECK(stack.isValid()); + + SingleCylinderPortalShell shell3{vol3}; + BOOST_CHECK(shell3.isValid()); + + CylinderStackPortalShell stack2{ + gctx, {&stack, &shell3}, BinningValue::binZ, *logger}; + BOOST_CHECK(stack2.isValid()); + + using enum CylinderVolumeBounds::Face; + + auto lookup = [](auto& shell, CylinderPortalShell::Face face, + Vector3 position, + Vector3 direction) -> const TrackingVolume* { + const auto* portal = shell.portal(face); + BOOST_REQUIRE_NE(portal, nullptr); + return portal->resolveVolume(gctx, position, direction).value(); + }; + + // Interconnection in the r direction + + BOOST_CHECK_EQUAL(lookup(shell1, InnerCylinder, 23_mm * Vector3::UnitX(), + -Vector3::UnitX()), + nullptr); + BOOST_CHECK_EQUAL( + lookup(shell1, InnerCylinder, 23_mm * Vector3::UnitX(), Vector3::UnitX()), + &vol1); + + BOOST_CHECK_EQUAL( + lookup(shell1, OuterCylinder, 48_mm * Vector3::UnitX(), Vector3::UnitX()), + &gap); + + BOOST_CHECK_EQUAL(lookup(gapShell, InnerCylinder, 48_mm * Vector3::UnitX(), + -Vector3::UnitX()), + &vol1); + + BOOST_CHECK_EQUAL(lookup(gapShell, OuterCylinder, 250_mm * Vector3::UnitX(), + Vector3::UnitX()), + &vol2); + + BOOST_CHECK_EQUAL(lookup(shell2, InnerCylinder, 250_mm * Vector3::UnitX(), + -Vector3::UnitX()), + &gap); + + BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(), + Vector3::UnitX()), + nullptr); + + BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(), + -Vector3::UnitX()), + &vol2); + + BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(), + -Vector3::UnitX()), + &vol2); + + // Left connection + + BOOST_CHECK_EQUAL(lookup(shell1, NegativeDisc, Vector3(30_mm, 0, -200_mm), + -Vector3::UnitZ()), + nullptr); + + BOOST_CHECK_EQUAL(lookup(shell1, NegativeDisc, Vector3(30_mm, 0, -200_mm), + Vector3::UnitZ()), + &vol1); + + BOOST_CHECK_EQUAL(lookup(gapShell, NegativeDisc, Vector3(60_mm, 0, -200_mm), + -Vector3::UnitZ()), + nullptr); + + BOOST_CHECK_EQUAL(lookup(gapShell, NegativeDisc, Vector3(60_mm, 0, -200_mm), + Vector3::UnitZ()), + &gap); + + BOOST_CHECK_EQUAL(lookup(shell2, NegativeDisc, Vector3(300_mm, 0, -200_mm), + -Vector3::UnitZ()), + nullptr); + + BOOST_CHECK_EQUAL(lookup(shell2, NegativeDisc, Vector3(300_mm, 0, -200_mm), + Vector3::UnitZ()), + &vol2); + + // Right connection + + BOOST_CHECK_EQUAL(lookup(shell1, PositiveDisc, Vector3(30_mm, 0, 200_mm), + -Vector3::UnitZ()), + &vol1); + + BOOST_CHECK_EQUAL( + lookup(shell1, PositiveDisc, Vector3(30_mm, 0, 200_mm), Vector3::UnitZ()), + &vol3); + + BOOST_CHECK_EQUAL(lookup(gapShell, PositiveDisc, Vector3(60_mm, 0, 200_mm), + -Vector3::UnitZ()), + &gap); + + BOOST_CHECK_EQUAL(lookup(gapShell, PositiveDisc, Vector3(60_mm, 0, 200_mm), + Vector3::UnitZ()), + &vol3); + + BOOST_CHECK_EQUAL(lookup(shell2, PositiveDisc, Vector3(300_mm, 0, 200_mm), + -Vector3::UnitZ()), + &vol2); + + BOOST_CHECK_EQUAL(lookup(shell2, PositiveDisc, Vector3(300_mm, 0, 200_mm), + Vector3::UnitZ()), + &vol3); + + // Right outer connection + + BOOST_CHECK_EQUAL(lookup(shell3, PositiveDisc, Vector3(300_mm, 0, 400_mm), + -Vector3::UnitZ()), + &vol3); + + BOOST_CHECK_EQUAL(lookup(shell3, PositiveDisc, Vector3(300_mm, 0, 400_mm), + Vector3::UnitZ()), + nullptr); +} + +BOOST_AUTO_TEST_CASE(ConnectOuter) { + auto cyl1 = makeVolume(30_mm, 40_mm, 100_mm); + auto cyl2 = makeVolume(0_mm, 50_mm, 110_mm); + + SingleCylinderPortalShell shell{cyl1}; + + using enum CylinderVolumeBounds::Face; + BOOST_CHECK_EQUAL( + shell.portal(OuterCylinder)->getLink(Direction::AlongNormal), nullptr); + BOOST_CHECK_EQUAL( + shell.portal(InnerCylinder)->getLink(Direction::OppositeNormal), nullptr); + BOOST_CHECK_EQUAL(shell.portal(PositiveDisc)->getLink(Direction::AlongNormal), + nullptr); + BOOST_CHECK_EQUAL( + shell.portal(NegativeDisc)->getLink(Direction::OppositeNormal), nullptr); + + shell.connectOuter(cyl2); + + BOOST_CHECK_NE(shell.portal(OuterCylinder)->getLink(Direction::AlongNormal), + nullptr); + BOOST_CHECK_NE( + shell.portal(InnerCylinder)->getLink(Direction::OppositeNormal), nullptr); + BOOST_CHECK_NE(shell.portal(PositiveDisc)->getLink(Direction::AlongNormal), + nullptr); + BOOST_CHECK_NE(shell.portal(NegativeDisc)->getLink(Direction::OppositeNormal), + nullptr); +} + +BOOST_AUTO_TEST_CASE(RegisterInto) { + using enum CylinderVolumeBounds::Face; + TrackingVolume vol1( + Transform3::Identity(), + std::make_shared(0_mm, 100_mm, 100_mm)); + + SingleCylinderPortalShell shell{vol1}; + + BOOST_CHECK_EQUAL(vol1.portals().size(), 0); + + shell.applyToVolume(); + BOOST_CHECK_EQUAL(vol1.portals().size(), 3); +} + +BOOST_AUTO_TEST_SUITE_END() // CylinderStack +BOOST_AUTO_TEST_SUITE_END() + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Geometry/PortalTests.cpp b/Tests/UnitTests/Core/Geometry/PortalTests.cpp index 49081748649..8997ccedda7 100644 --- a/Tests/UnitTests/Core/Geometry/PortalTests.cpp +++ b/Tests/UnitTests/Core/Geometry/PortalTests.cpp @@ -12,6 +12,7 @@ #include #include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/CompositePortalLink.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GridPortalLink.hpp" @@ -171,10 +172,9 @@ BOOST_AUTO_TEST_CASE(Cylinder) { BOOST_CHECK_NE(merged12.getLink(Direction::AlongNormal), nullptr); BOOST_CHECK_EQUAL(merged12.getLink(Direction::OppositeNormal), nullptr); - auto grid12 = dynamic_cast( + auto composite12 = dynamic_cast( merged12.getLink(Direction::AlongNormal)); - BOOST_REQUIRE_NE(grid12, nullptr); - grid12->printContents(std::cout); + BOOST_REQUIRE_NE(composite12, nullptr); BOOST_CHECK_EQUAL( merged12 @@ -528,6 +528,67 @@ BOOST_AUTO_TEST_CASE(Material) { BOOST_CHECK_EQUAL(portal12.surface().surfaceMaterial(), material.get()); } +BOOST_AUTO_TEST_CASE(GridCreationOnFuse) { + Transform3 base = Transform3::Identity(); + + auto vol1 = std::make_shared( + base, std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol2 = std::make_shared( + base, std::make_shared(30_mm, 40_mm, 100_mm)); + + auto vol3 = std::make_shared( + base, std::make_shared(40_mm, 50_mm, 100_mm)); + + auto vol4 = std::make_shared( + base, std::make_shared(40_mm, 50_mm, 100_mm)); + + auto disc1 = + Surface::makeShared(Transform3::Identity(), 30_mm, 60_mm); + + auto disc2 = + Surface::makeShared(Transform3::Identity(), 60_mm, 90_mm); + + auto disc3 = + Surface::makeShared(Transform3::Identity(), 90_mm, 120_mm); + + auto trivial1 = std::make_unique(disc1, *vol1); + BOOST_REQUIRE(trivial1); + auto trivial2 = std::make_unique(disc2, *vol2); + BOOST_REQUIRE(trivial2); + auto trivial3 = std::make_unique(disc3, *vol3); + BOOST_REQUIRE(trivial3); + + std::vector> links; + links.push_back(std::move(trivial1)); + links.push_back(std::move(trivial2)); + links.push_back(std::move(trivial3)); + + auto composite = std::make_unique(std::move(links), + BinningValue::binR); + + auto discOpposite = + Surface::makeShared(Transform3::Identity(), 30_mm, 120_mm); + + auto trivialOpposite = + std::make_unique(discOpposite, *vol4); + + Portal aPortal{gctx, std::move(composite), nullptr}; + Portal bPortal{gctx, nullptr, std::move(trivialOpposite)}; + + Portal fused = Portal::fuse(gctx, aPortal, bPortal, *logger); + + BOOST_CHECK_NE(dynamic_cast( + fused.getLink(Direction::OppositeNormal)), + nullptr); + + const auto* grid = dynamic_cast( + fused.getLink(Direction::AlongNormal)); + BOOST_REQUIRE_NE(grid, nullptr); + + BOOST_CHECK_EQUAL(grid->grid().axes().front()->getNBins(), 3); +} + BOOST_AUTO_TEST_SUITE_END() // Fusing BOOST_AUTO_TEST_CASE(Construction) { diff --git a/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp b/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp index cbace6a6559..efde97d9b41 100644 --- a/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp +++ b/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp @@ -9,30 +9,33 @@ #include #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/DetectorElementBase.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/ProtoLayer.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" #include "Acts/Surfaces/RectangleBounds.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/RangeXD.hpp" -#include #include #include #include #include -#include #include namespace Acts::Test::Layers { +GeometryContext tgContext = GeometryContext(); + BOOST_AUTO_TEST_SUITE(Geometry) BOOST_AUTO_TEST_CASE(ProtoLayerTests) { - GeometryContext tgContext = GeometryContext(); + using enum BinningValue; // Create a proto layer with 4 surfaces on the x/y grid auto recBounds = std::make_shared(3., 6.); @@ -105,20 +108,20 @@ BOOST_AUTO_TEST_CASE(ProtoLayerTests) { // Test 1 - identity transform auto protoLayer = createProtoLayer(Transform3::Identity()); - CHECK_CLOSE_ABS(protoLayer.range(BinningValue::binX), 12., 1e-8); - CHECK_CLOSE_ABS(protoLayer.medium(BinningValue::binX), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayer.min(BinningValue::binX), -6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.max(BinningValue::binX), 6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.range(BinningValue::binY), 6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.medium(BinningValue::binY), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayer.min(BinningValue::binY), -3., 1e-8); - CHECK_CLOSE_ABS(protoLayer.max(BinningValue::binY), 3., 1e-8); - CHECK_CLOSE_ABS(protoLayer.range(BinningValue::binZ), 12., 1e-8); - CHECK_CLOSE_ABS(protoLayer.medium(BinningValue::binZ), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayer.min(BinningValue::binZ), -6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.max(BinningValue::binZ), 6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.max(BinningValue::binR), std::hypot(3, 6), 1e-8); - CHECK_CLOSE_ABS(protoLayer.min(BinningValue::binR), 3., 1e-8); + CHECK_CLOSE_ABS(protoLayer.range(binX), 12., 1e-8); + CHECK_CLOSE_ABS(protoLayer.medium(binX), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayer.min(binX), -6., 1e-8); + CHECK_CLOSE_ABS(protoLayer.max(binX), 6., 1e-8); + CHECK_CLOSE_ABS(protoLayer.range(binY), 6., 1e-8); + CHECK_CLOSE_ABS(protoLayer.medium(binY), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayer.min(binY), -3., 1e-8); + CHECK_CLOSE_ABS(protoLayer.max(binY), 3., 1e-8); + CHECK_CLOSE_ABS(protoLayer.range(binZ), 12., 1e-8); + CHECK_CLOSE_ABS(protoLayer.medium(binZ), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayer.min(binZ), -6., 1e-8); + CHECK_CLOSE_ABS(protoLayer.max(binZ), 6., 1e-8); + CHECK_CLOSE_ABS(protoLayer.max(binR), std::hypot(3, 6), 1e-8); + CHECK_CLOSE_ABS(protoLayer.min(binR), 3., 1e-8); // Test 1a @@ -127,16 +130,15 @@ BOOST_AUTO_TEST_CASE(ProtoLayerTests) { auto protoLayerRot = createProtoLayer(AngleAxis3(-0.345, Vector3::UnitZ()) * Transform3::Identity()); - BOOST_CHECK_NE(protoLayer.min(BinningValue::binX), -6.); - CHECK_CLOSE_ABS(protoLayerRot.medium(BinningValue::binX), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.medium(BinningValue::binY), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.range(BinningValue::binZ), 12., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.medium(BinningValue::binZ), 0., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.min(BinningValue::binZ), -6., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.max(BinningValue::binZ), 6., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.min(BinningValue::binR), 3., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.max(BinningValue::binR), std::hypot(3, 6), - 1e-8); + BOOST_CHECK_NE(protoLayer.min(binX), -6.); + CHECK_CLOSE_ABS(protoLayerRot.medium(binX), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.medium(binY), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.range(binZ), 12., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.medium(binZ), 0., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.min(binZ), -6., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.max(binZ), 6., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.min(binR), 3., 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.max(binR), std::hypot(3, 6), 1e-8); std::stringstream sstream; protoLayerRot.toStream(sstream); @@ -155,6 +157,111 @@ Extent in space : BOOST_CHECK_EQUAL(sstream.str(), oString); } +BOOST_AUTO_TEST_CASE(OrientedLayer) { + using enum BinningValue; + using namespace Acts::UnitLiterals; + + Transform3 base = Transform3::Identity(); + + auto recBounds = std::make_shared(3_mm, 6_mm); + + std::vector> detectorElements; + + auto makeFan = [&](double yrot, double thickness = 0) { + detectorElements.clear(); + + std::size_t nSensors = 8; + double deltaPhi = 2 * M_PI / nSensors; + double r = 20_mm; + std::vector> surfaces; + for (std::size_t i = 0; i < nSensors; i++) { + // Create a fan of sensors + + Transform3 trf = base * AngleAxis3{yrot, Vector3::UnitY()} * + AngleAxis3{deltaPhi * i, Vector3::UnitZ()} * + Translation3(Vector3::UnitX() * r); + + auto& element = detectorElements.emplace_back( + std::make_unique(trf, recBounds, thickness)); + + surfaces.push_back(element->surface().getSharedPtr()); + } + return surfaces; + }; + + std::vector> surfaces = makeFan(0_degree); + + ProtoLayer protoLayer(tgContext, surfaces); + + BOOST_CHECK_EQUAL(protoLayer.surfaces().size(), 8); + BOOST_CHECK_CLOSE(protoLayer.min(binX), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binX), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binY), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binY), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binZ), 0_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binZ), 0_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binR), 17_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binR), 23.769728648_mm, 1e-8); + + surfaces = makeFan(45_degree); + + // Do NOT provide rotation matrix: sizing will be affected + protoLayer = {tgContext, surfaces}; + + BOOST_CHECK_EQUAL(protoLayer.surfaces().size(), 8); + BOOST_CHECK_CLOSE(protoLayer.min(binX), -16.26345596_mm, 1e-4); + BOOST_CHECK_CLOSE(protoLayer.max(binX), 16.26345596_mm, 1e-4); + BOOST_CHECK_CLOSE(protoLayer.min(binY), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binY), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binZ), -16.26345596_mm, 1e-4); + BOOST_CHECK_CLOSE(protoLayer.max(binZ), 16.26345596_mm, 1e-4); + + protoLayer = {tgContext, surfaces, + Transform3{AngleAxis3{45_degree, Vector3::UnitY()}}.inverse()}; + + BOOST_CHECK_EQUAL(protoLayer.surfaces().size(), 8); + BOOST_CHECK_CLOSE(protoLayer.range(binX), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binX), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binX), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.range(binY), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binY), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binY), 23_mm, 1e-8); + CHECK_SMALL(protoLayer.range(binZ), 1e-14); + CHECK_SMALL(protoLayer.min(binZ), 1e-14); + CHECK_SMALL(protoLayer.max(binZ), 1e-14); + + surfaces = makeFan(0_degree, 10_mm); + + protoLayer = {tgContext, surfaces}; + + BOOST_CHECK_EQUAL(protoLayer.surfaces().size(), 8); + BOOST_CHECK_CLOSE(protoLayer.range(binX), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binX), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binX), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.range(binY), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binY), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binY), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.range(binZ), 10_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binZ), -5_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binZ), 5_mm, 1e-8); + + surfaces = makeFan(45_degree, 10_mm); + + protoLayer = {tgContext, surfaces, + Transform3{AngleAxis3{45_degree, Vector3::UnitY()}}.inverse()}; + + BOOST_CHECK_EQUAL(protoLayer.surfaces().size(), 8); + BOOST_CHECK_CLOSE(protoLayer.range(binX), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binX), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binX), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.range(binY), 46_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binY), -23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binY), 23_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.range(binZ), 10_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.min(binZ), -5_mm, 1e-8); + BOOST_CHECK_CLOSE(protoLayer.max(binZ), 5_mm, 1e-8); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test::Layers diff --git a/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp b/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp index 4f263e693f7..9d89c1c96c6 100644 --- a/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp +++ b/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp @@ -10,7 +10,6 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Seed.hpp" #include "Acts/EventData/SpacePointContainer.hpp" -#include "Acts/Geometry/Extent.hpp" #include "Acts/Seeding/BinnedGroup.hpp" #include "Acts/Seeding/SeedFilter.hpp" #include "Acts/Seeding/SeedFilterConfig.hpp" @@ -146,6 +145,7 @@ int main(int argc, char** argv) { Acts::SeedFinderConfig config; // silicon detector max config.rMax = 160._mm; + config.rMin = 0._mm; config.deltaRMin = 5._mm; config.deltaRMax = 160._mm; config.deltaRMinTopSP = config.deltaRMin; @@ -173,19 +173,16 @@ int main(int argc, char** argv) { int numPhiNeighbors = 1; - // extent used to store r range for middle spacepoint - Acts::Extent rRangeSPExtent; - config.useVariableMiddleSPRange = false; const Acts::Range1D rMiddleSPRange; std::vector> zBinNeighborsTop; std::vector> zBinNeighborsBottom; - auto bottomBinFinder = std::make_unique>( - Acts::GridBinFinder<2ul>(numPhiNeighbors, zBinNeighborsBottom)); - auto topBinFinder = std::make_unique>( - Acts::GridBinFinder<2ul>(numPhiNeighbors, zBinNeighborsTop)); + auto bottomBinFinder = std::make_unique>( + numPhiNeighbors, zBinNeighborsBottom, 0); + auto topBinFinder = std::make_unique>( + numPhiNeighbors, zBinNeighborsTop, 0); Acts::SeedFilterConfig sfconf; Acts::ATLASCuts atlasCuts = Acts::ATLASCuts(); @@ -213,8 +210,7 @@ int main(int argc, char** argv) { Acts::CylindricalSpacePointGridCreator::createGrid(gridConf, gridOpts); Acts::CylindricalSpacePointGridCreator::fillGrid( - config, options, grid, spContainer.begin(), spContainer.end(), - rRangeSPExtent); + config, options, grid, spContainer.begin(), spContainer.end()); auto spGroup = Acts::CylindricalBinnedGroup( std::move(grid), *bottomBinFinder, *topBinFinder); diff --git a/Tests/UnitTests/Core/Surfaces/AnnulusBoundsTests.cpp b/Tests/UnitTests/Core/Surfaces/AnnulusBoundsTests.cpp index c284d6d4796..9f09fb2c213 100644 --- a/Tests/UnitTests/Core/Surfaces/AnnulusBoundsTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/AnnulusBoundsTests.cpp @@ -23,10 +23,10 @@ namespace Acts::Test { BOOST_AUTO_TEST_SUITE(Surfaces) -double minRadius = 7.2; -double maxRadius = 12.0; -double minPhi = 0.74195; -double maxPhi = 1.33970; +ActsScalar minRadius = 7.2; +ActsScalar maxRadius = 12.0; +ActsScalar minPhi = 0.74195; +ActsScalar maxPhi = 1.33970; Vector2 offset(-2., 2.); @@ -123,6 +123,25 @@ BOOST_AUTO_TEST_CASE(AnnulusBoundsProperties) { BOOST_CHECK_EQUAL(aBounds.get(AnnulusBounds::eMaxPhiRel), maxPhi); } +/// Unit tests for AnnulusBounds vertices +BOOST_AUTO_TEST_CASE(AnnulusBoundsVertices) { + /// Test construction with radii and default sector + AnnulusBounds aBounds(minRadius, maxRadius, minPhi, maxPhi, offset); + + // Retrieve the corners + auto corners = aBounds.corners(); + BOOST_CHECK_EQUAL(corners.size(), 4); + + // Retrieve the vertices + auto vertices = aBounds.vertices(0u); + BOOST_CHECK_EQUAL(vertices.size(), 4); + + // Now generate with more segments + unsigned int nQuarterSegments = 12; + vertices = aBounds.vertices(nQuarterSegments); + BOOST_CHECK_EQUAL(vertices.size(), 14u); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Surfaces/ConvexPolygonBoundsTests.cpp b/Tests/UnitTests/Core/Surfaces/ConvexPolygonBoundsTests.cpp index bdb4c2948be..6b6712c230b 100644 --- a/Tests/UnitTests/Core/Surfaces/ConvexPolygonBoundsTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/ConvexPolygonBoundsTests.cpp @@ -101,6 +101,10 @@ BOOST_AUTO_TEST_CASE(ConvexPolygonBoundsRecreation) { std::copy_n(valvector.begin(), poly<4>::eSize, values.begin()); poly<4> recreated(values); BOOST_CHECK_EQUAL(original, recreated); + + // Get the vertices back + auto rvertices = original.vertices(); + BOOST_CHECK_EQUAL(rvertices.size(), 4u); } BOOST_AUTO_TEST_CASE(ConvexPolygonBoundsDynamicTest) { diff --git a/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp b/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp index c858f10f6db..184d69e341f 100644 --- a/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp @@ -42,7 +42,7 @@ namespace Acts::Test { const GeometryContext tgContext = GeometryContext(); const std::vector> testModes = { - {"Triangulate", 72}, {"Extrema", 1}}; + {"Triangulate", 18}, {"Extrema", 1}}; const Transform3 transform = Transform3::Identity(); const double epsAbs = 1e-12; @@ -61,9 +61,8 @@ BOOST_AUTO_TEST_CASE(ConeSurfacePolyhedrons) { const double rMax = hzPos * std::tan(alpha); - for (const auto& mode : testModes) { - ACTS_INFO("\tMode: " << std::get(mode)); - const unsigned int segments = std::get(mode); + for (const auto& [mode, segments] : testModes) { + ACTS_INFO("\tMode: " << mode); /// The full cone on one side { @@ -81,9 +80,10 @@ BOOST_AUTO_TEST_CASE(ConeSurfacePolyhedrons) { CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).min(), 0_mm, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), hzPos, epsAbs); - const unsigned int expectedFaces = segments < 4 ? 4 : segments; + const unsigned int expectedFaces = 4 * segments; BOOST_CHECK_EQUAL(oneConePh.faces.size(), expectedFaces); - BOOST_CHECK_EQUAL(oneConePh.vertices.size(), expectedFaces + 1); + // full segments + overlap at (pi/pi) + tip + BOOST_CHECK_EQUAL(oneConePh.vertices.size(), expectedFaces + 2); } /// The full cone on one side @@ -106,6 +106,11 @@ BOOST_AUTO_TEST_CASE(ConeSurfacePolyhedrons) { CHECK_CLOSE_ABS(extent.range(BinningValue::binR).max(), rMax, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).min(), hzpMin, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), hzPos, epsAbs); + + const unsigned int expectedFaces = 4 * segments; + BOOST_CHECK_EQUAL(oneConePiecePh.faces.size(), expectedFaces); + BOOST_CHECK_EQUAL(oneConePiecePh.vertices.size(), + (expectedFaces + 1) * 2); } /// The full cone on both sides @@ -124,9 +129,11 @@ BOOST_AUTO_TEST_CASE(ConeSurfacePolyhedrons) { CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).min(), hzNeg, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), hzPos, epsAbs); - const unsigned int expectedFaces = segments < 4 ? 8 : 2 * segments; + const unsigned int expectedFaces = 2 * segments * 4; + const unsigned int expectedVertices = 2 * (4 * segments + 1) + 1; + BOOST_CHECK_EQUAL(twoConesPh.faces.size(), expectedFaces); - BOOST_CHECK_EQUAL(twoConesPh.vertices.size(), expectedFaces + 1); + BOOST_CHECK_EQUAL(twoConesPh.vertices.size(), expectedVertices); } /// A centered sectoral cone on both sides @@ -143,13 +150,16 @@ BOOST_AUTO_TEST_CASE(ConeSurfacePolyhedrons) { const auto extent = sectoralConesPh.extent(); CHECK_CLOSE_ABS(extent.range(BinningValue::binX).min(), 0, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binX).max(), rMax, epsAbs); - // CHECK_CLOSE_ABS(extent.range(BinningValue::binY).min(), ???, - // epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binY).max(), - // ???, epsAbs); + CHECK_CLOSE_ABS(extent.range(BinningValue::binY).min(), + -rMax * std::sin(phiSector), epsAbs); + CHECK_CLOSE_ABS(extent.range(BinningValue::binY).max(), + rMax * std::sin(phiSector), epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binR).min(), 0_mm, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binR).max(), rMax, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).min(), hzNeg, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), hzPos, epsAbs); + + // Segment numbers are further checked with the VertexHelper checks } } } @@ -185,8 +195,8 @@ BOOST_AUTO_TEST_CASE(CylinderSurfacePolyhedrons) { CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).min(), -hZ, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), hZ, epsAbs); - const unsigned int expectedFaces = segments < 4 ? 4 : segments; - const unsigned int expectedVertices = segments < 4 ? 8 : 2 * segments; + const unsigned int expectedFaces = 4 * segments; + const unsigned int expectedVertices = (4 * segments + 1) * 2; BOOST_CHECK_EQUAL(fullCylinderPh.faces.size(), expectedFaces); BOOST_CHECK_EQUAL(fullCylinderPh.vertices.size(), expectedVertices); } @@ -248,7 +258,8 @@ BOOST_AUTO_TEST_CASE(DiscSurfacePolyhedrons) { CHECK_CLOSE_ABS(extent.range(BinningValue::binZ).max(), 0., epsAbs); const unsigned int expectedFaces = 1; - const unsigned int expectedVertices = segments > 4 ? segments + 1 : 4 + 1; + // Segments + overlap + center + const unsigned int expectedVertices = 4 * segments + 1 + 1; BOOST_CHECK_EQUAL(fullDiscPh.faces.size(), expectedFaces); BOOST_CHECK_EQUAL(fullDiscPh.vertices.size(), expectedVertices); } @@ -353,14 +364,7 @@ BOOST_AUTO_TEST_CASE(DiscSurfacePolyhedrons) { auto annulusDisc = Surface::makeShared(transform, annulus); auto annulusDiscPh = annulusDisc->polyhedronRepresentation(tgContext, segments); - const auto extent = annulusDiscPh.extent(); - // CHECK_CLOSE_ABS(extent.range(BinningValue::binX).min(), ???, - // epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binX).max(), - // ???, epsAbs); - // CHECK_CLOSE_ABS(extent.range(BinningValue::binY).min(), ???, - // epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binY).max(), - // ???, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binR).min(), minRadius, epsAbs); CHECK_CLOSE_ABS(extent.range(BinningValue::binR).max(), maxRadius, diff --git a/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp b/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp index 573c9f55ce9..ce745bec85c 100644 --- a/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp +++ b/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp @@ -102,7 +102,7 @@ class SurfaceStub : public RegularSurface { /// Return a Polyhedron for the surfaces Polyhedron polyhedronRepresentation(const GeometryContext& /*gctx*/, - std::size_t /*lseg */) const final { + unsigned int /* ignored */) const final { std::vector vertices; std::vector> faces; std::vector> triangularMesh; diff --git a/Tests/UnitTests/Core/Surfaces/VerticesHelperTests.cpp b/Tests/UnitTests/Core/Surfaces/VerticesHelperTests.cpp index d2838760081..2b52064cb8d 100644 --- a/Tests/UnitTests/Core/Surfaces/VerticesHelperTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/VerticesHelperTests.cpp @@ -92,6 +92,127 @@ BOOST_AUTO_TEST_CASE(VerticesHelperOnHyperPlane) { } } +BOOST_AUTO_TEST_CASE(GeneratePhiSegments) { + // Case (1): a small segment is given, no cartesian maximum vertex + ActsScalar minPhi = 0.1; + ActsScalar maxPhi = 0.3; + + auto phis = VerticesHelper::phiSegments(minPhi, maxPhi); + BOOST_CHECK_EQUAL(phis.size(), 2u); + BOOST_CHECK(phis[0] == minPhi); + BOOST_CHECK(phis[1] == maxPhi); + + // Case (2) a small segment is given, with one maximum vertex at phi = 0 + minPhi = -0.1; + phis = VerticesHelper::phiSegments(minPhi, maxPhi); + BOOST_CHECK_EQUAL(phis.size(), 3u); + BOOST_CHECK(phis[0] == minPhi); + BOOST_CHECK(phis[1] == 0.); + BOOST_CHECK(phis[2] == maxPhi); + + // Case (3) a small segment is given, with one maximum vertex at phi = 2pi, + // and one extra value + phis = VerticesHelper::phiSegments(minPhi, maxPhi, {0.25}); + BOOST_CHECK_EQUAL(phis.size(), 4u); + BOOST_CHECK(phis[0] == minPhi); + BOOST_CHECK(phis[1] == 0.); + BOOST_CHECK(phis[2] == 0.25); + BOOST_CHECK(phis[3] == maxPhi); + + // Case (4) a small segment is given, with one maximum vertex at phi = 2pi, + // and two extra values, one outside & hence throw an exception + BOOST_CHECK_THROW(VerticesHelper::phiSegments(minPhi, maxPhi, {0.25, 0.5}), + std::invalid_argument); + + // Case (5) an invalid phi range is given + BOOST_CHECK_THROW(VerticesHelper::phiSegments(0.8, 0.2, {0.25, 0.5}), + std::invalid_argument); + + // Case (6) a wrong number of minimum segments is given + BOOST_CHECK_THROW(VerticesHelper::phiSegments(0.1, 0.3, {0.25, 0.5}, 3), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(GenerateSegmentVertices) { + // Case (1): a small segment is given, no cartesian maximum vertex & 1 step + // segment + ActsScalar rx = 10.; + ActsScalar ry = 10.; + ActsScalar minPhi = 0.1; + ActsScalar maxPhi = 0.3; + + auto vertices = VerticesHelper::segmentVertices( + {rx, ry}, minPhi, maxPhi, {}, 1); + std::size_t expectedVertices = 2u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Now with a reference phi value + vertices = VerticesHelper::segmentVertices( + {rx, ry}, minPhi, maxPhi, {0.2}, 1); + expectedVertices = 3u; // the reference is inserted + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Now with more vertices - the the two corners and the ones from the + // reference + unsigned int quarterVertices = 36; + vertices = VerticesHelper::segmentVertices( + {rx, ry}, minPhi, maxPhi, {}, quarterVertices); + expectedVertices = + static_cast((maxPhi - minPhi) / M_PI_2 * quarterVertices) + + 2u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Case (2) a small segment is given, with one maximum vertex at phi = 0 + minPhi = -0.1; + vertices = VerticesHelper::segmentVertices( + {rx, ry}, minPhi, maxPhi, {}, 1); + expectedVertices = 3u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Same with more segments + quarterVertices = 12; + vertices = VerticesHelper::segmentVertices( + {rx, ry}, minPhi, maxPhi, {}, quarterVertices); + // Extrema will be covered by the segments + expectedVertices = + static_cast((maxPhi - minPhi) / M_PI_2 * quarterVertices) + + 2u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); +} + +BOOST_AUTO_TEST_CASE(GenerateCircleEllipseVertices) { + // Case (1): A full disc + ActsScalar ri = 0.; + ActsScalar ro = 10.; + + // Extreme points in phi - only outer radius + auto vertices = VerticesHelper::circularVertices(ri, ro, 0., M_PI, 1u); + unsigned int expectedVertices = 5u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Case (2): A ring + ri = 3.; + + // Extreme points in phi - only outer radius + vertices = VerticesHelper::circularVertices(ri, ro, 0., M_PI, 1u); + expectedVertices = 10u; + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Now with 10 bins per sector + ri = 0.; + + vertices = VerticesHelper::circularVertices(ri, ro, 0., M_PI, 10u); + expectedVertices = 41u; // 4 sectors + 1 overlap at (-pi/pi) + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); + + // Now ellipsiod + ActsScalar riy = 4.; + ActsScalar roy = 14.; + vertices = VerticesHelper::ellipsoidVertices(ri, riy, ro, roy, 0., M_PI, 10u); + expectedVertices = 41u; // 4 sectors + 1 overlap at (-pi/pi) + BOOST_CHECK_EQUAL(vertices.size(), expectedVertices); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::detail::Test diff --git a/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp b/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp index 7ef5373f41b..bce69cba0b2 100644 --- a/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp @@ -1054,7 +1054,7 @@ BOOST_AUTO_TEST_CASE(Material) { ViewConfig viewContainer = {.color = {220, 220, 0}}; viewContainer.triangulate = triangulate; ViewConfig viewGrid = {.color = {220, 0, 0}}; - viewGrid.nSegments = 8; + viewGrid.quarterSegments = 8; viewGrid.offset = 3.; viewGrid.triangulate = triangulate; diff --git a/Tests/UnitTests/Core/Utilities/CMakeLists.txt b/Tests/UnitTests/Core/Utilities/CMakeLists.txt index 68b4ba5a655..2c00476c116 100644 --- a/Tests/UnitTests/Core/Utilities/CMakeLists.txt +++ b/Tests/UnitTests/Core/Utilities/CMakeLists.txt @@ -35,6 +35,7 @@ add_unittest(Result ResultTests.cpp) add_unittest(TypeList TypeListTests.cpp) add_unittest(UnitVectors UnitVectorsTests.cpp) add_unittest(Delegate DelegateTests.cpp) +add_unittest(DelegateChainBuilder DelegateChainBuilderTests.cpp) add_unittest(HashedString HashedStringTests.cpp) if(ACTS_BUILD_CUDA_FEATURES) add_unittest(Cuda CudaTests.cu) @@ -54,7 +55,7 @@ target_compile_definitions( add_unittest(ParticleData ParticleDataTests.cpp) add_unittest(Zip ZipTests.cpp) add_unittest(TransformRange TransformRangeTests.cpp) -add_unittest(QuickMath QuickMathTests.cpp) +add_unittest(MathHelpers MathHelpersTests.cpp) add_unittest(TrackHelpers TrackHelpersTests.cpp) add_unittest(GraphViz GraphVizTests.cpp) diff --git a/Tests/UnitTests/Core/Utilities/DelegateChainBuilderTests.cpp b/Tests/UnitTests/Core/Utilities/DelegateChainBuilderTests.cpp new file mode 100644 index 00000000000..783eb9a85e2 --- /dev/null +++ b/Tests/UnitTests/Core/Utilities/DelegateChainBuilderTests.cpp @@ -0,0 +1,118 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include +#include + +#include "Acts/Utilities/DelegateChainBuilder.hpp" + +using namespace Acts; + +struct AddTo { + int value = 0; + + void add(int &x) const { x += value; } +}; + +void addFive(int &x) { + x += 5; +} + +BOOST_AUTO_TEST_SUITE(DelegateChainBuilderTests) + +BOOST_AUTO_TEST_CASE(DelegateChainBuilderAdd) { + AddTo a1{1}, a2{2}, a3{3}; + int x = 0; + + // Basic building + OwningDelegate chain = DelegateChainBuilder{} + .add<&AddTo::add>(&a1) + .add<&addFive>() + .add<&AddTo::add>(&a2) + .add<&AddTo::add>(&a3) + .build(); + chain(x); + BOOST_CHECK_EQUAL(x, 11); + + chain.disconnect(); + + // In case of no return types, we can rebind the owning delegate with a chain + // of different size + chain = DelegateChainBuilder{} + .add<&AddTo::add>(&a1) + .add<&addFive>() + .add<&AddTo::add>(&a3) + .build(); + + x = 0; + + chain(x); + BOOST_CHECK_EQUAL(x, 9); + + // CTAD helper from delegate type + chain = DelegateChainBuilder{chain} + .add<&AddTo::add>(&a1) + .add<&addFive>() + .add<&AddTo::add>(&a3) + .build(); + + x = 0; + + chain(x); + BOOST_CHECK_EQUAL(x, 9); + + Delegate nonOwning; + + // In case of a single callable, we can store it in a non-owning delegate + DelegateChainBuilder{}.add<&AddTo::add>(&a1).store(nonOwning); + + x = 0; + nonOwning(x); + BOOST_CHECK_EQUAL(x, 1); +} + +struct GetInt { + int value; + + int get() const { return value; } +}; + +int getSix() { + return 6; +} + +BOOST_AUTO_TEST_CASE(DelegateChainBuilderReturn) { + GetInt g1{1}, g2{2}, g3{3}; + + Delegate(), void, DelegateType::Owning> chain = + DelegateChainBuilder{} + .add<&GetInt::get>(&g1) + .add<&getSix>() + .add<&GetInt::get>(&g2) + .add<&GetInt::get>(&g3) + .build(); + + auto results = chain(); + std::vector expected = {1, 6, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(results.begin(), results.end(), + expected.begin(), expected.end()); + + Delegate(), void, DelegateType::Owning> delegate; + DelegateChainBuilder{} + .add<&GetInt::get>(&g1) + .add<&getSix>() + .add<&GetInt::get>(&g3) + .store(delegate); + + auto results2 = delegate(); + expected = {1, 6, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(results2.begin(), results2.end(), + expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Utilities/MathHelpersTests.cpp b/Tests/UnitTests/Core/Utilities/MathHelpersTests.cpp new file mode 100644 index 00000000000..32c19ed91c7 --- /dev/null +++ b/Tests/UnitTests/Core/Utilities/MathHelpersTests.cpp @@ -0,0 +1,44 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include +#include + +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Utilities/MathHelpers.hpp" + +#include + +namespace bdata = boost::unit_test::data; + +const auto expDist = bdata::random( + (bdata::engine = std::mt19937{}, bdata::seed = 0, + bdata::distribution = std::uniform_real_distribution(-4, 4))); + +BOOST_AUTO_TEST_SUITE(Utilities) + +BOOST_DATA_TEST_CASE(fastHypot, expDist ^ expDist ^ bdata::xrange(100), xExp, + yExp, i) { + (void)i; + + const double x = std::pow(10, xExp); + const double y = std::pow(10, yExp); + + const float fastFloat = + Acts::fastHypot(static_cast(x), static_cast(y)); + const double fastDouble = Acts::fastHypot(x, y); + + const float stdFloat = + std::hypot(static_cast(x), static_cast(y)); + const double stdDouble = std::hypot(x, y); + + CHECK_CLOSE_REL(stdFloat, fastFloat, 1e-6); + CHECK_CLOSE_REL(stdDouble, fastDouble, 1e-6); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Utilities/QuickMathTests.cpp b/Tests/UnitTests/Core/Utilities/QuickMathTests.cpp deleted file mode 100644 index 6952ea78b14..00000000000 --- a/Tests/UnitTests/Core/Utilities/QuickMathTests.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. - -#include -#include - -#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" -#include "Acts/Utilities/QuickMath.hpp" - -namespace bdata = boost::unit_test::data; - -const auto expDist = bdata::random( - (bdata::engine = std::mt19937{}, bdata::seed = 0, - bdata::distribution = std::uniform_real_distribution(-4, 4))); - -BOOST_AUTO_TEST_SUITE(Utilities) - -BOOST_DATA_TEST_CASE(fastInverseSqrt, expDist ^ bdata::xrange(100), exp, i) { - (void)i; - - const double x = std::pow(10, exp); - - const float fastFloat = Acts::fastInverseSqrt(static_cast(x)); - const double fastDouble = Acts::fastInverseSqrt(x); - - const double stdFloat = 1.0 / std::sqrt(static_cast(x)); - const double stdDouble = 1.0 / std::sqrt(x); - - CHECK_CLOSE_REL(stdFloat, fastFloat, 0.01); - CHECK_CLOSE_REL(stdDouble, fastDouble, 0.01); -} - -BOOST_DATA_TEST_CASE(fastPow, expDist ^ expDist ^ bdata::xrange(100), baseExp, - exp, i) { - (void)i; - - const double base = std::pow(10, baseExp); - - const double fast = Acts::fastPow(base, exp); - const double fastMorePrecise = Acts::fastPowMorePrecise(base, exp); - - const double std = std::pow(base, exp); - - CHECK_CLOSE_REL(fast, std, 0.15); - CHECK_CLOSE_REL(fastMorePrecise, std, 0.1); -} - -// BOOST_AUTO_TEST_CASE(fastPowChart) { -// std::cout << "a ref obs" << std::endl; -// for (double aExp = -4; aExp <= 4; aExp += 0.01) { -// double a = std::pow(10, aExp); -// double ref = std::pow(a, 0.25); -// double obs = Acts::fastPow(a, 0.25); - -// std::cout << a << " " << ref << " " << obs << std::endl; -// } -// } - -BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Visualization/EventDataView3DTests.cpp b/Tests/UnitTests/Core/Visualization/EventDataView3DTests.cpp index 36f823a02b6..bac538e4b58 100644 --- a/Tests/UnitTests/Core/Visualization/EventDataView3DTests.cpp +++ b/Tests/UnitTests/Core/Visualization/EventDataView3DTests.cpp @@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(BoundTrackParametersVisualizationObj) { for (const auto& objerr : objErrors) { std::cout << objerr << std::endl; } - BOOST_CHECK_EQUAL(std::count(objTest.begin(), objTest.end(), '\n'), 1458); + BOOST_CHECK_EQUAL(std::count(objTest.begin(), objTest.end(), '\n'), 4924); } BOOST_AUTO_TEST_CASE(BoundTrackParametersVisualizationPly) { @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(BoundTrackParametersVisualizationPly) { for (const auto& plyerr : plyErrors) { std::cout << plyerr << std::endl; } - BOOST_CHECK_EQUAL(std::count(plyTest.begin(), plyTest.end(), '\n'), 973); + BOOST_CHECK_EQUAL(std::count(plyTest.begin(), plyTest.end(), '\n'), 3143); } BOOST_AUTO_TEST_CASE(MeasurementVisualizationObj) { @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(MultiTrajectoryVisualizationObj) { for (const auto& objerr : objErrors) { std::cout << objerr << std::endl; } - BOOST_CHECK_EQUAL(std::count(objTest.begin(), objTest.end(), '\n'), 31010); + BOOST_CHECK_EQUAL(std::count(objTest.begin(), objTest.end(), '\n'), 103796); } BOOST_AUTO_TEST_CASE(MultiTrajectoryVisualizationPly) { @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(MultiTrajectoryVisualizationPly) { for (const auto& plyerr : plyErrors) { std::cout << plyerr << std::endl; } - BOOST_CHECK_EQUAL(std::count(plyTest.begin(), plyTest.end(), '\n'), 20521); + BOOST_CHECK_EQUAL(std::count(plyTest.begin(), plyTest.end(), '\n'), 66091); } BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Visualization/SurfaceView3DBase.hpp b/Tests/UnitTests/Core/Visualization/SurfaceView3DBase.hpp index be83a27cb7b..146935279f0 100644 --- a/Tests/UnitTests/Core/Visualization/SurfaceView3DBase.hpp +++ b/Tests/UnitTests/Core/Visualization/SurfaceView3DBase.hpp @@ -68,7 +68,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, coneSurfaces.push_back(cone); GeometryView3D::drawSurface(helper, *cone, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_ConeSurface") + tag); helper.write(cStream); helper.clear(); @@ -80,7 +80,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, coneSurfaces.push_back(cone); GeometryView3D::drawSurface(helper, *cone, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_ConeSurfaceSector") + tag); helper.write(cStream); helper.clear(); @@ -92,7 +92,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, coneSurfaces.push_back(cone); GeometryView3D::drawSurface(helper, *cone, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_ConeSurfaceSectorShifted") + tag); helper.write(cStream); helper.clear(); @@ -126,7 +126,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, cylinderSurfaces.push_back(cylinder); GeometryView3D::drawSurface(helper, *cylinder, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_CylinderSurface") + tag); helper.write(cStream); helper.clear(); @@ -138,7 +138,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, cylinderSurfaces.push_back(cylinder); GeometryView3D::drawSurface(helper, *cylinder, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_CylinderSurfaceSector") + tag); helper.write(cStream); helper.clear(); @@ -150,7 +150,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, cylinderSurfaces.push_back(cylinder); GeometryView3D::drawSurface(helper, *cylinder, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_CylinderSurfaceSectorShifted") + tag); helper.write(cStream); helper.clear(); @@ -179,7 +179,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, auto bbSurface = Surface::makeShared(identity, bbBounds); GeometryView3D::drawSurface(helper, *bbSurface, gctx, Transform3::Identity(), sConfig); - ; + helper.write(bbPath); helper.write(cStream); helper.clear(); @@ -199,7 +199,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, radialSurfaces.push_back(disc); GeometryView3D::drawSurface(helper, *disc, gctx, Transform3::Identity(), sConfig); - ; + helper.write(std::string("Surfaces_DiscSurfaceFull") + tag); helper.write(cStream); helper.clear(); @@ -335,7 +335,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, planarSurfaces.push_back(plane); GeometryView3D::drawSurface(helper, *plane, gctx, Transform3::Identity(), sConfig); - ; + helper.write(name + tag); helper.write(cStream); helper.clear(); @@ -349,7 +349,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, planarSurfaces.push_back(plane); GeometryView3D::drawSurface(helper, *plane, gctx, Transform3::Identity(), sConfig); - ; + helper.write(name + tag); helper.write(cStream); helper.clear(); @@ -363,7 +363,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, planarSurfaces.push_back(plane); GeometryView3D::drawSurface(helper, *plane, gctx, Transform3::Identity(), sConfig); - ; + helper.write(name + tag); helper.write(cStream); helper.clear(); @@ -377,7 +377,7 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, planarSurfaces.push_back(plane); GeometryView3D::drawSurface(helper, *plane, gctx, Transform3::Identity(), sConfig); - ; + helper.write(name + tag); helper.write(cStream); helper.clear(); diff --git a/Tests/UnitTests/Core/Visualization/TrackingGeometryView3DBase.hpp b/Tests/UnitTests/Core/Visualization/TrackingGeometryView3DBase.hpp index b2fb89c0ee8..ea2e409222d 100644 --- a/Tests/UnitTests/Core/Visualization/TrackingGeometryView3DBase.hpp +++ b/Tests/UnitTests/Core/Visualization/TrackingGeometryView3DBase.hpp @@ -37,18 +37,22 @@ static inline std::string run(IVisualization3D& helper, bool triangulate, const std::string& tag) { std::stringstream cStream; - ViewConfig viewSensitive = {.color = {0, 180, 240}}; - viewSensitive.triangulate = triangulate; - ViewConfig viewPassive = {.color = {240, 280, 0}}; - viewPassive.triangulate = triangulate; - ViewConfig viewVolume = {.color = {220, 220, 0}}; - viewVolume.triangulate = triangulate; - ViewConfig viewContainer = {.color = {220, 220, 0}}; - viewContainer.triangulate = triangulate; - ViewConfig viewGrid = {.color = {220, 0, 0}}; - viewGrid.nSegments = 8; - viewGrid.offset = 3.; - viewGrid.triangulate = triangulate; + ViewConfig viewSensitive = {.color = {0, 180, 240}, + .quarterSegments = 72, + .triangulate = triangulate}; + ViewConfig viewPassive = {.color = {240, 280, 0}, + .quarterSegments = 72, + .triangulate = triangulate}; + ViewConfig viewVolume = {.color = {220, 220, 0}, + .quarterSegments = 72, + .triangulate = triangulate}; + ViewConfig viewContainer = {.color = {220, 220, 0}, + .quarterSegments = 72, + .triangulate = triangulate}; + ViewConfig viewGrid = {.color = {220, 0, 0}, + .offset = 3., + .quarterSegments = 8, + .triangulate = triangulate}; const Acts::TrackingVolume& tgVolume = *(tGeometry->highestTrackingVolume()); diff --git a/Tests/UnitTests/Core/Visualization/Visualization3DTests.cpp b/Tests/UnitTests/Core/Visualization/Visualization3DTests.cpp index 58958bbbe6c..ea3e810d119 100644 --- a/Tests/UnitTests/Core/Visualization/Visualization3DTests.cpp +++ b/Tests/UnitTests/Core/Visualization/Visualization3DTests.cpp @@ -335,6 +335,8 @@ BOOST_AUTO_TEST_CASE(ColorTests) { BOOST_CHECK_EQUAL(grey, Color(std::array{128 / 255.0, 128 / 255.0, 128 / 255.0})); BOOST_CHECK_EQUAL(grey, Color(128 / 255.0, 128 / 255.0, 128 / 255.0)); + + static_assert(Color{"#0000ff"} == Color(0, 0, 255)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp index fc3d33d801f..d59ff4a135e 100644 --- a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp +++ b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp @@ -11,9 +11,8 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/detail/GenerateParameters.hpp" -#include "Acts/EventData/detail/TestSourceLink.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "ActsExamples/EventData/Measurement.hpp" @@ -30,15 +29,13 @@ using namespace Acts; using namespace Acts::detail::Test; using namespace ActsExamples; -using SourceLink = Acts::detail::Test::TestSourceLink; namespace bd = boost::unit_test::data; namespace { constexpr BoundIndices boundIndices[] = { eBoundLoc0, eBoundLoc1, eBoundTime, eBoundPhi, eBoundTheta, eBoundQOverP, }; -const TestSourceLink sourceOrig; -const Acts::SourceLink source{sourceOrig}; +constexpr Acts::GeometryIdentifier geoId = 1; // fix seed for reproducible tests std::default_random_engine rng(123); } // namespace @@ -54,8 +51,7 @@ BOOST_DATA_TEST_CASE(VariableBoundOne, bd::make(boundIndices), index) { auto [params, cov] = generateParametersCovariance(rng); - FixedBoundMeasurementProxy<1> meas = container.makeMeasurement<1>(); - meas.setSourceLink(source); + FixedBoundMeasurementProxy<1> meas = container.makeMeasurement<1>(geoId); meas.setSubspaceIndices(std::array{index}); meas.parameters() = params; meas.covariance() = cov; @@ -66,8 +62,7 @@ BOOST_DATA_TEST_CASE(VariableBoundOne, bd::make(boundIndices), index) { } BOOST_CHECK_EQUAL(meas.parameters(), params); BOOST_CHECK_EQUAL(meas.covariance(), cov); - BOOST_CHECK_EQUAL(meas.sourceLink().template get(), - sourceOrig); + BOOST_CHECK_EQUAL(meas.geometryId(), geoId); } BOOST_DATA_TEST_CASE(VariableBoundOneEmplace, bd::make(boundIndices), index) { @@ -76,7 +71,7 @@ BOOST_DATA_TEST_CASE(VariableBoundOneEmplace, bd::make(boundIndices), index) { auto [params, cov] = generateParametersCovariance(rng); FixedBoundMeasurementProxy<1> meas = - container.emplaceMeasurement<1>(source, std::array{index}, params, cov); + container.emplaceMeasurement<1>(geoId, std::array{index}, params, cov); BOOST_CHECK_EQUAL(meas.size(), 1); for (auto i : boundIndices) { @@ -84,8 +79,7 @@ BOOST_DATA_TEST_CASE(VariableBoundOneEmplace, bd::make(boundIndices), index) { } BOOST_CHECK_EQUAL(meas.parameters(), params); BOOST_CHECK_EQUAL(meas.covariance(), cov); - BOOST_CHECK_EQUAL(meas.sourceLink().template get(), - sourceOrig); + BOOST_CHECK_EQUAL(meas.geometryId(), geoId); } BOOST_AUTO_TEST_CASE(VariableBoundAll) { @@ -94,8 +88,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundAll) { auto [params, cov] = generateBoundParametersCovariance(rng); FixedBoundMeasurementProxy meas = - container.makeMeasurement(); - meas.setSourceLink(source); + container.makeMeasurement(geoId); meas.setSubspaceIndices(std::array{eBoundLoc0, eBoundLoc1, eBoundTime, eBoundPhi, eBoundTheta, eBoundQOverP}); meas.parameters() = params; @@ -107,7 +100,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundAll) { } BOOST_CHECK_EQUAL(meas.parameters(), params); BOOST_CHECK_EQUAL(meas.covariance(), cov); - BOOST_CHECK_EQUAL(meas.sourceLink().get(), sourceOrig); + BOOST_CHECK_EQUAL(meas.geometryId(), geoId); } BOOST_AUTO_TEST_CASE(VariableBoundAllEmplace) { @@ -117,7 +110,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundAllEmplace) { FixedBoundMeasurementProxy meas = container.emplaceMeasurement( - source, + geoId, std::array{eBoundLoc0, eBoundLoc1, eBoundTime, eBoundPhi, eBoundTheta, eBoundQOverP}, params, cov); @@ -128,7 +121,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundAllEmplace) { } BOOST_CHECK_EQUAL(meas.parameters(), params); BOOST_CHECK_EQUAL(meas.covariance(), cov); - BOOST_CHECK_EQUAL(meas.sourceLink().get(), sourceOrig); + BOOST_CHECK_EQUAL(meas.geometryId(), geoId); } BOOST_AUTO_TEST_CASE(VariableBoundReassign) { @@ -137,8 +130,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundReassign) { // generate w/ two parameter auto [params1, cov1] = generateParametersCovariance(rng); - VariableBoundMeasurementProxy meas = container.makeMeasurement(2); - meas.setSourceLink(source); + VariableBoundMeasurementProxy meas = container.makeMeasurement(2, geoId); meas.setSubspaceIndices(std::array{eBoundPhi, eBoundTheta}); meas.parameters() = params1; meas.covariance() = cov1; @@ -154,8 +146,7 @@ BOOST_AUTO_TEST_CASE(VariableBoundReassign) { // reassign w/ all parameters auto [paramsN, covN] = generateBoundParametersCovariance(rng); - meas = container.makeMeasurement(eBoundSize); - meas.setSourceLink(source); + meas = container.makeMeasurement(eBoundSize, geoId); meas.setSubspaceIndices(std::array{eBoundLoc0, eBoundLoc1, eBoundTime, eBoundPhi, eBoundTheta, eBoundQOverP}); meas.parameters() = paramsN; diff --git a/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp b/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp index 1a3d9662a2a..c9c21b0983a 100644 --- a/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp +++ b/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp @@ -20,6 +20,7 @@ #include "ActsExamples/Io/Csv/CsvMeasurementReader.hpp" #include "ActsExamples/Io/Csv/CsvMeasurementWriter.hpp" +#include #include #include #include @@ -28,7 +29,6 @@ using namespace ActsExamples; using namespace Acts::Test; BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { - IndexSourceLinkContainer sourceLinksOriginal; MeasurementContainer measOriginal; ClusterContainer clusterOriginal; IndexMultimap mapOriginal; @@ -44,14 +44,11 @@ BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { std::uniform_real_distribution distf(0.0, 1.0); for (auto i = 0ul; i < nMeasurements; ++i) { - IndexSourceLink sl(someGeoId, static_cast(i)); - sourceLinksOriginal.insert(sl); - Acts::Vector2 p = Acts::Vector2::Random(); Acts::SquareMatrix2 c = Acts::SquareMatrix2::Random(); - FixedBoundMeasurementProxy<2> m = measOriginal.makeMeasurement<2>(); - m.setSourceLink(Acts::SourceLink(sl)); + FixedBoundMeasurementProxy<2> m = + measOriginal.makeMeasurement<2>(someGeoId); m.setSubspaceIndices(std::array{Acts::eBoundLoc0, Acts::eBoundLoc1}); m.parameters() = p; m.covariance() = c; @@ -117,14 +114,12 @@ BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { readerConfig.outputMeasurementSimHitsMap = writerConfig.inputMeasurementSimHitsMap; readerConfig.outputClusters = writerConfig.inputClusters; - readerConfig.outputSourceLinks = "sourcelinks"; CsvMeasurementReader reader(readerConfig, Acts::Logging::WARNING); - auto readTool = - writeTool.add(readerConfig.outputSourceLinks, sourceLinksOriginal); + auto readTool = writeTool.add(readerConfig.outputMeasurements, measOriginal); - const auto [measRead, clusterRead, mapRead, sourceLinksRead] = + const auto [measRead, clusterRead, mapRead, measRead2] = readTool.read(reader); /////////// @@ -152,7 +147,7 @@ BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { std::abs(ca.activation - cb.activation) < 1.e-4; }; - BOOST_CHECK(std::any_of(b.channels.begin(), b.channels.end(), match)); + BOOST_CHECK(std::ranges::any_of(b.channels, match)); } } @@ -163,10 +158,10 @@ BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { BOOST_REQUIRE(a == b); } - static_assert(std::is_same_v, - decltype(sourceLinksOriginal)>); - BOOST_REQUIRE(sourceLinksRead.size() == sourceLinksOriginal.size()); - for (const auto &[a, b] : Acts::zip(sourceLinksRead, sourceLinksOriginal)) { + static_assert( + std::is_same_v, decltype(measOriginal)>); + BOOST_REQUIRE(measRead.size() == measOriginal.size()); + for (const auto &[a, b] : Acts::zip(measRead, measOriginal)) { BOOST_REQUIRE(a.geometryId() == b.geometryId()); BOOST_REQUIRE(a.index() == b.index()); } diff --git a/Tests/UnitTests/Fatras/Digitization/SegmentizerTests.cpp b/Tests/UnitTests/Fatras/Digitization/SegmentizerTests.cpp index 844122027d2..ad74a9024d1 100644 --- a/Tests/UnitTests/Fatras/Digitization/SegmentizerTests.cpp +++ b/Tests/UnitTests/Fatras/Digitization/SegmentizerTests.cpp @@ -217,9 +217,9 @@ BOOST_DATA_TEST_CASE( ".csv"); /// Run the Segmentizer - auto cSegement = cl.segments(geoCtx, *surface, segmentation, {start, end}); + auto cSegments = cl.segments(geoCtx, *surface, segmentation, {start, end}); - for (const auto& cs : cSegement) { + for (const auto& cs : cSegments) { csvHelper.writeLine(segments, cs.path2D[0], cs.path2D[1]); } diff --git a/Tests/UnitTests/Fatras/Kernel/SimulationActorTests.cpp b/Tests/UnitTests/Fatras/Kernel/SimulationActorTests.cpp index de87bf883c1..3306c5702d6 100644 --- a/Tests/UnitTests/Fatras/Kernel/SimulationActorTests.cpp +++ b/Tests/UnitTests/Fatras/Kernel/SimulationActorTests.cpp @@ -12,6 +12,7 @@ #include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" @@ -148,6 +149,11 @@ struct MockNavigator { return state.currentSurface; } + const Acts::TrackingVolume *currentVolume( + const MockNavigatorState & /*state*/) const { + return nullptr; + } + bool endOfWorldReached(const MockNavigatorState & /*state*/) const { return false; } @@ -158,6 +164,10 @@ struct MockPropagatorState { MockStepperState stepping; Acts::GeometryContext geoContext; Acts::PropagatorStage stage = Acts::PropagatorStage::invalid; + + struct { + std::vector constrainToVolumeIds; + } options; }; template @@ -234,7 +244,13 @@ BOOST_AUTO_TEST_CASE(HitsOnEmptySurface) { BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p); BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e); + // call.actor: pre propagation + f.state.stage = Acts::PropagatorStage::prePropagation; + f.actor.act(f.state, f.stepper, f.navigator, f.result, + Acts::getDummyLogger()); + // call.actor: surface selection -> one hit, no material -> no secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -261,6 +277,7 @@ BOOST_AUTO_TEST_CASE(HitsOnEmptySurface) { BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum()); // call.actor again: one more hit, still no secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -307,7 +324,13 @@ BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) { BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p); BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e); + // call.actor: pre propagation + f.state.stage = Acts::PropagatorStage::prePropagation; + f.actor.act(f.state, f.stepper, f.navigator, f.result, + Acts::getDummyLogger()); + // call.actor: surface selection -> one hit, material -> one secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -336,6 +359,7 @@ BOOST_AUTO_TEST_CASE(HitsOnMaterialSurface) { tol); // call.actor again: one more hit, one more secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -382,7 +406,13 @@ BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) { BOOST_CHECK_EQUAL(f.actor.initialParticle.absoluteMomentum(), f.p); BOOST_CHECK_EQUAL(f.actor.initialParticle.energy(), f.e); + // call.actor: pre propagation + f.state.stage = Acts::PropagatorStage::prePropagation; + f.actor.act(f.state, f.stepper, f.navigator, f.result, + Acts::getDummyLogger()); + // call.actor: no surface sel. -> no hit, no material -> no secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -409,6 +439,7 @@ BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) { BOOST_CHECK_EQUAL(f.state.stepping.p, f.result.particle.absoluteMomentum()); // call.actor again: no hit, still no secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -445,7 +476,13 @@ BOOST_AUTO_TEST_CASE(NoHitsEmptySurface) { BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) { Fixture f(125_MeV, makeMaterialSurface()); + // call.actor: pre propagation + f.state.stage = Acts::PropagatorStage::prePropagation; + f.actor.act(f.state, f.stepper, f.navigator, f.result, + Acts::getDummyLogger()); + // call.actor: no surface sel. -> no hit, material -> one secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -473,6 +510,7 @@ BOOST_AUTO_TEST_CASE(NoHitsMaterialSurface) { tol); // call.actor again: still no hit, one more secondary + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -513,7 +551,13 @@ BOOST_AUTO_TEST_CASE(Decay) { // inverse Lorentz factor for proper time dilation: 1/gamma = m/E const auto gammaInv = f.m / f.e; + // call.actor: pre propagation + f.state.stage = Acts::PropagatorStage::prePropagation; + f.actor.act(f.state, f.stepper, f.navigator, f.result, + Acts::getDummyLogger()); + // first step w/ defaults leaves particle alive + f.state.stage = Acts::PropagatorStage::postStep; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); BOOST_CHECK(f.result.isAlive); @@ -526,6 +570,7 @@ BOOST_AUTO_TEST_CASE(Decay) { BOOST_CHECK_EQUAL(f.result.particle.properTime(), 0_ns); // second step w/ defaults increases proper time + f.state.stage = Acts::PropagatorStage::postStep; f.state.stepping.time += 1_ns; f.actor.act(f.state, f.stepper, f.navigator, f.result, Acts::getDummyLogger()); @@ -539,6 +584,7 @@ BOOST_AUTO_TEST_CASE(Decay) { CHECK_CLOSE_REL(f.result.particle.properTime(), gammaInv * 1_ns, tol); // third step w/ proper time limit decays the particle + f.state.stage = Acts::PropagatorStage::postStep; f.state.stepping.time += 1_ns; f.result.properTimeLimit = f.result.particle.properTime() + gammaInv * 0.5_ns; f.actor.act(f.state, f.stepper, f.navigator, f.result, diff --git a/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp index bc33298431a..6f2c78620e9 100644 --- a/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp @@ -42,7 +42,7 @@ IndexedSurfacesConverter::Options generateDrawOptions() { sensitiveStyle.highlights = {"onmouseover", "onmouseout"}; sensitiveStyle.strokeWidth = 0.5; sensitiveStyle.strokeColor = {0, 0, 0}; - sensitiveStyle.nSegments = 72u; + sensitiveStyle.quarterSegments = 72u; std::pair allSensitives = {GeometryIdentifier(0u), sensitiveStyle}; diff --git a/Tests/UnitTests/Plugins/ActSVG/LayerSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/LayerSvgConverterTests.cpp index 7dd3a93fe95..ff4afc789b6 100644 --- a/Tests/UnitTests/Plugins/ActSVG/LayerSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/LayerSvgConverterTests.cpp @@ -45,16 +45,17 @@ void setupTools() { std::shared_ptr generateDiscLayer(Acts::ActsScalar rInner, Acts::ActsScalar rOuter, - unsigned int nSegments, + unsigned int quarterSegments, unsigned int nRings, bool useTrapezoids = false) { // Some preparations setupTools(); + unsigned int fullSegments = 4 * quarterSegments; std::vector> moduleSurfaces; - Acts::ActsScalar phiStep = 2 * M_PI / nSegments; + Acts::ActsScalar phiStep = 2 * M_PI / fullSegments; Acts::ActsScalar rStep = (rOuter - rInner) / nRings; // Reserve & fill - moduleSurfaces.reserve(nSegments * nRings); + moduleSurfaces.reserve(fullSegments * nRings); // Radial disc if (!useTrapezoids) { for (unsigned int ir = 0; ir < nRings; ++ir) { @@ -62,7 +63,7 @@ std::shared_ptr generateDiscLayer(Acts::ActsScalar rInner, rBounds = std::make_shared( rInner + ir * rStep - 0.025 * rInner, rInner + (ir + 1u) * rStep + 0.025 * rInner, 0.55 * phiStep, 0.); - for (unsigned int is = 0; is < nSegments; ++is) { + for (unsigned int is = 0; is < fullSegments; ++is) { // Place the module auto placement = Acts::Transform3::Identity(); if ((is % 2) != 0u) { @@ -82,14 +83,14 @@ std::shared_ptr generateDiscLayer(Acts::ActsScalar rInner, Acts::ActsScalar yHalf = rStep * 0.5125; Acts::ActsScalar xHalfMin = - 1.15 * (rInner + ir * rStep) * M_PI / nSegments; + 1.15 * (rInner + ir * rStep) * M_PI / fullSegments; Acts::ActsScalar xHalfMax = - 1.15 * (rInner + (ir + 1) * rStep) * M_PI / nSegments; + 1.15 * (rInner + (ir + 1) * rStep) * M_PI / fullSegments; std::shared_ptr tBounds = std::make_shared(xHalfMin, xHalfMax, yHalf); - for (unsigned int is = 0; is < nSegments; ++is) { + for (unsigned int is = 0; is < fullSegments; ++is) { // Setting the phi Acts::ActsScalar cphi = -M_PI + is * phiStep; Acts::Vector3 center(radius * std::cos(cphi), radius * std::sin(cphi), @@ -111,7 +112,7 @@ std::shared_ptr generateDiscLayer(Acts::ActsScalar rInner, } } // Let's create the disc layer - return lCreator->discLayer(tgContext, moduleSurfaces, nRings, nSegments); + return lCreator->discLayer(tgContext, moduleSurfaces, nRings, fullSegments); } } // namespace @@ -125,7 +126,7 @@ BOOST_AUTO_TEST_CASE(DiscLayerRadialSvg) { discLayerStyle.highlights = {"mouseover", "mouseout"}; discLayerStyle.strokeColor = {25, 25, 25}; discLayerStyle.strokeWidth = 0.5; - discLayerStyle.nSegments = 72u; + discLayerStyle.quarterSegments = 72u; Acts::GeometryIdentifier geoID{0}; @@ -155,7 +156,7 @@ BOOST_AUTO_TEST_CASE(DiscLayerTrapezoidSvg) { discLayerStyle.highlights = {"mouseover", "mouseout"}; discLayerStyle.strokeColor = {25, 25, 25}; discLayerStyle.strokeWidth = 0.5; - discLayerStyle.nSegments = 72u; + discLayerStyle.quarterSegments = 72u; Acts::GeometryIdentifier geoID{0}; @@ -185,7 +186,7 @@ BOOST_AUTO_TEST_CASE(CylinderLayerSvg) { cylinderLayerStyle.highlights = {"mouseover", "mouseout"}; cylinderLayerStyle.strokeColor = {25, 25, 25}; cylinderLayerStyle.strokeWidth = 0.5; - cylinderLayerStyle.nSegments = 72u; + cylinderLayerStyle.quarterSegments = 72u; Acts::GeometryIdentifier geoID{0}; diff --git a/Tests/UnitTests/Plugins/ActSVG/PortalSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/PortalSvgConverterTests.cpp index 7f4cfa5d425..9fb1fdffa40 100644 --- a/Tests/UnitTests/Plugins/ActSVG/PortalSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/PortalSvgConverterTests.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(CylinderPortalsSvg) { portalStyle.highlights = {}; portalStyle.strokeColor = {25, 25, 25}; portalStyle.strokeWidth = 0.5; - portalStyle.nSegments = 72u; + portalStyle.quarterSegments = 72u; Acts::ActsScalar rInner = 10.; Acts::ActsScalar rOuter = 100.; @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(CylinderContainerPortalsSvg) { portalStyle.highlights = {}; portalStyle.strokeColor = {25, 25, 25}; portalStyle.strokeWidth = 0.5; - portalStyle.nSegments = 72u; + portalStyle.quarterSegments = 72u; Acts::ActsScalar rInner = 10.; Acts::ActsScalar rMiddle = 100.; diff --git a/Tests/UnitTests/Plugins/ActSVG/SurfaceSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/SurfaceSvgConverterTests.cpp index 49c92f3aaf9..8450453720c 100644 --- a/Tests/UnitTests/Plugins/ActSVG/SurfaceSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/SurfaceSvgConverterTests.cpp @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(PlanarSurfaces) { planarStyle.highlightColor = {255, 153, 51}; planarStyle.highlights = {"mouseover", "mouseout"}; planarStyle.strokeWidth = 0.5; - planarStyle.nSegments = 0u; + planarStyle.quarterSegments = 0u; // Rectangle case auto rectangleBounds = std::make_shared(36., 64.); @@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(DiscSurfaces) { discStyle.highlightColor = {153, 204, 0}; discStyle.highlights = {"mouseover", "mouseout"}; discStyle.strokeWidth = 0.5; - discStyle.nSegments = 72u; + discStyle.quarterSegments = 72u; auto transform = Acts::Transform3::Identity(); transform.pretranslate(Acts::Vector3{20., 20., 100.}); diff --git a/Tests/UnitTests/Plugins/ActSVG/TrackingGeometrySvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/TrackingGeometrySvgConverterTests.cpp index 7c869e90de7..3b7a8856875 100644 --- a/Tests/UnitTests/Plugins/ActSVG/TrackingGeometrySvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/TrackingGeometrySvgConverterTests.cpp @@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(CylindricalTrackingGeometrySvg) { cylinderLayerStyle.highlights = {"mouseover", "mouseout"}; cylinderLayerStyle.strokeColor = {25, 25, 25}; cylinderLayerStyle.strokeWidth = 0.5; - cylinderLayerStyle.nSegments = 72u; + cylinderLayerStyle.quarterSegments = 72u; Acts::GeometryIdentifier geoID{0}; diff --git a/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp b/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp index 97c1c7dff13..2ed99f4a68b 100644 --- a/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp @@ -107,10 +107,11 @@ BOOST_AUTO_TEST_CASE(DetrayVolumeConversion) { auto [volumes, portals, rootVolumes] = beampipe->construct(tContext); auto volume = volumes.front(); - DetrayConversionUtils::GeometryIdCache geoIdCache; + std::vector dVolumes = {volume.get()}; + DetrayConversionUtils::Cache cCache(dVolumes); detray::io::volume_payload payload = DetrayGeometryConverter::convertVolume( - geoIdCache, tContext, *volume, {volume.get()}, *logger); + cCache, tContext, *volume, *logger); // Check the volume payload BOOST_CHECK(payload.name == "BeamPipe"); @@ -134,10 +135,10 @@ BOOST_AUTO_TEST_CASE(CylindricalDetector) { auto detector = buildCylindricalDetector(tContext); // Convert the detector - DetrayConversionUtils::GeometryIdCache geoIdCache; + DetrayConversionUtils::Cache cCache(detector->volumes()); detray::io::detector_payload payload = - DetrayGeometryConverter::convertDetector(geoIdCache, tContext, *detector, + DetrayGeometryConverter::convertDetector(cCache, tContext, *detector, *logger); // Test the payload - we have six volumes diff --git a/Tests/UnitTests/Plugins/GeoModel/CMakeLists.txt b/Tests/UnitTests/Plugins/GeoModel/CMakeLists.txt index 61711992918..a09476dae5b 100644 --- a/Tests/UnitTests/Plugins/GeoModel/CMakeLists.txt +++ b/Tests/UnitTests/Plugins/GeoModel/CMakeLists.txt @@ -4,6 +4,7 @@ add_unittest(GeoModelDetectorElement GeoModelDetectorElementTests.cpp) add_unittest(GeoBoxConverter GeoBoxConverterTests.cpp) add_unittest(GeoTrdConverter GeoTrdConverterTests.cpp) add_unittest(GeoDetectorObjectTest GeoDetectorObjectTest.cpp) +add_unittest(GeoModelDetectorElementITk GeoModelDetectorElementITkTests.cpp) add_unittest(GeoPolyConverterTests GeoPolyConverterTests.cpp) add_unittest(GeoTrdToVolumeTest GeoTrdToVolumeTests.cpp) add_unittest(GeoBoxToVolumeTest GeoBoxToVolumeTest.cpp) diff --git a/Tests/UnitTests/Plugins/GeoModel/GeoModelDetectorElementITkTests.cpp b/Tests/UnitTests/Plugins/GeoModel/GeoModelDetectorElementITkTests.cpp new file mode 100644 index 00000000000..0c945ebb6dd --- /dev/null +++ b/Tests/UnitTests/Plugins/GeoModel/GeoModelDetectorElementITkTests.cpp @@ -0,0 +1,90 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +// This file is part of the Acts project. +// +// Copyright (C) 2024 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 + +#include "Acts/Plugins/GeoModel/GeoModelDetectorElementITk.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(GeoModelPlugin) + +BOOST_AUTO_TEST_CASE(ITkIdentifierTests) { + auto test = [](int hw, int bec, int lw, int em, int pm, int side) { + Acts::ITkIdentifier id(hw, bec, lw, em, pm, side); + BOOST_CHECK_EQUAL(id.hardware(), hw); + BOOST_CHECK_EQUAL(id.barrelEndcap(), bec); + BOOST_CHECK_EQUAL(id.layerWheel(), lw); + BOOST_CHECK_EQUAL(id.etaModule(), em); + BOOST_CHECK_EQUAL(id.phiModule(), pm); + BOOST_CHECK_EQUAL(id.side(), side); + }; + + for (int hw : {0, 1}) { + for (int bec : {-2, 0, 2}) { + for (int lw : {0, 10}) { + for (int em : {-10, 0, 10}) { + for (int pm : {10, 0}) { + for (int side : {0, 1}) { + test(hw, bec, lw, em, pm, side); + } + } + } + } + } + } +} + +BOOST_AUTO_TEST_CASE(GeoModelDetectorElementConstruction) { + Acts::GeometryContext gctx{}; + + auto material = GeoIntrusivePtr(new GeoMaterial("Material", 1.0)); + auto box = GeoIntrusivePtr(new GeoBox(100, 200, 2)); + auto log = GeoIntrusivePtr(new GeoLogVol("LogVolumeXY", box, material)); + auto fphys = GeoIntrusivePtr(new GeoFullPhysVol(log)); + auto rBounds = std::make_shared(100, 200); + + auto element = + Acts::GeoModelDetectorElement::createDetectorElement( + fphys, rBounds, Acts::Transform3::Identity(), 2.0); + + const int hardware = 0, barrelEndcap = -2, layerWheel = 100, phiModule = 200, + etaModule = 300, side = 1; + + auto [itkElement, _] = Acts::GeoModelDetectorElementITk::convertFromGeomodel( + element, element->surface().getSharedPtr(), gctx, hardware, barrelEndcap, + layerWheel, etaModule, phiModule, side); + + BOOST_CHECK_EQUAL(element->surface().type(), itkElement->surface().type()); + BOOST_CHECK_EQUAL(element->surface().bounds().type(), + itkElement->surface().bounds().type()); + BOOST_CHECK_NE(element->surface().associatedDetectorElement(), + itkElement->surface().associatedDetectorElement()); + BOOST_CHECK_EQUAL(itkElement->identifier().barrelEndcap(), barrelEndcap); + BOOST_CHECK_EQUAL(itkElement->identifier().hardware(), hardware); + BOOST_CHECK_EQUAL(itkElement->identifier().layerWheel(), layerWheel); + BOOST_CHECK_EQUAL(itkElement->identifier().phiModule(), phiModule); + BOOST_CHECK_EQUAL(itkElement->identifier().etaModule(), etaModule); + BOOST_CHECK_EQUAL(itkElement->identifier().side(), side); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/Json/CMakeLists.txt b/Tests/UnitTests/Plugins/Json/CMakeLists.txt index 1f8573fdf50..141f86fb3fe 100644 --- a/Tests/UnitTests/Plugins/Json/CMakeLists.txt +++ b/Tests/UnitTests/Plugins/Json/CMakeLists.txt @@ -15,3 +15,4 @@ add_unittest(UtilitiesJsonConverter UtilitiesJsonConverterTests.cpp) add_unittest(SurfaceBoundsJsonConverter SurfaceBoundsJsonConverterTests.cpp) add_unittest(SurfaceJsonConverter SurfaceJsonConverterTests.cpp) add_unittest(VolumeBoundsJsonConverter VolumeBoundsJsonConverterTests.cpp) +add_unittest(TrackParametersJsonConverter TrackParametersJsonConverterTests.cpp) diff --git a/Tests/UnitTests/Plugins/Json/TrackParametersJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/TrackParametersJsonConverterTests.cpp new file mode 100644 index 00000000000..566464a1889 --- /dev/null +++ b/Tests/UnitTests/Plugins/Json/TrackParametersJsonConverterTests.cpp @@ -0,0 +1,97 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Plugins/Json/TrackParametersJsonConverter.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" + +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(TrackParametersJsonIO) + +BOOST_AUTO_TEST_CASE(TrackParametersJsonIO) { + Acts::GeometryContext gctx; + + // Track parameters + Acts::Vector4 position(1., 2., 3., 4.); + Acts::ActsScalar phi = 0.1; + Acts::ActsScalar theta = 0.2; + Acts::ActsScalar qOverP = 3.0; + Acts::ParticleHypothesis particle = Acts::ParticleHypothesis::electron(); + Acts::FreeMatrix freeCov = Acts::FreeMatrix::Identity(); + Acts::BoundMatrix boundCov = Acts::BoundMatrix::Identity(); + + auto surface = Acts::Surface::makeShared( + Acts::Transform3::Identity(), + std::make_shared(10., 10.)); + surface->assignGeometryId(Acts::GeometryIdentifier(1u)); + + // Free track parameters conversion + Acts::FreeTrackParameters ftp(position, phi, theta, qOverP, freeCov, + particle); + + nlohmann::json ftpJson = ftp; + + Acts::FreeTrackParameters ftpRead = ftpJson; + + BOOST_CHECK_EQUAL(ftp.position(), ftpRead.position()); + BOOST_CHECK_EQUAL(ftp.direction(), ftpRead.direction()); + BOOST_CHECK_EQUAL(ftp.qOverP(), ftpRead.qOverP()); + BOOST_CHECK_EQUAL(ftp.covariance().value(), ftpRead.covariance().value()); + BOOST_CHECK_EQUAL(ftp.particleHypothesis(), ftpRead.particleHypothesis()); + + // Curvilinear track parameters conversion + Acts::CurvilinearTrackParameters ctp(position, phi, theta, qOverP, boundCov, + particle); + + nlohmann::json ctpJson = ctp; + + Acts::CurvilinearTrackParameters ctpRead = ctpJson; + + BOOST_CHECK_EQUAL(ctp.position(), ctpRead.position()); + BOOST_CHECK_EQUAL(ctp.direction(), ctpRead.direction()); + BOOST_CHECK_EQUAL(ctp.qOverP(), ctpRead.qOverP()); + BOOST_CHECK_EQUAL(ctp.covariance().value(), ctpRead.covariance().value()); + BOOST_CHECK_EQUAL(ctp.particleHypothesis(), ctpRead.particleHypothesis()); + + BOOST_CHECK(ctp.referenceSurface().transform(gctx).isApprox( + ctpRead.referenceSurface().transform(gctx))); + BOOST_CHECK_EQUAL(ctp.referenceSurface().geometryId(), + ctpRead.referenceSurface().geometryId()); + BOOST_CHECK_EQUAL(ctp.referenceSurface().bounds(), + ctpRead.referenceSurface().bounds()); + + // Bound track parameters conversion + Acts::BoundVector boundPosition{1., 2., 3., 4., 5., 6.}; + Acts::BoundTrackParameters btp(surface, boundPosition, boundCov, particle); + + nlohmann::json btpJson = btp; + + Acts::BoundTrackParameters btpRead = btpJson; + + BOOST_CHECK_EQUAL(btp.position(gctx), btpRead.position(gctx)); + BOOST_CHECK_EQUAL(btp.direction(), btpRead.direction()); + BOOST_CHECK_EQUAL(btp.qOverP(), btpRead.qOverP()); + BOOST_CHECK_EQUAL(btp.covariance().value(), btpRead.covariance().value()); + BOOST_CHECK_EQUAL(btp.particleHypothesis(), btpRead.particleHypothesis()); + + BOOST_CHECK(btp.referenceSurface().transform(gctx).isApprox( + btpRead.referenceSurface().transform(gctx))); + BOOST_CHECK_EQUAL(btp.referenceSurface().geometryId(), + btpRead.referenceSurface().geometryId()); + BOOST_CHECK_EQUAL(btp.referenceSurface().bounds(), + btpRead.referenceSurface().bounds()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/TGeo/TGeoTubeConversionTests.cpp b/Tests/UnitTests/Plugins/TGeo/TGeoTubeConversionTests.cpp index 3309895866b..2f18c391958 100644 --- a/Tests/UnitTests/Plugins/TGeo/TGeoTubeConversionTests.cpp +++ b/Tests/UnitTests/Plugins/TGeo/TGeoTubeConversionTests.cpp @@ -99,7 +99,6 @@ BOOST_AUTO_TEST_CASE(TGeoTube_to_CylinderSurface) { objVis, center, center + 1.2 * bR * rotation.col(1), 4., 2.5, green); GeometryView3D::drawArrowForward( objVis, center, center + 1.2 * bhZ * rotation.col(2), 4., 2.5, blue); - objVis.write("TGeoConversion_TGeoTube_CylinderSurface_" + std::to_string(icyl)); objVis.clear(); diff --git a/cmake/ActsConfig.cmake.in b/cmake/ActsConfig.cmake.in index b66c5ae3544..7ed4b3884a5 100644 --- a/cmake/ActsConfig.cmake.in +++ b/cmake/ActsConfig.cmake.in @@ -77,8 +77,8 @@ if(PluginPodio IN_LIST Acts_COMPONENTS) find_dependency(podio @podio_VERSION@ CONFIG EXACT) endif() if(PluginGeoModel IN_LIST Acts_COMPONENTS) - find_dependency(GeoModelCore @GeoModel_VERSION@ CONFIG EXACT) - find_dependency(GeoModelIO @GeoModel_VERSION@ CONFIG EXACT) + find_dependency(GeoModelCore @GeoModelCore_VERSION@ CONFIG EXACT) + find_dependency(GeoModelIO @GeoModelIO_VERSION@ CONFIG EXACT) endif() if (PluginHashing IN_LIST Acts_COMPONENTS) find_dependency(Annoy @ANNOY_VERSION@ CONFIG EXACT) diff --git a/docs/getting_started.md b/docs/getting_started.md index fbc247a3e05..f17ec5abcf1 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -9,9 +9,9 @@ following commands will clone the repository, configure, and build the core library: ```console -$ git clone https://github.com/acts-project/acts -$ cmake -B -S -$ cmake --build +git clone https://github.com/acts-project/acts +cmake -B -S +cmake --build ``` For a full list of dependencies, including specific versions, see the @@ -23,27 +23,27 @@ section. The following dependencies are required to build the ACTS core library: -- A C++17 compatible compiler (recent versions of either gcc and clang should work) -- [CMake](https://cmake.org) >= 3.14 -- [Boost](https://www.boost.org) >= 1.71 with `filesystem`, `program_options`, and `unit_test_framework` -- [Eigen](https://eigen.tuxfamily.org) >= 3.3.7 +- A C++17 compatible compiler (recent versions of either gcc and clang should work) +- [CMake](https://cmake.org) >= 3.14 +- [Boost](https://www.boost.org) >= 1.71 with `filesystem`, `program_options`, and `unit_test_framework` +- [Eigen](https://eigen.tuxfamily.org) >= 3.3.7 The following dependencies are optional and are needed to build additional components: -- [CUDA](https://developer.nvidia.com/cuda-zone) for the CUDA plugin and the Exa.TrkX plugin and its examples -- [DD4hep](http://dd4hep.cern.ch) >= 1.11 for the DD4hep plugin and some examples -- [Doxygen](http://doxygen.org) >= 1.8.15 for the documentation -- [Geant4](https://geant4.org/) for some examples -- [HepMC](https://gitlab.cern.ch/hepmc/HepMC3) >= 3.2.1 for some examples -- [Intel Threading Building Blocks](https://github.com/oneapi-src/oneTBB) >= 2020.1 for the examples -- [ONNX Runtime](https://onnxruntime.ai/) >= 1.12.0 for the ONNX plugin, the Exa.TrkX plugin and some examples -- [Pythia8](https://pythia.org) for some examples -- [ROOT](https://root.cern.ch) >= 6.20 for the TGeo plugin and the examples -- [Sphinx](https://www.sphinx-doc.org) >= 2.0 with [Breathe](https://breathe.readthedocs.io/en/latest/), [Exhale](https://exhale.readthedocs.io/en/latest/), and [recommonmark](https://recommonmark.readthedocs.io/en/latest/index.html) extensions for the documentation -- [cugraph](https://github.com/rapidsai/cugraph) for the Exa.TrkX plugin -- [libtorch](https://pytorch.org/cppdocs/installing.html) for the Exa.TrkX plugin -- [Pybind11](https://github.com/pybind/pybind11) for the Python bindings of the examples +- [CUDA](https://developer.nvidia.com/cuda-zone) for the CUDA plugin and the Exa.TrkX plugin and its examples +- [DD4hep](http://dd4hep.cern.ch) >= 1.11 for the DD4hep plugin and some examples +- [Doxygen](http://doxygen.org) >= 1.8.15 for the documentation +- [Geant4](https://geant4.org/) for some examples +- [HepMC](https://gitlab.cern.ch/hepmc/HepMC3) >= 3.2.1 for some examples +- [Intel Threading Building Blocks](https://github.com/oneapi-src/oneTBB) >= 2020.1 for the examples +- [ONNX Runtime](https://onnxruntime.ai/) >= 1.12.0 for the ONNX plugin, the Exa.TrkX plugin and some examples +- [Pythia8](https://pythia.org) for some examples +- [ROOT](https://root.cern.ch) >= 6.20 for the TGeo plugin and the examples +- [Sphinx](https://www.sphinx-doc.org) >= 2.0 with [Breathe](https://breathe.readthedocs.io/en/latest/), [Exhale](https://exhale.readthedocs.io/en/latest/), and [recommonmark](https://recommonmark.readthedocs.io/en/latest/index.html) extensions for the documentation +- [cugraph](https://github.com/rapidsai/cugraph) for the Exa.TrkX plugin +- [libtorch](https://pytorch.org/cppdocs/installing.html) for the Exa.TrkX plugin +- [Pybind11](https://github.com/pybind/pybind11) for the Python bindings of the examples There are some additional dependencies that are automatically provided as part of the build system. @@ -69,7 +69,7 @@ runs the configuration and searches for the dependencies. The `` directory is automatically created. ```console -$ cmake -B -S +cmake -B -S ``` The build can be configured via various options that are listed in detail in the @@ -77,19 +77,19 @@ The build can be configured via various options that are listed in detail in the The previous command could be e.g. modified to ```console -$ cmake -B -S -DACTS_BUILD_UNITTESTS=on -DACTS_BUILD_FATRAS=on +cmake -B -S -DACTS_BUILD_UNITTESTS=on -DACTS_BUILD_FATRAS=on ``` After the configuration succeeded, the software is build. This is also done with cmake via the following command ```console -$ cmake --build +cmake --build ``` This automatically calls the configure build tool, e.g. Make or Ninja. To build only a specific target, the target names has to be separated from the CMake options by `--`, i.e. ```console -$ cmake --build -- ActsFatras # to build the Fatras library +cmake --build -- ActsFatras # to build the Fatras library ``` The build commands are the same regardless of where you are building the @@ -103,8 +103,8 @@ e.g. CERNs lxplus login machines, the dependencies can be easily satisfied via a LCG releases available through CVMFS. A setup script is provided to activate a compatible releases that can be used as follows: ```console -$ cd -$ source CI/setup_cvmfs_lcg.sh +cd +source CI/setup_cvmfs_lcg.sh ``` After sourcing the setup script, you can build ACTS as described above. The @@ -112,10 +112,10 @@ following commands will build ACTS in the `/build` directory with the Fatras component. ```console -$ cd -$ source CI/setup_cvmfs_lcg.sh -$ cmake -B build -S . -DACTS_BUILD_FATRAS=on -$ cmake --build build +cd +source CI/setup_cvmfs_lcg.sh +cmake -B build -S . -DACTS_BUILD_FATRAS=on +cmake --build build ``` ### In a container @@ -144,13 +144,13 @@ available tags, e.g. for the `ubuntu2004` image, you can use the following command: ```console -$ docker search --list-tags ghcr.io/acts-project/ubuntu2404 +docker search --list-tags ghcr.io/acts-project/ubuntu2404 ``` The following command then downloads a stable tag of the `ubuntu2404` image: ```console -$ docker pull ghcr.io/acts-project/ubuntu2404:51 +docker pull ghcr.io/acts-project/ubuntu2404:51 ``` This should print the image id as part of the output. You can also find out the @@ -163,7 +163,7 @@ following command will make the source directory available as `/acts` in the container and start an interactive `bash` shell ```console -$ docker run --volume=:/acts:ro --interactive --tty /bin/bash +docker run --volume=:/acts:ro --interactive --tty /bin/bash ``` where `` is the image id that was previously mentioned. If you are using the Ubuntu-based image you are already good to go. For the images based on LCG releases, you can now activate the LCG release in the container shell by sourcing a setup script: @@ -191,6 +191,7 @@ install ACTS' dependencies; see the [building with Spack](misc/spack) page for more information. (build_docs)= + ## Building the documentation The documentation uses [Doxygen][doxygen] to extract the source code @@ -201,8 +202,8 @@ need to have [Doxygen][doxygen] version `1.9.5` or newer installed. package manager `pip`: ```console -$ cd -$ pip install -r docs/requirements.txt +cd +pip install -r docs/requirements.txt ``` :::{tip} @@ -211,8 +212,8 @@ environment](https://realpython.com/python-virtual-environments-a-primer/) for this purpose! For example, run ```console -$ python -m venv docvenv -$ source docvenv/bin/activate +python -m venv docvenv +source docvenv/bin/activate ``` to create a local virtual environment, and then run the `pip` command above. @@ -221,13 +222,13 @@ to create a local virtual environment, and then run the `pip` command above. To activate the documentation build targets, the `ACTS_BUILD_DOCS` option has to be set ```console -$ cmake -B -S -DACTS_BUILD_DOCS=on +cmake -B -S -DACTS_BUILD_DOCS=on ``` Then the documentation can be build with this target ```console -$ cmake --build --target docs +cmake --build --target docs ``` The default option includes the Doxygen, Sphinx, and the Breathe extension, @@ -239,8 +240,6 @@ of errors you will need to manually pull in symbols to be documented. [doxygen]: https://doxygen.nl/ [sphinx]: https://www.sphinx-doc.org [breathe]: https://breathe.readthedocs.io -[exhale]: https://exhale.readthedocs.io -[rtd_acts]: https://acts.readthedocs.io ## Build options @@ -248,7 +247,7 @@ CMake options can be set by adding `-D