diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index b42793a85b8..af5645db84e 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -466,6 +466,7 @@ jobs: && sudo chown $USER /usr/local/acts && wget --verbose --progress=dot:giga --continue --retry-connrefused --tries=5 --timeout=2 -O deps.tar.gz https://acts.web.cern.ch/ACTS/ci/macOS/deps.67dd08d.tar.gz && tar -xf deps.tar.gz -C /usr/local/acts + && python3 -m pip install pyyaml jinja2 - name: Restore ccache uses: actions/cache/restore@v3 @@ -485,6 +486,7 @@ jobs: cmake -B build -S . -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DPython_EXECUTABLE=$(command -v python3) -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-Werror -DCMAKE_CXX_STANDARD=17 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c85bdf75f42..397fc5505e8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ variables: CCACHE_DIR: ${CI_PROJECT_DIR}/ccache CCACHE_MAXSIZE: 2G CCACHE_KEY_SUFFIX: r1 + CTEST_OUTPUT_ON_FAILURE: 1 clang_tidy: stage: build @@ -49,6 +50,7 @@ clang_tidy: clang-tidy/clang-tidy.log clang-tidy/clang-tidy.json --exclude "*thirdparty*" + --exclude "*ActsPodioEdm*" # Check the combined report against the defined limits - CI/clang_tidy/check_clang_tidy.py --report clang-tidy/clang-tidy.json --config CI/clang_tidy/limits.yml @@ -58,7 +60,7 @@ clang_tidy: build_exatrkx: stage: build - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker @@ -104,7 +106,7 @@ test_exatrkx_unittests: stage: test needs: - build_exatrkx - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker-gpu-nvidia script: @@ -114,7 +116,7 @@ test_exatrkx_python: stage: test needs: - build_exatrkx - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker-gpu-nvidia script: diff --git a/CI/physmon/fpe_masks.yml b/CI/physmon/fpe_masks.yml index 1c8d227d8b6..037d21de4b3 100644 --- a/CI/physmon/fpe_masks.yml +++ b/CI/physmon/fpe_masks.yml @@ -3,6 +3,8 @@ "Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp:177": FLTUND: 1 "Core/include/Acts/TrackFitting/detail/GsfUtils.hpp:197": - FLTUND: 1 + FLTUND: 1 +"Core/include/Acts/TrackFitting/detail/GsfUtils.hpp:201": + FLTUND: 1 "Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.ipp:120": FLTUND: 1 diff --git a/CI/physmon/reference/performance_ambi_ttbar.root b/CI/physmon/reference/performance_ambi_ttbar.root index 50d3b0daeab..0ad0b43cd6f 100644 Binary files a/CI/physmon/reference/performance_ambi_ttbar.root and b/CI/physmon/reference/performance_ambi_ttbar.root differ diff --git a/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root b/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root index 9b10f1ebeac..a9a393e8afb 100644 Binary files a/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root and b/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_truth_estimated_hist.root b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root index a57fd2b5680..9aa8250a1a4 100644 Binary files a/CI/physmon/reference/performance_amvf_truth_estimated_hist.root and b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root index b8641f44ba5..21a84cef063 100644 Binary files a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root and b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_ttbar_hist.root b/CI/physmon/reference/performance_amvf_ttbar_hist.root index 283657a0ac9..16504e57111 100644 Binary files a/CI/physmon/reference/performance_amvf_ttbar_hist.root and b/CI/physmon/reference/performance_amvf_ttbar_hist.root differ diff --git a/CI/physmon/reference/performance_ckf_ttbar.root b/CI/physmon/reference/performance_ckf_ttbar.root index 997ddd93a88..dd7dbcdffa7 100644 Binary files a/CI/physmon/reference/performance_ckf_ttbar.root and b/CI/physmon/reference/performance_ckf_ttbar.root differ diff --git a/CI/physmon/reference/performance_gsf.root b/CI/physmon/reference/performance_gsf.root index bc163b417a7..4e95eec70f7 100644 Binary files a/CI/physmon/reference/performance_gsf.root and b/CI/physmon/reference/performance_gsf.root differ diff --git a/CI/physmon/reference/performance_ivf_truth_estimated_hist.root b/CI/physmon/reference/performance_ivf_truth_estimated_hist.root index f5c0bcb3342..93d69a88111 100644 Binary files a/CI/physmon/reference/performance_ivf_truth_estimated_hist.root and b/CI/physmon/reference/performance_ivf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root index 8be38cd0627..349e70671ce 100644 Binary files a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root and b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_seeding_ttbar.root b/CI/physmon/reference/performance_seeding_ttbar.root index 68c32c00b06..8aeb7ae7379 100644 Binary files a/CI/physmon/reference/performance_seeding_ttbar.root and b/CI/physmon/reference/performance_seeding_ttbar.root differ diff --git a/CI/physmon/reference/performance_truth_tracking.root b/CI/physmon/reference/performance_truth_tracking.root index 069dacb1b94..28ffe8c18d7 100644 Binary files a/CI/physmon/reference/performance_truth_tracking.root and b/CI/physmon/reference/performance_truth_tracking.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root b/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root index 839efacb38e..4e63e8dcdbd 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root and b/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root differ diff --git a/CI/physmon/workflows/physmon_ckf_tracking.py b/CI/physmon/workflows/physmon_ckf_tracking.py index 9b81f311dce..0ce835a6eaf 100755 --- a/CI/physmon/workflows/physmon_ckf_tracking.py +++ b/CI/physmon/workflows/physmon_ckf_tracking.py @@ -143,9 +143,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None - if label in ["seeded", "orthogonal"] - else "particles_input", outputProtoVertices="ivf_protovertices", outputVertices="ivf_fittedVertices", vertexFinder=VertexFinder.Iterative, @@ -156,9 +153,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None - if label in ["seeded", "orthogonal"] - else "particles_input", outputProtoVertices="amvf_protovertices", outputVertices="amvf_fittedVertices", vertexFinder=VertexFinder.AMVF, @@ -172,7 +166,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, - associatedParticles=None, outputProtoVertices="amvf_gridseeder_protovertices", outputVertices="amvf_gridseeder_fittedVertices", vertexFinder=VertexFinder.AMVF, diff --git a/CI/physmon/workflows/physmon_track_finding_ttbar.py b/CI/physmon/workflows/physmon_track_finding_ttbar.py index 7edce9f39e5..7107a6a492c 100755 --- a/CI/physmon/workflows/physmon_track_finding_ttbar.py +++ b/CI/physmon/workflows/physmon_track_finding_ttbar.py @@ -45,7 +45,7 @@ ), acts.examples.Sequencer.FpeMask( "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp", - (172, 173), + (235, 236), acts.FpeType.FLTOVF, 1, ), @@ -161,7 +161,6 @@ s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None, outputProtoVertices="amvf_protovertices", outputVertices="amvf_fittedVertices", vertexFinder=VertexFinder.AMVF, @@ -172,7 +171,6 @@ s, setup.field, seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, - associatedParticles=None, outputProtoVertices="amvf_gridseeder_protovertices", outputVertices="amvf_gridseeder_fittedVertices", vertexFinder=VertexFinder.AMVF, diff --git a/CI/physmon/workflows/physmon_truth_tracking_gsf.py b/CI/physmon/workflows/physmon_truth_tracking_gsf.py index 518fc04af95..ca92f7b183a 100755 --- a/CI/physmon/workflows/physmon_truth_tracking_gsf.py +++ b/CI/physmon/workflows/physmon_truth_tracking_gsf.py @@ -12,7 +12,7 @@ with tempfile.TemporaryDirectory() as temp: s = acts.examples.Sequencer( - events=500, + events=10000, numThreads=-1, logLevel=acts.logging.INFO, fpeMasks=acts.examples.Sequencer.FpeMask.fromFile( @@ -23,8 +23,8 @@ tp = Path(temp) runTruthTrackingGsf( setup.trackingGeometry, - setup.digiConfig, setup.field, + setup.digiConfig, outputDir=tp, s=s, ) diff --git a/CI/physmon/workflows/physmon_truth_tracking_kalman.py b/CI/physmon/workflows/physmon_truth_tracking_kalman.py index 325ba8968ad..c01e1a0e182 100755 --- a/CI/physmon/workflows/physmon_truth_tracking_kalman.py +++ b/CI/physmon/workflows/physmon_truth_tracking_kalman.py @@ -24,7 +24,7 @@ runTruthTrackingKalman( setup.trackingGeometry, setup.field, - digiConfigFile=setup.digiConfig, + setup.digiConfig, outputDir=tp, s=s, ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1735449311e..946452b2bd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ option(ACTS_USE_SYSTEM_ACTSVG "Use the ActSVG system library" ${ACTS_USE_SYSTEM_ option(ACTS_BUILD_PLUGIN_ACTSVG "Build SVG display plugin" OFF) option(ACTS_BUILD_PLUGIN_CUDA "Build CUDA plugin" OFF) option(ACTS_BUILD_PLUGIN_DD4HEP "Build DD4hep plugin" OFF) +option(ACTS_BUILD_PLUGIN_PODIO "Build Podio plugin" OFF) option(ACTS_BUILD_PLUGIN_EDM4HEP "Build EDM4hep plugin" OFF) option(ACTS_BUILD_PLUGIN_FPEMON "Build FPE monitoring plugin" OFF) option(ACTS_BUILD_PLUGIN_GEANT4 "Build Geant4 plugin" OFF) @@ -128,6 +129,9 @@ set_option_if( set_option_if( ACTS_BUILD_PLUGIN_EDM4HEP ACTS_BUILD_EXAMPLES_EDM4HEP OR ACTS_BUILD_EVERYTHING) +set_option_if( + ACTS_BUILD_PLUGIN_PODIO + ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_GEANT4 ACTS_BUILD_EXAMPLES_GEANT4 OR ACTS_BUILD_EVERYTHING) @@ -136,7 +140,7 @@ set_option_if( ACTS_BUILD_PLUGIN_DD4HEP OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_IDENTIFICATION - ACTS_BUILD_PLUGIN_TGEO OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) + ACTS_BUILD_PLUGIN_TGEO OR ACTS_BUILD_PLUGIN_PODIO OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_JSON ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) @@ -187,7 +191,7 @@ set(_acts_autodiff_version 0.6) set(_acts_boost_version 1.71.0) set(_acts_dd4hep_version 1.21) set(_acts_edm4hep_version 0.7) -set(_acts_podio_version 0.6) +set(_acts_podio_version 0.16) set(_acts_doxygen_version 1.9.4) set(_acts_eigen3_version 3.3.7) set(_acts_hepmc3_version 3.2.1) @@ -344,8 +348,11 @@ endif() if(ACTS_BUILD_PLUGIN_ONNX OR ACTS_EXATRKX_ENABLE_ONNX) find_package(OnnxRuntime ${_acts_onnxruntime_version} REQUIRED) endif() -if(ACTS_BUILD_PLUGIN_EDM4HEP) +if(ACTS_BUILD_PLUGIN_EDM4HEP OR ACTS_BUILD_PLUGIN_PODIO) find_package(podio ${_acts_podio_version} REQUIRED CONFIG) + find_package(ROOT ${_acts_root_version} REQUIRED CONFIG COMPONENTS Core) +endif() +if(ACTS_BUILD_PLUGIN_EDM4HEP) find_package(EDM4HEP ${_acts_edm4hep_version} REQUIRED CONFIG) endif() if(ACTS_BUILD_PLUGIN_GEANT4) diff --git a/Core/include/Acts/Detector/Blueprint.hpp b/Core/include/Acts/Detector/Blueprint.hpp new file mode 100644 index 00000000000..ead405ff8cc --- /dev/null +++ b/Core/include/Acts/Detector/Blueprint.hpp @@ -0,0 +1,155 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Definitions/Common.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include +#include +#include + +namespace Acts { +namespace Experimental { + +class IInternalStructureBuilder; + +/// A Blueprint is an instruction tree that allows you to defina a tree sequence +/// of volume building using the provided tools. +/// +/// It follows tree nomenclature and can define: +/// +/// - a root node (the top of the tree) +/// - a branch node (also called inner node) +/// - leaf node (also called terminal node) +/// +/// Leaf nodes can have internal builders attached, while all the external +/// builders will be created when the blueprint is interpreted. +namespace Blueprint { + +struct Node final { + /// Branch constructor + /// + /// @param n name of the node + /// @param t the transform + /// @param bt the boundary type + /// @param bv the boundary values + /// @param bss the binning values + /// @param cs the children of the node + Node(const std::string& n, const Transform3& t, VolumeBounds::BoundsType bt, + const std::vector& bv, const std::vector& bss, + std::vector> cs = {}) + : name(n), + transform(t), + boundsType(bt), + boundaryValues(bv), + children(std::move(cs)), + binning(bss) { + for_each(children.begin(), children.end(), + [this](std::unique_ptr& c) { c->parent = this; }); + } + + /// Leaf constructor + /// + /// @param n name of the node + /// @param t the transform + /// @param bt the boundary type + /// @param bv the boundary values + /// @param isb the internal structure builder (optional) + Node(const std::string& n, const Transform3& t, VolumeBounds::BoundsType bt, + const std::vector& bv, + std::shared_ptr isb = nullptr) + : name(n), + transform(t), + boundsType(bt), + boundaryValues(bv), + internalsBuilder(std::move(isb)) {} + + /// Name identification of this node + std::string name = ""; + /// Transform definition of this node + Transform3 transform = Transform3::Identity(); + /// Boundary definition of this node + VolumeBounds::BoundsType boundsType = VolumeBounds::eOther; + /// The boundary type + std::vector boundaryValues = {}; + /// Parent node - nullptr for root only + const Node* parent = nullptr; + /// Branch definitions: children + std::vector> children = {}; + /// Branch definition binning + std::vector binning = {}; + /// Internal structure builder - for leaf nodes + std::shared_ptr internalsBuilder = nullptr; + + /// @brief Check if it is a leaf node + bool isLeaf() const { return children.empty(); } + + /// @brief Check is it is a root + bool isRoot() const { return parent == nullptr; } + + /// @brief Method to add a child to this branch + /// @param c the child to be added + void add(std::unique_ptr c) { + c->parent = this; + children.push_back(std::move(c)); + } + + /// @brief Turn into a dot output + template + void dotStream(stream_type& ss, + const std::string& graphName = "blueprint") const { + if (isRoot()) { + ss << "digraph " << graphName << " {" << '\n'; + ss << name + << " [shape=\"circle\";style=\"filled\";fillcolor=\"darkorange\"];" + << '\n'; + + } else if (isLeaf()) { + std::string color = + (internalsBuilder != nullptr) ? "darkolivegreen1" : "darkolivegreen3"; + + ss << name << " [shape=\"box\";style=\"filled\";fillcolor=\""; + ss << color << "\"];" << '\n'; + } else { + ss << name << " [shape=\"diamond\"];" << '\n'; + } + + ss << name << " [label=\"" << name << "\"];" << '\n'; + for (const auto& c : children) { + ss << name << " -> " << c->name << ";" << '\n'; + c->dotStream(ss); + } + if (children.empty()) { + ss << name + "_shape" + << " [shape=\"cylinder\";style=\"filled\";fillcolor=\"lightgrey\"];" + << '\n'; + ss << name << " -> " << name + "_shape" + << ";" << '\n'; + } + + if (internalsBuilder != nullptr) { + ss << name + "_int" + << " [shape=\"doubleoctagon\";style=\"filled\";fillcolor=" + "\"cadetblue1\"];" + << '\n'; + ss << name << " -> " << name + "_int" + << ";" << '\n'; + } + if (isRoot()) { + ss << "}" << '\n'; + } + } +}; + +} // namespace Blueprint +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/BlueprintHelper.hpp b/Core/include/Acts/Detector/detail/BlueprintHelper.hpp new file mode 100644 index 00000000000..076facab8e5 --- /dev/null +++ b/Core/include/Acts/Detector/detail/BlueprintHelper.hpp @@ -0,0 +1,38 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Detector/Blueprint.hpp" +#include "Acts/Utilities/BinningData.hpp" + +namespace Acts { + +namespace Experimental { + +namespace detail { +namespace BlueprintHelper { + +/// @brief Sort the nodes in the blueprint container node +/// +/// @param node the node for which the children should be sorted +/// @param recursive if the sorting should be done recursively to children +void sort(Blueprint::Node& node, bool recursive = true); + +/// @brief Fill the gaps in the blueprint container node +/// +/// @param node the node for with the gaps should be filled +/// @param adjustToParent nodes, if nodes should be adjusted to parent +/// +/// @note currently only cylindrical volumes are supported +void fillGaps(Blueprint::Node& node, bool adjustToParent = true); + +} // namespace BlueprintHelper +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp index b737f621c41..3c5e35d6634 100644 --- a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp +++ b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp @@ -84,8 +84,8 @@ class GenericParticleHypothesis { return m_chargeType.extractCharge(qOverP); } - /// Extracts the signed charge from the `q over p` track parameter using the - /// charge hypothesis. + /// Extracts the particle momentum from the `q over p` track parameter using + /// the charge hypothesis. /// /// @param qOverP the `q over p` track parameter. template diff --git a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp similarity index 73% rename from Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp rename to Core/include/Acts/EventData/MultiComponentTrackParameters.hpp index e8f76e107ac..bc2a6a112f6 100644 --- a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp @@ -10,6 +10,7 @@ #include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include @@ -30,9 +31,14 @@ namespace Acts { /// covariance does return std::nullopt; /// TODO Add constructor from range and projector maybe? class MultiComponentBoundTrackParameters { + public: using Parameters = BoundTrackParameters; using ParticleHypothesis = Parameters::ParticleHypothesis; + using Scalar = typename Parameters::Scalar; + using ParametersVector = typename Parameters::ParametersVector; + using CovarianceMatrix = typename Parameters::CovarianceMatrix; + private: std::vector>> m_components; std::shared_ptr m_surface; @@ -62,10 +68,6 @@ class MultiComponentBoundTrackParameters { } public: - using Scalar = typename Parameters::Scalar; - using ParametersVector = typename Parameters::ParametersVector; - using CovarianceMatrix = typename Parameters::CovarianceMatrix; - /// Construct from multiple components template MultiComponentBoundTrackParameters( @@ -224,4 +226,78 @@ class MultiComponentBoundTrackParameters { } }; +/// This class mimics the behaviour of the curvilinear parameters for ordinary +/// track parameters. To adopt this concept, a "common surface" is constructed, +/// and all parameters are projected onto this surface. The use of this is +/// questionable, and if the result is reasonable depends largely on the initial +/// multi component state. However, the propagator infrastructure forces the +/// existence of this type +/// @tparam charge_t Helper type to interpret the particle charge/momentum +class MultiComponentCurvilinearTrackParameters + : public MultiComponentBoundTrackParameters { + using covariance_t = BoundSquareMatrix; + + public: + using ConstructionTuple = std::tuple; + + private: + using Base = MultiComponentBoundTrackParameters; + + using BaseConstructionTuple = + std::tuple, + std::vector>>; + + /// We need this helper function in order to construct the base class properly + static BaseConstructionTuple construct( + const std::vector& curvi) { + // TODO where to get a geometry context here + Acts::GeometryContext gctx{}; + + // Construct and average surface + Acts::Vector3 avgPos = Acts::Vector3::Zero(); + Acts::Vector3 avgDir = Acts::Vector3::Zero(); + for (const auto& [w, pos4, dir, qop, cov] : curvi) { + avgPos += w * pos4.template segment<3>(0); + avgDir += w * dir; + } + + auto s = Surface::makeShared(avgPos, avgDir); + + std::vector> bound; + bound.reserve(curvi.size()); + + // Project the position onto the surface, keep everything else as is + for (const auto& [w, pos4, dir, qop, cov] : curvi) { + Vector3 newPos = + s->intersect(gctx, pos4.template segment<3>(eFreePos0), dir, false) + .closest() + .position(); + + BoundVector bv = + detail::transformFreeToCurvilinearParameters(pos4[eTime], dir, qop); + + // Because of the projection this should never fail + bv.template segment<2>(eBoundLoc0) = + *(s->globalToLocal(gctx, newPos, dir)); + + bound.emplace_back(w, bv, cov); + } + + return {s, bound}; + } + + /// Private constructor from a tuple + MultiComponentCurvilinearTrackParameters( + const BaseConstructionTuple& t, ParticleHypothesis particleHypothesis) + : Base(std::get<0>(t), std::get<1>(t), particleHypothesis) {} + + public: + MultiComponentCurvilinearTrackParameters( + const std::vector& cmps, + ParticleHypothesis particleHypothesis) + : MultiComponentCurvilinearTrackParameters(construct(cmps), + particleHypothesis) {} +}; + } // namespace Acts diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp index 274415a421f..570e69f11b7 100644 --- a/Core/include/Acts/Propagator/AtlasStepper.hpp +++ b/Core/include/Acts/Propagator/AtlasStepper.hpp @@ -291,7 +291,7 @@ class AtlasStepper { }; AtlasStepper(std::shared_ptr bField) - : m_bField(std::move(bField)){}; + : m_bField(std::move(bField)) {} State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, @@ -1239,7 +1239,7 @@ class AtlasStepper { if (std::abs(EST) > std::abs(state.options.tolerance)) { h = h * .5; // neutralize the sign of h again - state.stepping.stepSize.setValue(h * state.options.direction); + state.stepping.stepSize.setAccuracy(h * state.options.direction); // dltm = 0.; nStepTrials++; continue; diff --git a/Core/include/Acts/Propagator/ConstrainedStep.hpp b/Core/include/Acts/Propagator/ConstrainedStep.hpp index d8012207e6c..6d7de1bed0e 100644 --- a/Core/include/Acts/Propagator/ConstrainedStep.hpp +++ b/Core/include/Acts/Propagator/ConstrainedStep.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -45,11 +46,10 @@ class ConstrainedStep { using Scalar = ActsScalar; /// the types of constraints - /// from accuracy - this can vary up and down given a good step estimator /// from actor - this would be a typical navigation step /// from aborter - this would be a target condition /// from user - this is user given for what reason ever - enum Type : int { accuracy = 0, actor = 1, aborter = 2, user = 3 }; + enum Type : int { actor = 0, aborter = 1, user = 2 }; /// Number of iterations needed by the stepsize finder /// (e.g. Runge-Kutta) of the stepper. @@ -59,7 +59,7 @@ class ConstrainedStep { /// constructor from Scalar /// @param value is the user given initial value - constexpr explicit ConstrainedStep(Scalar value) { m_values[user] = value; } + constexpr explicit ConstrainedStep(Scalar value) { setUser(value); } /// set accuracy by one Scalar /// @@ -67,30 +67,50 @@ class ConstrainedStep { /// exposed to the Propagator /// /// @param value is the new accuracy value - constexpr void setValue(Scalar value) { - /// set the accuracy value - m_values[accuracy] = value; + constexpr void setAccuracy(Scalar value) { + assert(value > 0 && "ConstrainedStep accuracy must be > 0."); + // set the accuracy value + m_accuracy = value; + } + + /// set user by one Scalar + /// + /// @param value is the new user value + constexpr void setUser(Scalar value) { + // TODO enable assert; see https://github.com/acts-project/acts/issues/2543 + // assert(value != 0 && "ConstrainedStep user must be != 0."); + // set the user value + m_values[user] = value; } /// returns the min step size - constexpr Scalar value() const { return value(currentType()); } + constexpr Scalar value() const { + Scalar min = *std::min_element(m_values.begin(), m_values.end()); + // accuracy is always positive and therefore handled separately + Scalar result = std::min(std::abs(min), m_accuracy); + return std::signbit(min) ? -result : result; + } /// Access a specific value /// /// @param type is the requested parameter type constexpr Scalar value(Type type) const { return m_values[type]; } - /// Access the currently leading type - constexpr Type currentType() const { - return Type(std::min_element(m_values.begin(), m_values.end()) - - m_values.begin()); - } + /// Access the accuracy value + /// + /// @param type is the requested parameter type + constexpr Scalar accuracy() const { return m_accuracy; } /// release a certain constraint value /// /// @param type is the constraint type to be released constexpr void release(Type type) { m_values[type] = kNotSet; } + /// release accuracy + /// + /// @param type is the constraint type to be released + constexpr void releaseAccuracy() { m_accuracy = kNotSet; } + /// Update the step size of a certain type /// /// Only navigation and target abortion step size @@ -106,19 +126,16 @@ class ConstrainedStep { // check the current value and set it if appropriate // this will also allow signed values due to overstepping if (std::abs(value) < std::abs(m_values[type])) { + // TODO enable assert; see + // https://github.com/acts-project/acts/issues/2543 + // assert(value != 0 && "ConstrainedStep user must be != 0."); m_values[type] = value; } } - constexpr void scale(Scalar factor) { - assert(factor > 0 && "ConstrainedStep scale factor was zero or negative."); - m_values[accuracy] = value() * factor; - } - std::ostream& toStream(std::ostream& os) const { // Helper method to avoid unreadable screen output - auto streamValue = [&](Type type) { - Scalar val = value(type); + auto streamValue = [&](Scalar val) { os << std::setw(5); if (std::abs(val) == kNotSet) { os << (val > 0 ? "+∞" : "-∞"); @@ -128,13 +145,13 @@ class ConstrainedStep { }; os << "("; - streamValue(accuracy); + streamValue(m_accuracy); os << ", "; - streamValue(actor); + streamValue(value(actor)); os << ", "; - streamValue(aborter); + streamValue(value(aborter)); os << ", "; - streamValue(user); + streamValue(value(user)); os << ")"; return os; @@ -150,7 +167,9 @@ class ConstrainedStep { inline static constexpr auto kNotSet = std::numeric_limits::max(); /// the step size tuple - std::array m_values = {kNotSet, kNotSet, kNotSet, kNotSet}; + std::array m_values = {kNotSet, kNotSet, kNotSet}; + /// the accuracy value - this can vary up and down given a good step estimator + Scalar m_accuracy = kNotSet; }; inline std::ostream& operator<<(std::ostream& os, const ConstrainedStep& step) { diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index 934c49e61b9..675190ca381 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -7,6 +7,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include "Acts/EventData/detail/TransformationBoundToFree.hpp" +#include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Propagator/detail/CovarianceEngine.hpp" template @@ -137,13 +138,11 @@ Acts::Result Acts::EigenStepper::step( // size, going up to the point where it can return an estimate of the local // integration error. The results are stated in the local variables above, // allowing integration to continue once the error is deemed satisfactory - const auto tryRungeKuttaStep = - [&](const ConstrainedStep& step) -> Result { + const auto tryRungeKuttaStep = [&](const double h) -> Result { // helpers because bool and std::error_code are ambiguous constexpr auto success = &Result::success; constexpr auto failure = &Result::failure; - const double h = step.value() * state.options.direction; // State the square and half of the step size h2 = h * h; half_h = h * 0.5; @@ -188,12 +187,14 @@ Acts::Result Acts::EigenStepper::step( return success(error_estimate <= state.options.tolerance); }; - double stepSizeScaling = 1.; + const double initialH = + state.stepping.stepSize.value() * state.options.direction; + double h = initialH; size_t nStepTrials = 0; // Select and adjust the appropriate Runge-Kutta step size as given // ATL-SOFT-PUB-2009-001 while (true) { - auto res = tryRungeKuttaStep(state.stepping.stepSize); + auto res = tryRungeKuttaStep(h); if (!res.ok()) { return res.error(); } @@ -201,17 +202,16 @@ Acts::Result Acts::EigenStepper::step( break; } - stepSizeScaling = + const double stepSizeScaling = std::min(std::max(0.25f, std::sqrt(std::sqrt(static_cast( state.options.tolerance / std::abs(2. * error_estimate))))), 4.0f); - state.stepping.stepSize.scale(stepSizeScaling); + h *= stepSizeScaling; // If step size becomes too small the particle remains at the initial // place - if (std::abs(state.stepping.stepSize.value()) < - std::abs(state.options.stepSizeCutOff)) { + if (std::abs(h) < std::abs(state.options.stepSizeCutOff)) { // Not moving due to too low momentum needs an aborter return EigenStepperError::StepSizeStalled; } @@ -225,9 +225,6 @@ Acts::Result Acts::EigenStepper::step( nStepTrials++; } - // use the adjusted step size - const double h = state.stepping.stepSize.value() * state.options.direction; - // When doing error propagation, update the associated Jacobian matrix if (state.stepping.covTransport) { // The step transport matrix in global coordinates @@ -257,16 +254,17 @@ Acts::Result Acts::EigenStepper::step( state.stepping.derivative.template segment<3>(4) = sd.k4; } state.stepping.pathAccumulated += h; - if (state.stepping.stepSize.currentType() == - ConstrainedStep::Type::accuracy) { - stepSizeScaling = std::min( - std::max(0.25f, - std::sqrt(std::sqrt(static_cast( - state.options.tolerance / std::abs(error_estimate))))), - 4.0f); - state.stepping.stepSize.scale(stepSizeScaling); + const double stepSizeScaling = std::min( + std::max(0.25f, + std::sqrt(std::sqrt(static_cast( + state.options.tolerance / std::abs(error_estimate))))), + 4.0f); + const double nextAccuracy = std::abs(h * stepSizeScaling); + const double previousAccuracy = std::abs(state.stepping.stepSize.accuracy()); + const double initialStepLength = std::abs(initialH); + if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) { + state.stepping.stepSize.setAccuracy(nextAccuracy); } - state.stepping.stepSize.nStepTrials = nStepTrials; return h; diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp index db96af50651..38e0d07dfcf 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" @@ -223,10 +223,6 @@ class MultiEigenStepperLoop /// surface std::size_t m_stepLimitAfterFirstComponentOnSurface = 50; - /// How to extract a single component state when calling .boundState() or - /// .curvilinearState() - MixtureReductionMethod m_finalReductionMethod = MixtureReductionMethod::eMean; - /// The logger (used if no logger is provided by caller of methods) std::unique_ptr m_logger; @@ -243,11 +239,17 @@ class MultiEigenStepperLoop using SingleState = typename SingleStepper::State; /// @brief Use the definitions from the Single-stepper - using typename SingleStepper::BoundState; using typename SingleStepper::Covariance; - using typename SingleStepper::CurvilinearState; using typename SingleStepper::Jacobian; + /// @brief Define an own bound state + using BoundState = + std::tuple; + + /// @brief Define an own curvilinear state + using CurvilinearState = std::tuple; + /// @brief The reducer type using Reducer = component_reducer_t; @@ -256,12 +258,9 @@ class MultiEigenStepperLoop /// Constructor from a magnetic field and a optionally provided Logger MultiEigenStepperLoop(std::shared_ptr bField, - MixtureReductionMethod finalReductionMethod = - MixtureReductionMethod::eMean, std::unique_ptr logger = getDefaultLogger("GSF", Logging::INFO)) : EigenStepper(std::move(bField)), - m_finalReductionMethod(finalReductionMethod), m_logger(std::move(logger)) {} struct State { @@ -469,7 +468,7 @@ class MultiEigenStepperLoop cmp.state, state.navigation, state.options, state.geoContext); } - Result boundState( + Result boundState( const Surface& surface, bool transportCov, const FreeToBoundCorrection& freeToBoundCorrection) { return detail::boundState( diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp index 9d6f8b51f1b..2c6d4e4970f 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp @@ -17,12 +17,8 @@ auto MultiEigenStepperLoop::boundState( -> Result { assert(!state.components.empty()); - if (numberComponents(state) == 1) { - return SingleStepper::boundState(state.components.front().state, surface, - transportCov, freeToBoundCorrection); - } - - SmallVector> states; + std::vector> cmps; + cmps.reserve(numberComponents(state)); double accumulatedPathLength = 0.0; for (auto i = 0ul; i < numberComponents(state); ++i) { @@ -47,7 +43,7 @@ auto MultiEigenStepperLoop::boundState( if (bs.ok()) { const auto& btp = std::get(*bs); - states.emplace_back( + cmps.emplace_back( state.components[i].weight, btp.parameters(), btp.covariance().value_or(Acts::BoundSquareMatrix::Zero())); accumulatedPathLength += @@ -55,20 +51,12 @@ auto MultiEigenStepperLoop::boundState( } } - if (states.empty()) { + if (cmps.empty()) { return MultiStepperError::AllComponentsConversionToBoundFailed; } - const auto [finalPars, cov] = - Acts::reduceGaussianMixture(states, surface, m_finalReductionMethod); - - std::optional finalCov = std::nullopt; - if (cov != BoundSquareMatrix::Zero()) { - finalCov = cov; - } - - return BoundState{BoundTrackParameters(surface.getSharedPtr(), finalPars, - finalCov, particleHypothesis(state)), + return BoundState{MultiComponentBoundTrackParameters( + surface.getSharedPtr(), cmps, state.particleHypothesis), Jacobian::Zero(), accumulatedPathLength}; } @@ -78,53 +66,26 @@ auto MultiEigenStepperLoop::curvilinearState(State& state, -> CurvilinearState { assert(!state.components.empty()); - if (numberComponents(state) == 1) { - return SingleStepper::curvilinearState(state.components.front().state, - transportCov); - } else if (m_finalReductionMethod == MixtureReductionMethod::eMaxWeight) { - auto cmpIt = std::max_element( - state.components.begin(), state.components.end(), - [](const auto& a, const auto& b) { return a.weight < b.weight; }); - - return SingleStepper::curvilinearState(cmpIt->state, transportCov); - } else { - Vector4 pos4 = Vector4::Zero(); - Vector3 dir = Vector3::Zero(); - ActsScalar qop = 0.0; - BoundSquareMatrix cov = BoundSquareMatrix::Zero(); - ActsScalar pathLenth = 0.0; - ActsScalar sumOfWeights = 0.0; - - for (auto i = 0ul; i < numberComponents(state); ++i) { - const auto [cp, jac, pl] = SingleStepper::curvilinearState( - state.components[i].state, transportCov); - - pos4 += state.components[i].weight * cp.fourPosition(state.geoContext); - dir += state.components[i].weight * cp.direction(); - qop += state.components[i].weight * (cp.charge() / cp.absoluteMomentum()); - if (cp.covariance()) { - cov += state.components[i].weight * *cp.covariance(); - } - pathLenth += state.components[i].weight * pathLenth; - sumOfWeights += state.components[i].weight; - } - - pos4 /= sumOfWeights; - dir /= sumOfWeights; - qop /= sumOfWeights; - pathLenth /= sumOfWeights; - cov /= sumOfWeights; - - std::optional finalCov = std::nullopt; - if (cov != BoundSquareMatrix::Zero()) { - finalCov = cov; - } + std::vector< + std::tuple> + cmps; + cmps.reserve(numberComponents(state)); + double accumulatedPathLength = 0.0; - return CurvilinearState{ - CurvilinearTrackParameters(pos4, dir, qop, finalCov, - particleHypothesis(state)), - Jacobian::Zero(), pathLenth}; + for (auto i = 0ul; i < numberComponents(state); ++i) { + const auto [cp, jac, pl] = SingleStepper::curvilinearState( + state.components[i].state, transportCov); + + cmps.emplace_back(state.components[i].weight, + cp.fourPosition(state.geoContext), cp.direction(), + (cp.charge() / cp.absoluteMomentum()), + cp.covariance().value_or(BoundSquareMatrix::Zero())); + accumulatedPathLength += state.components[i].weight * pl; } + + return CurvilinearState{ + MultiComponentCurvilinearTrackParameters(cmps, state.particleHypothesis), + Jacobian::Zero(), accumulatedPathLength}; } template diff --git a/Core/include/Acts/Propagator/Propagator.hpp b/Core/include/Acts/Propagator/Propagator.hpp index 439edc44f95..ddfd01c4022 100644 --- a/Core/include/Acts/Propagator/Propagator.hpp +++ b/Core/include/Acts/Propagator/Propagator.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2016-2019 CERN for the benefit of the Acts project +// Copyright (C) 2016-2023 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 @@ -16,12 +16,14 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/TrackParametersConcept.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Propagator/AbortList.hpp" #include "Acts/Propagator/ActionList.hpp" #include "Acts/Propagator/StandardAborters.hpp" #include "Acts/Propagator/StepperConcept.hpp" +#include "Acts/Propagator/detail/ParameterTraits.hpp" #include "Acts/Propagator/detail/VoidPropagatorComponents.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" @@ -199,10 +201,26 @@ struct PropagatorOptions : public PropagatorPlainOptions { /// template class Propagator final { + /// Re-define bound track parameters dependent on the stepper + using StepperBoundTrackParameters = + detail::stepper_bound_parameters_type_t; + static_assert( + Concepts::BoundTrackParametersConcept, + "Stepper bound track parameters do not fulfill bound " + "parameters concept."); + + /// Re-define curvilinear track parameters dependent on the stepper + using StepperCurvilinearTrackParameters = + detail::stepper_curvilinear_parameters_type_t; + static_assert( + Concepts::BoundTrackParametersConcept, + "Stepper bound track parameters do not fulfill bound " + "parameters concept."); + using Jacobian = BoundMatrix; - using BoundState = std::tuple; + using BoundState = std::tuple; using CurvilinearState = - std::tuple; + std::tuple; static_assert(StepperStateConcept, "Stepper does not fulfill stepper concept."); @@ -349,7 +367,7 @@ class Propagator final { template Result< - action_list_t_result_t> propagate(const parameters_t& start, const propagator_options_t& options, bool makeCurvilinear = true) const; @@ -376,12 +394,12 @@ class Propagator final { template Result< - action_list_t_result_t> propagate( const parameters_t& start, const propagator_options_t& options, bool makeCurvilinear, - action_list_t_result_t&& inputResult) const; @@ -406,8 +424,9 @@ class Propagator final { template - Result> + Result< + action_list_t_result_t> propagate(const parameters_t& start, const Surface& target, const propagator_options_t& options) const; @@ -433,12 +452,13 @@ class Propagator final { template - Result> + Result< + action_list_t_result_t> propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options, - action_list_t_result_t inputResult) const; diff --git a/Core/include/Acts/Propagator/Propagator.ipp b/Core/include/Acts/Propagator/Propagator.ipp index 6562dfbe20d..79d21ac680b 100644 --- a/Core/include/Acts/Propagator/Propagator.ipp +++ b/Core/include/Acts/Propagator/Propagator.ipp @@ -94,10 +94,10 @@ auto Acts::Propagator::propagate(const parameters_t& start, const propagator_options_t& options, bool makeCurvilinear) const -> Result> { // Type of track parameters produced by the propagation - using ReturnParameterType = CurvilinearTrackParameters; + using ReturnParameterType = StepperCurvilinearTrackParameters; static_assert(std::is_copy_constructible::value, "return track parameter type must be copy-constructible"); @@ -117,11 +117,11 @@ template ::propagate( const parameters_t& start, const propagator_options_t& options, bool makeCurvilinear, - action_list_t_result_t&& inputResult) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); @@ -129,7 +129,7 @@ auto Acts::Propagator::propagate( using ResultType = std::decay_t; // Type of track parameters produced by the propagation - using ReturnParameterType = CurvilinearTrackParameters; + using ReturnParameterType = StepperCurvilinearTrackParameters; static_assert(std::is_copy_constructible::value, "return track parameter type must be copy-constructible"); @@ -160,7 +160,7 @@ auto Acts::Propagator::propagate( // Apply the loop protection - it resets the internal path limit detail::setupLoopProtection( state, m_stepper, state.options.abortList.template get(), - logger()); + false, logger()); // Perform the actual propagation & check its outcome auto result = propagate_impl(state, inputResult); if (result.ok()) { @@ -169,7 +169,7 @@ auto Acts::Propagator::propagate( auto curvState = m_stepper.curvilinearState(state.stepping); // Fill the end parameters inputResult.endParameters = - std::get(curvState); + std::get(curvState); // Only fill the transport jacobian when covariance transport was done if (state.stepping.covTransport) { inputResult.transportJacobian = std::get(curvState); @@ -188,13 +188,13 @@ auto Acts::Propagator::propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); // Type of track parameters produced at the end of the propagation - using return_parameter_type = BoundTrackParameters; + using return_parameter_type = StepperBoundTrackParameters; // Type of the full propagation result, including output from actions using ResultType = @@ -211,11 +211,11 @@ template ::propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options, - action_list_t_result_t inputResult) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); @@ -249,7 +249,7 @@ auto Acts::Propagator::propagate( // Apply the loop protection, it resets the internal path limit detail::setupLoopProtection( state, m_stepper, state.options.abortList.template get(), - logger()); + false, logger()); // Perform the actual propagation auto result = propagate_impl(state, inputResult); @@ -264,7 +264,7 @@ auto Acts::Propagator::propagate( const auto& bs = *bsRes; // Fill the end parameters - inputResult.endParameters = std::get(bs); + inputResult.endParameters = std::get(bs); // Only fill the transport jacobian when covariance transport was done if (state.stepping.covTransport) { inputResult.transportJacobian = std::get(bs); diff --git a/Core/include/Acts/Propagator/StandardAborters.hpp b/Core/include/Acts/Propagator/StandardAborters.hpp index aee88303615..8ea571187bc 100644 --- a/Core/include/Acts/Propagator/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/StandardAborters.hpp @@ -68,21 +68,20 @@ struct PathLimitReached { double distance = std::abs(internalLimit) - std::abs(state.stepping.pathAccumulated); double tolerance = state.options.targetTolerance; - stepper.setStepSize(state.stepping, distance, ConstrainedStep::aborter, - false); bool limitReached = (std::abs(distance) < std::abs(tolerance)); if (limitReached) { ACTS_VERBOSE("Target: x | " << "Path limit reached at distance " << distance); // reaching the target means navigation break navigator.targetReached(state.navigation, true); - } else { - ACTS_VERBOSE("Target: 0 | " - << "Target stepSize (path limit) updated to " - << stepper.outputStepSize(state.stepping)); + return true; } - // path limit check - return limitReached; + stepper.setStepSize(state.stepping, distance, ConstrainedStep::aborter, + false); + ACTS_VERBOSE("Target: 0 | " + << "Target stepSize (path limit) updated to " + << stepper.outputStepSize(state.stepping)); + return false; } }; diff --git a/Core/include/Acts/Propagator/detail/LoopProtection.hpp b/Core/include/Acts/Propagator/detail/LoopProtection.hpp index ddff0556eb8..c8170ce0976 100644 --- a/Core/include/Acts/Propagator/detail/LoopProtection.hpp +++ b/Core/include/Acts/Propagator/detail/LoopProtection.hpp @@ -18,11 +18,17 @@ namespace detail { template void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, - path_aborter_t& pathAborter, const Logger& logger) { + path_aborter_t& pathAborter, bool releaseLimit, + const Logger& logger) { if (!state.options.loopProtection) { return; } + if (releaseLimit) { + pathAborter.internalLimit = + state.options.direction * std::numeric_limits::max(); + } + // Get the field at the start position auto fieldRes = stepper.getField(state.stepping, stepper.position(state.stepping)); @@ -32,7 +38,7 @@ void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, ACTS_WARNING("Field lookup was unsuccessful, this is very likely an error"); return; } - Vector3 field = *fieldRes; + const Vector3 field = *fieldRes; const double B = field.norm(); if (B == 0) { return; @@ -43,13 +49,18 @@ void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, // Calculate the full helix path const double helixPath = state.options.direction * 2 * M_PI * p / B; // And set it as the loop limit if it overwrites the internal limit - double loopLimit = state.options.loopFraction * helixPath; - double pathLimit = pathAborter.internalLimit; - if (std::abs(loopLimit) < std::abs(pathLimit)) { + const double loopLimit = state.options.loopFraction * helixPath; + const double previousLimit = pathAborter.internalLimit; + if (std::abs(loopLimit) < std::abs(previousLimit)) { pathAborter.internalLimit = loopLimit; ACTS_VERBOSE("Path aborter limit set to " - << loopLimit << " (full helix = " << helixPath << ")"); + << loopLimit << " (full helix = " << helixPath + << ", previous limit = " << previousLimit << ")"); + } else { + ACTS_VERBOSE("Path aborter limit not updated to " + << loopLimit << " (full helix = " << helixPath + << ", previous limit = " << previousLimit << ")"); } } diff --git a/Core/include/Acts/Propagator/detail/ParameterTraits.hpp b/Core/include/Acts/Propagator/detail/ParameterTraits.hpp new file mode 100644 index 00000000000..33cfaa681f0 --- /dev/null +++ b/Core/include/Acts/Propagator/detail/ParameterTraits.hpp @@ -0,0 +1,42 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/EventData/detail/CorrectedTransformationFreeToBound.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include +#include + +namespace Acts::detail { + +template +struct stepper_bound_parameters { + using result_type = decltype(std::declval().boundState( + std::declval(), std::declval(), + std::declval(), std::declval())); + using type = + std::decay_t(*std::declval()))>; +}; + +template +using stepper_bound_parameters_type_t = + typename stepper_bound_parameters::type; + +template +struct stepper_curvilinear_parameters { + using result_type = decltype(std::declval().curvilinearState( + std::declval(), std::declval())); + using type = std::decay_t(std::declval()))>; +}; + +template +using stepper_curvilinear_parameters_type_t = + typename stepper_curvilinear_parameters::type; +} // namespace Acts::detail diff --git a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp index 7d3f9cad300..be50a0546ed 100644 --- a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp +++ b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp @@ -34,7 +34,7 @@ struct TripletCandidate { /// @param q Whether the candidate is high or low quality TripletCandidate(external_space_point_t& b, external_space_point_t& m, external_space_point_t& t, float w, float z, bool q) - : bottom(&b), middle(&m), top(&t), weight(w), zOrigin(z), isQuality(q){}; + : bottom(&b), middle(&m), top(&t), weight(w), zOrigin(z), isQuality(q) {} /// @brief Copy operations TripletCandidate(const TripletCandidate&) = default; diff --git a/Core/include/Acts/Seeding/GNN_DataStorage.hpp b/Core/include/Acts/Seeding/GNN_DataStorage.hpp new file mode 100644 index 00000000000..7a6b1c3b0e8 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_DataStorage.hpp @@ -0,0 +1,316 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Seeding/GNN_Geometry.hpp" + +#include +#include +#include + +namespace Acts { + +constexpr size_t MAX_SEG_PER_NODE = 1000; // was 30 +constexpr size_t N_SEG_CONNS = 6; // was 6 + +// new sp struct +template +struct FTF_SP { + const space_point_t *SP; // want inside to have pointer + int FTF_ID; + int combined_ID; + FTF_SP(const space_point_t *sp, int id, int combined_id) + : SP(sp), FTF_ID(id), combined_ID{combined_id} { + if (SP->sourceLinks().size() == 1) { // pixels have 1 SL + m_isPixel = true; + } else { + m_isPixel = false; + } + m_phi = std::atan(SP->x() / SP->y()); + }; + bool isPixel() const { return m_isPixel; } + bool isSCT() const { return !m_isPixel; } + float phi() const { return m_phi; } + bool m_isPixel; + float m_phi; +}; + +template +class TrigFTF_GNN_Node { + public: + struct CompareByPhi { + bool operator()(const TrigFTF_GNN_Node *n1, + const TrigFTF_GNN_Node *n2) { + return (n1->m_sp_FTF.phi() < n2->m_sp_FTF.phi()); + } + }; + + TrigFTF_GNN_Node(const FTF_SP &FTF_sp, float minT = -100.0, + float maxT = 100.0) + : m_sp_FTF(FTF_sp), m_minCutOnTau(minT), m_maxCutOnTau(maxT) {} + + inline void addIn(int i) { + if (m_in.size() < MAX_SEG_PER_NODE) { + m_in.push_back(i); + } + } + + inline void addOut(int i) { + if (m_out.size() < MAX_SEG_PER_NODE) { + m_out.push_back(i); + } + } + + inline bool isConnector() const { + if (m_in.empty() || m_out.empty()) { + return false; + } + return true; + } + + inline bool isFull() const { + if (m_in.size() == MAX_SEG_PER_NODE && m_out.size() == MAX_SEG_PER_NODE) { + return true; + } else { + return false; + } + } + + const FTF_SP &m_sp_FTF; + + std::vector m_in; // indices of the edges in the edge storage + std::vector m_out; + float m_minCutOnTau, m_maxCutOnTau; +}; + +template +class TrigFTF_GNN_EtaBin { + public: + TrigFTF_GNN_EtaBin() { m_vn.clear(); } + + ~TrigFTF_GNN_EtaBin() { + for (typename std::vector *>::iterator it = + m_vn.begin(); + it != m_vn.end(); ++it) { + delete (*it); + } + } + + void sortByPhi() { + std::sort(m_vn.begin(), m_vn.end(), + typename Acts::TrigFTF_GNN_Node::CompareByPhi()); + } + + bool empty() const { return m_vn.empty(); } + + void generatePhiIndexing(float dphi) { + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + // float phi = pN->m_sp.phi(); + // float phi = (std::atan(pN->m_sp.x() / pN->m_sp.y())); + float phi = pN->m_sp_FTF.phi(); + if (phi <= M_PI - dphi) { + continue; + } + + m_vPhiNodes.push_back( + std::pair(phi - 2 * M_PI, nIdx)); + } + + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + float phi = pN->m_sp_FTF.phi(); + m_vPhiNodes.push_back(std::pair(phi, nIdx)); + } + + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + float phi = pN->m_sp_FTF.phi(); + if (phi >= -M_PI + dphi) { + break; + } + m_vPhiNodes.push_back( + std::pair(phi + 2 * M_PI, nIdx)); + } + } + + std::vector *> m_vn; + // TODO change to + // std::vector>> m_vn; + std::vector> m_vPhiNodes; +}; + +template +class TrigFTF_GNN_DataStorage { + public: + TrigFTF_GNN_DataStorage(const TrigFTF_GNN_Geometry &g) + : m_geo(g) { + m_etaBins.reserve(g.num_bins()); + for (int k = 0; k < g.num_bins(); k++) { + m_etaBins.emplace_back(TrigFTF_GNN_EtaBin()); + } + } + + int addSpacePoint(const FTF_SP &sp, bool useClusterWidth) { + const TrigFTF_GNN_Layer *pL = + m_geo.getTrigFTF_GNN_LayerByKey(sp.combined_ID); + + if (pL == nullptr) { + return -1; + } + + int binIndex = pL->getEtaBin(sp.SP->z(), sp.SP->r()); + + if (binIndex == -1) { + return -2; + } + + bool isBarrel = (pL->m_layer.m_type == 0); + + if (isBarrel) { + float min_tau = -100.0; + float max_tau = 100.0; + // can't do this bit yet as dont have cluster width + if (useClusterWidth) { + // const Trk::SpacePoint* osp = sp.offlineSpacePoint(); + // const InDet::PixelCluster* pCL = dynamic_cast(osp->clusterList().first); + // float cluster_width = pCL->width().widthPhiRZ().y(); + float cluster_width = 1; // temporary while cluster width not available + min_tau = 6.7 * (cluster_width - 0.2); + max_tau = + 1.6 + 0.15 / (cluster_width + 0.2) + 6.1 * (cluster_width - 0.2); + } + + m_etaBins.at(binIndex).m_vn.push_back(new TrigFTF_GNN_Node( + sp, min_tau, max_tau)); // adding ftf member to nodes + } else { + if (useClusterWidth) { + // const Trk::SpacePoint* osp = sp.offlineSpacePoint(); + // const InDet::PixelCluster* pCL = dynamic_cast(osp->clusterList().first); + // float cluster_width = pCL->width().widthPhiRZ().y(); + float cluster_width = 1; // temporary while cluster width not available + if (cluster_width > 0.2) { + return -3; + } + } + m_etaBins.at(binIndex).m_vn.push_back( + new TrigFTF_GNN_Node(sp)); + } + + return 0; + } + + // for safety to prevent passing as copy + TrigFTF_GNN_DataStorage(const TrigFTF_GNN_DataStorage &) = delete; + TrigFTF_GNN_DataStorage &operator=(const TrigFTF_GNN_DataStorage &) = delete; + + unsigned int numberOfNodes() const { + unsigned int n = 0; + + for (auto &b : m_etaBins) { + n += b.m_vn.size(); + } + return n; + } + + void getConnectingNodes( + std::vector *> &vn) { + vn.clear(); + vn.reserve(numberOfNodes()); + for (const auto &b : m_etaBins) { + for (typename std::vector< + TrigFTF_GNN_Node *>::const_iterator nIt = + b.m_vn.begin(); + nIt != b.m_vn.end(); ++nIt) { + if ((*nIt)->m_in.empty()) { + continue; + } + if ((*nIt)->m_out.empty()) { + continue; + } + vn.push_back(*nIt); + } + } + } + + void sortByPhi() { + for (auto &b : m_etaBins) { + b.sortByPhi(); + } + } + + void generatePhiIndexing(float dphi) { + for (auto &b : m_etaBins) { + b.generatePhiIndexing(dphi); + } + } + + const TrigFTF_GNN_EtaBin &getEtaBin(int idx) const { + if (idx >= static_cast(m_etaBins.size())) { + idx = idx - 1; + } + return m_etaBins.at(idx); + } + + protected: + const TrigFTF_GNN_Geometry &m_geo; + + std::vector> m_etaBins; +}; + +template +class TrigFTF_GNN_Edge { + public: + struct CompareLevel { + public: + bool operator()(const TrigFTF_GNN_Edge *pS1, const TrigFTF_GNN_Edge *pS2) { + return pS1->m_level > pS2->m_level; + } + }; + + TrigFTF_GNN_Edge(TrigFTF_GNN_Node *n1, + TrigFTF_GNN_Node *n2, float p1, float p2, + float p3, float p4) + : m_n1(n1), m_n2(n2), m_level(1), m_next(1) { + m_p[0] = p1; + m_p[1] = p2; + m_p[2] = p3; + m_p[3] = p4; + } + + TrigFTF_GNN_Edge() : m_n1(nullptr), m_n2(nullptr), m_level(-1), m_next(-1) {} + + // TrigFTF_GNN_Edge(const TrigFTF_GNN_Edge &e) + // : m_n1(e.m_n1), m_n2(e.m_n2) {} + + // inline void initialize(TrigFTF_GNN_Node *n1, + // TrigFTF_GNN_Node *n2) { + // m_n1 = n1; + // m_n2 = n2; + // m_level = 1; + // m_next = 1; + // m_nNei = 0; + // } + + TrigFTF_GNN_Node *m_n1{nullptr}; + TrigFTF_GNN_Node *m_n2{nullptr}; + + signed char m_level{}, m_next{}; + + unsigned char m_nNei{0}; + float m_p[4]{}; + + unsigned int m_vNei[N_SEG_CONNS]{}; // global indices of the connected edges +}; + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/GNN_Geometry.hpp b/Core/include/Acts/Seeding/GNN_Geometry.hpp new file mode 100644 index 00000000000..ed4909caa87 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_Geometry.hpp @@ -0,0 +1,386 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/TrackFinding/FasTrackConnector.hpp" + +#include +#include +#include +#include +#include + +namespace Acts { +class TrigInDetSiLayer { + public: + int m_subdet; // combined ID + int m_type; // 0: barrel, +/-n : endcap + float m_refCoord; + float m_minBound, m_maxBound; + + TrigInDetSiLayer(int subdet, short int type, float center, float min, + float max) + : m_subdet(subdet), + m_type(type), + m_refCoord(center), + m_minBound(min), + m_maxBound(max) {} +}; + +template +class TrigFTF_GNN_Layer { + public: + TrigFTF_GNN_Layer(const TrigInDetSiLayer &ls, float ew, int bin0) + : m_layer(ls), m_etaBinWidth(ew) { + if (m_layer.m_type == 0) { // barrel + m_r1 = m_layer.m_refCoord; + m_r2 = m_layer.m_refCoord; + m_z1 = m_layer.m_minBound; + m_z2 = m_layer.m_maxBound; + } else { // endcap + m_r1 = m_layer.m_minBound; + m_r2 = m_layer.m_maxBound; + m_z1 = m_layer.m_refCoord; + m_z2 = m_layer.m_refCoord; + } + + float t1 = m_z1 / m_r1; + float eta1 = -std::log(sqrt(1 + t1 * t1) - t1); + + float t2 = m_z2 / m_r2; + float eta2 = -std::log(sqrt(1 + t2 * t2) - t2); + + m_minEta = eta1; + m_maxEta = eta2; + + if (m_maxEta < m_minEta) { + m_minEta = eta2; + m_maxEta = eta1; + } + + m_maxEta += 1e-6; // increasing them slightly to avoid range_check + // exceptions + m_minEta -= 1e-6; + + float deltaEta = m_maxEta - m_minEta; + + int binCounter = bin0; + + if (deltaEta < m_etaBinWidth) { + m_nBins = 1; + m_bins.push_back(binCounter++); + m_etaBin = deltaEta; + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } else { // endcap + m_minRadius.push_back(m_layer.m_minBound - 2.0); + m_maxRadius.push_back(m_layer.m_maxBound + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } + } else { + float nB = static_cast(deltaEta / m_etaBinWidth); + m_nBins = nB; + if (deltaEta - m_etaBinWidth * nB > 0.5 * m_etaBinWidth) { + m_nBins++; + } + m_etaBin = deltaEta / m_nBins; + + if (m_nBins == 1) { + m_bins.push_back(binCounter++); + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } else { // endcap + m_minRadius.push_back(m_layer.m_minBound - 2.0); + m_maxRadius.push_back(m_layer.m_maxBound + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } + } else { + float eta = m_minEta + 0.5 * m_etaBin; + + for (int i = 1; i <= m_nBins; i++) { + m_bins.push_back(binCounter++); + + float e1 = eta - 0.5 * m_etaBin; + float e2 = eta + 0.5 * m_etaBin; + + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + float z1 = m_layer.m_refCoord * std::sinh(e1); + m_minBinCoord.push_back(z1); + float z2 = m_layer.m_refCoord * std::sinh(e2); + m_maxBinCoord.push_back(z2); + } else { // endcap + float r = m_layer.m_refCoord / std::sinh(e1); + m_minBinCoord.push_back(r); + m_minRadius.push_back(r - 2.0); + r = m_layer.m_refCoord / std::sinh(e2); + m_maxBinCoord.push_back(r); + m_maxRadius.push_back(r + 2.0); + } + + eta += m_etaBin; + } + } + } + } + + int getEtaBin(float zh, float rh) const { + if (m_bins.size() == 1) { + return m_bins.at(0); + } + float t1 = zh / rh; + float eta = -std::log(std::sqrt(1 + t1 * t1) - t1); + + int idx = static_cast((eta - m_minEta) / m_etaBin); + + if (idx < 0) { + idx = 0; + } + if (idx >= static_cast(m_bins.size())) { + idx = static_cast(m_bins.size()) - 1; + } + return m_bins.at(idx); // index in the global storage + } + + float getMinBinRadius(int idx) const { + if (idx >= static_cast(m_minRadius.size())) { + idx = idx - 1; + } + if (idx < 0) { + idx = 0; + } + return m_minRadius.at(idx); + } + + float getMaxBinRadius(int idx) const { + if (idx >= static_cast(m_maxRadius.size())) { + idx = idx - 1; + } + if (idx < 0) { + idx = 0; + } + return m_maxRadius.at(idx); + } + + int num_bins() const { return m_bins.size(); } + + bool verifyBin(const TrigFTF_GNN_Layer *pL, int b1, int b2, + float min_z0, float max_z0) const { + float z1min = m_minBinCoord.at(b1); + float z1max = m_maxBinCoord.at(b1); + float r1 = m_layer.m_refCoord; + + if (m_layer.m_type == 0 && pL->m_layer.m_type == 0) { // barrel -> barrel + + const float tol = 5.0; + + float min_b2 = pL->m_minBinCoord.at(b2); + float max_b2 = pL->m_maxBinCoord.at(b2); + + float r2 = pL->m_layer.m_refCoord; + + float A = r2 / (r2 - r1); + float B = r1 / (r2 - r1); + + float z0_min = z1min * A - max_b2 * B; + float z0_max = z1max * A - min_b2 * B; + + if (z0_max < min_z0 - tol || z0_min > max_z0 + tol) { + return false; + } + return true; + } + + if (m_layer.m_type == 0 && pL->m_layer.m_type != 0) { // barrel -> endcap + + const float tol = 10.0; + + float z2 = pL->m_layer.m_refCoord; + float r2max = pL->m_maxBinCoord.at(b2); + float r2min = pL->m_minBinCoord.at(b2); + + if (r2max <= r1) { + return false; + } + if (r2min <= r1) { + r2min = r1 + 1e-3; + } + + float z0_max = 0.0; + float z0_min = 0.0; + + if (z2 > 0) { + z0_max = (z1max * r2max - z2 * r1) / (r2max - r1); + z0_min = (z1min * r2min - z2 * r1) / (r2min - r1); + } else { + z0_max = (z1max * r2min - z2 * r1) / (r2min - r1); + z0_min = (z1min * r2max - z2 * r1) / (r2max - r1); + } + + if (z0_max < min_z0 - tol || z0_min > max_z0 + tol) { + return false; + } + return true; + } + + return true; + } + + const TrigInDetSiLayer &m_layer; + std::vector m_bins; // eta-bin indices + std::vector m_minRadius; + std::vector m_maxRadius; + std::vector m_minBinCoord; + std::vector m_maxBinCoord; + + float m_minEta{}, m_maxEta{}; + + protected: + float m_etaBinWidth{}, m_phiBinWidth{}; + + float m_r1{}, m_z1{}, m_r2{}, m_z2{}; + float m_nBins{}; + float m_etaBin{}; +}; + +template +class TrigFTF_GNN_Geometry { + public: + TrigFTF_GNN_Geometry(const std::vector &layers, + std::unique_ptr &conn) + + : m_fastrack(std::move(conn)) { + const float min_z0 = -168.0; + const float max_z0 = 168.0; + + m_etaBinWidth = m_fastrack->m_etaBin; + for (const auto &layer : layers) { + const TrigFTF_GNN_Layer *pL = + addNewLayer(layer, m_nEtaBins); + m_nEtaBins += pL->num_bins(); + } + + // calculating bin tables in the connector... + + for (std::map>::const_iterator it = + m_fastrack->m_connMap.begin(); + it != m_fastrack->m_connMap.end(); ++it) { + const std::vector &vConn = (*it).second; + + for (std::vector::const_iterator cIt = + vConn.begin(); + cIt != vConn.end(); ++cIt) { + unsigned int src = (*cIt)->m_src; // n2 : the new connectors + unsigned int dst = (*cIt)->m_dst; // n1 + + const TrigFTF_GNN_Layer *pL1 = + getTrigFTF_GNN_LayerByKey(dst); + const TrigFTF_GNN_Layer *pL2 = + getTrigFTF_GNN_LayerByKey(src); + + if (pL1 == nullptr) { + std::cout << " skipping invalid dst layer " << dst << std::endl; + continue; + } + if (pL2 == nullptr) { + std::cout << " skipping invalid src layer " << src << std::endl; + continue; + } + int nSrcBins = pL2->m_bins.size(); + int nDstBins = pL1->m_bins.size(); + + (*cIt)->m_binTable.resize(nSrcBins * nDstBins, 0); + + for (int b1 = 0; b1 < nDstBins; b1++) { // loop over bins in Layer 1 + for (int b2 = 0; b2 < nSrcBins; b2++) { // loop over bins in Layer 2 + if (!pL1->verifyBin(pL2, b1, b2, min_z0, max_z0)) { + continue; + } + int address = b1 + b2 * nDstBins; + (*cIt)->m_binTable.at(address) = 1; + } + } + } + } + } + + TrigFTF_GNN_Geometry() = default; + + // for safety to prevent passing as copy + TrigFTF_GNN_Geometry(const TrigFTF_GNN_Geometry &) = delete; + TrigFTF_GNN_Geometry &operator=(const TrigFTF_GNN_Geometry &) = delete; + + ~TrigFTF_GNN_Geometry() { + for (typename std::vector *>::iterator it = + m_layArray.begin(); + it != m_layArray.end(); ++it) { + delete (*it); + } + + m_layMap.clear(); + m_layArray.clear(); + } + + const TrigFTF_GNN_Layer *getTrigFTF_GNN_LayerByKey( + unsigned int key) const { + typename std::map *>::const_iterator it = + m_layMap.find(key); + if (it == m_layMap.end()) { + return nullptr; + } + + return (*it).second; + } + + const TrigFTF_GNN_Layer *getTrigFTF_GNN_LayerByIndex( + int idx) const { + return m_layArray.at(idx); + } + + int num_bins() const { return m_nEtaBins; } + + Acts::FasTrackConnector *fastrack() const { return m_fastrack.get(); } + + protected: + const TrigFTF_GNN_Layer *addNewLayer(const TrigInDetSiLayer &l, + int bin0) { + unsigned int layerKey = l.m_subdet; // this should be combined ID + float ew = m_etaBinWidth; + + TrigFTF_GNN_Layer *pHL = + new TrigFTF_GNN_Layer(l, ew, bin0); + + m_layMap.insert(std::pair *>( + layerKey, pHL)); + m_layArray.push_back(pHL); + return pHL; + } + + float m_etaBinWidth{}; + + std::map *> m_layMap; + std::vector *> m_layArray; + + int m_nEtaBins{0}; + + std::unique_ptr m_fastrack; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp b/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp new file mode 100644 index 00000000000..5c388c83504 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp @@ -0,0 +1,386 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Seeding/GNN_DataStorage.hpp" //includes geo which has trigindetsilayer, may move this to trigbase + +#include +#include +#include +#include +#include +#include + +template +struct TrigFTF_GNN_EdgeState { + public: + struct Compare { + bool operator()(const struct TrigFTF_GNN_EdgeState* s1, + const struct TrigFTF_GNN_EdgeState* s2) { + return s1->m_J > s2->m_J; + } + }; + + TrigFTF_GNN_EdgeState() = default; + + TrigFTF_GNN_EdgeState(bool f) : m_initialized(f) {} + + void initialize(Acts::TrigFTF_GNN_Edge* pS) { + m_initialized = true; + + m_J = 0.0; + m_vs.clear(); + + // n2->n1 + + float dx = pS->m_n1->m_sp_FTF.SP->x() - pS->m_n2->m_sp_FTF.SP->x(); + float dy = pS->m_n1->m_sp_FTF.SP->y() - pS->m_n2->m_sp_FTF.SP->y(); + float L = std::sqrt(dx * dx + dy * dy); + + m_s = dy / L; + m_c = dx / L; + + // transform for extrapolation and update + // x' = x*m_c + y*m_s + // y' = -x*m_s + y*m_c + + m_refY = pS->m_n2->m_sp_FTF.SP->r(); + m_refX = + pS->m_n2->m_sp_FTF.SP->x() * m_c + pS->m_n2->m_sp_FTF.SP->y() * m_s; + + // X-state: y, dy/dx, d2y/dx2 + + m_X[0] = + -pS->m_n2->m_sp_FTF.SP->x() * m_s + pS->m_n2->m_sp_FTF.SP->y() * m_c; + m_X[1] = 0.0; + m_X[2] = 0.0; + + // Y-state: z, dz/dr + + m_Y[0] = pS->m_n2->m_sp_FTF.SP->z(); + m_Y[1] = (pS->m_n1->m_sp_FTF.SP->z() - pS->m_n2->m_sp_FTF.SP->z()) / + (pS->m_n1->m_sp_FTF.SP->r() - pS->m_n2->m_sp_FTF.SP->r()); + + memset(&m_Cx[0][0], 0, sizeof(m_Cx)); + memset(&m_Cy[0][0], 0, sizeof(m_Cy)); + + m_Cx[0][0] = 0.25; + m_Cx[1][1] = 0.001; + m_Cx[2][2] = 0.001; + + m_Cy[0][0] = 1.5; + m_Cy[1][1] = 0.001; + } + + void clone(const struct TrigFTF_GNN_EdgeState& st) { + memcpy(&m_X[0], &st.m_X[0], sizeof(m_X)); + memcpy(&m_Y[0], &st.m_Y[0], sizeof(m_Y)); + memcpy(&m_Cx[0][0], &st.m_Cx[0][0], sizeof(m_Cx)); + memcpy(&m_Cy[0][0], &st.m_Cy[0][0], sizeof(m_Cy)); + m_refX = st.m_refX; + m_refY = st.m_refY; + m_c = st.m_c; + m_s = st.m_s; + m_J = st.m_J; + m_vs.clear(); + m_vs.reserve(st.m_vs.size()); + std::copy(st.m_vs.begin(), st.m_vs.end(), std::back_inserter(m_vs)); + + m_initialized = true; + } + + float m_J{}; + + std::vector*> m_vs; + + float m_X[3]{}, m_Y[2]{}, m_Cx[3][3]{}, m_Cy[2][2]{}; + float m_refX{}, m_refY{}, m_c{}, m_s{}; + + bool m_initialized{false}; +}; + +#define MAX_EDGE_STATE 2500 + +template +class TrigFTF_GNN_TrackingFilter { + public: + TrigFTF_GNN_TrackingFilter( + const std::vector& g, + std::vector>& sb) + : m_geo(g), m_segStore(sb) {} + + void followTrack(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& output) { + if (pS->m_level == -1) { + return; // already collected + } + m_globalStateCounter = 0; + + // create track state + + TrigFTF_GNN_EdgeState* pInitState = + &m_stateStore[m_globalStateCounter++]; + + pInitState->initialize(pS); + + m_stateVec.clear(); + + // recursive branching and propagation + + propagate(pS, *pInitState); + + if (m_stateVec.empty()) { + return; + } + std::sort(m_stateVec.begin(), m_stateVec.end(), + typename TrigFTF_GNN_EdgeState::Compare()); + + TrigFTF_GNN_EdgeState* best = (*m_stateVec.begin()); + + output.clone(*best); + + m_globalStateCounter = 0; + } + + protected: + void propagate(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& ts) { + if (m_globalStateCounter >= MAX_EDGE_STATE) { + return; + } + TrigFTF_GNN_EdgeState* p_new_ts = + &m_stateStore[m_globalStateCounter++]; + + TrigFTF_GNN_EdgeState& new_ts = *p_new_ts; + new_ts.clone(ts); + + new_ts.m_vs.push_back(pS); + + bool accepted = update(pS, new_ts); // update using n1 of the segment + + if (!accepted) { + return; // stop further propagation + } + int level = pS->m_level; + + std::list*> lCont; + + for (int nIdx = 0; nIdx < pS->m_nNei; + nIdx++) { // loop over the neighbours of this segment + unsigned int nextSegmentIdx = pS->m_vNei[nIdx]; + + Acts::TrigFTF_GNN_Edge* pN = + &(m_segStore.at(nextSegmentIdx)); + + if (pN->m_level == -1) { + continue; // already collected + } + if (pN->m_level == level - 1) { + lCont.push_back(pN); + } + } + if (lCont.empty()) { // the end of chain + + // store in the vector + if (m_globalStateCounter < MAX_EDGE_STATE) { + if (m_stateVec.empty()) { // add the first segment state + TrigFTF_GNN_EdgeState* p = + &m_stateStore[m_globalStateCounter++]; + p->clone(new_ts); + m_stateVec.push_back(p); + } else { // compare with the best and add + float best_so_far = (*m_stateVec.begin())->m_J; + if (new_ts.m_J > best_so_far) { + TrigFTF_GNN_EdgeState* p = + &m_stateStore[m_globalStateCounter++]; + p->clone(new_ts); + m_stateVec.push_back(p); + } + } + } + } else { // branching + int nBranches = 0; + for (typename std::list< + Acts::TrigFTF_GNN_Edge*>::iterator sIt = + lCont.begin(); + sIt != lCont.end(); ++sIt, nBranches++) { + propagate((*sIt), new_ts); // recursive call + } + } + } + + bool update(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& ts) { + const float sigma_t = 0.0003; + const float sigma_w = 0.00009; + + const float sigmaMS = 0.016; + + const float sigma_x = 0.25; // was 0.22 + const float sigma_y = 2.5; // was 1.7 + + const float weight_x = 0.5; + const float weight_y = 0.5; + + const float maxDChi2_x = 60.0; // 35.0; + const float maxDChi2_y = 60.0; // 31.0; + + const float add_hit = 14.0; + + if (ts.m_Cx[2][2] < 0.0 || ts.m_Cx[1][1] < 0.0 || ts.m_Cx[0][0] < 0.0) { + std::cout << "Negative cov_x" << std::endl; + } + + if (ts.m_Cy[1][1] < 0.0 || ts.m_Cy[0][0] < 0.0) { + std::cout << "Negative cov_y" << std::endl; + } + + // add ms. + + ts.m_Cx[2][2] += sigma_w * sigma_w; + ts.m_Cx[1][1] += sigma_t * sigma_t; + + int type1 = getLayerType(pS->m_n1->m_sp_FTF.combined_ID); + + float t2 = type1 == 0 ? 1.0 + ts.m_Y[1] * ts.m_Y[1] + : 1.0 + 1.0 / (ts.m_Y[1] * ts.m_Y[1]); + float s1 = sigmaMS * t2; + float s2 = s1 * s1; + + s2 *= std::sqrt(t2); + + ts.m_Cy[1][1] += s2; + + // extrapolation + + float X[3], Y[2]; + float Cx[3][3], Cy[2][2]; + + float refX{}, refY{}, mx{}, my{}; + + float x{}, y{}, z{}, r{}; + + x = pS->m_n1->m_sp_FTF.SP->x(); + y = pS->m_n1->m_sp_FTF.SP->y(); + z = pS->m_n1->m_sp_FTF.SP->z(); + r = pS->m_n1->m_sp_FTF.SP->r(); + + refX = x * ts.m_c + y * ts.m_s; + mx = -x * ts.m_s + y * ts.m_c; // measured X[0] + refY = r; + my = z; // measured Y[0] + + float A = refX - ts.m_refX; + float B = 0.5 * A * A; + float dr = refY - ts.m_refY; + + X[0] = ts.m_X[0] + ts.m_X[1] * A + ts.m_X[2] * B; + X[1] = ts.m_X[1] + ts.m_X[2] * A; + X[2] = ts.m_X[2]; + + Cx[0][0] = ts.m_Cx[0][0] + 2 * ts.m_Cx[0][1] * A + 2 * ts.m_Cx[0][2] * B + + A * A * ts.m_Cx[1][1] + 2 * A * B * ts.m_Cx[1][2] + + B * B * ts.m_Cx[2][2]; + Cx[0][1] = Cx[1][0] = ts.m_Cx[0][1] + ts.m_Cx[1][1] * A + + ts.m_Cx[1][2] * B + ts.m_Cx[0][2] * A + + A * A * ts.m_Cx[1][2] + A * B * ts.m_Cx[2][2]; + Cx[0][2] = Cx[2][0] = ts.m_Cx[0][2] + ts.m_Cx[1][2] * A + ts.m_Cx[2][2] * B; + + Cx[1][1] = ts.m_Cx[1][1] + 2 * A * ts.m_Cx[1][2] + A * A * ts.m_Cx[2][2]; + Cx[1][2] = Cx[2][1] = ts.m_Cx[1][2] + ts.m_Cx[2][2] * A; + + Cx[2][2] = ts.m_Cx[2][2]; + + Y[0] = ts.m_Y[0] + ts.m_Y[1] * dr; + Y[1] = ts.m_Y[1]; + + Cy[0][0] = ts.m_Cy[0][0] + 2 * ts.m_Cy[0][1] * dr + dr * dr * ts.m_Cy[1][1]; + Cy[0][1] = Cy[1][0] = ts.m_Cy[0][1] + dr * ts.m_Cy[1][1]; + Cy[1][1] = ts.m_Cy[1][1]; + + // chi2 test + + float resid_x = mx - X[0]; + float resid_y = my - Y[0]; + + float CHx[3] = {Cx[0][0], Cx[0][1], Cx[0][2]}; + float CHy[2] = {Cy[0][0], Cy[0][1]}; + + float sigma_rz = 0.0; + + int type = getLayerType(pS->m_n1->m_sp_FTF.combined_ID); + + if (type == 0) { // barrel TO-DO: split into barrel Pixel and barrel SCT + sigma_rz = sigma_y * sigma_y; + } else { + sigma_rz = sigma_y * ts.m_Y[1]; + sigma_rz = sigma_rz * sigma_rz; + } + + float Dx = 1.0 / (Cx[0][0] + sigma_x * sigma_x); + + float Dy = 1.0 / (Cy[0][0] + sigma_rz); + + float dchi2_x = resid_x * resid_x * Dx; + float dchi2_y = resid_y * resid_y * Dy; + + if (dchi2_x > maxDChi2_x || dchi2_y > maxDChi2_y) { + return false; + } + + ts.m_J += add_hit - dchi2_x * weight_x - dchi2_y * weight_y; + + // state update + + float Kx[3] = {Dx * Cx[0][0], Dx * Cx[0][1], Dx * Cx[0][2]}; + float Ky[2] = {Dy * Cy[0][0], Dy * Cy[0][1]}; + + for (int i = 0; i < 3; i++) { + ts.m_X[i] = X[i] + Kx[i] * resid_x; + } + for (int i = 0; i < 2; i++) { + ts.m_Y[i] = Y[i] + Ky[i] * resid_y; + } + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + ts.m_Cx[i][j] = Cx[i][j] - Kx[i] * CHx[j]; + } + } + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + ts.m_Cy[i][j] = Cy[i][j] - Ky[i] * CHy[j]; + } + } + ts.m_refX = refX; + ts.m_refY = refY; + return true; + } + + int getLayerType(int l) { + auto iterator = find_if(m_geo.begin(), m_geo.end(), [l](auto n) { + return n.m_subdet == l; + }); // iterator to vector member with this id + int index = std::distance(m_geo.begin(), iterator); + + return m_geo.at(index).m_type; + } + + const std::vector& m_geo; + + std::vector>& m_segStore; + + std::vector*> m_stateVec; + + TrigFTF_GNN_EdgeState m_stateStore[MAX_EDGE_STATE]; + + int m_globalStateCounter{0}; +}; diff --git a/Core/include/Acts/Seeding/SeedFinderFTF.hpp b/Core/include/Acts/Seeding/SeedFinderFTF.hpp new file mode 100644 index 00000000000..b03d7306082 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTF.hpp @@ -0,0 +1,103 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style + +#include "Acts/EventData/SpacePointData.hpp" +#include "Acts/Seeding/InternalSeed.hpp" +#include "Acts/Seeding/InternalSpacePoint.hpp" +#include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" +#include "Acts/TrackFinding/RoiDescriptor.hpp" +#include "Acts/Utilities/KDTree.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Acts { + +template +struct GNN_TrigTracklet { + public: + GNN_TrigTracklet(std::vector *> &vSP, + std::vector> &tbuf) + : m_track(vSP), m_seeds(tbuf) {} + + std::vector *> m_track; + std::vector> m_seeds; +}; + +template +class SeedFinderFTF { + public: + static constexpr std::size_t NDims = 3; + + using seed_t = Seed; + // using internal_sp_t = InternalSpacePoint; + // using tree_t = KDTree; + + // constructors + SeedFinderFTF(const SeedFinderFTFConfig &config, + const TrigFTF_GNN_Geometry &GNNgeo); + + ~SeedFinderFTF(); //!!! is it dangerous not to use default? got def in ipp + SeedFinderFTF() = default; + SeedFinderFTF(const SeedFinderFTF &) = delete; + SeedFinderFTF &operator=( + const SeedFinderFTF &) = delete; + + void loadSpacePoints( + const std::vector> &FTF_SP_vect); + + void createSeeds( + const Acts::RoiDescriptor &roi, + const Acts::TrigFTF_GNN_Geometry &gnngeo); + + // create seeeds function + template + void createSeeds_old(const Acts::SeedFinderOptions &options, + const input_container_t &spacePoints, + output_container_t &out_cont, + callable_t &&extract_coordinates) const; + + template + std::vector createSeeds_old(const Acts::SeedFinderOptions &options, + const input_container_t &spacePoints, + callable_t &&extract_coordinates) const; + + private: + enum Dim { DimPhi = 0, DimR = 1, DimZ = 2 }; + + // config object + SeedFinderFTFConfig m_config; + + void runGNN_TrackFinder( + std::vector> &vTracks, + const Acts::RoiDescriptor &roi, + const Acts::TrigFTF_GNN_Geometry &gnngeo); + + // needs to be member of class so can accessed by all member functions + TrigFTF_GNN_DataStorage *m_storage; + + // for create seeds: + std::vector> m_triplets; +}; + +} // namespace Acts + +#include "Acts/Seeding/SeedFinderFTF.ipp" diff --git a/Core/include/Acts/Seeding/SeedFinderFTF.ipp b/Core/include/Acts/Seeding/SeedFinderFTF.ipp new file mode 100644 index 00000000000..38696c7ade1 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTF.ipp @@ -0,0 +1,732 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// SeedFinderFTF.ipp +// TODO: update to C++17 style + +#include "Acts/Definitions/Algebra.hpp" //for M_PI +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Seeding/SeedFilter.hpp" +#include "Acts/Seeding/SeedFinder.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" +#include "Acts/Seeding/SeedFinderUtils.hpp" +#include "Acts/Utilities/BinningType.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +// core so in ACTS namespace + +namespace Acts { + +template +SeedFinderFTF::SeedFinderFTF( + const SeedFinderFTFConfig& config, + const TrigFTF_GNN_Geometry& GNNgeo) + : m_config(config) { + m_storage = new TrigFTF_GNN_DataStorage(GNNgeo); +} + +template +SeedFinderFTF::~SeedFinderFTF() { + delete m_storage; + + m_storage = nullptr; +} + +// define loadspace points function +template +void SeedFinderFTF::loadSpacePoints( + const std::vector>& FTF_SP_vect) { + for (const auto& FTF_sp : FTF_SP_vect) { + bool is_Pixel = FTF_sp.isPixel(); + if (!is_Pixel) { + continue; + } + m_storage->addSpacePoint(FTF_sp, (m_config.m_useClusterWidth > 0)); + } + + m_config.m_nMaxPhiSlice = 1; + m_config.m_phiSliceWidth = 2 * M_PI / m_config.m_nMaxPhiSlice; + + m_storage->sortByPhi(); + + m_storage->generatePhiIndexing(1.5 * m_config.m_phiSliceWidth); +} + +template +void SeedFinderFTF::runGNN_TrackFinder( + std::vector>& vTracks, + const Acts::RoiDescriptor& roi, + const Acts::TrigFTF_GNN_Geometry& gnngeo) { + // long term move these to ftf finder config, then m_config. to access them + const int MaxEdges = 2000000; + + const float cut_dphi_max = 0.012; + const float cut_dcurv_max = 0.001; + const float cut_tau_ratio_max = 0.007; + const float min_z0 = -2800; // roiDescriptor->zedMinus(); //blank for now, + // get from config eventually + const float max_z0 = 2800; // roiDescriptor->zedPlus(); + + const float maxOuterRadius = 550.0; + const float cut_zMinU = + min_z0 + + maxOuterRadius * roi.dzdrMinus(); // dzdr can only find =0 in athena + const float cut_zMaxU = max_z0 + maxOuterRadius * roi.dzdrPlus(); + + float m_minR_squ = 1; // set earlier + float m_maxCurv = 1; + + const float maxKappa_high_eta = 0.8 / m_minR_squ; + const float maxKappa_low_eta = 0.6 / m_minR_squ; + + // 1. loop over stages + + int currentStage = 0; + + const Acts::FasTrackConnector& fastrack = *(gnngeo.fastrack()); + + std::vector> edgeStorage; + + edgeStorage.reserve(MaxEdges); + + int nEdges = 0; + + for (std::map>::const_iterator + it = fastrack.m_layerGroups.begin(); + it != fastrack.m_layerGroups.end(); ++it, currentStage++) { + // loop over L1 layers for the current stage + + for (const auto& layerGroup : (*it).second) { + unsigned int dst = layerGroup.m_dst; // n1 : inner nodes + + const TrigFTF_GNN_Layer* pL1 = + gnngeo.getTrigFTF_GNN_LayerByKey(dst); + + if (pL1 == nullptr) { + continue; + } + + for (const auto& conn : + layerGroup.m_sources) { // loop over L2(L1) for the current stage + + unsigned int src = conn->m_src; // n2 : the new connectors + + const TrigFTF_GNN_Layer* pL2 = + gnngeo.getTrigFTF_GNN_LayerByKey(src); + + if (pL2 == nullptr) { + continue; + } + int nDstBins = pL1->m_bins.size(); + int nSrcBins = pL2->m_bins.size(); + + for (int b1 = 0; b1 < nDstBins; b1++) { // loop over bins in Layer 1 + + const TrigFTF_GNN_EtaBin& B1 = + m_storage->getEtaBin(pL1->m_bins.at(b1)); + + if (B1.empty()) { + continue; + } + + float rb1 = pL1->getMinBinRadius(b1); + + // 3. loops over source eta-bins + + for (int b2 = 0; b2 < nSrcBins; b2++) { // loop over bins in Layer 2 + + if (m_config.m_useEtaBinning && (nSrcBins + nDstBins > 2)) { + if (conn->m_binTable[b1 + b2 * nDstBins] != 1) { + continue; // using precomputed LUT + } + } + + const TrigFTF_GNN_EtaBin& B2 = + m_storage->getEtaBin(pL2->m_bins.at(b2)); + + if (B2.empty()) { + continue; + } + + float rb2 = pL2->getMaxBinRadius(b2); + + // calculated delta Phi for rb1 ---> rb2 extrapolation + + float deltaPhi = + 0.5f * + m_config + .m_phiSliceWidth; // the default sliding window along phi + + if (m_config.m_useEtaBinning) { + deltaPhi = 0.001f + m_maxCurv * std::fabs(rb2 - rb1); + } + + unsigned int first_it = 0; + for (typename std::vector< + TrigFTF_GNN_Node*>::const_iterator + n1It = B1.m_vn.begin(); + n1It != B1.m_vn.end(); ++n1It) { // loop over nodes in Layer 1 + + TrigFTF_GNN_Node* n1 = (*n1It); + + if (n1->m_in.size() >= MAX_SEG_PER_NODE) { + continue; + } + + float r1 = n1->m_sp_FTF.SP->r(); + float x1 = n1->m_sp_FTF.SP->x(); + float y1 = n1->m_sp_FTF.SP->y(); + float z1 = n1->m_sp_FTF.SP->z(); + float phi1 = std::atan(x1 / y1); + + float minPhi = phi1 - deltaPhi; + float maxPhi = phi1 + deltaPhi; + + for (unsigned int n2PhiIdx = first_it; + n2PhiIdx < B2.m_vPhiNodes.size(); + n2PhiIdx++) { // sliding window over nodes in Layer 2 + + float phi2 = B2.m_vPhiNodes.at(n2PhiIdx).first; + + if (phi2 < minPhi) { + first_it = n2PhiIdx; + continue; + } + if (phi2 > maxPhi) { + break; + } + + TrigFTF_GNN_Node* n2 = + B2.m_vn.at(B2.m_vPhiNodes.at(n2PhiIdx).second); + + if (n2->m_out.size() >= MAX_SEG_PER_NODE) { + continue; + } + if (n2->isFull()) { + continue; + } + + float r2 = n2->m_sp_FTF.SP->r(); + + float dr = r2 - r1; + + if (dr < m_config.m_minDeltaRadius) { + continue; + } + + float z2 = n2->m_sp_FTF.SP->z(); + + float dz = z2 - z1; + float tau = dz / dr; + float ftau = std::fabs(tau); + if (ftau > 36.0) { + continue; + } + + if (ftau < n1->m_minCutOnTau) { + continue; + } + if (ftau < n2->m_minCutOnTau) { + continue; + } + if (ftau > n1->m_maxCutOnTau) { + continue; + } + if (ftau > n2->m_maxCutOnTau) { + continue; + } + + if (m_config.m_doubletFilterRZ) { + float z0 = z1 - r1 * tau; + + if (z0 < min_z0 || z0 > max_z0) { + continue; + } + + float zouter = z0 + maxOuterRadius * tau; + + if (zouter < cut_zMinU || zouter > cut_zMaxU) { + continue; + } + } + + float dx = n2->m_sp_FTF.SP->x() - x1; + float dy = n2->m_sp_FTF.SP->y() - y1; + + float L2 = 1 / (dx * dx + dy * dy); + + float D = + (n2->m_sp_FTF.SP->y() * x1 - y1 * n2->m_sp_FTF.SP->x()) / + (r1 * r2); + + float kappa = D * D * L2; + + if (ftau < 4.0) { // eta = 2.1 + if (kappa > maxKappa_low_eta) { + continue; + } + + } else { + if (kappa > maxKappa_high_eta) { + continue; + } + } + + // match edge candidate against edges incoming to n2 + + float exp_eta = std::sqrt(1 + tau * tau) - tau; + + bool isGood = + n2->m_in.size() <= + 2; // we must have enough incoming edges to decide + + if (!isGood) { + float uat_1 = 1.0f / exp_eta; + + for (const auto& n2_in_idx : n2->m_in) { + float tau2 = edgeStorage.at(n2_in_idx).m_p[0]; + float tau_ratio = tau2 * uat_1 - 1.0f; + + if (std::fabs(tau_ratio) > cut_tau_ratio_max) { // bad + // match + continue; + } + isGood = true; // good match found + break; + } + } + if (!isGood) { + continue; // no moatch found, skip creating [n1 <- n2] edge + } + + float curv = D * std::sqrt(L2); // signed curvature + float dPhi2 = std::asin(curv * r2); + float dPhi1 = std::asin(curv * r1); + + if (nEdges < MaxEdges) { + edgeStorage.emplace_back(n1, n2, exp_eta, curv, phi1 + dPhi1, + phi2 + dPhi2); + + n1->addIn(nEdges); + n2->addOut(nEdges); + + nEdges++; + } + } // loop over n2 (outer) nodes + } // loop over n1 (inner) nodes + } // loop over source eta bins + } // loop over dst eta bins + } // loop over L2(L1) layers + } // loop over dst layers + } // loop over the stages of doublet making + + std::vector*> vNodes; + + m_storage->getConnectingNodes(vNodes); + + if (vNodes.empty()) { + return; + } + + int nNodes = vNodes.size(); + + for (int nodeIdx = 0; nodeIdx < nNodes; nodeIdx++) { + const TrigFTF_GNN_Node* pN = vNodes.at(nodeIdx); + + std::vector> in_sort, out_sort; + in_sort.resize(pN->m_in.size()); + out_sort.resize(pN->m_out.size()); + + for (int inIdx = 0; inIdx < static_cast(pN->m_in.size()); inIdx++) { + int inEdgeIdx = pN->m_in.at(inIdx); + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(inEdgeIdx)); + in_sort[inIdx].second = inEdgeIdx; + in_sort[inIdx].first = pS->m_p[0]; + } + for (int outIdx = 0; outIdx < static_cast(pN->m_out.size()); + outIdx++) { + int outEdgeIdx = pN->m_out.at(outIdx); + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(outEdgeIdx)); + out_sort[outIdx].second = outEdgeIdx; + out_sort[outIdx].first = pS->m_p[0]; + } + + std::sort(in_sort.begin(), in_sort.end()); + std::sort(out_sort.begin(), out_sort.end()); + + unsigned int last_out = 0; + + for (unsigned int in_idx = 0; in_idx < in_sort.size(); + in_idx++) { // loop over incoming edges + + int inEdgeIdx = in_sort[in_idx].second; + + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(inEdgeIdx)); + + pS->m_nNei = 0; + float tau1 = pS->m_p[0]; + float uat_1 = 1.0f / tau1; + float curv1 = pS->m_p[1]; + float Phi1 = pS->m_p[2]; + + for (unsigned int out_idx = last_out; out_idx < out_sort.size(); + out_idx++) { + int outEdgeIdx = out_sort[out_idx].second; + + Acts::TrigFTF_GNN_Edge* pNS = + &(edgeStorage.at(outEdgeIdx)); + + float tau2 = pNS->m_p[0]; + float tau_ratio = tau2 * uat_1 - 1.0f; + + if (tau_ratio < -cut_tau_ratio_max) { + last_out = out_idx; + continue; + } + if (tau_ratio > cut_tau_ratio_max) { + break; + } + + float dPhi = pNS->m_p[3] - Phi1; + + if (dPhi < -M_PI) { + dPhi += 2 * M_PI; + } else if (dPhi > M_PI) { + dPhi -= 2 * M_PI; + } + + if (dPhi < -cut_dphi_max || dPhi > cut_dphi_max) { + continue; + } + + float curv2 = pNS->m_p[1]; + float dcurv = curv2 - curv1; + + if (dcurv < -cut_dcurv_max || dcurv > cut_dcurv_max) { + continue; + } + + pS->m_vNei[pS->m_nNei++] = outEdgeIdx; + if (pS->m_nNei >= N_SEG_CONNS) { + break; + } + } + } + } + + const int maxIter = 15; + + int maxLevel = 0; + + int iter = 0; + + std::vector*> v_old; + + for (int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) { + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(edgeIndex)); + if (pS->m_nNei == 0) { + continue; + } + v_old.push_back(pS); // TO-DO: increment level for segments as they already + // have at least one neighbour + } + + for (; iter < maxIter; iter++) { + // generate proposals + std::vector*> v_new; + v_new.clear(); + + for (auto pS : v_old) { + int next_level = pS->m_level; + + for (int nIdx = 0; nIdx < pS->m_nNei; nIdx++) { + unsigned int nextEdgeIdx = pS->m_vNei[nIdx]; + + Acts::TrigFTF_GNN_Edge* pN = + &(edgeStorage.at(nextEdgeIdx)); + + if (pS->m_level == pN->m_level) { + next_level = pS->m_level + 1; + v_new.push_back(pS); + break; + } + } + + pS->m_next = next_level; // proposal + } + // update + + int nChanges = 0; + + for (auto pS : v_new) { + if (pS->m_next != pS->m_level) { + nChanges++; + pS->m_level = pS->m_next; + if (maxLevel < pS->m_level) { + maxLevel = pS->m_level; + } + } + } + + if (nChanges == 0) { + break; + } + + v_old = std::move(v_new); + v_new.clear(); + } + + int minLevel = 3; // a triplet + 2 confirmation + + std::vector*> vSeeds; + + vSeeds.reserve(MaxEdges / 2); + + for (int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) { + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(edgeIndex)); + + if (pS->m_level < minLevel) { + continue; + } + + vSeeds.push_back(pS); + } + + m_triplets.clear(); + + std::sort( + vSeeds.begin(), vSeeds.end(), + typename Acts::TrigFTF_GNN_Edge::CompareLevel()); + + if (vSeeds.empty()) { + return; + } + + // backtracking + + TrigFTF_GNN_TrackingFilter tFilter( + m_config.m_layerGeometry, edgeStorage); + + for (auto pS : vSeeds) { + if (pS->m_level == -1) { + continue; + } + + TrigFTF_GNN_EdgeState rs(false); + + tFilter.followTrack(pS, rs); + + if (!rs.m_initialized) { + continue; + } + + if (static_cast(rs.m_vs.size()) < minLevel) { + continue; + } + + std::vector*> vSP; + + for (typename std::vector*>:: + reverse_iterator sIt = rs.m_vs.rbegin(); + sIt != rs.m_vs.rend(); ++sIt) { + (*sIt)->m_level = -1; // mark as collected + + if (sIt == rs.m_vs.rbegin()) { + vSP.push_back(&(*sIt)->m_n1->m_sp_FTF); + } + vSP.push_back(&(*sIt)->m_n2->m_sp_FTF); + } + + if (vSP.size() < 3) { + continue; + } + + // making triplets + + unsigned int nTriplets = 0; + + std::vector> output; + + for (unsigned int idx_m = 1; idx_m < vSP.size() - 1; idx_m++) { + const FTF_SP& spM = *vSP.at(idx_m); + const double pS_r = spM.SP->r(); + const double pS_x = spM.SP->x(); + const double pS_y = spM.SP->y(); + const double cosA = pS_x / pS_r; + const double sinA = pS_y / pS_r; + + for (unsigned int idx_o = idx_m + 1; idx_o < vSP.size(); idx_o++) { + const FTF_SP& spO = *vSP.at(idx_o); + + double dx = spO.SP->x() - pS_x; + double dy = spO.SP->y() - pS_y; + double R2inv = 1.0 / (dx * dx + dy * dy); + double xn = dx * cosA + dy * sinA; + double yn = -dx * sinA + dy * cosA; + + const double uo = xn * R2inv; + const double vo = yn * R2inv; + + for (unsigned int idx_i = 0; idx_i < idx_m; idx_i++) { + const FTF_SP& spI = *vSP.at(idx_i); + + dx = spI.SP->x() - pS_x; + dy = spI.SP->y() - pS_y; + R2inv = 1.0 / (dx * dx + dy * dy); + + xn = dx * cosA + dy * sinA; + yn = -dx * sinA + dy * cosA; + + const double ui = xn * R2inv; + const double vi = yn * R2inv; + + // 1. pT estimate + + const double du = uo - ui; + if (du == 0.0) { + continue; + } + const double A = (vo - vi) / du; + const double B = vi - A * ui; + const double R_squ = (1 + A * A) / (B * B); + + if (R_squ < m_minR_squ) { + continue; + } + + // 2. d0 cut + + const double fabs_d0 = std::abs(pS_r * (B * pS_r - A)); + + if (fabs_d0 > m_config.m_tripletD0Max) { + continue; + } + + // 3. phi0 cut + + // if (!roi.isFullscan()) { + // const double uc = 2 * B * pS_r - A; + // // const double phi0 = std::atan2(sinA - uc * cosA, cosA + uc * + // sinA); + // // if ( !RoiUtil::containsPhi( *roiDescriptor, phi0 ) ) + // { + // // continue; + // // } + // } + + // 4. add new triplet + + const double Q = fabs_d0 * fabs_d0; + + output.emplace_back(spI, spM, spO, Q); + + nTriplets++; + + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + + if (output.empty()) { + continue; + } + + vTracks.emplace_back(vSP, output); + } +} + +template +void SeedFinderFTF::createSeeds( + const Acts::RoiDescriptor& roi, + const Acts::TrigFTF_GNN_Geometry& gnngeo) { + std::vector> + vTracks; // make empty vector + + vTracks.reserve(5000); + + runGNN_TrackFinder(vTracks, roi, gnngeo); // returns filled vector + + if (vTracks.empty()) { + return; + } + + m_triplets.clear(); // member of class , saying not declared, maybe public? + + for (auto& track : vTracks) { + for (auto& seed : track.m_seeds) { // access mmeber of GNN_TrigTracklet + + float newQ = seed.Q(); // function of TrigInDetTriplet + if (m_config.m_LRTmode) { + // In LRT mode penalize pixels in Triplets + if (seed.s1().isPixel()) { + newQ += 1000; // functions of TrigSiSpacePointBase + } + if (seed.s2().isPixel()) { + newQ += 1000; + } + if (seed.s3().isPixel()) { + newQ += 1000; + } + } else { + // In normal (non LRT) mode penalise SSS by 1000, PSS (if enabled) and + // PPS by 10000 + if (seed.s3().isSCT()) { + newQ += seed.s1().isSCT() ? 1000.0 : 10000.0; + } + } + seed.Q(newQ); + m_triplets.emplace_back(seed); + } + } + vTracks.clear(); +} + +// // still to be developed +template +template +void SeedFinderFTF::createSeeds_old( + const Acts::SeedFinderOptions& /*options*/, + const input_container_t& /*spacePoints*/, output_container_t& /*out_cont*/, + callable_t&& /*extract_coordinates*/) const {} + +template +template +std::vector> +SeedFinderFTF::createSeeds_old( + const Acts::SeedFinderOptions& options, + const input_container_t& spacePoints, + callable_t&& extract_coordinates) const { + std::vector r; + createSeeds_old(options, spacePoints, r, + std::forward(extract_coordinates)); + return r; +} + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp b/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp new file mode 100644 index 00000000000..320d8136548 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp @@ -0,0 +1,90 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Seeding/SeedConfirmationRangeConfig.hpp" +#include "Acts/Seeding/TrigBase.hpp" //definition of Trigsispacepoint base and trigtriplets + +#include + +// core algorithm so in acts namespace +namespace Acts { + +template +class SeedFilter; + +template +struct SeedFinderFTFConfig { + // // how many sigmas of scattering angle should be considered? + float sigmaScattering = 5; + + // Seed cut + float minPt = 400. * Acts::UnitConstants::MeV; + + ///////////some declared not filled in by reco: ////// + std::shared_ptr> seedFilter; + + // //detector ROI + // // derived values, set on SeedFinder construction + float highland = 0; + float maxScatteringAngle2 = 0; + // bool isInInternalUnits = false; + /// for load space points + unsigned int maxSeedsPerSpM = 5; + + float m_phiSliceWidth{}; + float m_nMaxPhiSlice{}; + bool m_useClusterWidth = false; + std::string fastrack_input_file; + std::vector m_layerGeometry; + + // for run function + // m_settings: + bool m_LRTmode = true; // eventually want to set from full chain + bool m_useEtaBinning = true; + bool m_doubletFilterRZ = true; + float m_minDeltaRadius = 5.0; // eventually set in config or to equivalent + // acts 2.0 but increasing to test loops + // float m_maxDeltaRadius = 270.0 ; + float m_tripletD0Max = 4.0; // m_settings + unsigned int m_maxTripletBufferLength = 3; + + // ROI: + bool containsPhi() { + return false; + // need to implement this function + } + + //// + // 2 member functions + SeedFinderFTFConfig calculateDerivedQuantities() const { + // thorw statement if the isInternalUnits member is false, ie if dont call + // this function + SeedFinderFTFConfig config = *this; + // use a formula to calculate scattering + + return config; + } + + SeedFinderFTFConfig toInternalUnits() const { + // throw statement if the isInternalUnits member is false, ie if dont call + // this function + SeedFinderFTFConfig config = *this; + // divides inputs by 1mm, all ones input + // changes member inInInternalUnits to true + return config; + } + +}; // end of config struct + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/TrigBase.hpp b/Core/include/Acts/Seeding/TrigBase.hpp new file mode 100644 index 00000000000..4737bf819fa --- /dev/null +++ b/Core/include/Acts/Seeding/TrigBase.hpp @@ -0,0 +1,45 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Seeding/GNN_TrackingFilter.hpp" + +#include + +#define MAX_SILICON_LAYER_NUM 19 +#define OffsetEndcapPixels 7 +#define OffsetBarrelSCT 3 +#define OffsetEndcapSCT 10 + +template +class TrigInDetTriplet { + public: + TrigInDetTriplet() = delete; // to prevent creation w/o initialization + + TrigInDetTriplet(Acts::FTF_SP s1, + Acts::FTF_SP s2, + Acts::FTF_SP s3, float Q) + : m_s1(std::move(s1)), m_s2(std::move(s2)), m_s3(std::move(s3)), m_Q(Q) {} + + TrigInDetTriplet(TrigInDetTriplet* t) + : m_s1(t->m_s1), m_s2(t->m_s2), m_s3(t->m_s3), m_Q(t->m_Q) {} + + const Acts::FTF_SP& s1() const { return m_s1; } + const Acts::FTF_SP& s2() const { return m_s2; } + const Acts::FTF_SP& s3() const { return m_s3; } + float Q() const { return m_Q; } + void Q(double newQ) { m_Q = newQ; } + + protected: + Acts::FTF_SP m_s1; + Acts::FTF_SP m_s2; + Acts::FTF_SP m_s3; + float m_Q; // Quality +}; diff --git a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp index 885a07d4066..fabe59e7a65 100644 --- a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp +++ b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp @@ -142,6 +142,8 @@ constexpr int PolygonDynamic = -1; template <> class ConvexPolygonBounds : public ConvexPolygonBoundsBase { public: + constexpr static int eSize = -1; + /// Default constructor, deleted ConvexPolygonBounds() = delete; diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index cebd0f35606..16e89fe91ce 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -47,6 +47,16 @@ namespace Acts { +enum class CombinatorialKalmanFilterTargetSurfaceStrategy { + /// Use the first trackstate to reach target surface + first, + /// Use the last trackstate to reach target surface + last, + /// Use the first or last trackstate to reach target surface depending on the + /// distance + firstOrLast, +}; + /// Track quality summary for one trajectory. /// /// This could be used to decide if a track is to be recorded when the @@ -200,6 +210,11 @@ struct CombinatorialKalmanFilterOptions { /// The smoothing target surface const Surface* smoothingTargetSurface = nullptr; + /// Strategy to propagate to reference surface + CombinatorialKalmanFilterTargetSurfaceStrategy + smoothingTargetSurfaceStrategy = + CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -212,51 +227,52 @@ struct CombinatorialKalmanFilterOptions { template struct CombinatorialKalmanFilterResult { - // Fitted states that the actor has handled. + /// Fitted states that the actor has handled. traj_t* fittedStates{nullptr}; - // This is used internally to store candidate trackstates + /// This is used internally to store candidate trackstates std::shared_ptr stateBuffer; std::vector trackStateCandidates; - // This is the indices of the 'tip' of the tracks stored in multitrajectory. - // This corresponds to the last measurement state in the multitrajectory. + /// This is the indices of the 'tip' of the tracks stored in multitrajectory. + /// This corresponds to the last measurement state in the multitrajectory. std::vector lastMeasurementIndices; - // This is the indices of the 'tip' of the tracks stored in multitrajectory. - // This corresponds to the last state in the multitrajectory. + /// This is the indices of the 'tip' of the tracks stored in multitrajectory. + /// This corresponds to the last state in the multitrajectory. std::vector lastTrackIndices; - // The Parameters at the provided surface for separate tracks + /// The Parameters at the provided surface for separate tracks std::unordered_map fittedParameters; - // The indices of the 'tip' of the unfinished tracks + /// The indices of the 'tip' of the unfinished tracks std::vector> activeTips; - // The indices of track states and corresponding source links on different - // surfaces + /// The indices of track states and corresponding source links on different + /// surfaces std::unordered_map> sourcelinkTips; - // Indicator if filtering has been done + /// Indicator if filtering has been done bool filtered = false; - // Indicator if smoothing has been done. + /// Indicator if smoothing has been done. bool smoothed = false; - // The index for the current smoothing track + /// The index for the current smoothing track MultiTrajectoryTraits::IndexType iSmoothed = 0; - // Indicator if track finding has been done + /// Indicator if track finding has been done bool finished = false; - Result result{Result::success()}; + /// Last encountered error + Result lastError{Result::success()}; - // TODO place into options and make them accessible? - AbortList abortList; + /// Path limit aborter + PathLimitReached pathLimitReached; }; /// Combinatorial Kalman filter to find tracks. @@ -333,6 +349,11 @@ class CombinatorialKalmanFilter { /// The smoothing target surface const Surface* smoothingTargetSurface = nullptr; + /// Strategy to propagate to reference surface + CombinatorialKalmanFilterTargetSurfaceStrategy + smoothingTargetSurfaceStrategy = + CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -397,7 +418,7 @@ class CombinatorialKalmanFilter { auto res = filter(surface, state, stepper, navigator, result); if (!res.ok()) { ACTS_ERROR("Error in filter: " << res.error()); - result.result = res.error(); + result.lastError = res.error(); } } @@ -465,7 +486,8 @@ class CombinatorialKalmanFilter { } } - if (result.abortList(state, stepper, navigator, result, logger())) { + if (endOfWorldReached(state, stepper, navigator, logger()) || + result.pathLimitReached(state, stepper, navigator, logger())) { navigator.targetReached(state.navigation, false); if (result.activeTips.empty()) { // we are already done @@ -510,7 +532,7 @@ class CombinatorialKalmanFilter { auto res = finalize(state, stepper, navigator, result); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); - result.result = res.error(); + result.lastError = res.error(); } result.smoothed = true; @@ -530,7 +552,9 @@ class CombinatorialKalmanFilter { if (result.smoothed and (smoothingTargetSurface == nullptr or targetReached(state, stepper, navigator, - *smoothingTargetSurface, logger()))) { + *smoothingTargetSurface, logger()) or + result.pathLimitReached(state, stepper, navigator, + logger()))) { ACTS_VERBOSE( "Completing the track with last measurement index = " << result.lastMeasurementIndices.at(result.iSmoothed)); @@ -541,15 +565,14 @@ class CombinatorialKalmanFilter { stepper.boundState(state.stepping, *smoothingTargetSurface); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); - result.result = res.error(); - return; + result.lastError = res.error(); + } else { + auto fittedState = *res; + // Assign the fitted parameters + result.fittedParameters.emplace( + result.lastMeasurementIndices.at(result.iSmoothed), + std::get(fittedState)); } - - auto fittedState = *res; - // Assign the fitted parameters - result.fittedParameters.emplace( - result.lastMeasurementIndices.at(result.iSmoothed), - std::get(fittedState)); } // If there are more trajectories to handle: @@ -613,9 +636,8 @@ class CombinatorialKalmanFilter { materialInteractor(navigator.currentSurface(state.navigation), state, stepper, navigator, MaterialUpdateStage::FullUpdate); - detail::setupLoopProtection( - state, stepper, result.abortList.template get(), - logger()); + detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, + logger()); } /// @brief CombinatorialKalmanFilter actor operation: @@ -1181,6 +1203,7 @@ class CombinatorialKalmanFilter { // Screen output for debugging ACTS_VERBOSE("Apply smoothing on " << nStates << " filtered track states."); + // Smooth the track states auto smoothRes = m_extensions.smoother(state.geoContext, *result.fittedStates, @@ -1195,8 +1218,7 @@ class CombinatorialKalmanFilter { return Result::success(); } - // Obtain the smoothed parameters at first/last measurement state. - // The first state can also be a material state + // Obtain the smoothed parameters at first/last measurement state auto firstCreatedState = result.fittedStates->getTrackState(firstStateIndex); auto lastCreatedMeasurement = @@ -1212,7 +1234,8 @@ class CombinatorialKalmanFilter { .closest(); }; - // The smoothed free params at the first/last measurement state + // The smoothed free params at the first/last measurement state. + // (the first state can also be a material state) auto firstParams = MultiTrajectoryHelpers::freeSmoothed( state.options.geoContext, firstCreatedState); auto lastParams = MultiTrajectoryHelpers::freeSmoothed( @@ -1223,18 +1246,31 @@ class CombinatorialKalmanFilter { const auto lastIntersection = target(lastParams); // Update the stepping parameters - in order to progress to destination. - // At the same time, reverse navigation direction for further - // stepping if necessary. + // At the same time, reverse navigation direction for further stepping if + // necessary. // @note The stepping parameters is updated to the smoothed parameters at // either the first measurement state or the last measurement state. It // assumes the target surface is not within the first and the last // smoothed measurement state. Also, whether the intersection is on // surface is not checked here. - bool closerToFirstCreatedState = - std::abs(firstIntersection.pathLength()) <= - std::abs(lastIntersection.pathLength()); + bool useFirstTrackState = true; + switch (smoothingTargetSurfaceStrategy) { + case CombinatorialKalmanFilterTargetSurfaceStrategy::first: + useFirstTrackState = true; + break; + case CombinatorialKalmanFilterTargetSurfaceStrategy::last: + useFirstTrackState = false; + break; + case CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast: + useFirstTrackState = std::abs(firstIntersection.pathLength()) <= + std::abs(lastIntersection.pathLength()); + break; + default: + ACTS_ERROR("Unknown target surface strategy"); + return KalmanFitterError::SmoothFailed; + } bool reverseDirection = false; - if (closerToFirstCreatedState) { + if (useFirstTrackState) { stepper.resetState(state.stepping, firstCreatedState.smoothed(), firstCreatedState.smoothedCovariance(), firstCreatedState.referenceSurface(), @@ -1254,9 +1290,10 @@ class CombinatorialKalmanFilter { "target surface"); state.options.direction = state.options.direction.invert(); } - const auto& surface = closerToFirstCreatedState + const auto& surface = useFirstTrackState ? firstCreatedState.referenceSurface() : lastCreatedMeasurement.referenceSurface(); + ACTS_VERBOSE( "Smoothing successful, updating stepping state to smoothed " "parameters at surface " @@ -1270,6 +1307,9 @@ class CombinatorialKalmanFilter { state.options.direction * stepper.direction(state.stepping), &surface, nullptr); + detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, + logger()); + return Result::success(); } @@ -1281,6 +1321,9 @@ class CombinatorialKalmanFilter { /// The Surface being targeted SurfaceReached targetReached{std::numeric_limits::lowest()}; + /// End of world aborter + EndOfWorldReached endOfWorldReached; + /// Actor logger instance const Logger* actorLogger{nullptr}; /// Updater logger instance @@ -1302,7 +1345,7 @@ class CombinatorialKalmanFilter { bool operator()(propagator_state_t& /*state*/, const stepper_t& /*stepper*/, const navigator_t& /*navigator*/, const result_t& result, const Logger& /*logger*/) const { - if (!result.result.ok() or result.finished) { + if (result.finished) { return true; } return false; @@ -1366,6 +1409,8 @@ class CombinatorialKalmanFilter { propOptions.actionList.template get(); combKalmanActor.filterTargetSurface = tfOptions.filterTargetSurface; combKalmanActor.smoothingTargetSurface = tfOptions.smoothingTargetSurface; + combKalmanActor.smoothingTargetSurfaceStrategy = + tfOptions.smoothingTargetSurfaceStrategy; combKalmanActor.multipleScattering = tfOptions.multipleScattering; combKalmanActor.energyLoss = tfOptions.energyLoss; combKalmanActor.smoothing = tfOptions.smoothing; @@ -1416,27 +1461,31 @@ class CombinatorialKalmanFilter { // This is regarded as a failure. // @TODO: Implement distinguishment between the above two cases if // necessary - if (combKalmanResult.result.ok() and not combKalmanResult.finished) { - combKalmanResult.result = Result( + if (combKalmanResult.lastError.ok() and not combKalmanResult.finished) { + combKalmanResult.lastError = Result( CombinatorialKalmanFilterError::PropagationReachesMaxSteps); } - if (!combKalmanResult.result.ok()) { + if (!combKalmanResult.lastError.ok()) { ACTS_ERROR("CombinatorialKalmanFilter failed: " - << combKalmanResult.result.error() << " " - << combKalmanResult.result.error().message() + << combKalmanResult.lastError.error() << " " + << combKalmanResult.lastError.error().message() << " with the initial parameters: \n" << initialParameters.parameters()); - return combKalmanResult.result.error(); } std::vector tracks; for (auto tip : combKalmanResult.lastMeasurementIndices) { + auto it = combKalmanResult.fittedParameters.find(tip); + if (it == combKalmanResult.fittedParameters.end()) { + continue; + } + auto track = trackContainer.getTrack(trackContainer.addTrack()); track.tipIndex() = tip; - const BoundTrackParameters& parameters = - combKalmanResult.fittedParameters.find(tip)->second; + + const BoundTrackParameters& parameters = it->second; track.parameters() = parameters.parameters(); track.covariance() = *parameters.covariance(); track.setReferenceSurface(parameters.referenceSurface().getSharedPtr()); diff --git a/Core/include/Acts/TrackFinding/FasTrackConnector.hpp b/Core/include/Acts/TrackFinding/FasTrackConnector.hpp new file mode 100644 index 00000000000..658dbae9271 --- /dev/null +++ b/Core/include/Acts/TrackFinding/FasTrackConnector.hpp @@ -0,0 +1,53 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +// Consider to moving to detail subdirectory +#include +#include +#include +#include + +namespace Acts { + +struct FasTrackConnection { + public: + FasTrackConnection(unsigned int s, unsigned int d); + + unsigned int m_src, m_dst; + std::vector m_binTable; +}; + +class FasTrackConnector { + public: + struct LayerGroup { + LayerGroup(unsigned int l1Key, + const std::vector &v) + : m_dst(l1Key), m_sources(v) {} + + unsigned int m_dst; // the target layer of the group + std::vector + m_sources; // the source layers of the group + }; + + FasTrackConnector(std::ifstream &inFile); + + ~FasTrackConnector(); + + float m_etaBin{}; + + std::map> m_layerGroups; + std::map> m_connMap; + // TODO: change to std::map > + // m_connMap; or std::map> > m_connMap; +}; + +} // namespace Acts diff --git a/Core/include/Acts/TrackFinding/RoiDescriptor.hpp b/Core/include/Acts/TrackFinding/RoiDescriptor.hpp new file mode 100644 index 00000000000..33f8c5d2ac5 --- /dev/null +++ b/Core/include/Acts/TrackFinding/RoiDescriptor.hpp @@ -0,0 +1,212 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include +#include +#include +#include +#include + +#include + +namespace Acts { + +class RoiDescriptor { + public: + // iterator + using roi_iterator = std::vector::const_iterator; + /// convenient + static constexpr bool FULLSCAN = true; + static constexpr bool ROI = false; + + /** + * @brief constructor + * @param eta eta of RoI + * @param etaMinus eta at rear of RoI + * @param etaPlus eta at front of RoI + * @param phi phi of RoI + * @param phiMinus minimum phi of RoI + * @param phiPlus maximum phi of RoI + * @param zed zed of RoI + * @param zedMinus zed at rear of RoI + * @param zedPlus zed at front of RoI + */ + RoiDescriptor(double eta, double etaMinus, double etaPlus, double phi, + double phiMinus, double phiPlus, double zed = 0, + double zedMinus = -s_zedWidthDefault, + double zedPlus = s_zedWidthDefault); + // zedminus - s_zedWidthDefault = 225 //from ROIDescriptor + + /* + * need an explicit class copy constructor + */ + RoiDescriptor(const RoiDescriptor& roi); + RoiDescriptor& operator=(const RoiDescriptor& r); + + // Destructor + ~RoiDescriptor(); + + // Methods to retrieve data members + + double phi() const { return m_phi; } + double eta() const { return m_eta; } + double zed() const { return m_zed; } + + /// these quantities probably don't need to be used any more + /// - they are implemented here only because we had them in + /// the original legacy interface + + double zedPlus() const { + return m_zedPlus; + } //!< z at the most forward end of the RoI + double zedMinus() const { + return m_zedMinus; + } //!< z at the most backward end of the RoI + + double etaPlus() const { return m_etaPlus; } //!< gets eta at zedPlus + double etaMinus() const { return m_etaMinus; } //!< gets eta at zMinus + + double phiPlus() const { return m_phiPlus; } //!< gets phiPlus + double phiMinus() const { return m_phiMinus; } //!< gets phiMinus + + /// versioning + int version() const { return m_version; } + void version(int v) { m_version = v; } + + /// output + // virtual operator std::string() const ; + + /// is this a full scan RoI? + bool isFullscan() const { return m_fullscan; } + + /// SuperRoI compatibility methods + + /// am I a SuperRoi? + bool composite() const { return m_composite; } + void setComposite(bool b = true) { m_composite = b; } + + /// always manage constituents ??? + bool manageConstituents() const { return m_manageConstituents; } + void manageConstituents(bool b) { m_manageConstituents = b; } + + /// number of constituents + unsigned size() const { return m_roiDescriptors.size(); } + + /// find an RoiDescriptor constituent + const RoiDescriptor* at(int i) const { return m_roiDescriptors.at(i); } + + /// clear the vector + void clear() { m_roiDescriptors.clear(); } // setComposite(false); } + + /// reserve elements in vector + void reserve(size_t s) { m_roiDescriptors.reserve(s); } + + /// add a RoiDescriptor + void push_back(const RoiDescriptor* roi) { + m_roiDescriptors.push_back(roi); + setComposite(true); + } + + /// iterators + roi_iterator begin() const { return m_roiDescriptors.begin(); } + roi_iterator end() const { return m_roiDescriptors.end(); } + + /// return the gradients + double dzdrMinus() const { + return m_dzdrMinus; + } //!< dz/dr at the rear of the RoI + double dzdrPlus() const { + return m_dzdrPlus; + } //!< dz/dr at the front of the RoI + + double drdzMinus() const { + return m_drdzMinus; + } //!< dr/dz at the rear of the RoI + double drdzPlus() const { + return m_drdzPlus; + } //!< dr/dz at the front of the RoI + + /// methods to calculate z position at the RoI boundary + /// at a given radius + double zedMin(double r) const; + double zedMax(double r) const; + + double zedOuterPlus() const { + return m_zedOuterPlus; + } //!< z at the most forward end of the RoI + double zedOuterMinus() const { + return m_zedOuterMinus; + } //!< z at the most backward end of the RoI + + double rhoMin(double z) const; + double rhoMax(double z) const; + + static double zedWidthDefault() { return s_zedWidthDefault; } + + /// set default z-width (but only before any RoiDescriptor has been created) + static void zedWidthDefault(double d); + + // fromn trig + unsigned int roiId() const { return m_roiId; } + unsigned int l1Id() const { return m_l1Id; } + unsigned int roiWord() const { return m_roiWord; } + + private: + /// default parameters - there may be better ways, but this will do + static std::atomic s_zedWidthDefault; + /// to ensure default width is only set once at job startup + static std::atomic s_firstInstanceCreated; + + float m_phi{}; //!< phi of RoI center + float m_eta{}; //!< eta of RoI center + float m_zed{}; //!< zed of RoI center + + float m_phiMinus{}; //!< most negative RoI in azimuthal + float m_phiPlus{}; //!< most positive RoI in azimuthal + float m_etaMinus{}; //!< eta of RoI at zedMinus + float m_etaPlus{}; //!< eta of RoI at zedPlus + float m_zedMinus{}; //!< z position at most negative position along the + //!< beamline + float + m_zedPlus{}; //!< z position at most positive position along the beamline + + float m_dzdrMinus{}; //!< dz/dr at the rear of the RoI + float m_dzdrPlus{}; //!< dz/dr at the front of the RoI + + float m_drdzMinus{}; //!< dr/dz at the rear of the RoI + float m_drdzPlus{}; //!< dr/dz at the front of the RoI + + float + m_zedOuterMinus{}; //!< z at rear of RoI at the outer radius ( = 1100 mm) + float + m_zedOuterPlus{}; //!< z at front of RoI at the outer radius ( = 1100 mm) + + bool m_fullscan{}; //!< flag this as a full detector RoI + bool m_composite{}; //!< flag this as a composite RoI + bool m_manageConstituents{}; //!< flag to determine whether constituents + //!< should be managed + + int m_version{}; //!< transient version identifier + + std::vector m_roiDescriptors; //!< roi constituents + + // from trig + unsigned int m_l1Id{0}; //!< lvl1 event number + unsigned int m_roiId{0}; //!< RoI number + unsigned int m_roiWord{0}; //!< lvl1 RoI word from which this RoI was + //!< initially constructed + + // std::string str( const RoiDescriptor& d ); //; + using Actors = ActionList; using Aborters = AbortList<>; std::vector backwardSequence( @@ -161,7 +160,7 @@ struct GaussianSumFitter { // Initialize the backward propagation with the DirectNavigator auto bwdPropInitializer = [this](const auto& opts) { - using Actors = ActionList; + using Actors = ActionList; using Aborters = AbortList; PropagatorOptions propOptions(opts.geoContext, @@ -265,7 +264,8 @@ struct GaussianSumFitter { detail::IsMultiComponentBoundParameters; typename propagator_t::template action_list_t_result_t< - CurvilinearTrackParameters, decltype(fwdPropOptions.actionList)> + MultiComponentCurvilinearTrackParameters, + decltype(fwdPropOptions.actionList)> inputResult; auto& r = inputResult.template get(); @@ -277,7 +277,7 @@ struct GaussianSumFitter { if constexpr (not IsMultiParameters::value) { MultiComponentBoundTrackParameters params( sParameters.referenceSurface().getSharedPtr(), - sParameters.parameters(), sParameters.covariance(), + sParameters.parameters(), *sParameters.covariance(), sParameters.particleHypothesis()); return m_propagator.propagate(params, fwdPropOptions, false, @@ -336,7 +336,8 @@ struct GaussianSumFitter { using PM = TrackStatePropMask; typename propagator_t::template action_list_t_result_t< - BoundTrackParameters, decltype(bwdPropOptions.actionList)> + MultiComponentBoundTrackParameters, + decltype(bwdPropOptions.actionList)> inputResult; // Unfortunately we must construct the result type here to be able to @@ -447,17 +448,23 @@ struct GaussianSumFitter { if (options.referenceSurface) { const auto& params = *bwdResult->endParameters; - track.parameters() = params.parameters(); - track.covariance() = params.covariance().value(); + + const auto [finalPars, finalCov] = Acts::reduceGaussianMixture( + params.components(), params.referenceSurface(), + options.stateReductionMethod, [](auto& t) { + return std::tie(std::get<0>(t), std::get<1>(t), *std::get<2>(t)); + }); + + track.parameters() = finalPars; + track.covariance() = finalCov; + track.setReferenceSurface(params.referenceSurface().getSharedPtr()); if (trackContainer.hasColumn( hashString(GsfConstants::kFinalMultiComponentStateColumn))) { ACTS_DEBUG("Add final multi-component state to track") - const auto& fsr = bwdResult->template get< - Acts::detail::FinalStateCollector::result_type>(); track.template component( - GsfConstants::kFinalMultiComponentStateColumn) = fsr.pars; + GsfConstants::kFinalMultiComponentStateColumn) = std::move(params); } } diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index 5a0b75cf327..125d7ca3b71 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -78,7 +78,6 @@ struct Gx2FitterExtensions { /// Retrieves the associated surface from a source link SourceLinkSurfaceAccessor surfaceAccessor; - // @TODO get an own Calibrator and Updater /// Default constructor which connects the default void components Gx2FitterExtensions() { calibrator.template connect<&detail::voidFitterCalibrator>(); @@ -235,11 +234,15 @@ void collector(typename traj_t::TrackStateProxy& trackStateProxy, auto measurement = trackStateProxy.template calibrated(); auto covarianceMeasurement = trackStateProxy.template calibratedCovariance(); - // calculate residuals and return with covariances and jacobians - auto projJacobian = - (trackStateProxy.effectiveProjector() * result.jacobianFromStart).eval(); - auto projPredicted = - (trackStateProxy.effectiveProjector() * predicted).eval(); + // Project Jacobian and predicted measurements into the measurement dimensions + auto projJacobian = (trackStateProxy.projector() + .template topLeftCorner() * + result.jacobianFromStart) + .eval(); + auto projPredicted = (trackStateProxy.projector() + .template topLeftCorner() * + predicted) + .eval(); ACTS_VERBOSE("Processing and collecting measurements in Actor:\n" << "\tMeasurement:\t" << measurement.transpose() @@ -248,6 +251,7 @@ void collector(typename traj_t::TrackStateProxy& trackStateProxy, << "\n\tProjected Jacobian:\t" << projJacobian << "\n\tCovariance Measurements:\t" << covarianceMeasurement); + // Collect residuals, covariances, and projected jacobians for (size_t i = 0; i < measDim; i++) { if (covarianceMeasurement(i, i) < 1e-10) { ACTS_WARNING("Invalid covariance of measurement: cov(" << i << "," << i @@ -345,6 +349,13 @@ class Gx2Fitter { /// Calibration context for the fit const CalibrationContext* calibrationContext{nullptr}; + /// The current iteration of the fitter. + /// The variable is updated in fit(). + /// The actor needs to know the current iteration for adding new + /// trackStates. During the first iteration, each measurement surfaces will + /// be added to the track. + size_t nUpdate = Acts::MultiTrajectoryTraits::kInvalid; + /// @brief Gx2f actor operation /// /// @tparam propagator_state_t is the type of Propagator state @@ -391,38 +402,108 @@ class Gx2Fitter { auto sourcelink_it = inputMeasurements->find(surface->geometryId()); if (sourcelink_it != inputMeasurements->end()) { + ACTS_VERBOSE("Measurement surface " << surface->geometryId() + << " detected."); + + // Transport the covariance to the surface stepper.transportCovarianceToBound(state.stepping, *surface, freeToBoundCorrection); - auto res = stepper.boundState(state.stepping, *surface, false, - freeToBoundCorrection); - if (!res.ok()) { - std::cout << "dbgActor: res = stepper.boundState res not ok" - << std::endl; - return; - } - auto& [boundParams, jacobian, pathLength] = *res; - result.jacobianFromStart = jacobian * result.jacobianFromStart; - // add a full TrackState entry multi trajectory - // (this allocates storage for all components, we will set them later) - auto fittedStates = *result.fittedStates; - const auto newTrackIndex = fittedStates.addTrackState( - TrackStatePropMask::All, result.lastTrackIndex); + ACTS_VERBOSE( + "Actor - indices before processing:" + << "\n\t" + << "result.lastMeasurementIndex: " << result.lastMeasurementIndex + << "\n\t" + << "result.lastTrackIndex: " << result.lastTrackIndex << "\n\t" + << "result.fittedStates->size(): " << result.fittedStates->size()) + + // TODO generalize the update of the currentTrackIndex + auto& fittedStates = *result.fittedStates; + + // Mask for the track states. We don't need Smoothed and Filtered + TrackStatePropMask mask = + ~(TrackStatePropMask::Smoothed | TrackStatePropMask::Filtered); + + // Checks if an existing surface is found during the gx2f-iteration. + // If not, a new index will be generated afterwards. + // During the first iteration, we will always create a new index. + size_t currentTrackIndex = Acts::MultiTrajectoryTraits::kInvalid; + if (nUpdate == 0) { + ACTS_VERBOSE(" processSurface: nUpdate == 0 decision"); + + // Add a TrackState entry multi trajectory. This allocates + // storage for all components, which we will set later. + currentTrackIndex = + fittedStates.addTrackState(mask, result.lastTrackIndex); + } else { + ACTS_VERBOSE(" processSurface: nUpdate > 0 decision"); + + if (result.lastTrackIndex == + Acts::MultiTrajectoryTraits::kInvalid) { + currentTrackIndex = 0; + ACTS_VERBOSE(" processSurface: currentTrackIndex (kInv->0) = " + << currentTrackIndex); + } else if (result.lastTrackIndex < fittedStates.size() - 1) { + currentTrackIndex = result.lastTrackIndex + 1; + ACTS_VERBOSE(" processSurface: currentTrackIndex (n+1) = " + << currentTrackIndex); + } else { + // Add a TrackState entry multi trajectory. This allocates + // storage for all components, which we will set later. + currentTrackIndex = + fittedStates.addTrackState(mask, result.lastTrackIndex); + ACTS_VERBOSE(" processSurface: currentTrackIndex (ADD NEW)= " + << currentTrackIndex); + } + } // now get track state proxy back - auto trackStateProxy = fittedStates.getTrackState(newTrackIndex); - trackStateProxy.setReferenceSurface(surface->getSharedPtr()); - // assign the source link to the track state - trackStateProxy.setUncalibratedSourceLink(sourcelink_it->second); - - // Fill the track state - trackStateProxy.predicted() = std::move(boundParams.parameters()); + typename traj_t::TrackStateProxy trackStateProxy = + fittedStates.getTrackState(currentTrackIndex); + + // Set the trackStateProxy components with the state from the ongoing + // propagation + { + trackStateProxy.setReferenceSurface(surface->getSharedPtr()); + // Bind the transported state to the current surface + auto res = stepper.boundState(state.stepping, *surface, false, + freeToBoundCorrection); + if (!res.ok()) { + result.result = res.error(); + return; + } + auto& [boundParams, jacobian, pathLength] = *res; + + // Fill the track state + trackStateProxy.predicted() = std::move(boundParams.parameters()); + if (boundParams.covariance().has_value()) { + trackStateProxy.predictedCovariance() = + std::move(*boundParams.covariance()); + } + + trackStateProxy.jacobian() = std::move(jacobian); + trackStateProxy.pathLength() = std::move(pathLength); + } // We have predicted parameters, so calibrate the uncalibrated input // measurement extensions.calibrator(state.geoContext, *calibrationContext, sourcelink_it->second, trackStateProxy); + // Get and set the type flags + auto typeFlags = trackStateProxy.typeFlags(); + typeFlags.set(TrackStateFlag::ParameterFlag); + if (surface->surfaceMaterial() != nullptr) { + typeFlags.set(TrackStateFlag::MaterialFlag); + } + + result.jacobianFromStart = + trackStateProxy.jacobian() * result.jacobianFromStart; + + // Collect: + // - Residuals + // - Covariances + // - ProjectedJacobians if (trackStateProxy.calibratedSize() == 1) { collector<1>(trackStateProxy, result, *actorLogger); } else if (trackStateProxy.calibratedSize() == 2) { @@ -432,17 +513,27 @@ class Gx2Fitter { "Only measurements of 1 and 2 dimensions are implemented yet."); } - if (boundParams.covariance().has_value()) { - trackStateProxy.predictedCovariance() = - std::move(*boundParams.covariance()); - } - - trackStateProxy.jacobian() = std::move(jacobian); - trackStateProxy.pathLength() = std::move(pathLength); + // Set the measurement type flag + typeFlags.set(TrackStateFlag::MeasurementFlag); + // We count the processed state + ++result.processedStates; + ACTS_VERBOSE("Actor - indices after processing, before over writing:" + << "\n\t" + << "result.lastMeasurementIndex: " + << result.lastMeasurementIndex << "\n\t" + << "trackStateProxy.index(): " << trackStateProxy.index() + << "\n\t" + << "result.lastTrackIndex: " << result.lastTrackIndex + << "\n\t" + << "currentTrackIndex: " << currentTrackIndex) + result.lastMeasurementIndex = currentTrackIndex; + result.lastTrackIndex = currentTrackIndex; + } else { + ACTS_INFO("Actor: This case is not implemented yet") } } - if (result.surfaceCount > 17) { + if (result.surfaceCount > 900) { ACTS_INFO("Actor: finish due to limit. Result might be garbage."); result.finished = true; } @@ -524,12 +615,17 @@ class Gx2Fitter { using PropagatorOptions = Acts::PropagatorOptions; const size_t reducedMatrixSize = 4; - Acts::CurvilinearTrackParameters params = sParameters; + start_parameters_t params = sParameters; BoundVector deltaParams = BoundVector::Zero(); double chi2sum = 0; BoundMatrix aMatrix = BoundMatrix::Zero(); BoundVector bVector = BoundVector::Zero(); + // Create a index of the 'tip' of the track stored in multitrajectory. It is + // needed outside the update loop. It will be updated with each iteration + // and used for the final track + size_t tipIndex = Acts::MultiTrajectoryTraits::kInvalid; + ACTS_VERBOSE("params:\n" << params); /// Actual Fitting ///////////////////////////////////////////////////////// @@ -555,6 +651,7 @@ class Gx2Fitter { gx2fActor.extensions = gx2fOptions.extensions; gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get(); gx2fActor.actorLogger = m_actorLogger.get(); + gx2fActor.nUpdate = nUpdate; typename propagator_t::template action_list_t_result_t< CurvilinearTrackParameters, Actors> @@ -570,7 +667,7 @@ class Gx2Fitter { // TODO Improve Propagator + Actor [allocate before loop], rewrite // makeMeasurements auto& propRes = *result; - auto gx2fResult = std::move(propRes.template get()); + GX2FResult gx2fResult = std::move(propRes.template get()); ACTS_VERBOSE("gx2fResult.collectorResiduals.size() = " << gx2fResult.collectorResiduals.size()); @@ -617,6 +714,8 @@ class Gx2Fitter { ACTS_VERBOSE("bVector:\n" << bVector); ACTS_VERBOSE("deltaParams:\n" << deltaParams); + tipIndex = gx2fResult.lastMeasurementIndex; + // TODO check delta params and abort // similar to: // if (sum(delta_params) < 1e-3) { @@ -642,14 +741,13 @@ class Gx2Fitter { // Prepare track for return auto track = trackContainer.getTrack(trackContainer.addTrack()); + track.tipIndex() = tipIndex; track.parameters() = params.parameters(); track.covariance() = fullCovariancePredicted; - // TODO track.tipIndex() = gx2fResult.lastMeasurementIndex; - // TODO track.setReferenceSurface(params.referenceSurface().getSharedPtr()); - // TODO track.nMeasurements() = gx2fResult.measurementStates; - // TODO track.nHoles() = gx2fResult.measurementHoles; - calculateTrackQuantities( - track); // TODO write test for calculateTrackQuantities + track.setReferenceSurface(params.referenceSurface().getSharedPtr()); + + // TODO write test for calculateTrackQuantities + calculateTrackQuantities(track); // Return the converted Track return track; diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index d8b897bd143..06837077f9d 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -46,6 +46,16 @@ namespace Acts { +enum class KalmanFitterTargetSurfaceStrategy { + /// Use the first trackstate to reach target surface + first, + /// Use the last trackstate to reach target surface + last, + /// Use the first or last trackstate to reach target surface depending on the + /// distance + firstOrLast, +}; + /// Extension struct which holds delegates to customise the KF behavior template struct KalmanFitterExtensions { @@ -157,6 +167,10 @@ struct KalmanFitterOptions { /// The reference Surface const Surface* referenceSurface = nullptr; + /// Strategy to propagate to reference surface + KalmanFitterTargetSurfaceStrategy referenceSurfaceStrategy = + KalmanFitterTargetSurfaceStrategy::firstOrLast; + /// Whether to consider multiple scattering bool multipleScattering = true; @@ -293,6 +307,10 @@ class KalmanFitter { /// The target surface const Surface* targetSurface = nullptr; + /// Strategy to propagate to target surface + KalmanFitterTargetSurfaceStrategy targetSurfaceStrategy = + KalmanFitterTargetSurfaceStrategy::firstOrLast; + /// Allows retrieving measurements for a surface const std::map* inputMeasurements = nullptr; @@ -925,10 +943,8 @@ class KalmanFitter { return KalmanFitterError::SmoothFailed; } // Screen output for debugging - if (logger().doPrint(Logging::VERBOSE)) { - ACTS_VERBOSE("Apply smoothing on " << nStates - << " filtered track states."); - } + ACTS_VERBOSE("Apply smoothing on " << nStates + << " filtered track states."); // Smooth the track states auto smoothRes = @@ -972,18 +988,31 @@ class KalmanFitter { const auto lastIntersection = target(lastParams); // Update the stepping parameters - in order to progress to destination. - // At the same time, reverse navigation direction for further - // stepping if necessary. + // At the same time, reverse navigation direction for further stepping if + // necessary. // @note The stepping parameters is updated to the smoothed parameters at // either the first measurement state or the last measurement state. It // assumes the target surface is not within the first and the last // smoothed measurement state. Also, whether the intersection is on // surface is not checked here. - bool closerToFirstCreatedState = - std::abs(firstIntersection.pathLength()) <= - std::abs(lastIntersection.pathLength()); + bool useFirstTrackState = true; + switch (targetSurfaceStrategy) { + case KalmanFitterTargetSurfaceStrategy::first: + useFirstTrackState = true; + break; + case KalmanFitterTargetSurfaceStrategy::last: + useFirstTrackState = false; + break; + case KalmanFitterTargetSurfaceStrategy::firstOrLast: + useFirstTrackState = std::abs(firstIntersection.pathLength()) <= + std::abs(lastIntersection.pathLength()); + break; + default: + ACTS_ERROR("Unknown target surface strategy"); + return KalmanFitterError::SmoothFailed; + } bool reverseDirection = false; - if (closerToFirstCreatedState) { + if (useFirstTrackState) { stepper.resetState(state.stepping, firstCreatedState.smoothed(), firstCreatedState.smoothedCovariance(), firstCreatedState.referenceSurface(), @@ -1003,9 +1032,10 @@ class KalmanFitter { "target surface"); state.options.direction = state.options.direction.invert(); } - const auto& surface = closerToFirstCreatedState + const auto& surface = useFirstTrackState ? firstCreatedState.referenceSurface() : lastCreatedMeasurement.referenceSurface(); + ACTS_VERBOSE( "Smoothing successful, updating stepping state to smoothed " "parameters at surface " @@ -1239,6 +1269,7 @@ class KalmanFitter { auto& kalmanActor = kalmanOptions.actionList.template get(); kalmanActor.inputMeasurements = &inputMeasurements; kalmanActor.targetSurface = kfOptions.referenceSurface; + kalmanActor.targetSurfaceStrategy = kfOptions.referenceSurfaceStrategy; kalmanActor.multipleScattering = kfOptions.multipleScattering; kalmanActor.energyLoss = kfOptions.energyLoss; kalmanActor.reversedFiltering = kfOptions.reversedFiltering; diff --git a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp index 5b72a3a8935..abe2b787c0a 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" @@ -771,46 +771,5 @@ struct GsfActor { } }; -/// An actor that collects the final multi component state once the propagation -/// finished -struct FinalStateCollector { - using MultiPars = Acts::GsfConstants::FinalMultiComponentState; - - struct result_type { - MultiPars pars; - }; - - template - void operator()(propagator_state_t& state, const stepper_t& stepper, - const navigator_t& navigator, result_type& result, - const Logger& /*logger*/) const { - if (not(navigator.targetReached(state.navigation) and - navigator.currentSurface(state.navigation))) { - return; - } - - const auto& surface = *navigator.currentSurface(state.navigation); - std::vector< - std::tuple>> - states; - - for (auto cmp : stepper.componentIterable(state.stepping)) { - auto singleState = cmp.singleState(state); - auto bs = cmp.singleStepper(stepper).boundState(singleState.stepping, - surface, true); - - if (bs.ok()) { - const auto& btp = std::get(*bs); - states.emplace_back(cmp.weight(), btp.parameters(), btp.covariance()); - } - } - - result.pars = typename MultiPars::value_type( - surface.getSharedPtr(), states, - stepper.particleHypothesis(state.stepping)); - } -}; - } // namespace detail } // namespace Acts diff --git a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp index 919d591abd0..5d38f44a6b6 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp @@ -10,7 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Utilities/Logger.hpp" diff --git a/Core/include/Acts/TrackFitting/detail/SymmetricKlDistanceMatrix.hpp b/Core/include/Acts/TrackFitting/detail/SymmetricKlDistanceMatrix.hpp index cd63e8cdb35..3c511091e95 100644 --- a/Core/include/Acts/TrackFitting/detail/SymmetricKlDistanceMatrix.hpp +++ b/Core/include/Acts/TrackFitting/detail/SymmetricKlDistanceMatrix.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/TrackFitting/detail/GsfUtils.hpp" diff --git a/Core/include/Acts/Utilities/FiniteStateMachine.hpp b/Core/include/Acts/Utilities/FiniteStateMachine.hpp index c7458b318f1..8dff97c9a26 100644 --- a/Core/include/Acts/Utilities/FiniteStateMachine.hpp +++ b/Core/include/Acts/Utilities/FiniteStateMachine.hpp @@ -121,12 +121,12 @@ class FiniteStateMachine { /// Default constructor. The default state is taken to be the first in the /// `States` template arguments FiniteStateMachine() - : m_state( - typename std::tuple_element<0, std::tuple>::type{}){}; + : m_state(typename std::tuple_element<0, std::tuple>::type{}) { + } /// Constructor from an explicit state. The FSM is initialized to this state. /// @param state Initial state for the FSM. - FiniteStateMachine(StateVariant state) : m_state(std::move(state)){}; + FiniteStateMachine(StateVariant state) : m_state(std::move(state)) {} /// Get the current state of the FSM (as a variant). /// @return StateVariant The current state of the FSM. diff --git a/Core/include/Acts/Utilities/Result.hpp b/Core/include/Acts/Utilities/Result.hpp index 32dc01e1008..f865ee1e64c 100644 --- a/Core/include/Acts/Utilities/Result.hpp +++ b/Core/include/Acts/Utilities/Result.hpp @@ -39,7 +39,7 @@ class Result { Result& operator=(const Result& other) = delete; /// Move construction is allowed - Result(Result&& other) : m_var(std::move(other.m_var)){}; + Result(Result&& other) : m_var(std::move(other.m_var)) {} /// Move assignment is allowed /// @param other The other result instance, rvalue reference @@ -117,17 +117,33 @@ class Result { /// @return Reference to value stored in the variant. T& operator*() noexcept { return std::get(m_var); } + /// Returns a reference into the variant to the valid value. + /// @note If `!res.ok()`, this method will abort (noexcept) + /// @return Reference to value stored in the variant. + const T& operator*() const noexcept { return std::get(m_var); } + /// Allows to access members of the stored object with `res->foo` /// similar to `std::optional`. /// @note If `!res.ok()`, this method will abort (noexcept) /// @return Pointer to value stored in the variant. T* operator->() noexcept { return &std::get(m_var); } + /// Allows to access members of the stored object with `res->foo` + /// similar to `std::optional`. + /// @note If `!res.ok()`, this method will abort (noexcept) + /// @return Pointer to value stored in the variant. + const T* operator->() const noexcept { return &std::get(m_var); } + /// Returns a reference to the error stored in the result. /// @note If `res.ok()` this method will abort (noexcept) /// @return Reference to the error E& error() & noexcept { return std::get(m_var); } + /// Returns a reference to the error stored in the result. + /// @note If `res.ok()` this method will abort (noexcept) + /// @return Reference to the error + const E& error() const& noexcept { return std::get(m_var); } + /// Returns the error by-value. /// @note If `res.ok()` this method will abort (noexcept) /// @return The error @@ -137,18 +153,15 @@ class Result { /// @note This is the lvalue version, returns a reference to the value /// @return The valid value as a reference T& value() & { - if (m_var.index() != 0) { - if constexpr (std::is_same_v) { - std::stringstream ss; - const auto& e = std::get(m_var); - ss << "Value called on error value: " << e.category().name() << ": " - << e.message() << " [" << e.value() << "]"; - throw std::runtime_error(ss.str()); - } else { - throw std::runtime_error("Value called on error value"); - } - } + checkValueAccess(); + return std::get(m_var); + } + /// Retrieves the valid value from the result object. + /// @note This is the lvalue version, returns a reference to the value + /// @return The valid value as a reference + const T& value() const& { + checkValueAccess(); return std::get(m_var); } @@ -157,6 +170,14 @@ class Result { /// by-value and moves out of the variant. /// @return The valid value by value, moved out of the variant. T value() && { + checkValueAccess(); + return std::move(std::get(m_var)); + } + + private: + std::variant m_var; + + void checkValueAccess() const { if (m_var.index() != 0) { if constexpr (std::is_same_v) { std::stringstream ss; @@ -168,12 +189,7 @@ class Result { throw std::runtime_error("Value called on error value"); } } - - return std::move(std::get(m_var)); } - - private: - std::variant m_var; }; /// Template specialization for the void case. @@ -202,7 +218,7 @@ class Result { /// Move constructor /// @param other The other result object, rvalue ref - Result(Result&& other) : m_opt(std::move(other.m_opt)){}; + Result(Result&& other) : m_opt(std::move(other.m_opt)) {} /// Move assignment operator /// @param other The other result object, rvalue ref @@ -248,6 +264,11 @@ class Result { /// @return Reference to the error E& error() & noexcept { return m_opt.value(); } + /// Returns a reference to the error stored in the result. + /// @note If `res.ok()` this method will abort (noexcept) + /// @return Reference to the error + const E& error() const& noexcept { return m_opt.value(); } + /// Returns the error by-value. /// @note If `res.ok()` this method will abort (noexcept) /// @return Reference to the error diff --git a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.hpp b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.hpp index 11ea1b51c0a..5bd7b12b94f 100644 --- a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.hpp +++ b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.hpp @@ -82,9 +82,6 @@ class HelicalTrackLinearizer { /// Tolerance determining how close we need to get to the Perigee surface to /// reach it during propagation ActsScalar targetTolerance = 1e-12; - - // Minimum q/p value - double minQoP = 1e-15; }; /// @brief Constructor diff --git a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp index 8244bac9dc2..943dea07b61 100644 --- a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp +++ b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp @@ -74,21 +74,21 @@ Acts::Result Acts:: // q over p ActsScalar qOvP = paramsAtPCA(BoundIndices::eBoundQOverP); - + // Rest mass ActsScalar m0 = params.particleHypothesis().mass(); - ActsScalar p = std::abs(params.particleHypothesis().absoluteCharge() / qOvP); + // Momentum + ActsScalar p = params.particleHypothesis().extractMomentum(qOvP); // Speed in units of c ActsScalar beta = p / std::hypot(p, m0); // Transverse speed (i.e., speed in the x-y plane) ActsScalar betaT = beta * sinTheta; - // Momentu at the PCA + // Momentum direction at the PCA Vector3 momentumAtPCA(phi, theta, qOvP); - // Complete Jacobian (consists of positionJacobian and momentumJacobian) - ActsMatrix completeJacobian = - ActsMatrix::Zero(eBoundSize, eLinSize); + // Particle charge + ActsScalar absoluteCharge = params.particleHypothesis().absoluteCharge(); // get the z-component of the B-field at the PCA auto field = @@ -98,9 +98,14 @@ Acts::Result Acts:: } ActsScalar Bz = (*field)[eZ]; - // If there is no magnetic field the particle has a straight trajectory - // If there is a constant magnetic field it has a helical trajectory - if (Bz == 0. || std::abs(qOvP) < m_cfg.minQoP) { + // Complete Jacobian (consists of positionJacobian and momentumJacobian) + ActsMatrix completeJacobian = + ActsMatrix::Zero(eBoundSize, eLinSize); + + // The particle moves on a straight trajectory if its charge is 0 or if there + // is no B field. Conversely, if it has a charge and the B field is constant, + // it moves on a helical trajectory. + if (absoluteCharge == 0. or Bz == 0.) { // Derivatives can be found in Eqs. 5.39 and 5.40 of Ref. (1). // Since we propagated to the PCA (point P in Ref. (1)), we evaluate the // Jacobians there. One can show that, in this case, RTilde = 0 and QTilde = diff --git a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp index a5a698e0295..1fc3b2830d7 100644 --- a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp +++ b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp @@ -276,7 +276,7 @@ Acts::ImpactPointEstimator:: double timeOnTrack = trkParams.parameters()[BoundIndices::eBoundTime]; ActsScalar m0 = trkParams.particleHypothesis().mass(); - ActsScalar p = std::abs(absoluteCharge / qOvP); + ActsScalar p = trkParams.particleHypothesis().extractMomentum(qOvP); // Speed in units of c ActsScalar beta = p / std::hypot(p, m0); @@ -350,7 +350,7 @@ Acts::ImpactPointEstimator:: double tP = trkParams.parameters()[BoundIndices::eBoundTime]; ActsScalar m0 = trkParams.particleHypothesis().mass(); - ActsScalar p = std::abs(absoluteCharge / qOvP); + ActsScalar p = trkParams.particleHypothesis().extractMomentum(qOvP); // Speed in units of c ActsScalar beta = p / std::hypot(p, m0); diff --git a/Core/src/Detector/CMakeLists.txt b/Core/src/Detector/CMakeLists.txt index 00ee9e9a813..4907baf86b9 100644 --- a/Core/src/Detector/CMakeLists.txt +++ b/Core/src/Detector/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources( ActsCore PRIVATE + detail/BlueprintHelper.cpp detail/CylindricalDetectorHelper.cpp detail/PortalHelper.cpp detail/SupportHelper.cpp diff --git a/Core/src/Detector/detail/BlueprintHelper.cpp b/Core/src/Detector/detail/BlueprintHelper.cpp new file mode 100644 index 00000000000..7e240f0efae --- /dev/null +++ b/Core/src/Detector/detail/BlueprintHelper.cpp @@ -0,0 +1,184 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Detector/detail/BlueprintHelper.hpp" + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Tolerance.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" + +#include + +namespace { + +std::array cylEndpointsZ( + const Acts::Experimental::Blueprint::Node& node) { + Acts::Vector3 axisZ = node.transform.rotation().col(2); + auto halfZ = node.boundaryValues[2]; + Acts::Vector3 center = node.transform.translation(); + Acts::Vector3 p0 = center - halfZ * axisZ; + Acts::Vector3 p1 = center + halfZ * axisZ; + return {p0, p1}; +} + +} // namespace + +void Acts::Experimental::detail::BlueprintHelper::sort(Blueprint::Node& node, + bool recursive) { + if (node.children.size() < 2u) { + return; + } + // Sort along x, y, z + if (node.binning.size() == 1) { + auto bVal = node.binning.front(); + // x,y,z binning along the axis + if (bVal == binX or bVal == binY or bVal == binZ) { + Vector3 nodeCenter = node.transform.translation(); + Vector3 nodeSortAxis = node.transform.rotation().col(bVal); + std::sort( + node.children.begin(), node.children.end(), + [&](const auto& a, const auto& b) { + return (a->transform.translation() - nodeCenter).dot(nodeSortAxis) < + (b->transform.translation() - nodeCenter).dot(nodeSortAxis); + }); + } else if (bVal == binR and node.boundsType == VolumeBounds::eCylinder) { + std::sort(node.children.begin(), node.children.end(), + [](const auto& a, const auto& b) { + return 0.5 * (a->boundaryValues[0] + a->boundaryValues[1]) < + 0.5 * (b->boundaryValues[0] + b->boundaryValues[1]); + }); + } + } + + // Sort the children + if (recursive) { + for (auto& child : node.children) { + sort(*child, true); + } + } +} + +void Acts::Experimental::detail::BlueprintHelper::fillGaps( + Blueprint::Node& node, bool adjustToParent) { + // Return if this is a leaf node + if (node.isLeaf()) { + return; + } + + if (node.boundsType == VolumeBounds::eCylinder) { + // Nodes must be sorted + sort(node, false); + + // Container values + auto cInnerR = node.boundaryValues[0]; + auto cOuterR = node.boundaryValues[1]; + auto cHalfZ = node.boundaryValues[2]; + + std::vector> gaps; + // Only 1D binning implemented for the moment + if (node.binning.size() == 1) { + auto bVal = node.binning.front(); + if (bVal == binZ) { + // adjust inner/outer radius + if (adjustToParent) { + std::for_each(node.children.begin(), node.children.end(), + [&](auto& child) { + child->boundaryValues[0] = cInnerR; + child->boundaryValues[1] = cOuterR; + }); + } + auto [negC, posC] = cylEndpointsZ(node); + // Assume sorted along the local z axis + unsigned int igap = 0; + for (auto& child : node.children) { + auto [neg, pos] = cylEndpointsZ(*child); + if ((neg - negC).norm() > s_onSurfaceTolerance) { + // Fill a gap node + auto gapName = node.name + "_gap_" + std::to_string(igap); + auto gapTransform = Transform3::Identity(); + gapTransform.rotate(node.transform.rotation()); + gapTransform.translate(0.5 * (neg + negC)); + auto gap = std::make_unique( + gapName, gapTransform, VolumeBounds::eCylinder, + std::vector{cInnerR, cOuterR, negC.z() - neg.z()}); + gaps.push_back(std::move(gap)); + ++igap; + } + // Set to new current negative value + negC = pos; + } + // Check if a last one needs to be filled + if ((negC - posC).norm() > s_onSurfaceTolerance) { + // Fill a gap node + auto gapName = node.name + "_gap_" + std::to_string(igap); + auto gapTransform = Transform3::Identity(); + gapTransform.rotate(node.transform.rotation()); + gapTransform.translate(0.5 * (negC + posC)); + auto gap = std::make_unique( + gapName, gapTransform, VolumeBounds::eCylinder, + std::vector{cInnerR, cOuterR, posC.z() - negC.z()}); + gaps.push_back(std::move(gap)); + } + + } else if (bVal == binR) { + // We have binning in R present + if (adjustToParent) { + std::for_each(node.children.begin(), node.children.end(), + [&](auto& child) { + child->transform = node.transform; + child->boundaryValues[2] = cHalfZ; + }); + } + // Fill the gaps in R + unsigned int igap = 0; + ActsScalar lastR = cInnerR; + for (auto& child : node.children) { + ActsScalar iR = child->boundaryValues[0]; + if (std::abs(iR - lastR) > s_onSurfaceTolerance) { + auto gap = std::make_unique( + node.name + "_gap_" + std::to_string(igap), node.transform, + VolumeBounds::eCylinder, + std::vector{lastR, iR, cHalfZ}); + gaps.push_back(std::move(gap)); + ++igap; + } + // Set to new outer radius + lastR = child->boundaryValues[1]; + } + // Check if a last one needs to be filled + if (std::abs(lastR - cOuterR) > s_onSurfaceTolerance) { + auto gap = std::make_unique( + node.name + "_gap_" + std::to_string(igap), node.transform, + VolumeBounds::eCylinder, + std::vector{lastR, cOuterR, cHalfZ}); + gaps.push_back(std::move(gap)); + } + } else { + throw std::runtime_error( + "BlueprintHelper: gap filling not implemented for " + "cylinder and this binning type."); + } + } + // Insert + for (auto& gap : gaps) { + node.add(std::move(gap)); + } + + // Sort again after inserting + sort(node, false); + // Fill the gaps recursively + for (auto& child : node.children) { + fillGaps(*child, adjustToParent); + } + + } else { + throw std::runtime_error( + "BlueprintHelper: gap filling not implemented for " + "this boundary type"); + } +} diff --git a/Core/src/Geometry/CuboidVolumeBuilder.cpp b/Core/src/Geometry/CuboidVolumeBuilder.cpp index 0e7c0df51e0..0d0abcb0528 100644 --- a/Core/src/Geometry/CuboidVolumeBuilder.cpp +++ b/Core/src/Geometry/CuboidVolumeBuilder.cpp @@ -169,6 +169,9 @@ std::shared_ptr Acts::CuboidVolumeBuilder::buildVolume( LayerConfig lCfg; lCfg.surfaceCfg = {sCfg}; + lCfg.envelopeX = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm}; + lCfg.envelopeY = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm}; + lCfg.envelopeZ = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm}; cfg.layerCfg.push_back(lCfg); } diff --git a/Core/src/Geometry/LayerCreator.cpp b/Core/src/Geometry/LayerCreator.cpp index b481614dc85..f1e14190546 100644 --- a/Core/src/Geometry/LayerCreator.cpp +++ b/Core/src/Geometry/LayerCreator.cpp @@ -87,7 +87,6 @@ Acts::MutableLayerPtr Acts::LayerCreator::cylinderLayer( // correctly defined using the halflength Translation3 addTranslation(0., 0., 0.); if (transform.isApprox(Transform3::Identity())) { - // double shift = -(layerZ + envZShift); addTranslation = Translation3(0., 0., layerZ); ACTS_VERBOSE(" - layer z shift = " << -layerZ); } @@ -344,11 +343,14 @@ Acts::MutableLayerPtr Acts::LayerCreator::planeLayer( layerThickness = (protoLayer.max(binY) - protoLayer.min(binY)); break; } - default: { + case BinningValue::binZ: { layerHalf1 = 0.5 * (protoLayer.max(binX) - protoLayer.min(binX)); layerHalf2 = 0.5 * (protoLayer.max(binY) - protoLayer.min(binY)); layerThickness = (protoLayer.max(binZ) - protoLayer.min(binZ)); + break; } + default: + throw std::invalid_argument("Invalid binning value"); } double centerX = 0.5 * (protoLayer.max(binX) + protoLayer.min(binX)); @@ -363,13 +365,15 @@ Acts::MutableLayerPtr Acts::LayerCreator::planeLayer( ACTS_VERBOSE(" - from Y min/max = " << protoLayer.min(binY) << " / " << protoLayer.max(binY)); ACTS_VERBOSE(" - with Z thickness = " << layerThickness); + ACTS_VERBOSE(" - incl envelope = " << protoLayer.envelope[bValue][0u] + << " / " + << protoLayer.envelope[bValue][1u]); // create the layer transforms if not given // we need to transform in case centerX/centerY/centerZ != 0, so that the // layer will be correctly defined Translation3 addTranslation(0., 0., 0.); if (transform.isApprox(Transform3::Identity())) { - // double shift = (layerZ + envZShift); addTranslation = Translation3(centerX, centerY, centerZ); ACTS_VERBOSE(" - layer shift = " << "(" << centerX << ", " << centerY << ", " << centerZ diff --git a/Core/src/Surfaces/DiscTrapezoidBounds.cpp b/Core/src/Surfaces/DiscTrapezoidBounds.cpp index d293b5a9921..d774cbd9313 100644 --- a/Core/src/Surfaces/DiscTrapezoidBounds.cpp +++ b/Core/src/Surfaces/DiscTrapezoidBounds.cpp @@ -64,10 +64,13 @@ std::vector Acts::DiscTrapezoidBounds::vertices( unsigned int /*lseg*/) const { Vector2 cAxis(std::cos(get(eAveragePhi)), std::sin(get(eAveragePhi))); Vector2 nAxis(cAxis.y(), -cAxis.x()); - return {get(eMinR) * cAxis - get(eHalfLengthXminR) * nAxis, - get(eMinR) * cAxis + get(eHalfLengthXminR) * nAxis, - m_ymax * cAxis + get(eHalfLengthXmaxR) * nAxis, - m_ymax * cAxis - get(eHalfLengthXmaxR) * nAxis}; + auto ymin = std::sqrt(get(eMinR) * get(eMinR) - + get(eHalfLengthXminR) * get(eHalfLengthXminR)); + auto halfY = (m_ymax - ymin) / 2; + return {-halfY * cAxis - get(eHalfLengthXminR) * nAxis, + -halfY * cAxis + get(eHalfLengthXminR) * nAxis, + halfY * cAxis + get(eHalfLengthXmaxR) * nAxis, + halfY * cAxis - get(eHalfLengthXmaxR) * nAxis}; } // ostream operator overload diff --git a/Core/src/TrackFinding/CMakeLists.txt b/Core/src/TrackFinding/CMakeLists.txt index aed8b9a1fd5..6b9cfe720e9 100644 --- a/Core/src/TrackFinding/CMakeLists.txt +++ b/Core/src/TrackFinding/CMakeLists.txt @@ -3,5 +3,7 @@ target_sources( PRIVATE CombinatorialKalmanFilterError.cpp MeasurementSelector.cpp + FasTrackConnector.cpp + RoiDescriptor.cpp AmbiguityTrackClustering.cpp ) diff --git a/Core/src/TrackFinding/FasTrackConnector.cpp b/Core/src/TrackFinding/FasTrackConnector.cpp new file mode 100644 index 00000000000..4bde548f85a --- /dev/null +++ b/Core/src/TrackFinding/FasTrackConnector.cpp @@ -0,0 +1,197 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// TODO: update to C++17 style +#include "Acts/TrackFinding/FasTrackConnector.hpp" + +#include +#include +#include +#include +#include + +namespace Acts { + +FasTrackConnection::FasTrackConnection(unsigned int s, unsigned int d) + : m_src(s), m_dst(d) {} + +FasTrackConnector::FasTrackConnector(std::ifstream &inFile) { + m_layerGroups.clear(); + + int nLinks{}; + + inFile >> nLinks >> m_etaBin; + + for (int l = 0; l < nLinks; l++) { + unsigned int stage{}, lIdx{}, src{}, dst{}, nEntries{}; + int height{}, width{}; + + inFile >> lIdx >> stage >> src >> dst >> height >> width >> nEntries; + + FasTrackConnection *pC = new FasTrackConnection(src, dst); + + int dummy{}; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + inFile >> dummy; // pC->m_binTable[j+i*width]; + } + } + + int vol_id = src / 1000; + + if (vol_id == 13 || vol_id == 12 || vol_id == 14) { + delete pC; + continue; + } + + vol_id = dst / 1000; + + if (vol_id == 13 || vol_id == 12 || vol_id == 14) { + delete pC; + continue; + } + + std::map>::iterator it = + m_connMap.find(stage); + + if (it == m_connMap.end()) { + std::vector v(1, pC); + m_connMap.insert(std::make_pair(stage, v)); + } else { + (*it).second.push_back(pC); + } + } + + // re-arrange the connection stages + + std::list lConns; + + std::map> newConnMap; + + for (const auto &conn : m_connMap) { + std::copy(conn.second.begin(), conn.second.end(), + std::back_inserter(lConns)); + } + + int stageCounter = 0; + + while (!lConns.empty()) { + std::unordered_map> + mCounter; // layerKey, nDst, nSrc + + for (const auto &conn : lConns) { + auto entryIt = mCounter.find(conn->m_dst); + if (entryIt != mCounter.end()) { + (*entryIt).second.first++; + } else { + int nDst = 1; + int nSrc = 0; + mCounter.insert( + std::make_pair(conn->m_dst, std::make_pair(nDst, nSrc))); + } + + entryIt = mCounter.find(conn->m_src); + if (entryIt != mCounter.end()) { + (*entryIt).second.second++; + } else { + int nDst = 0; + int nSrc = 1; + mCounter.insert( + std::make_pair(conn->m_src, std::make_pair(nDst, nSrc))); + } + } + + // find layers with nSrc = 0 + + std::set zeroLayers; + + for (const auto &layerCounts : mCounter) { + if (layerCounts.second.second != 0) { + continue; + } + + zeroLayers.insert(layerCounts.first); + } + + // remove connections which use zeroLayer as destination + + std::vector theStage; + + std::list::iterator cIt = lConns.begin(); + + while (cIt != lConns.end()) { + if (zeroLayers.find((*cIt)->m_dst) != + zeroLayers.end()) { // check if contains + theStage.push_back(*cIt); + cIt = lConns.erase(cIt); + continue; + } + cIt++; + } + newConnMap.insert(std::make_pair(stageCounter, theStage)); + stageCounter++; + } + + // create layer groups + + int currentStage = 0; + + // the doublet making is done using "outside-in" approach hence the reverse + // iterations + + for (std::map>::reverse_iterator + it = newConnMap.rbegin(); + it != newConnMap.rend(); ++it, currentStage++) { + const std::vector &vConn = (*it).second; + + // loop over links, extract all connections for the stage, group sources by + // L1 (dst) index + + std::map> l1ConnMap; + + for (const auto *conn : vConn) { + unsigned int dst = conn->m_dst; + + std::map>::iterator + l1MapIt = l1ConnMap.find(dst); + if (l1MapIt != l1ConnMap.end()) { + (*l1MapIt).second.push_back(conn); + } else { + std::vector v = {conn}; + l1ConnMap.insert(std::make_pair(dst, v)); + } + } + + std::vector lgv; + + lgv.reserve(l1ConnMap.size()); + + for (const auto &l1Group : l1ConnMap) { + lgv.push_back(LayerGroup(l1Group.first, l1Group.second)); + } + + m_layerGroups.insert(std::make_pair(currentStage, lgv)); + } + + newConnMap.clear(); +} + +FasTrackConnector::~FasTrackConnector() { + m_layerGroups.clear(); + for (std::map>::iterator it = + m_connMap.begin(); + it != m_connMap.end(); ++it) { + for (std::vector::iterator cIt = (*it).second.begin(); + cIt != (*it).second.end(); ++cIt) { + delete (*cIt); + } + } +} + +} // namespace Acts diff --git a/Core/src/TrackFinding/RoiDescriptor.cpp b/Core/src/TrackFinding/RoiDescriptor.cpp new file mode 100644 index 00000000000..4ae7288a7af --- /dev/null +++ b/Core/src/TrackFinding/RoiDescriptor.cpp @@ -0,0 +1,32 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// TODO: update to C++17 style +#include "Acts/TrackFinding/RoiDescriptor.hpp" + +#include +#include + +namespace Acts { + +Acts::RoiDescriptor::RoiDescriptor(double eta, double etaMinus, double etaPlus, + double phi, double phiMinus, double phiPlus, + double zed, double zedMinus, double zedPlus) + : m_phi(phi), + m_eta(eta), + m_zed(zed), + m_phiMinus(phiMinus), + m_phiPlus(phiPlus), + m_etaMinus(etaMinus), + m_etaPlus(etaPlus), + m_zedMinus(zedMinus), + m_zedPlus(zedPlus) {} + +Acts::RoiDescriptor::~RoiDescriptor() = default; + +} // namespace Acts diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp index cd1ec3a4156..a6ba4c7fab5 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp @@ -111,9 +111,9 @@ struct DigiComponentsConfig { class DigitizationConfig { public: DigitizationConfig(bool merge, double sigma, bool commonCorner) - : DigitizationConfig( - merge, sigma, commonCorner, - Acts::GeometryHierarchyMap()){}; + : DigitizationConfig(merge, sigma, commonCorner, + Acts::GeometryHierarchyMap()) { + } DigitizationConfig( bool doMerge, double mergeNsigma, bool mergeCommonCorner, diff --git a/Examples/Algorithms/TrackFinding/CMakeLists.txt b/Examples/Algorithms/TrackFinding/CMakeLists.txt index f55bcda945b..ab7b025c8a2 100644 --- a/Examples/Algorithms/TrackFinding/CMakeLists.txt +++ b/Examples/Algorithms/TrackFinding/CMakeLists.txt @@ -7,6 +7,7 @@ add_library( src/TrackFindingAlgorithmFunction.cpp src/HoughTransformSeeder.cpp src/TrackParamsEstimationAlgorithm.cpp + src/SeedingFTFAlgorithm.cpp ) target_include_directories( diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingFTFAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingFTFAlgorithm.hpp new file mode 100644 index 00000000000..a1022d61566 --- /dev/null +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingFTFAlgorithm.hpp @@ -0,0 +1,92 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2020 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/. + +// basing off of SeedingOrtho + +#pragma once + +#include "Acts/Seeding/InternalSeed.hpp" +#include "Acts/Seeding/SeedFilterConfig.hpp" +#include "Acts/Seeding/SeedFinderFTF.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" +// in core +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Seeding/SpacePointGrid.hpp" +#include "ActsExamples/EventData/SimSeed.hpp" +#include "ActsExamples/EventData/SimSpacePoint.hpp" +#include "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" + +#include +#include + +namespace ActsExamples { + +class SeedingFTFAlgorithm final : public IAlgorithm { + public: + struct Config { + std::vector inputSpacePoints; + + std::string outputSeeds; + + Acts::SeedFilterConfig seedFilterConfig; + Acts::SeedFinderFTFConfig seedFinderConfig; + Acts::SeedFinderOptions seedFinderOptions; + + std::string layerMappingFile; + + std::vector geometrySelection; + + std::string inputSourceLinks; + + std::shared_ptr trackingGeometry; + + std::map, std::pair> ACTS_FTF_Map; + + bool fill_module_csv = false; + }; + + // constructor: + /// @param cfg is the algorithm configuration + /// @param lvl is the logging level + SeedingFTFAlgorithm(Config cfg, Acts::Logging::Level lvl); + + // code to make the algorithm run + /// @param txt is the algorithm context with event information + /// @return a process code indication success or failure + ProcessCode execute(const AlgorithmContext &ctx) const override; + + // access to config + const Config &config() const { return m_cfg; } + + // own class functions + // make the map + std::map, std::pair> Make_ACTS_FTF_Map() const; + // make the vector of space points with FTF Info + std::vector> Make_FTF_spacePoints( + const AlgorithmContext &ctx, + std::map, std::pair> map) const; + // layer numbering + std::vector LayerNumbering() const; + + private: + Config m_cfg; + + std::unique_ptr> mGNNgeo; + + std::vector>> + m_inputSpacePoints{}; + + WriteDataHandle m_outputSeeds{this, "OutputSeeds"}; + + ReadDataHandle m_inputSourceLinks{ + this, "InputSourceLinks"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index c70274c66fe..69df5b24e59 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -97,6 +97,8 @@ class TrackFindingAlgorithm final : public IAlgorithm { std::optional trackSelectorCfg = std::nullopt; /// Run backward finding bool backward = false; + /// Maximum number of propagation steps + unsigned int maxSteps = 100000; }; /// Constructor of the track finding algorithm diff --git a/Examples/Algorithms/TrackFinding/src/SeedingFTFAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/SeedingFTFAlgorithm.cpp new file mode 100644 index 00000000000..2f5b9d55edf --- /dev/null +++ b/Examples/Algorithms/TrackFinding/src/SeedingFTFAlgorithm.cpp @@ -0,0 +1,379 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2020 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/TrackFinding/SeedingFTFAlgorithm.hpp" + +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Seeding/Seed.hpp" +#include "Acts/Seeding/SeedFilter.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/EventData/ProtoTrack.hpp" +#include "ActsExamples/EventData/SimSeed.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace std; + +template class Acts::TrigFTF_GNN_Layer; +template class Acts::TrigFTF_GNN_Geometry; +template class Acts::TrigFTF_GNN_Node; +template class Acts::TrigFTF_GNN_EtaBin; +template struct Acts::FTF_SP; +template class Acts::TrigFTF_GNN_DataStorage; +template class Acts::TrigFTF_GNN_Edge; + +// constructor: +ActsExamples::SeedingFTFAlgorithm::SeedingFTFAlgorithm( + ActsExamples::SeedingFTFAlgorithm::Config cfg, Acts::Logging::Level lvl) + : ActsExamples::IAlgorithm("SeedingAlgorithm", lvl), m_cfg(std::move(cfg)) { + // fill config struct + m_cfg.layerMappingFile = m_cfg.layerMappingFile; + + m_cfg.seedFilterConfig = m_cfg.seedFilterConfig.toInternalUnits(); + + m_cfg.seedFinderConfig = + m_cfg.seedFinderConfig.toInternalUnits().calculateDerivedQuantities(); + + m_cfg.seedFinderOptions = + m_cfg.seedFinderOptions.toInternalUnits().calculateDerivedQuantities( + m_cfg.seedFinderConfig); + + for (const auto &spName : m_cfg.inputSpacePoints) { + if (spName.empty()) { + throw std::invalid_argument("Invalid space point input collection"); + } + + auto &handle = m_inputSpacePoints.emplace_back( + std::make_unique>( + this, + "InputSpacePoints#" + std::to_string(m_inputSpacePoints.size()))); + handle->initialize(spName); + } + + m_outputSeeds.initialize(m_cfg.outputSeeds); + + m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); + + m_cfg.seedFinderConfig.seedFilter = + std::make_unique>( + Acts::SeedFilter(m_cfg.seedFilterConfig)); + + // map + m_cfg.ACTS_FTF_Map = Make_ACTS_FTF_Map(); + // input trig vector + m_cfg.seedFinderConfig.m_layerGeometry = LayerNumbering(); + + std::ifstream input_ifstream( + m_cfg.seedFinderConfig.fastrack_input_file.c_str(), std::ifstream::in); + + // fastrack + std::unique_ptr input_fastrack = + std::make_unique(input_ifstream); + + mGNNgeo = std::make_unique>( + m_cfg.seedFinderConfig.m_layerGeometry, input_fastrack); + +} // this is not FTF config type because it is a member of the algs config, + // which is of type FTF cofig + +// execute: +ActsExamples::ProcessCode ActsExamples::SeedingFTFAlgorithm::execute( + const AlgorithmContext &ctx) const { + std::vector> FTF_spacePoints = + Make_FTF_spacePoints(ctx, m_cfg.ACTS_FTF_Map); + + for (auto sp : FTF_spacePoints) { + ACTS_DEBUG("FTF space points: " + << " FTF_id: " << sp.FTF_ID << " z: " << sp.SP->z() + << " r: " << sp.SP->r() << " ACTS volume: " + << sp.SP->sourceLinks() + .front() + .get() + .geometryId() + .volume() + << "\n"); + } + + // this is now calling on a core algorithm + Acts::SeedFinderFTF finder(m_cfg.seedFinderConfig, *mGNNgeo); + + // need this function as create_coords is needed for seeds + std::function( + const SimSpacePoint *sp)> + create_coordinates = [](const SimSpacePoint *sp) { + Acts::Vector3 position(sp->x(), sp->y(), sp->z()); + Acts::Vector2 variance(sp->varianceR(), sp->varianceZ()); + return std::make_pair(position, variance); + }; + // output of function needed for seed + + finder.loadSpacePoints(FTF_spacePoints); + + Acts::RoiDescriptor internalRoi(0, -4.5, 4.5, 0, -M_PI, M_PI, 0, -150.0, + 150.0); + + finder.createSeeds(internalRoi, *mGNNgeo); + + // still to develop + SimSeedContainer seeds = finder.createSeeds_old( + m_cfg.seedFinderOptions, FTF_spacePoints, create_coordinates); + + m_outputSeeds(ctx, std::move(seeds)); + + return ActsExamples::ProcessCode::SUCCESS; +} + +std::map, std::pair> +ActsExamples::SeedingFTFAlgorithm::Make_ACTS_FTF_Map() const { + map, std::pair> ACTS_FTF; + std::ifstream data( + m_cfg.layerMappingFile); // 0 in this file refers to no FTF ID + std::string line; + std::vector> parsedCsv; + while (std::getline(data, line)) { + std::stringstream lineStream(line); + std::string cell; + std::vector parsedRow; + while (std::getline(lineStream, cell, ',')) { + parsedRow.push_back(cell); + } + + parsedCsv.push_back(parsedRow); + } + // file in format ACTS_vol,ACTS_lay,ACTS_mod,FTF_id + for (auto i : parsedCsv) { + int ACTS_vol = stoi(i[0]); + int ACTS_lay = stoi(i[1]); + int ACTS_mod = stoi(i[2]); + int FTF = stoi(i[5]); + int eta_mod = stoi(i[6]); + int ACTS_joint = ACTS_vol * 100 + ACTS_lay; + ACTS_FTF.insert({{ACTS_joint, ACTS_mod}, {FTF, eta_mod}}); + } + + return ACTS_FTF; +} + +std::vector> +ActsExamples::SeedingFTFAlgorithm::Make_FTF_spacePoints( + const AlgorithmContext &ctx, + std::map, std::pair> map) const { + // create space point vectors + std::vector spacePoints; + std::vector> FTF_spacePoints; + FTF_spacePoints.reserve( + m_inputSpacePoints.size()); // not sure if this is enough + + // for loop filling space + for (const auto &isp : m_inputSpacePoints) { + for (const auto &spacePoint : (*isp)(ctx)) { + // fill original space point vector + spacePoints.push_back(&spacePoint); + + // FTF space point vector + // loop over space points, call on map + const auto &source_link = spacePoint.sourceLinks(); + const auto &index_source_link = + source_link.front().get(); + + // warning if source link empty + if (source_link.empty()) { + // warning in officaial acts format + ACTS_WARNING("warning source link vector is empty"); + continue; + } + int ACTS_vol_id = index_source_link.geometryId().volume(); + int ACTS_lay_id = index_source_link.geometryId().layer(); + int ACTS_mod_id = index_source_link.geometryId().sensitive(); + + // dont want strips or HGTD + if (ACTS_vol_id == 2 or ACTS_vol_id == 22 or ACTS_vol_id == 23 or + ACTS_vol_id == 24) { + continue; + } + + // Search for vol, lay and module=0, if doesn't esist (end) then search + // for full thing vol*100+lay as first number in pair then 0 or mod id + auto ACTS_joint_id = ACTS_vol_id * 100 + ACTS_lay_id; + auto key = std::make_pair( + ACTS_joint_id, + 0); // here the key needs to be pair of(vol*100+lay, 0) + auto Find = map.find(key); + + if (Find == + map.end()) { // if end then make new key of (vol*100+lay, modid) + key = std::make_pair(ACTS_joint_id, ACTS_mod_id); // mod ID + Find = map.find(key); + } + + // warning if key not in map + if (Find == map.end()) { + ACTS_WARNING("Key not found in FTF map for volume id: " + << ACTS_vol_id << " and layer id: " << ACTS_lay_id); + continue; + } + + // now should be pixel with FTF ID: + int FTF_id = + Find->second + .first; // new map the item is a pair so want first from it + + if (FTF_id == 0) { + ACTS_WARNING("No assigned FTF ID for key for volume id: " + << ACTS_vol_id << " and layer id: " << ACTS_lay_id); + } + + // access IDs from map + int eta_mod = Find->second.second; + int combined_id = FTF_id * 1000 + eta_mod; + + // fill FTF vector with current sapce point and ID + FTF_spacePoints.emplace_back(&spacePoint, FTF_id, combined_id); + } + } + ACTS_VERBOSE("Space points successfully assigned FTF ID"); + + return FTF_spacePoints; +} + +std::vector +ActsExamples::SeedingFTFAlgorithm::LayerNumbering() const { + std::vector input_vector; + std::vector count_vector; + + m_cfg.trackingGeometry->visitSurfaces([this, &input_vector, &count_vector]( + const Acts::Surface *surface) { + Acts::GeometryIdentifier geoid = surface->geometryId(); + auto ACTS_vol_id = geoid.volume(); + auto ACTS_lay_id = geoid.layer(); + auto mod_id = geoid.sensitive(); + auto bounds_vect = surface->bounds().values(); + auto center = surface->center(Acts::GeometryContext()); + + // make bounds global + Acts::Vector3 globalFakeMom(1, 1, 1); + Acts::Vector2 min_bound_local = + Acts::Vector2(bounds_vect[0], bounds_vect[1]); + Acts::Vector2 max_bound_local = + Acts::Vector2(bounds_vect[2], bounds_vect[3]); + Acts::Vector3 min_bound_global = surface->localToGlobal( + Acts::GeometryContext(), min_bound_local, globalFakeMom); + Acts::Vector3 max_bound_global = surface->localToGlobal( + Acts::GeometryContext(), max_bound_local, globalFakeMom); + + if (min_bound_global(0) > + max_bound_global(0)) { // checking that not wrong way round + min_bound_global.swap(max_bound_global); + } + + float rc = 0.0; + float minBound = 100000.0; + float maxBound = -100000.0; + + // convert to FTF ID + auto ACTS_joint_id = ACTS_vol_id * 100 + ACTS_lay_id; + auto key = + std::make_pair(ACTS_joint_id, + 0); // here the key needs to be pair of(vol*100+lay, 0) + auto Find = m_cfg.ACTS_FTF_Map.find(key); + int FTF_id = Find->second.first; // new map, item is pair want first + if (Find == + m_cfg.ACTS_FTF_Map + .end()) { // if end then make new key of (vol*100+lay, modid) + key = std::make_pair(ACTS_joint_id, mod_id); // mod ID + Find = m_cfg.ACTS_FTF_Map.find(key); + FTF_id = Find->second.first; + } + + short barrel_ec = 0; // a variable that says if barrrel, 0 = barrel + int eta_mod = Find->second.second; + + // assign barrel_ec depending on FTF_layer + if (79 < FTF_id && FTF_id < 85) { // 80s, barrel + barrel_ec = 0; + } else if (89 < FTF_id && FTF_id < 99) { // 90s positive + barrel_ec = 2; + } else { // 70s negative + barrel_ec = -2; + } + + if (barrel_ec == 0) { + rc = sqrt(center(0) * center(0) + + center(1) * center(1)); // barrel center in r + // bounds of z + if (min_bound_global(2) < minBound) { + minBound = min_bound_global(2); + } + if (max_bound_global(2) > maxBound) { + maxBound = max_bound_global(2); + } + } else { + rc = center(2); // not barrel center in Z + // bounds of r + float min = sqrt(min_bound_global(0) * min_bound_global(0) + + min_bound_global(1) * min_bound_global(1)); + float max = sqrt(max_bound_global(0) * max_bound_global(0) + + max_bound_global(1) * max_bound_global(1)); + if (min < minBound) { + minBound = min; + } + if (max > maxBound) { + maxBound = max; + } + } + + int combined_id = FTF_id * 1000 + eta_mod; + auto current_index = + find_if(input_vector.begin(), input_vector.end(), + [combined_id](auto n) { return n.m_subdet == combined_id; }); + if (current_index != input_vector.end()) { // not end so does exist + size_t index = std::distance(input_vector.begin(), current_index); + input_vector[index].m_refCoord += rc; + input_vector[index].m_minBound += minBound; + input_vector[index].m_maxBound += maxBound; + count_vector[index] += 1; // increase count at the index + + } else { // end so doesn't exists + // make new if one with FTF ID doesn't exist: + Acts::TrigInDetSiLayer new_FTF_ID(combined_id, barrel_ec, rc, minBound, + maxBound); + input_vector.push_back(new_FTF_ID); + count_vector.push_back( + 1); // so the element exists and not divinding by 0 + } + + if (m_cfg.fill_module_csv) { + fstream fout; + fout.open("ACTS_modules.csv", + ios::out | ios::app); // add to file each time + // print to csv for each module, no repeats so dont need to make + // map for averaging + fout << ACTS_vol_id << ", " // vol + << ACTS_lay_id << ", " // lay + << mod_id << ", " // module + << FTF_id << "," // FTF id + << eta_mod << "," // eta_mod + << center(2) << ", " // z + << sqrt(center(0) * center(0) + center(1) * center(1)) // r + << "\n"; + } + }); + + for (long unsigned int i = 0; i < input_vector.size(); i++) { + input_vector[i].m_refCoord = input_vector[i].m_refCoord / count_vector[i]; + } + + return input_vector; +} diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index 2e591f69b32..bebf8711e20 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -17,6 +17,7 @@ #include "Acts/Propagator/Propagator.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/TrackFitting/KalmanFitter.hpp" @@ -81,7 +82,7 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( Acts::Vector3{0., 0., 0.}); Acts::PropagatorPlainOptions pOptions; - pOptions.maxSteps = 100000; + pOptions.maxSteps = m_cfg.maxSteps; pOptions.direction = m_cfg.backward ? Acts::Direction::Backward : Acts::Direction::Forward; @@ -115,6 +116,8 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( ActsExamples::TrackFindingAlgorithm::TrackFinderOptions options( ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, extensions, pOptions, pSurface.get()); + options.smoothingTargetSurfaceStrategy = + Acts::CombinatorialKalmanFilterTargetSurfaceStrategy::first; // Perform the track finding for all initial parameters ACTS_DEBUG("Invoke track finding with " << initialParameters.size() diff --git a/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt b/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt index 29f0c1e28da..b4a0f280555 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt +++ b/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt @@ -1,6 +1,8 @@ add_library( ActsExamplesTrackFindingExaTrkX SHARED src/TrackFindingAlgorithmExaTrkX.cpp + src/PrototracksToParameters.cpp + src/TrackFindingFromPrototrackAlgorithm.cpp ) target_include_directories( @@ -13,6 +15,7 @@ target_link_libraries( PUBLIC ActsPluginExaTrkX ActsExamplesFramework + ActsExamplesTrackFinding ) install( diff --git a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/PrototracksToParameters.hpp b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/PrototracksToParameters.hpp new file mode 100644 index 00000000000..f679d6d211c --- /dev/null +++ b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/PrototracksToParameters.hpp @@ -0,0 +1,100 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Geometry/TrackingGeometry.hpp" +#include "Acts/MagneticField/MagneticFieldProvider.hpp" +#include "ActsExamples/EventData/ProtoTrack.hpp" +#include "ActsExamples/EventData/SimSeed.hpp" +#include "ActsExamples/EventData/Track.hpp" +#include "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" + +namespace ActsExamples { + +/// This algorithm computes track parameters for prototracks. +/// Unfortunately, it is not possible to create first seeds from prototracks, +/// and then use the more generic TrackParamEstimationAlgorithm. +/// +/// The reason is, that in the end we need a consistent collection of parameters +/// and prototracks. But if the parameter estimation fails for individual seeds, +/// it is not possible to recover a matching set of prototracks afterwards. +/// Therefore, these two steps must be unified into one algorithm. +/// +class PrototracksToParameters final : public IAlgorithm { + public: + struct Config { + /// The prototrack for that parameters should be computed + std::string inputProtoTracks; + /// The spacepoint collection + std::string inputSpacePoints; + /// The seeds created on-the-fly from which the parameters actually are + /// computed + std::string outputSeeds = "seeds-from-prototracks"; + /// The prototracks for which parameters where computed successfully + std::string outputProtoTracks = "remaining-prototracks"; + /// The output parameters + std::string outputParameters = "parameters"; + + /// Whether to make tight seeds (closest 3 hits to beampipe) or large + /// seeds (evenly spread across the prototrack) + bool buildTightSeeds = false; + + /// The tracking geometry + std::shared_ptr geometry; + /// Magnetic field variant. + std::shared_ptr magneticField; + + /// The minimum magnetic field to trigger the track parameters estimation + double bFieldMin = 0.1 * Acts::UnitConstants::T; + /// Initial covariance matrix diagonal. + std::array initialSigmas = { + 25 * Acts::UnitConstants::um, 100 * Acts::UnitConstants::um, + 0.02 * Acts::UnitConstants::degree, 0.02 * Acts::UnitConstants::degree, + 0.1 / Acts::UnitConstants::GeV, 10 * Acts::UnitConstants::ns}; + /// Inflate initial covariance. + std::array initialVarInflation = {1., 1., 1., 1., 1., 1.}; + /// Particle hypothesis. + Acts::ParticleHypothesis particleHypothesis = + Acts::ParticleHypothesis::pion(); + }; + + /// Construct the algorithm. + /// + /// @param cfg is the algorithm configuration + /// @param lvl is the logging level + PrototracksToParameters(Config cfg, Acts::Logging::Level lvl); + + ~PrototracksToParameters(); + + /// Run the algorithm. + /// + /// @param ctx is the algorithm context with event information + /// @return a process code indication success or failure + ProcessCode execute(const AlgorithmContext& ctx) const final; + + /// Const access to the config + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + Acts::BoundSquareMatrix m_covariance = Acts::BoundSquareMatrix::Zero(); + + WriteDataHandle m_outputSeeds{this, "OutputSeeds"}; + WriteDataHandle m_outputProtoTracks{this, + "OutputProtoTracks"}; + WriteDataHandle m_outputParameters{ + this, "OutputParameters"}; + ReadDataHandle m_inputSpacePoints{this, + "InputSpacePoints"}; + ReadDataHandle m_inputProtoTracks{this, + "InputProtoTracks"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingAlgorithmExaTrkX.hpp b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingAlgorithmExaTrkX.hpp index fa7aed4ac34..d6366d25b43 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingAlgorithmExaTrkX.hpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingAlgorithmExaTrkX.hpp @@ -19,9 +19,13 @@ #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" +#include #include #include +#include +#include + namespace ActsExamples { class TrackFindingAlgorithmExaTrkX final : public IAlgorithm { @@ -83,13 +87,26 @@ class TrackFindingAlgorithmExaTrkX final : public IAlgorithm { ActsExamples::ProcessCode execute( const ActsExamples::AlgorithmContext& ctx) const final; + /// Finalize and print timing + ActsExamples::ProcessCode finalize() final; + const Config& config() const { return m_cfg; } private: - // configuration Config m_cfg; Acts::ExaTrkXPipeline m_pipeline; + mutable std::mutex m_mutex; + + using Accumulator = boost::accumulators::accumulator_set< + float, boost::accumulators::features>; + + mutable struct { + Accumulator graphBuildingTime; + std::vector classifierTimes; + Accumulator trackBuildingTime; + } m_timing; ReadDataHandle m_inputSpacePoints{this, "InputSpacePoints"}; diff --git a/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp new file mode 100644 index 00000000000..3d80b12d4f9 --- /dev/null +++ b/Examples/Algorithms/TrackFindingExaTrkX/include/ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp @@ -0,0 +1,111 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/EventData/Measurement.hpp" +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/MagneticField/MagneticFieldProvider.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/TrackFinding/MeasurementSelector.hpp" +#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" +#include "ActsExamples/Framework/IAlgorithm.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp" + +#include +#include + +namespace ActsExamples { + +/// This algorithm starts a CKF of from a given prototrack. +/// This means: If for any given surface there exists a measurements +/// in the prototrack, this measurement is returned. +/// Otherwise (typically after the end of the prototrack), the algorithm +/// falls back to providing all possible measurements on the surface. +/// +class TrackFindingFromPrototrackAlgorithm final : public IAlgorithm { + public: + struct Config { + /// Input prototracks collection. + std::string inputProtoTracks; + + /// Input measurements + std::string inputMeasurements; + + /// Input source links + std::string inputSourceLinks; + + /// Input track parameters + std::string inputInitialTrackParameters; + + /// Output protoTracks collection. + std::string outputTracks; + + /// CKF measurement selector config + Acts::MeasurementSelector::Config measurementSelectorCfg; + + /// CKF function + std::shared_ptr findTracks; + + /// Tracking Geometry + std::shared_ptr trackingGeometry; + + /// Magnetic field + std::shared_ptr magneticField; + + /// Additional tag to distinguish loggers + std::string tag = ""; + }; + + /// Constructor of the track finding algorithm + /// + /// @param cfg is the config struct to configure the algorithm + /// @param level is the logging level + TrackFindingFromPrototrackAlgorithm(Config cfg, Acts::Logging::Level lvl); + + virtual ~TrackFindingFromPrototrackAlgorithm() {} + + /// 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; + + ActsExamples::ProcessCode finalize() override; + + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + + mutable std::mutex m_mutex; + mutable std::vector m_nTracksPerSeeds; + + ReadDataHandle m_inputProtoTracks{this, + "InputProtoTracks"}; + ReadDataHandle m_inputMeasurements{this, + "InputMeasurements"}; + ReadDataHandle m_inputSourceLinks{ + this, "InputSourceLinks"}; + ReadDataHandle m_inputInitialTrackParameters{ + this, "InputInitialTrackParameters"}; + + WriteDataHandle m_outputTracks{this, "OutputTracks"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp new file mode 100644 index 00000000000..2b211f66f10 --- /dev/null +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp @@ -0,0 +1,197 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/TrackFindingExaTrkX/PrototracksToParameters.hpp" + +#include "Acts/Seeding/BinFinder.hpp" +#include "Acts/Seeding/BinnedSPGroup.hpp" +#include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" +#include "Acts/Seeding/InternalSpacePoint.hpp" +#include "Acts/Seeding/SeedFilter.hpp" +#include "Acts/Seeding/SeedFinder.hpp" +#include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Utilities/Zip.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/ProtoTrack.hpp" +#include "ActsExamples/EventData/SimSeed.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Utilities/EventDataTransforms.hpp" + +#include + +using namespace ActsExamples; +using namespace Acts::UnitLiterals; + +namespace ActsExamples { + +PrototracksToParameters::PrototracksToParameters(Config cfg, + Acts::Logging::Level lvl) + : IAlgorithm("PrototracksToParsAndSeeds", lvl), m_cfg(std::move(cfg)) { + m_outputSeeds.initialize(m_cfg.outputSeeds); + m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); + m_inputProtoTracks.initialize(m_cfg.inputProtoTracks); + m_inputSpacePoints.initialize(m_cfg.inputSpacePoints); + m_outputParameters.initialize(m_cfg.outputParameters); + + if (m_cfg.geometry == nullptr) { + throw std::invalid_argument("No geometry given"); + } + if (m_cfg.magneticField == nullptr) { + throw std::invalid_argument("No magnetic field given"); + } + + // Set up the track parameters covariance (the same for all tracks) + for (std::size_t i = Acts::eBoundLoc0; i < Acts::eBoundSize; ++i) { + m_covariance(i, i) = m_cfg.initialVarInflation[i] * m_cfg.initialSigmas[i] * + m_cfg.initialSigmas[i]; + } +} + +PrototracksToParameters::~PrototracksToParameters() {} + +ProcessCode PrototracksToParameters::execute( + const AlgorithmContext &ctx) const { + auto bCache = m_cfg.magneticField->makeCache(ctx.magFieldContext); + const auto &sps = m_inputSpacePoints(ctx); + auto prototracks = m_inputProtoTracks(ctx); + + // Make some lookup tables. Allocate space for the maximum number of indices + // (max 2 source links per spacepoint) + std::vector indexToSpacepoint(2 * sps.size(), nullptr); + std::vector indexToGeoId( + 2 * sps.size(), Acts::GeometryIdentifier{0}); + + for (const auto &sp : sps) { + for (const auto &sl : sp.sourceLinks()) { + const auto &isl = sl.template get(); + indexToSpacepoint[isl.index()] = &sp; + indexToGeoId[isl.index()] = isl.geometryId(); + } + } + + ProtoTrackContainer seededTracks; + seededTracks.reserve(prototracks.size()); + + SimSeedContainer seeds; + seeds.reserve(prototracks.size()); + + TrackParametersContainer parameters; + parameters.reserve(prototracks.size()); + + // Loop over the prototracks to make seeds + ProtoTrack tmpTrack; + std::vector tmpSps; + std::size_t skippedTracks = 0; + for (auto &track : prototracks) { + ACTS_VERBOSE("Try to get seed from prototrack with " << track.size() + << " hits"); + // Make prototrack unique with respect to volume and layer + // so we don't get a seed where we have two spacepoints on the same layer + + // Here, we want to create a seed only if the prototrack with removed unique + // layer-volume spacepoints has 3 or more hits. However, if this is the + // case, we want to keep the whole prototrack. Therefore, we operate on a + // tmpTrack. + std::sort(track.begin(), track.end(), [&](auto a, auto b) { + if (indexToGeoId[a].volume() != indexToGeoId[b].volume()) { + return indexToGeoId[a].volume() < indexToGeoId[b].volume(); + } + return indexToGeoId[a].layer() < indexToGeoId[b].layer(); + }); + + tmpTrack.clear(); + std::unique_copy( + track.begin(), track.end(), std::back_inserter(tmpTrack), + [&](auto a, auto b) { + return indexToGeoId[a].volume() == indexToGeoId[b].volume() && + indexToGeoId[a].layer() == indexToGeoId[b].layer(); + }); + + // in this case we cannot seed properly + if (tmpTrack.size() < 3) { + ACTS_DEBUG( + "Cannot seed because less then three hits with unique (layer, " + "volume)"); + skippedTracks++; + continue; + } + + // Make the seed + tmpSps.clear(); + std::transform(track.begin(), track.end(), std::back_inserter(tmpSps), + [&](auto i) { return indexToSpacepoint[i]; }); + tmpSps.erase(std::remove_if(tmpSps.begin(), tmpSps.end(), + [](auto sp) { return sp == nullptr; }), + tmpSps.end()); + + if (tmpSps.size() < 3) { + ACTS_WARNING("Could not find all spacepoints, skip"); + skippedTracks++; + continue; + } + + std::sort(tmpSps.begin(), tmpSps.end(), + [](const auto &a, const auto &b) { return a->r() < b->r(); }); + + // Simply use r = m*z + t and solve for r=0 to find z vertex position... + // Probably not the textbook way to do + const auto m = (tmpSps.back()->r() - tmpSps.front()->r()) / + (tmpSps.back()->z() - tmpSps.front()->z()); + const auto t = tmpSps.front()->r() - m * tmpSps.front()->z(); + const auto z_vertex = -t / m; + const auto s = tmpSps.size(); + + SimSeed seed = + m_cfg.buildTightSeeds + ? SimSeed(*tmpSps[0], *tmpSps[1], *tmpSps[2], z_vertex) + : SimSeed(*tmpSps[0], *tmpSps[s / 2], *tmpSps[s - 1], z_vertex); + + // Compute parameters + const auto &bottomSP = seed.sp().front(); + const auto geoId = bottomSP->sourceLinks() + .front() + .template get() + .geometryId(); + const auto &surface = *m_cfg.geometry->findSurface(geoId); + + auto field = m_cfg.magneticField->getField( + {bottomSP->x(), bottomSP->y(), bottomSP->z()}, bCache); + if (!field.ok()) { + ACTS_ERROR("Field lookup error: " << field.error()); + return ProcessCode::ABORT; + } + + auto pars = Acts::estimateTrackParamsFromSeed( + ctx.geoContext, seed.sp().begin(), seed.sp().end(), surface, *field, + m_cfg.bFieldMin); + + if (not pars) { + ACTS_WARNING("Skip track because of bad params"); + } + + seededTracks.push_back(track); + seeds.emplace_back(std::move(seed)); + parameters.push_back(Acts::BoundTrackParameters( + surface.getSharedPtr(), *pars, m_covariance, m_cfg.particleHypothesis)); + } + + if (skippedTracks > 0) { + ACTS_WARNING("Skipped seeding of " << skippedTracks); + } + + ACTS_DEBUG("Seeded " << seeds.size() << " out of " << prototracks.size() + << " prototracks"); + + m_outputSeeds(ctx, std::move(seeds)); + m_outputProtoTracks(ctx, std::move(seededTracks)); + m_outputParameters(ctx, std::move(parameters)); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp index 4985cd255a2..4215828d62c 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingAlgorithmExaTrkX.cpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Plugins/ExaTrkX/TorchTruthGraphMetricsHook.hpp" +#include "Acts/Utilities/Zip.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" @@ -99,7 +100,7 @@ class ExamplesEdmHook : public Acts::ExaTrkXHook { targetGraph, logger.clone()); } - ~ExamplesEdmHook(){}; + ~ExamplesEdmHook() {} void operator()(const std::any& nodes, const std::any& edges) const override { ACTS_INFO("Metrics for total graph:"); @@ -145,11 +146,17 @@ ActsExamples::TrackFindingAlgorithmExaTrkX::TrackFindingAlgorithmExaTrkX( #endif m_inputSpacePoints.initialize(m_cfg.inputSpacePoints); + m_inputClusters.maybeInitialize(m_cfg.inputClusters); m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); m_inputSimHits.maybeInitialize(m_cfg.inputSimHits); m_inputParticles.maybeInitialize(m_cfg.inputParticles); m_inputMeasurementMap.maybeInitialize(m_cfg.inputMeasurementSimhitsMap); + + // reserve space for timing + m_timing.classifierTimes.resize( + m_cfg.edgeClassifiers.size(), + decltype(m_timing.classifierTimes)::value_type{0.f}); } /// Allow access to features with nice names @@ -233,7 +240,26 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithmExaTrkX::execute( ACTS_DEBUG("Avg activation: " << sumActivation / sumCells); // Run the pipeline - const auto trackCandidates = m_pipeline.run(features, spacepointIDs, *hook); + const auto trackCandidates = [&]() { + const int deviceHint = -1; + std::lock_guard lock(m_mutex); + + Acts::ExaTrkXTiming timing; + auto res = + m_pipeline.run(features, spacepointIDs, deviceHint, *hook, &timing); + + m_timing.graphBuildingTime(timing.graphBuildingTime.count()); + + assert(timing.classifierTimes.size() == m_timing.classifierTimes.size()); + for (auto [aggr, a] : + Acts::zip(m_timing.classifierTimes, timing.classifierTimes)) { + aggr(a.count()); + } + + m_timing.trackBuildingTime(timing.trackBuildingTime.count()); + + return res; + }(); ACTS_DEBUG("Done with pipeline, received " << trackCandidates.size() << " candidates"); @@ -252,3 +278,25 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithmExaTrkX::execute( return ActsExamples::ProcessCode::SUCCESS; } + +ActsExamples::ProcessCode TrackFindingAlgorithmExaTrkX::finalize() { + namespace ba = boost::accumulators; + + ACTS_INFO("Exa.TrkX timing info"); + { + const auto& t = m_timing.graphBuildingTime; + ACTS_INFO("- graph building: " << ba::mean(t) << " +- " + << std::sqrt(ba::variance(t))); + } + for (const auto& t : m_timing.classifierTimes) { + ACTS_INFO("- classifier: " << ba::mean(t) << " +- " + << std::sqrt(ba::variance(t))); + } + { + const auto& t = m_timing.trackBuildingTime; + ACTS_INFO("- track building: " << ba::mean(t) << " +- " + << std::sqrt(ba::variance(t))); + } + + return {}; +} diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp new file mode 100644 index 00000000000..d62ab4de413 --- /dev/null +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp @@ -0,0 +1,220 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp" + +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" + +#include +#include + +namespace { + +using namespace ActsExamples; + +struct ProtoTrackSourceLinkAccessor + : GeometryIdMultisetAccessor { + using BaseIterator = GeometryIdMultisetAccessor::Iterator; + using Iterator = Acts::SourceLinkAdapterIterator; + + std::unique_ptr loggerPtr; + Container protoTrackSourceLinks; + + // get the range of elements with requested geoId + std::pair range(const Acts::Surface& surface) const { + const auto& logger = *loggerPtr; + + if (protoTrackSourceLinks.contains(surface.geometryId())) { + auto [begin, end] = + protoTrackSourceLinks.equal_range(surface.geometryId()); + ACTS_VERBOSE("Select " << std::distance(begin, end) + << " source-links from prototrack on " + << surface.geometryId()); + return {Iterator{begin}, Iterator{end}}; + } + + assert(container != nullptr); + auto [begin, end] = container->equal_range(surface.geometryId()); + ACTS_VERBOSE("Select " << std::distance(begin, end) + << " source-links from collection on " + << surface.geometryId()); + return {Iterator{begin}, Iterator{end}}; + } +}; +} // namespace + +namespace ActsExamples { + +TrackFindingFromPrototrackAlgorithm::TrackFindingFromPrototrackAlgorithm( + Config cfg, Acts::Logging::Level lvl) + : IAlgorithm(cfg.tag + "CkfFromProtoTracks", lvl), m_cfg(cfg) { + 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); + + if (initialParameters.size() != protoTracks.size()) { + ACTS_FATAL("Inconsistent number of parameters and prototracks"); + return ProcessCode::ABORT; + } + + // Construct a perigee surface as the target surface + auto pSurface = Acts::Surface::makeShared( + Acts::Vector3{0., 0., 0.}); + + Acts::PropagatorPlainOptions pOptions; + pOptions.maxSteps = 10000; + + PassThroughCalibrator pcalibrator; + MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); + Acts::GainMatrixUpdater kfUpdater; + Acts::GainMatrixSmoother kfSmoother; + Acts::MeasurementSelector measSel{m_cfg.measurementSelectorCfg}; + + Acts::CombinatorialKalmanFilterExtensions + extensions; + extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( + &calibrator); + extensions.updater.connect< + &Acts::GainMatrixUpdater::operator()>( + &kfUpdater); + extensions.smoother.connect< + &Acts::GainMatrixSmoother::operator()>( + &kfSmoother); + extensions.measurementSelector + .connect<&Acts::MeasurementSelector::select>( + &measSel); + + // The source link accessor + ProtoTrackSourceLinkAccessor sourceLinkAccessor; + sourceLinkAccessor.loggerPtr = logger().clone("SourceLinkAccessor"); + sourceLinkAccessor.container = &sourceLinks; + + Acts::SourceLinkAccessorDelegate + slAccessorDelegate; + slAccessorDelegate.connect<&ProtoTrackSourceLinkAccessor::range>( + &sourceLinkAccessor); + + // Set the CombinatorialKalmanFilter options + TrackFindingAlgorithm::TrackFinderOptions options( + ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, + extensions, pOptions, &(*pSurface)); + + // Perform the track finding for all initial parameters + ACTS_DEBUG("Invoke track finding with " << initialParameters.size() + << " seeds."); + + auto trackContainer = std::make_shared(); + auto trackStateContainer = std::make_shared(); + + TrackContainer tracks(trackContainer, trackStateContainer); + + tracks.addColumn("trackGroup"); + Acts::TrackAccessor seedNumber("trackGroup"); + + std::size_t nSeed = 0; + std::size_t nFailed = 0; + + std::vector nTracksPerSeeds; + nTracksPerSeeds.reserve(initialParameters.size()); + + for (auto i = 0ul; i < initialParameters.size(); ++i) { + sourceLinkAccessor.protoTrackSourceLinks.clear(); + + // 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()) { + sourceLinkAccessor.protoTrackSourceLinks.insert(*it); + } else { + ACTS_FATAL("Proto track " << i << " contains invalid hit index" + << hitIndex); + return ProcessCode::ABORT; + } + } + + auto result = (*m_cfg.findTracks)(initialParameters.at(i), options, tracks); + nSeed++; + + if (!result.ok()) { + nFailed++; + ACTS_WARNING("Track finding failed for proto track " << i << " with error" + << result.error()); + continue; + } + + auto& tracksForSeed = result.value(); + + nTracksPerSeeds.push_back(tracksForSeed.size()); + + for (auto& track : tracksForSeed) { + seedNumber(track) = nSeed; + } + } + + { + std::lock_guard guard(m_mutex); + + std::copy(nTracksPerSeeds.begin(), nTracksPerSeeds.end(), + std::back_inserter(m_nTracksPerSeeds)); + } + + // TODO The computeSharedHits function is still a member function of + // TrackFindingAlgorithm, but could also be a free function. Uncomment this + // once this is done. + // Compute shared hits from all the reconstructed tracks if + // (m_cfg.computeSharedHits) { + // computeSharedHits(sourceLinks, tracks); + // } + + ACTS_INFO("Event " << ctx.eventNumber << ": " << nFailed << " / " << nSeed + << " failed (" << ((100.f * nFailed) / nSeed) << "%)"); + ACTS_DEBUG("Finalized track finding with " << tracks.size() + << " track candidates."); + auto constTrackStateContainer = + std::make_shared( + std::move(*trackStateContainer)); + + auto constTrackContainer = std::make_shared( + std::move(*trackContainer)); + + ConstTrackContainer constTracks{constTrackContainer, + constTrackStateContainer}; + + m_outputTracks(ctx, std::move(constTracks)); + return ActsExamples::ProcessCode::SUCCESS; +} + +ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::finalize() { + assert(std::distance(m_nTracksPerSeeds.begin(), m_nTracksPerSeeds.end()) > 0); + + ACTS_INFO("TrackFindingFromPrototracksAlgorithm statistics:"); + namespace ba = boost::accumulators; + using Accumulator = ba::accumulator_set< + float, ba::features>; + + Accumulator totalAcc; + std::for_each(m_nTracksPerSeeds.begin(), m_nTracksPerSeeds.end(), + [&](auto v) { totalAcc(static_cast(v)); }); + ACTS_INFO("- total number tracks: " << ba::sum(totalAcc)); + ACTS_INFO("- avg tracks per seed: " << ba::mean(totalAcc) << " +- " + << std::sqrt(ba::variance(totalAcc))); + + return {}; +} + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFitting/CMakeLists.txt b/Examples/Algorithms/TrackFitting/CMakeLists.txt index 7b6ebecc175..ca049947e51 100644 --- a/Examples/Algorithms/TrackFitting/CMakeLists.txt +++ b/Examples/Algorithms/TrackFitting/CMakeLists.txt @@ -5,7 +5,8 @@ add_library( src/TrackFittingAlgorithm.cpp src/KalmanFitterFunction.cpp src/RefittingAlgorithm.cpp - src/GsfFitterFunction.cpp) + src/GsfFitterFunction.cpp + src/GlobalChiSquareFitterFunction.cpp) target_include_directories( ActsExamplesTrackFitting PUBLIC $) diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp index de0dcf09650..cfbb31c24e4 100644 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp +++ b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp @@ -92,4 +92,21 @@ std::shared_ptr makeGsfFitterFunction( bool abortOnError, bool disableAllMaterialHandling, const Acts::Logger& logger); +/// Makes a fitter function object for the Global Chi Square Fitter (GX2F) +/// +/// @param trackingGeometry the trackingGeometry for the propagator +/// @param magneticField the magnetic field for the propagator +/// @param multipleScattering bool +/// @param energyLoss bool +/// @param freeToBoundCorrection bool +/// @param logger a logger instance +std::shared_ptr makeGlobalChiSquareFitterFunction( + std::shared_ptr trackingGeometry, + std::shared_ptr magneticField, + bool multipleScattering = true, bool energyLoss = true, + Acts::FreeToBoundCorrection freeToBoundCorrection = + Acts::FreeToBoundCorrection(), + const Acts::Logger& logger = *Acts::getDefaultLogger("Gx2f", + Acts::Logging::INFO)); + } // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFitting/src/GlobalChiSquareFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/GlobalChiSquareFitterFunction.cpp new file mode 100644 index 00000000000..a641f93de0e --- /dev/null +++ b/Examples/Algorithms/TrackFitting/src/GlobalChiSquareFitterFunction.cpp @@ -0,0 +1,162 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/. + +// TODO We still use some Kalman Fitter functionalities. Check for replacement + +#include "Acts/Definitions/Direction.hpp" +#include "Acts/Definitions/TrackParametrization.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/EventData/detail/CorrectedTransformationFreeToBound.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Propagator/DirectNavigator.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/Navigator.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/TrackFitting/GlobalChiSquareFitter.hpp" +#include "Acts/TrackFitting/KalmanFitter.hpp" +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" +#include "ActsExamples/EventData/Track.hpp" +#include "ActsExamples/TrackFitting/RefittingCalibrator.hpp" +#include "ActsExamples/TrackFitting/TrackFitterFunction.hpp" + +#include +#include +#include +#include +#include +#include + +namespace Acts { +class MagneticFieldProvider; +class SourceLink; +class Surface; +class TrackingGeometry; +} // namespace Acts + +namespace { + +using Stepper = Acts::EigenStepper<>; +using Propagator = Acts::Propagator; +using Fitter = + Acts::Experimental::Gx2Fitter; +using DirectPropagator = Acts::Propagator; +using DirectFitter = + Acts::KalmanFitter; + +using TrackContainer = + Acts::TrackContainer; + +using namespace ActsExamples; + +struct GlobalChiSquareFitterFunctionImpl final : public TrackFitterFunction { + Fitter fitter; + DirectFitter directFitter; + + bool multipleScattering = false; + bool energyLoss = false; + Acts::FreeToBoundCorrection freeToBoundCorrection; + + IndexSourceLink::SurfaceAccessor m_slSurfaceAccessor; + + GlobalChiSquareFitterFunctionImpl(Fitter&& f, DirectFitter&& df, + const Acts::TrackingGeometry& trkGeo) + : fitter(std::move(f)), + directFitter(std::move(df)), + m_slSurfaceAccessor{trkGeo} {} + + template + auto makeGx2fOptions(const GeneralFitterOptions& options, + const calibrator_t& calibrator) const { + Acts::Experimental::Gx2FitterExtensions + extensions; + extensions.calibrator.connect<&calibrator_t::calibrate>(&calibrator); + + extensions.surfaceAccessor + .connect<&IndexSourceLink::SurfaceAccessor::operator()>( + &m_slSurfaceAccessor); + + const Acts::Experimental::Gx2FitterOptions gx2fOptions( + options.geoContext, options.magFieldContext, options.calibrationContext, + extensions, options.propOptions, &(*options.referenceSurface), + multipleScattering, energyLoss, freeToBoundCorrection, 5); + + return gx2fOptions; + } + + TrackFitterResult operator()(const std::vector& sourceLinks, + const TrackParameters& initialParameters, + const GeneralFitterOptions& options, + const MeasurementCalibratorAdapter& calibrator, + TrackContainer& tracks) const override { + const auto gx2fOptions = makeGx2fOptions(options, calibrator); + return fitter.fit(sourceLinks.begin(), sourceLinks.end(), initialParameters, + gx2fOptions, tracks); + } + + // We need a placeholder for the directNavigator overload. Otherwise, we would + // have an unimplemented pure virtual method in a final class. + TrackFitterResult operator()( + const std::vector& /*sourceLinks*/, + const TrackParameters& /*initialParameters*/, + const GeneralFitterOptions& /*options*/, + const RefittingCalibrator& /*calibrator*/, + const std::vector& /*surfaceSequence*/, + TrackContainer& /*tracks*/) const override { + throw std::runtime_error( + "direct navigation with GX2 fitter is not implemented"); + } +}; + +} // namespace + +std::shared_ptr +ActsExamples::makeGlobalChiSquareFitterFunction( + std::shared_ptr trackingGeometry, + std::shared_ptr magneticField, + bool multipleScattering, bool energyLoss, + Acts::FreeToBoundCorrection freeToBoundCorrection, + const Acts::Logger& logger) { + // Stepper should be copied into the fitters + const Stepper stepper(std::move(magneticField)); + + // Standard fitter + const auto& geo = *trackingGeometry; + Acts::Navigator::Config cfg{std::move(trackingGeometry)}; + cfg.resolvePassive = false; + cfg.resolveMaterial = true; + cfg.resolveSensitive = true; + Acts::Navigator navigator(cfg, logger.cloneWithSuffix("Navigator")); + Propagator propagator(stepper, std::move(navigator), + logger.cloneWithSuffix("Propagator")); + Fitter trackFitter(std::move(propagator), logger.cloneWithSuffix("Fitter")); + + // Direct fitter + Acts::DirectNavigator directNavigator{ + logger.cloneWithSuffix("DirectNavigator")}; + DirectPropagator directPropagator(stepper, std::move(directNavigator), + logger.cloneWithSuffix("DirectPropagator")); + DirectFitter directTrackFitter(std::move(directPropagator), + logger.cloneWithSuffix("DirectFitter")); + + // build the fitter function. owns the fitter object. + auto fitterFunction = std::make_shared( + std::move(trackFitter), std::move(directTrackFitter), geo); + fitterFunction->multipleScattering = multipleScattering; + fitterFunction->energyLoss = energyLoss; + fitterFunction->freeToBoundCorrection = freeToBoundCorrection; + + return fitterFunction; +} diff --git a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp index 989d1ccfe72..2b556b5fae5 100644 --- a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp +++ b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp @@ -111,6 +111,7 @@ struct GsfFitterFunctionImpl final : public ActsExamples::TrackFitterFunction { weightCutoff, abortOnError, disableAllMaterialHandling}; + gsfOptions.stateReductionMethod = reductionMethod; gsfOptions.extensions.calibrator.connect<&calibrator_t::calibrate>( &calibrator); @@ -173,8 +174,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( bool abortOnError, bool disableAllMaterialHandling, const Acts::Logger& logger) { // Standard fitter - MultiStepper stepper(magneticField, finalReductionMethod, - logger.cloneWithSuffix("Step")); + MultiStepper stepper(magneticField, logger.cloneWithSuffix("Step")); const auto& geo = *trackingGeometry; Acts::Navigator::Config cfg{std::move(trackingGeometry)}; cfg.resolvePassive = false; @@ -188,7 +188,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( logger.cloneWithSuffix("GSF")); // Direct fitter - MultiStepper directStepper(std::move(magneticField), finalReductionMethod, + MultiStepper directStepper(std::move(magneticField), logger.cloneWithSuffix("Step")); Acts::DirectNavigator directNavigator{ logger.cloneWithSuffix("DirectNavigator")}; diff --git a/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp index f44bc84082f..b5425862285 100644 --- a/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp +++ b/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp @@ -81,13 +81,13 @@ struct KalmanFitterFunctionImpl final : public TrackFitterFunction { bool energyLoss = false; Acts::FreeToBoundCorrection freeToBoundCorrection; - IndexSourceLink::SurfaceAccessor m_slSurfaceAccessor; + IndexSourceLink::SurfaceAccessor slSurfaceAccessor; KalmanFitterFunctionImpl(Fitter&& f, DirectFitter&& df, const Acts::TrackingGeometry& trkGeo) : fitter(std::move(f)), directFitter(std::move(df)), - m_slSurfaceAccessor{trkGeo} {} + slSurfaceAccessor{trkGeo} {} template auto makeKfOptions(const GeneralFitterOptions& options, @@ -107,6 +107,8 @@ struct KalmanFitterFunctionImpl final : public TrackFitterFunction { options.geoContext, options.magFieldContext, options.calibrationContext, extensions, options.propOptions, &(*options.referenceSurface)); + kfOptions.referenceSurfaceStrategy = + Acts::KalmanFitterTargetSurfaceStrategy::first; kfOptions.multipleScattering = multipleScattering; kfOptions.energyLoss = energyLoss; kfOptions.freeToBoundCorrection = freeToBoundCorrection; @@ -114,7 +116,7 @@ struct KalmanFitterFunctionImpl final : public TrackFitterFunction { &calibrator); kfOptions.extensions.surfaceAccessor .connect<&IndexSourceLink::SurfaceAccessor::operator()>( - &m_slSurfaceAccessor); + &slSurfaceAccessor); return kfOptions; } diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp index de22b03914c..e96045bbb15 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp @@ -10,6 +10,8 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Utilities/VectorHelpers.hpp" +#include "ActsExamples/EventData/GeometryContainers.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsFatras/EventData/Particle.hpp" @@ -50,14 +52,38 @@ 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 or + 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)); + } + + std::size_t nInvalidCharge = 0; + std::size_t nInvalidMeasurementCount = 0; + // helper functions to select tracks - auto within = [](double x, double min, double max) { + auto within = [](auto x, auto min, auto max) { return (min <= x) and (x < max); }; + auto isValidParticle = [&](const ActsFatras::Particle& p) { const auto eta = Acts::VectorHelpers::eta(p.direction()); const auto phi = Acts::VectorHelpers::phi(p.direction()); @@ -67,7 +93,26 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( const bool validCharged = (p.charge() != 0) and not m_cfg.removeCharged; const bool validCharge = validNeutral or validCharged; const bool validSecondary = not m_cfg.removeSecondaries or !p.isSecondary(); - return validCharge and validSecondary and + + nInvalidCharge += static_cast(not 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()); + 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()); + } + + nInvalidMeasurementCount += + static_cast(not validMeasurementCount); + + return validCharge and validSecondary and validMeasurementCount and within(p.transverseMomentum(), m_cfg.ptMin, m_cfg.ptMax) and within(std::abs(eta), m_cfg.absEtaMin, m_cfg.absEtaMax) and within(eta, m_cfg.etaMin, m_cfg.etaMax) and @@ -79,9 +124,6 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( within(p.mass(), m_cfg.mMin, m_cfg.mMax); }; - // prepare input/ output types - const auto& inputParticles = m_inputParticles(ctx); - SimParticleContainer outputParticles; outputParticles.reserve(inputParticles.size()); @@ -97,6 +139,9 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( ACTS_DEBUG("event " << ctx.eventNumber << " selected " << outputParticles.size() << " from " << inputParticles.size() << " particles"); + ACTS_DEBUG("filtered out because of charge: " << nInvalidCharge); + ACTS_DEBUG("filtered out because of measurement count: " + << nInvalidMeasurementCount); m_outputParticles(ctx, std::move(outputParticles)); return ProcessCode::SUCCESS; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp index 92ce7445dc6..d6c9fba3bc9 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp @@ -12,7 +12,10 @@ #pragma once +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" @@ -30,6 +33,8 @@ class ParticleSelector final : public IAlgorithm { struct Config { /// The input particles collection. std::string inputParticles; + /// Input measurement particles map (Optional) + std::string inputMeasurementParticlesMap; /// The output particles collection. std::string outputParticles; // Minimum/maximum distance from the origin in the transverse plane. @@ -54,6 +59,9 @@ class ParticleSelector final : public IAlgorithm { // Rest mass cuts double mMin = 0; double mMax = std::numeric_limits::infinity(); + /// Measurement number cuts + std::size_t measurementsMin = 0; + std::size_t measurementsMax = std::numeric_limits::max(); /// Remove charged particles. bool removeCharged = false; /// Remove neutral particles. @@ -74,6 +82,8 @@ class ParticleSelector final : public IAlgorithm { Config m_cfg; ReadDataHandle m_inputParticles{this, "InputParticles"}; + ReadDataHandle> m_inputMap{ + this, "InputMeasurementParticlesMap"}; WriteDataHandle m_outputParticles{this, "OutputParticles"}; diff --git a/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp new file mode 100644 index 00000000000..b9042452399 --- /dev/null +++ b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/MeasurementMapSelector.hpp @@ -0,0 +1,95 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsFatras/EventData/Barcode.hpp" + +#include +#include + +namespace ActsExamples { + +/// Simple algorithm, that allows to extract a subset of a +/// measurment-particles-map. +/// This allows to conveniently work on subsets of the geometry. +/// +class MeasurementMapSelector final : public IAlgorithm { + using Map = IndexMultimap; + + public: + struct Config { + /// Input spacepoints collection. + std::string inputMeasurementParticleMap; + + /// Input source links + std::string inputSourceLinks; + + /// Output protoTracks collection. + std::string outputMeasurementParticleMap; + + /// What spacepoints to keep + std::vector geometrySelection; + }; + + /// Constructor of the track finding algorithm + /// + /// @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); + 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& inputMap = m_inputMap(ctx); + + Map outputMap; + + for (const auto geoId : m_cfg.geometrySelection) { + auto range = selectLowestNonZeroGeometryObject(inputSourceLinks, geoId); + for (const auto& sl : range) { + const auto [begin, end] = inputMap.equal_range(sl.index()); + outputMap.insert(begin, end); + } + } + + m_outputMap(ctx, std::move(outputMap)); + + return ProcessCode::SUCCESS; + } + + const Config& config() const { return m_cfg; } + + private: + // configuration + Config m_cfg; + + ReadDataHandle m_inputSourceLinks{ + this, "InputSourceLinks"}; + ReadDataHandle m_inputMap{this, "InputMeasurementParticleMap"}; + WriteDataHandle m_outputMap{this, "OutputMeasurementParticleMap"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Framework/ML/src/NeuralCalibrator.cpp b/Examples/Framework/ML/src/NeuralCalibrator.cpp index 3fa0174f74f..8e146d7c788 100644 --- a/Examples/Framework/ML/src/NeuralCalibrator.cpp +++ b/Examples/Framework/ML/src/NeuralCalibrator.cpp @@ -8,6 +8,7 @@ #include "Acts/EventData/SourceLink.hpp" #include "Acts/Utilities/CalibrationContext.hpp" +#include "Acts/Utilities/UnitVectors.hpp" #include #include @@ -96,8 +97,8 @@ void ActsExamples::NeuralCalibrator::calibrate( input[iInput++] = idxSourceLink.geometryId().volume(); input[iInput++] = idxSourceLink.geometryId().layer(); - input[iInput++] = trackState.parameters()[Acts::eBoundPhi]; - input[iInput++] = trackState.parameters()[Acts::eBoundTheta]; + + const Acts::Surface& referenceSurface = trackState.referenceSurface(); std::visit( [&](const auto& measurement) { @@ -107,6 +108,24 @@ void ActsExamples::NeuralCalibrator::calibrate( Acts::ActsSquareMatrix fcov = E * measurement.covariance() * E.transpose(); + Acts::Vector3 dir = Acts::makeDirectionFromPhiTheta( + fpar[Acts::eBoundPhi], fpar[Acts::eBoundTheta]); + Acts::Vector3 globalPosition = referenceSurface.localToGlobal( + gctx, fpar.segment<2>(Acts::eBoundLoc0), dir); + + // Rotation matrix. When applied to global coordinates, they + // are rotated into the local reference frame of the + // surface. Note that this such a rotation can be found by + // inverting a matrix whose columns correspond to the + // coordinate axes of the local coordinate system. + Acts::RotationMatrix3 rot = + referenceSurface.referenceFrame(gctx, globalPosition, dir) + .inverse(); + std::pair angles = + Acts::VectorHelpers::incidentAngles(dir, rot); + + input[iInput++] = angles.first; + input[iInput++] = angles.second; input[iInput++] = fpar[Acts::eBoundLoc0]; input[iInput++] = fpar[Acts::eBoundLoc1]; input[iInput++] = fcov(Acts::eBoundLoc0, Acts::eBoundLoc0); diff --git a/Examples/Framework/include/ActsExamples/Utilities/Helpers.hpp b/Examples/Framework/include/ActsExamples/Utilities/Helpers.hpp index 7d33ab2a80c..85d152c2b71 100644 --- a/Examples/Framework/include/ActsExamples/Utilities/Helpers.hpp +++ b/Examples/Framework/include/ActsExamples/Utilities/Helpers.hpp @@ -45,7 +45,7 @@ class Binning { } Binning(std::string title, std::vector bins) - : m_title(std::move(title)), m_bins(std::move(bins)){}; + : m_title(std::move(title)), m_bins(std::move(bins)) {} const auto& title() const { return m_title; } auto nBins() const { return m_bins.size() - 1; } diff --git a/Examples/Io/Csv/CMakeLists.txt b/Examples/Io/Csv/CMakeLists.txt index 5c42e19fa4b..fc62aa46678 100644 --- a/Examples/Io/Csv/CMakeLists.txt +++ b/Examples/Io/Csv/CMakeLists.txt @@ -15,6 +15,7 @@ add_library( src/CsvTrackParameterReader.cpp src/CsvTrackParameterWriter.cpp src/CsvMultiTrajectoryWriter.cpp + src/CsvProtoTrackWriter.cpp src/CsvSpacePointWriter.cpp src/CsvBFieldWriter.cpp) target_include_directories( diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp new file mode 100644 index 00000000000..c0ce3463eb3 --- /dev/null +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp @@ -0,0 +1,71 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/TrackParametrization.hpp" +#include "Acts/EventData/Measurement.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "ActsExamples/Digitization/DigitizationConfig.hpp" +#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/ProtoTrack.hpp" +#include "ActsExamples/EventData/SimSpacePoint.hpp" +#include "ActsExamples/Framework/WriterT.hpp" + +#include + +namespace ActsExamples { + +/// @class CsvProtoTrackWriter +/// +class CsvProtoTrackWriter final : public WriterT { + public: + struct Config { + /// Which prototracks to write + std::string inputPrototracks; + /// Spacepoint collection + std::string inputSpacepoints; + /// Output directory + std::string outputDir; + /// Number of decimal digits for floating point precision in output. + int outputPrecision = std::numeric_limits::max_digits10; + }; + + /// Constructor with + /// @param config configuration struct + /// @param level logging level + CsvProtoTrackWriter(const Config& config, Acts::Logging::Level level); + + /// Virtual destructor + ~CsvProtoTrackWriter() override; + + /// End-of-run hook + ProcessCode finalize() override; + + /// Get readonly access to the config parameters + const Config& config() const { return m_cfg; } + + protected: + /// This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + /// + /// @param ctx The Algorithm context with per event information + /// @param measurements is the data to be written out + ProcessCode writeT(const AlgorithmContext& ctx, + const ProtoTrackContainer& tracks) override; + + private: + Config m_cfg; + + ReadDataHandle m_inputSpacepoints{this, + "inputSpacepoints"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/Csv/src/CsvOutputData.hpp b/Examples/Io/Csv/src/CsvOutputData.hpp index 1903306fb0c..2eb166104c6 100644 --- a/Examples/Io/Csv/src/CsvOutputData.hpp +++ b/Examples/Io/Csv/src/CsvOutputData.hpp @@ -11,6 +11,8 @@ #pragma once +#include + #include #include @@ -304,4 +306,12 @@ struct TrackParameterData { cov_qopphi, cov_qoptheta); }; +struct ProtoTrackData { + std::size_t trackId; + Index measurementId; + double x, y, z; + + DFE_NAMEDTUPLE(ProtoTrackData, trackId, measurementId, x, y, z); +}; + } // namespace ActsExamples diff --git a/Examples/Io/Csv/src/CsvProtoTrackWriter.cpp b/Examples/Io/Csv/src/CsvProtoTrackWriter.cpp new file mode 100644 index 00000000000..0f7b1b0adfd --- /dev/null +++ b/Examples/Io/Csv/src/CsvProtoTrackWriter.cpp @@ -0,0 +1,67 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Intersection.hpp" +#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/SimSpacePoint.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" +#include "ActsExamples/Utilities/EventDataTransforms.hpp" +#include "ActsExamples/Utilities/Paths.hpp" +#include "ActsExamples/Utilities/Range.hpp" + +#include +#include +#include + +#include + +#include "CsvOutputData.hpp" + +ActsExamples::CsvProtoTrackWriter::CsvProtoTrackWriter( + const ActsExamples::CsvProtoTrackWriter::Config& config, + Acts::Logging::Level level) + : WriterT(config.inputPrototracks, "CsvProtoTrackWriter", level), + m_cfg(config) { + m_inputSpacepoints.initialize(m_cfg.inputSpacepoints); +} + +ActsExamples::CsvProtoTrackWriter::~CsvProtoTrackWriter() = default; + +ActsExamples::ProcessCode ActsExamples::CsvProtoTrackWriter::finalize() { + // Write the tree + return ProcessCode::SUCCESS; +} + +ActsExamples::ProcessCode ActsExamples::CsvProtoTrackWriter::writeT( + const AlgorithmContext& ctx, const ProtoTrackContainer& tracks) { + const auto& spacepoints = m_inputSpacepoints(ctx); + + // Open per-event file for all components + std::string path = + perEventFilepath(m_cfg.outputDir, "prototracks.csv", ctx.eventNumber); + + dfe::NamedTupleCsvWriter writer(path, m_cfg.outputPrecision); + + for (auto trackId = 0ul; trackId < tracks.size(); ++trackId) { + for (Index measurmentId : tracks[trackId]) { + const auto spr = findSpacePointForIndex(measurmentId, spacepoints); + if (spr == nullptr) { + ACTS_WARNING("Could not convert index " << measurmentId + << " to spacepoint"); + continue; + } + const auto& sp = *spr; + writer.append({trackId, measurmentId, sp.x(), sp.y(), sp.z()}); + } + } + return ActsExamples::ProcessCode::SUCCESS; +} diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp b/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp index 20733d4c310..3e4ea190b08 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.cpp @@ -143,6 +143,11 @@ ActsExamples::ProcessCode ActsExamples::CKFPerformanceWriter::writeT( const auto& particles = m_inputParticles(ctx); const auto& hitParticlesMap = m_inputMeasurementParticlesMap(ctx); + std::map particleTruthHitCount; + for (const auto& [_, pid] : hitParticlesMap) { + particleTruthHitCount[pid]++; + } + // Counter of truth-matched reco tracks std::map> matched; // Counter of truth-unmatched reco tracks @@ -183,7 +188,7 @@ ActsExamples::ProcessCode ActsExamples::CKFPerformanceWriter::writeT( identifyContributingParticles(hitParticlesMap, traj, trackTip, particleHitCounts); if (particleHitCounts.empty()) { - ACTS_WARNING( + ACTS_DEBUG( "No truth particle associated with this trajectory with entry " "index = " << trackTip); @@ -198,15 +203,26 @@ ActsExamples::ProcessCode ActsExamples::CKFPerformanceWriter::writeT( // Check if the trajectory is matched with truth. // If not, it will be classified as 'fake' + const bool recoMatched = + static_cast(nMajorityHits) / trajState.nMeasurements >= + m_cfg.truthMatchProbMin; + const bool truthMatched = + static_cast(nMajorityHits) / + particleTruthHitCount.at(majorityParticleId) >= + m_cfg.truthMatchProbMin; + bool isFake = false; - if (nMajorityHits * 1. / trajState.nMeasurements >= - m_cfg.truthMatchProbMin) { + if (not m_cfg.doubleMatching and recoMatched) { + matched[majorityParticleId].push_back( + {nMajorityHits, fittedParameters}); + } else if (m_cfg.doubleMatching and recoMatched and truthMatched) { matched[majorityParticleId].push_back( {nMajorityHits, fittedParameters}); } else { isFake = true; unmatched[majorityParticleId]++; } + // Fill fake rate plots m_fakeRatePlotTool.fill(m_fakeRatePlotCache, fittedParameters, isFake); diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp b/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp index 3d1c118fea7..8ef28983463 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/CKFPerformanceWriter.hpp @@ -66,6 +66,9 @@ class CKFPerformanceWriter final : public WriterT { DuplicationPlotTool::Config duplicationPlotToolConfig; TrackSummaryPlotTool::Config trackSummaryPlotToolConfig; + /// Whether to do double matching or not + bool doubleMatching = false; + /// Min reco-truth matching probability double truthMatchProbMin = 0.5; diff --git a/Examples/Io/Root/src/RootPropagationStepsWriter.cpp b/Examples/Io/Root/src/RootPropagationStepsWriter.cpp index bd52b91643d..a6c2c884b60 100644 --- a/Examples/Io/Root/src/RootPropagationStepsWriter.cpp +++ b/Examples/Io/Root/src/RootPropagationStepsWriter.cpp @@ -153,7 +153,7 @@ ActsExamples::ProcessCode ActsExamples::RootPropagationStepsWriter::writeT( m_dy.push_back(direction.y()); m_dz.push_back(direction.z()); - double accuracy = step.stepSize.value(Acts::ConstrainedStep::accuracy); + double accuracy = step.stepSize.accuracy(); double actor = step.stepSize.value(Acts::ConstrainedStep::actor); double aborter = step.stepSize.value(Acts::ConstrainedStep::aborter); double user = step.stepSize.value(Acts::ConstrainedStep::user); diff --git a/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp b/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp index 172b1de3a98..7e14671d540 100644 --- a/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp +++ b/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp @@ -545,12 +545,11 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectoryStatesWriter::writeT( m_res_eT[ipar].push_back(parameters[Acts::eBoundTime] - truthTIME); // track parameters error + // MARK: fpeMaskBegin(FLTINV, 1, #2348) m_err_eLOC0[ipar].push_back( - std::sqrt(covariance( // MARK: fpeMask(FLTINV, 1, #2348) - Acts::eBoundLoc0, Acts::eBoundLoc0))); + std::sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0))); m_err_eLOC1[ipar].push_back( - std::sqrt(covariance( // MARK: fpeMask(FLTINV, 1, #2348) - Acts::eBoundLoc1, Acts::eBoundLoc1))); + std::sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1))); m_err_ePHI[ipar].push_back( std::sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi))); m_err_eTHETA[ipar].push_back( @@ -559,6 +558,7 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectoryStatesWriter::writeT( std::sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP))); m_err_eT[ipar].push_back( std::sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime))); + // MARK: fpeMaskEnd(FLTINV) // track parameters pull m_pull_eLOC0[ipar].push_back( diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index a0cd587d051..2724d04933b 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -10,7 +10,8 @@ u = acts.UnitConstants SeedingAlgorithm = Enum( - "SeedingAlgorithm", "Default TruthSmeared TruthEstimated Orthogonal HoughTransform" + "SeedingAlgorithm", + "Default TruthSmeared TruthEstimated Orthogonal HoughTransform FTF", ) TruthSeedRanges = namedtuple( @@ -121,8 +122,8 @@ CkfConfig = namedtuple( "CkfConfig", - ["chi2CutOff", "numMeasurementsCutOff"], - defaults=[15.0, 10], + ["chi2CutOff", "numMeasurementsCutOff", "maxSteps"], + defaults=[15.0, 10, None], ) AmbiguityResolutionConfig = namedtuple( @@ -167,6 +168,8 @@ def addSeeding( trackingGeometry: acts.TrackingGeometry, field: acts.MagneticFieldProvider, geoSelectionConfigFile: Optional[Union[Path, str]] = None, + layerMappingConfigFile: Optional[Union[Path, str]] = None, + fastrack_inputConfigFile: Optional[Union[Path, str]] = None, seedingAlgorithm: SeedingAlgorithm = SeedingAlgorithm.Default, truthSeedRanges: Optional[TruthSeedRanges] = TruthSeedRanges(), particleSmearingSigmas: ParticleSmearingSigmas = ParticleSmearingSigmas(), @@ -314,6 +317,21 @@ def addSeeding( houghTransformConfig.outputSeeds = "seeds" houghTransformConfig.trackingGeometry = trackingGeometry seeds = addHoughTransformSeeding(s, houghTransformConfig, logLevel) + elif seedingAlgorithm == SeedingAlgorithm.FTF: + logger.info("Using FTF seeding") + # output of algs changed, only one output now + seeds = addFTFSeeding( + s, + spacePoints, + seedFinderConfigArg, + seedFinderOptionsArg, + seedFilterConfigArg, + trackingGeometry, + logLevel, + layerMappingConfigFile, + geoSelectionConfigFile, + fastrack_inputConfigFile, + ) else: logger.fatal("unknown seedingAlgorithm %s", seedingAlgorithm) @@ -778,6 +796,84 @@ def addHoughTransformSeeding( return ht.config.outputSeeds +def addFTFSeeding( + sequence: acts.examples.Sequencer, + spacePoints: str, + seedFinderConfigArg: SeedFinderConfigArg, + seedFinderOptionsArg: SeedFinderOptionsArg, + seedFilterConfigArg: SeedFilterConfigArg, + trackingGeometry: acts.TrackingGeometry, + logLevel: acts.logging.Level = None, + layerMappingConfigFile: Union[Path, str] = None, + geoSelectionConfigFile: Union[Path, str] = None, + fastrack_inputConfigFile: Union[Path, str] = None, +): + """FTF seeding""" + + logLevel = acts.examples.defaultLogging(sequence, logLevel)() + layerMappingFile = str(layerMappingConfigFile) # turn path into string + fastrack_inputFile = str(fastrack_inputConfigFile) + seedFinderConfig = acts.SeedFinderFTFConfig( + **acts.examples.defaultKWArgs( + sigmaScattering=seedFinderConfigArg.sigmaScattering, + maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM, + minPt=seedFinderConfigArg.minPt, + fastrack_input_file=fastrack_inputFile, + m_useClusterWidth=False, + ), + ) + seedFinderOptions = acts.SeedFinderOptions( + **acts.examples.defaultKWArgs( + beamPos=acts.Vector2(0.0, 0.0) + if seedFinderOptionsArg.beamPos == (None, None) + else acts.Vector2( + seedFinderOptionsArg.beamPos[0], seedFinderOptionsArg.beamPos[1] + ), + bFieldInZ=seedFinderOptionsArg.bFieldInZ, + ) + ) + seedFilterConfig = acts.SeedFilterConfig( + **acts.examples.defaultKWArgs( + maxSeedsPerSpM=seedFinderConfig.maxSeedsPerSpM, + deltaRMin=( + seedFinderConfigArg.deltaR[0] + if seedFilterConfigArg.deltaRMin is None + else seedFilterConfigArg.deltaRMin + ), + impactWeightFactor=seedFilterConfigArg.impactWeightFactor, + zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor, + compatSeedWeight=seedFilterConfigArg.compatSeedWeight, + compatSeedLimit=seedFilterConfigArg.compatSeedLimit, + numSeedIncrement=seedFilterConfigArg.numSeedIncrement, + seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement, + seedConfirmation=seedFilterConfigArg.seedConfirmation, + # curvatureSortingInFilter=seedFilterConfigArg.curvatureSortingInFilter, + maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf, + maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf, + useDeltaRorTopRadius=seedFilterConfigArg.useDeltaRorTopRadius, + ) + ) + + seedingAlg = acts.examples.SeedingFTFAlgorithm( + level=logLevel, + inputSpacePoints=[spacePoints], + outputSeeds="seeds", + seedFilterConfig=seedFilterConfig, + seedFinderConfig=seedFinderConfig, + seedFinderOptions=seedFinderOptions, + layerMappingFile=layerMappingFile, + geometrySelection=acts.examples.readJsonGeometryList( + str(geoSelectionConfigFile) + ), + inputSourceLinks="sourcelinks", + trackingGeometry=trackingGeometry, + fill_module_csv=False, + ) + + sequence.addAlgorithm(seedingAlg) + return seedingAlg.config.outputSeeds + + def addSeedPerformanceWriters( sequence: acts.examples.Sequencer, outputDirRoot: Union[Path, str], @@ -912,6 +1008,7 @@ def addTruthTrackingGsf( calibrator=acts.examples.makePassThroughCalibrator(), ) s.addAlgorithm(gsfAlg) + s.addWhiteboardAlias("tracks", gsfAlg.config.outputTracks) trackConverter = acts.examples.TracksToTrajectories( level=customLogLevel(), @@ -1005,6 +1102,9 @@ def addCKFTracks( findTracks=acts.examples.TrackFindingAlgorithm.makeTrackFinderFunction( trackingGeometry, field, customLogLevel() ), + **acts.examples.defaultKWArgs( + maxSteps=ckfConfig.maxSteps, + ), ) s.addAlgorithm(trackFinder) s.addWhiteboardAlias("tracks", trackFinder.config.outputTracks) @@ -1035,6 +1135,55 @@ def addCKFTracks( return s +def addGx2fTracks( + s: acts.examples.Sequencer, + trackingGeometry: acts.TrackingGeometry, + field: acts.MagneticFieldProvider, + # directNavigation: bool = False, + inputProtoTracks: str = "truth_particle_tracks", + multipleScattering: bool = False, + energyLoss: bool = False, + clusters: str = None, + calibrator: acts.examples.MeasurementCalibrator = acts.examples.makePassThroughCalibrator(), + logLevel: Optional[acts.logging.Level] = None, +) -> None: + customLogLevel = acts.examples.defaultLogging(s, logLevel) + + gx2fOptions = { + "multipleScattering": multipleScattering, + "energyLoss": energyLoss, + "freeToBoundCorrection": acts.examples.FreeToBoundCorrection(False), + "level": customLogLevel(), + } + + fitAlg = acts.examples.TrackFittingAlgorithm( + level=customLogLevel(), + inputMeasurements="measurements", + inputSourceLinks="sourcelinks", + inputProtoTracks=inputProtoTracks, + inputInitialTrackParameters="estimatedparameters", + inputClusters=clusters if clusters is not None else "", + outputTracks="gx2fTracks", + pickTrack=-1, + fit=acts.examples.makeGlobalChiSquareFitterFunction( + trackingGeometry, field, **gx2fOptions + ), + calibrator=calibrator, + ) + s.addAlgorithm(fitAlg) + s.addWhiteboardAlias("tracks", fitAlg.config.outputTracks) + + trackConverter = acts.examples.TracksToTrajectories( + level=customLogLevel(), + inputTracks=fitAlg.config.outputTracks, + outputTrajectories="gx2fTrajectories", + ) + s.addAlgorithm(trackConverter) + s.addWhiteboardAlias("trajectories", trackConverter.config.outputTrajectories) + + return s + + def addTrajectoryWriters( s: acts.examples.Sequencer, name: str, diff --git a/Examples/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index 8faf65e6f5a..f0a9854337e 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -40,11 +40,12 @@ "absEta", # (min,max) "pt", # (min,max) "m", # (min,max) + "measurements", # (min,max) "removeCharged", # bool "removeNeutral", # bool "removeSecondaries", # bool ], - defaults=[(None, None)] * 8 + [None] * 3, + defaults=[(None, None)] * 9 + [None] * 3, ) @@ -333,6 +334,7 @@ def addParticleSelection( config: ParticleSelectorConfig, inputParticles: str, outputParticles: str, + inputMeasurementParticlesMap: str = "", logLevel: Optional[acts.logging.Level] = None, ) -> None: """ @@ -370,6 +372,8 @@ def addParticleSelection( ptMax=config.pt[1], mMin=config.m[0], mMax=config.m[1], + measurementsMin=config.measurements[0], + measurementsMax=config.measurements[1], removeCharged=config.removeCharged, removeNeutral=config.removeNeutral, removeSecondaries=config.removeSecondaries, @@ -377,6 +381,7 @@ def addParticleSelection( level=customLogLevel(), inputParticles=inputParticles, outputParticles=outputParticles, + inputMeasurementParticlesMap=inputMeasurementParticlesMap, ) ) diff --git a/Examples/Python/src/ExaTrkXTrackFinding.cpp b/Examples/Python/src/ExaTrkXTrackFinding.cpp index 20babf97c76..ad27045da03 100644 --- a/Examples/Python/src/ExaTrkXTrackFinding.cpp +++ b/Examples/Python/src/ExaTrkXTrackFinding.cpp @@ -15,11 +15,9 @@ #include "Acts/Plugins/ExaTrkX/TorchMetricLearning.hpp" #include "Acts/Plugins/ExaTrkX/TorchTruthGraphMetricsHook.hpp" #include "Acts/Plugins/Python/Utilities.hpp" -#include "Acts/TrackFinding/MeasurementSelector.hpp" -#include "ActsExamples/TrackFinding/SeedingAlgorithm.hpp" -#include "ActsExamples/TrackFinding/SpacePointMaker.hpp" -#include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp" +#include "ActsExamples/TrackFindingExaTrkX/PrototracksToParameters.hpp" #include "ActsExamples/TrackFindingExaTrkX/TrackFindingAlgorithmExaTrkX.hpp" +#include "ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp" #include @@ -170,9 +168,10 @@ void addExaTrkXTrackFinding(Context &ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::TrackFindingAlgorithmExaTrkX, mex, "TrackFindingAlgorithmExaTrkX", inputSpacePoints, inputSimHits, - inputParticles, inputMeasurementSimhitsMap, outputProtoTracks, - graphConstructor, edgeClassifiers, trackBuilder, rScale, phiScale, zScale, - targetMinHits, targetMinPT); + inputParticles, inputClusters, inputMeasurementSimhitsMap, + outputProtoTracks, graphConstructor, edgeClassifiers, trackBuilder, + rScale, phiScale, zScale, cellCountScale, cellSumScale, clusterXScale, + clusterYScale, targetMinHits, targetMinPT); { auto cls = @@ -208,8 +207,22 @@ void addExaTrkXTrackFinding(Context &ctx) { py::arg("graphConstructor"), py::arg("edgeClassifiers"), py::arg("trackBuilder"), py::arg("level")) .def("run", &ExaTrkXPipeline::run, py::arg("features"), - py::arg("spacepoints"), py::arg("hook") = Acts::ExaTrkXHook{}); + py::arg("spacepoints"), py::arg("deviceHint") = -1, + py::arg("hook") = Acts::ExaTrkXHook{}, + py::arg("timing") = nullptr); } + + ACTS_PYTHON_DECLARE_ALGORITHM( + ActsExamples::PrototracksToParameters, mex, "PrototracksToParameters", + inputProtoTracks, inputSpacePoints, outputSeeds, outputParameters, + outputProtoTracks, geometry, magneticField, buildTightSeeds); + + ACTS_PYTHON_DECLARE_ALGORITHM( + ActsExamples::TrackFindingFromPrototrackAlgorithm, mex, + "TrackFindingFromPrototrackAlgorithm", inputProtoTracks, + inputMeasurements, inputSourceLinks, inputInitialTrackParameters, + outputTracks, measurementSelectorCfg, trackingGeometry, magneticField, + findTracks, tag); } } // namespace Acts::Python diff --git a/Examples/Python/src/Output.cpp b/Examples/Python/src/Output.cpp index 443f01cd8c9..4a05c9e583c 100644 --- a/Examples/Python/src/Output.cpp +++ b/Examples/Python/src/Output.cpp @@ -18,6 +18,7 @@ #include "ActsExamples/Io/Csv/CsvMultiTrajectoryWriter.hpp" #include "ActsExamples/Io/Csv/CsvParticleWriter.hpp" #include "ActsExamples/Io/Csv/CsvPlanarClusterWriter.hpp" +#include "ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp" #include "ActsExamples/Io/Csv/CsvSimHitWriter.hpp" #include "ActsExamples/Io/Csv/CsvSpacepointWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackParameterWriter.hpp" @@ -387,6 +388,10 @@ void addOutput(Context& ctx) { inputTrajectories, outputDir, outputStem, outputPrecision); + ACTS_PYTHON_DECLARE_WRITER(ActsExamples::CsvProtoTrackWriter, mex, + "CsvProtoTrackWriter", inputSpacepoints, + inputPrototracks, outputDir); + { using Writer = ActsExamples::CsvBFieldWriter; diff --git a/Examples/Python/src/TrackFinding.cpp b/Examples/Python/src/TrackFinding.cpp index 297e41005c3..897a21c98e1 100644 --- a/Examples/Python/src/TrackFinding.cpp +++ b/Examples/Python/src/TrackFinding.cpp @@ -12,6 +12,7 @@ #include "Acts/Seeding/SeedConfirmationRangeConfig.hpp" #include "Acts/Seeding/SeedFilterConfig.hpp" #include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" #include "Acts/Seeding/SeedFinderOrthogonalConfig.hpp" #include "Acts/Seeding/SpacePointGrid.hpp" #include "Acts/TrackFinding/MeasurementSelector.hpp" @@ -20,10 +21,12 @@ #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/TrackFinding/HoughTransformSeeder.hpp" #include "ActsExamples/TrackFinding/SeedingAlgorithm.hpp" +#include "ActsExamples/TrackFinding/SeedingFTFAlgorithm.hpp" #include "ActsExamples/TrackFinding/SeedingOrthogonalAlgorithm.hpp" #include "ActsExamples/TrackFinding/SpacePointMaker.hpp" #include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp" #include "ActsExamples/TrackFinding/TrackParamsEstimationAlgorithm.hpp" +#include "ActsExamples/Utilities/MeasurementMapSelector.hpp" #include "ActsExamples/Utilities/PrototracksToSeeds.hpp" #include "ActsExamples/Utilities/SeedsToPrototracks.hpp" #include "ActsExamples/Utilities/TracksToTrajectories.hpp" @@ -192,6 +195,24 @@ void addTrackFinding(Context& ctx) { patchKwargsConstructor(c); } + { + using Config = Acts::SeedFinderFTFConfig; + auto c = py::class_(m, "SeedFinderFTFConfig").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(minPt); + ACTS_PYTHON_MEMBER(sigmaScattering); + ACTS_PYTHON_MEMBER(highland); + ACTS_PYTHON_MEMBER(maxScatteringAngle2); + ACTS_PYTHON_MEMBER(fastrack_input_file); + ACTS_PYTHON_MEMBER(m_phiSliceWidth); + ACTS_PYTHON_MEMBER(m_nMaxPhiSlice); + ACTS_PYTHON_MEMBER(m_useClusterWidth); + ACTS_PYTHON_MEMBER(m_layerGeometry); + ACTS_PYTHON_MEMBER(maxSeedsPerSpM); + ACTS_PYTHON_STRUCT_END(); + patchKwargsConstructor(c); + } + { using seedConf = Acts::SeedConfirmationRangeConfig; auto c = py::class_(m, "SeedConfirmationRangeConfig") @@ -250,6 +271,12 @@ void addTrackFinding(Context& ctx) { outputSeeds, seedFilterConfig, seedFinderConfig, seedFinderOptions); + ACTS_PYTHON_DECLARE_ALGORITHM( + ActsExamples::SeedingFTFAlgorithm, mex, "SeedingFTFAlgorithm", + inputSpacePoints, outputSeeds, seedFilterConfig, seedFinderConfig, + seedFinderOptions, layerMappingFile, geometrySelection, inputSourceLinks, + trackingGeometry, ACTS_FTF_Map, fill_module_csv); + ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::HoughTransformSeeder, mex, "HoughTransformSeeder", inputSpacePoints, outputProtoTracks, inputSourceLinks, trackingGeometry, @@ -298,6 +325,8 @@ void addTrackFinding(Context& ctx) { ACTS_PYTHON_MEMBER(findTracks); ACTS_PYTHON_MEMBER(measurementSelectorCfg); ACTS_PYTHON_MEMBER(trackSelectorCfg); + ACTS_PYTHON_MEMBER(backward); + ACTS_PYTHON_MEMBER(maxSteps); ACTS_PYTHON_STRUCT_END(); } @@ -349,6 +378,11 @@ void addTrackFinding(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::PrototracksToSeeds, mex, "PrototracksToSeeds", inputProtoTracks, inputSpacePoints, outputSeeds, outputProtoTracks); + + ACTS_PYTHON_DECLARE_ALGORITHM( + ActsExamples::MeasurementMapSelector, mex, "MeasurementMapSelector", + inputMeasurementParticleMap, inputSourceLinks, + outputMeasurementParticleMap, geometrySelection); } } // namespace Acts::Python diff --git a/Examples/Python/src/TrackFitting.cpp b/Examples/Python/src/TrackFitting.cpp index 8dcf9f0e917..6451cba4d45 100644 --- a/Examples/Python/src/TrackFitting.cpp +++ b/Examples/Python/src/TrackFitting.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2021 CERN for the benefit of the Acts project +// Copyright (C) 2021-2023 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 @@ -127,6 +127,24 @@ void addTrackFitting(Context& ctx) { py::arg("weightCutoff"), py::arg("finalReductionMethod"), py::arg("abortOnError"), py::arg("disableAllMaterialHandling"), py::arg("level")); + + mex.def( + "makeGlobalChiSquareFitterFunction", + [](std::shared_ptr trackingGeometry, + std::shared_ptr magneticField, + bool multipleScattering, bool energyLoss, + Acts::FreeToBoundCorrection freeToBoundCorrection, + Logging::Level level) { + return ActsExamples::makeGlobalChiSquareFitterFunction( + trackingGeometry, magneticField, multipleScattering, energyLoss, + freeToBoundCorrection, *Acts::getDefaultLogger("Gx2f", level)); + }, + py::arg("trackingGeometry"), py::arg("magneticField"), + py::arg("multipleScattering"), py::arg("energyLoss"), + py::arg("freeToBoundCorrection"), py::arg("level")); + + // TODO add other important parameters like nUpdates + // TODO add also in trackfitterfunction } { diff --git a/Examples/Python/src/TruthTracking.cpp b/Examples/Python/src/TruthTracking.cpp index af4f36a2b4c..5b37e7b3753 100644 --- a/Examples/Python/src/TruthTracking.cpp +++ b/Examples/Python/src/TruthTracking.cpp @@ -106,6 +106,7 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_STRUCT_BEGIN(c, Config); ACTS_PYTHON_MEMBER(inputParticles); + ACTS_PYTHON_MEMBER(inputMeasurementParticlesMap); ACTS_PYTHON_MEMBER(outputParticles); ACTS_PYTHON_MEMBER(rhoMin); ACTS_PYTHON_MEMBER(rhoMax); @@ -123,6 +124,8 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_MEMBER(mMax); ACTS_PYTHON_MEMBER(ptMin); ACTS_PYTHON_MEMBER(ptMax); + ACTS_PYTHON_MEMBER(measurementsMin); + ACTS_PYTHON_MEMBER(measurementsMax); ACTS_PYTHON_MEMBER(removeCharged); ACTS_PYTHON_MEMBER(removeNeutral); ACTS_PYTHON_MEMBER(removeSecondaries); @@ -136,6 +139,8 @@ void addTruthTracking(Context& ctx) { pythonRangeProperty(c, "absEta", &Config::absEtaMin, &Config::absEtaMax); pythonRangeProperty(c, "m", &Config::mMin, &Config::mMax); pythonRangeProperty(c, "pt", &Config::ptMin, &Config::ptMax); + pythonRangeProperty(c, "measurements", &Config::measurementsMin, + &Config::measurementsMax); } { diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index a688d7a47f5..aaadba20ec3 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -22,49 +22,49 @@ test_itk_seeding__particles_final.root: 0d38f1a4ede5fb86844c156c002ada340011e140 test_itk_seeding__particles_initial.root: 88315e93ed4cb5d40a8721502048a9d1fc100e0a7d504e25fd4502c8302f1578 test_propagation__propagation_steps.root: 174301b25784dbb881196b658f2d7f99c0a2ea688a0129e6110fc19aa5cf8e54 test_material_recording__geant4_material_tracks.root: e411152d370775463c22b19a351dfc7bfe40b51985e10a7c1a010aebde80715d -test_truth_tracking_kalman[generic-0.0]__trackstates_fitter.root: 0cdfeb13eaff45df38451820fcc15a7dfb40e55dab220e6e1d4d6f027ee8d4c8 -test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: 1b76bad8f1e7cbaeaa2bc4f6d866a3b4289fc39947d950ab0c2efbeae77427e0 +test_truth_tracking_kalman[generic-0.0]__trackstates_fitter.root: 559321c478b63859b5d1b687e73d79655d1b3b12ebc34c2bd5ffa1049827b020 +test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: abb9520d5ace34139d993fb8214fda3b6649ec52c44e25928e9bce78209ef923 test_truth_tracking_kalman[generic-0.0]__performance_track_finder.root: ada9cda2ec3c648b144bdaa081d6eff923c80f3d484c4196006e847428cf67a8 -test_truth_tracking_kalman[generic-1000.0]__trackstates_fitter.root: 5d54954ad21b8153b1788ccc296680a50969c761924f1c5db0600033d4577a3c -test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: d809641c9414dac7284101c72a0cfd514699ec2b95468fd8640241ad4d32d8ad +test_truth_tracking_kalman[generic-1000.0]__trackstates_fitter.root: 0befe1ec82e461f64bac9d44f6b0d96f01263b0d6c094afdb4b1cc3d2c965095 +test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: bc0b98247953bac0f37e3600441c08afb9f73e190f718b108009970d61a32e72 test_truth_tracking_kalman[generic-1000.0]__performance_track_finder.root: ada9cda2ec3c648b144bdaa081d6eff923c80f3d484c4196006e847428cf67a8 -test_truth_tracking_kalman[odd-0.0]__trackstates_fitter.root: f56a0ff4bb33412e4922804688ca6e8957f2b226730deb6b8425d7630557cc29 -test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: cdf06f936f5a59d74f3a44f147e30c10bc12f43a73180acfda871e53e77d9d50 +test_truth_tracking_kalman[odd-0.0]__trackstates_fitter.root: 45e2c15e4b942a8adec909e50c8297aa8dd9217dc047dbc5e7ef61c02f4e4378 +test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: 119bae4e06305acab6ef86ca73acd1b1ea90eaa3c3b347d9655cf877419563d6 test_truth_tracking_kalman[odd-0.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 -test_truth_tracking_kalman[odd-1000.0]__trackstates_fitter.root: 97d89826902501803191f17c6a678181448d546276497fef946f33911b181bfc -test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: f434d1784a5621af80bb0a4bdf194f1ce8a0581b95e1abe79c0e55d7d37d85cb +test_truth_tracking_kalman[odd-1000.0]__trackstates_fitter.root: 707c0784deac8d7c63de38542f08f66dbe375f502b26f7d0aeaccf976d4bcf67 +test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: 8830b954f91e3da4722e5d6922a2a59fcbc2d14cbf7732eb997fd9f44f366262 test_truth_tracking_kalman[odd-1000.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 -test_truth_tracking_gsf[generic]__trackstates_gsf.root: 8d09c4c31bf65866645b779ceb764b2efabe82c64e2bdc84d77e343a8249ca4d -test_truth_tracking_gsf[generic]__tracksummary_gsf.root: 01f5a6e169642dc54307526c9921c6d5b9fd6e694e935df42ef8ed81a67babe2 -test_truth_tracking_gsf[odd]__trackstates_gsf.root: a2364a5072145c7eb4ecf8c67b2e48c8c528fe5ab9307d078c9e5f1f659d87e0 -test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 6cb9802b3afa85d41261a77da4ea20754fdcd54f86e682ec864764b1475994ed +test_truth_tracking_gsf[generic]__trackstates_gsf.root: 107d979b6e6b9a19f35a46cce9d3472fb383f79e287e6b7e880d860db423c520 +test_truth_tracking_gsf[generic]__tracksummary_gsf.root: ec4cf67cea309776ec2e13ac2263662a5bda3eebb766aa22a804ef9e17b782ea +test_truth_tracking_gsf[odd]__trackstates_gsf.root: 16c480cf80ccedb5327cdfc534387bf8ad0a331d423c5f707a945efa8e24aa8f +test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 500b88f9b1e28fb44064ae49e84f51f83ae15828aff228df94e53477e13ee995 test_particle_gun__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec test_material_mapping__material-map_tracks.root: 2deac7a48ff1185ba3889cfd4cc0bd0a6de57293048dfd6766f3c3907232e45e test_material_mapping__propagation-material.root: 84b04ebd5721550867f0f193b5eb4e9f3f205df542cb46090d320be6bff565c5 test_volume_material_mapping__material-map-volume_tracks.root: b95561a6247df9e3599a997daa6c1d76461e58f83059b82f2ec27229c9b35e6c test_volume_material_mapping__propagation-volume-material.root: ba0a302a2973498d257b70b78dfa6252d595c17960ee7fa754bd310bfaa9e7e8 -test_digitization_example[smeared]__measurements.root: 0888b7bb4da63d15be18c14c5a419c8813f9d76731da94751247dc4a6772c895 -test_digitization_example[geometric]__measurements.root: bade4e094a950f4ecf523b192294e4336da14b4ba6dc5164160cc8e3308cdcd2 +test_digitization_example[smeared]__measurements.root: 413d8a82904fe5ca9380eec309aa4b6c0a829450db92125457edaaff5a624e5a +test_digitization_example[geometric]__measurements.root: 29db434701433e30205a7a12fa3e026cd2f2e8c07d32644ef0a54350058d87c4 test_digitization_example_input[smeared]__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec -test_digitization_example_input[smeared]__measurements.root: dd6d1af353a676d28ac398d015a20db736dccec0fb104ea909d81c9156072e18 +test_digitization_example_input[smeared]__measurements.root: 2e4fd9d3e6244e53486496e024f147e9ab2dcadb07453aa926d7ebad473089a5 test_digitization_example_input[geometric]__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec -test_digitization_example_input[geometric]__measurements.root: 56a64b3df65fcd04d677fac6b6ee3f1978fe6d7777599bb918d40e514ef45a1f -test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: 1786b1f1e90a8fe2ad6f534072506bacf8dbcf1a7e25c283a5ab261a319479fb -test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 7d0fd1fa4c9057c7cce7d1acc06e18b0c2a35a047d0b23d646c7cd5d73bb8fe5 +test_digitization_example_input[geometric]__measurements.root: 2dfd1889028fe3a0964fe9f91bc617987fbecd122256a9bd07433da1931323bc +test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: c95a1e289b588477cc3946ab68f0aa396ca515edc18471cf6e964e806ccd2980 +test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 79d0c8f6a636e1ba19abcc3fb786735b1ea7b89290b01957f50927ba3f2f3481 test_ckf_tracks_example[generic-full_seeding]__performance_seeding_trees.root: 0e0676ffafdb27112fbda50d1cf627859fa745760f98073261dcf6db3f2f991e -test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: 7dd654c2223f3c53eac4ba496b3dec3739d29a443e9a77e718981bad44b95f1c -test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 6ab977c6061e75960217e7376bd459766981bbefd0b63f58d4f73db7c8cf4b3f +test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: ef8ff1d65a63d61ca1c6004960d7ff537cfd7d8fbd3920cf9411064f8d48ef9b +test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 149cb73cef1526dba115192aa2b66c4de6dcd9c7f81c73f1d54190d543b1e41f test_ckf_tracks_example[generic-truth_estimated]__performance_seeding.root: 1facb05c066221f6361b61f015cdf0918e94d9f3fce2269ec7b6a4dffeb2bc7e -test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 0351e6e87bbda82c666e3580f8437fa9736f9d95ec4dd8658129bc73bb3fbf6c -test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: cc69920c2a6b571baa680bd12a605e7b2f9d59afec7dcfc82565c4951f831ef6 -test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: 6a7630f6845bc5cfca18741fa1347244f35aed499a2585ac3d7e4f4cb31c87d5 -test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 38d4f157f342447c149be4a15450b4455757cc0ff23316c68c9a92bb16356508 +test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 10eb393cb419ff81b34c627db84bcfe89134bd58838325d894e184e00a75a9c4 +test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: e8e56e965799191c643eb4339f36044edf272807656fa73014cab4e78b1f1619 +test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: 9e15151308c0983fc681cbf60b478542814235512143755c092b921c7a9c19e3 +test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: c3feeabe87c49e7e8140f420af936767f6171276cd431dfb17a04f721c1895b0 test_ckf_tracks_example[odd-full_seeding]__performance_seeding_trees.root: 43c58577aafe07645e5660c4f43904efadf91d8cda45c5c04c248bbe0f59814f -test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: ffe11d3dbcc89ad78f96487167beafc36355e8359140883fa4f6f08b256e5e77 -test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 910af253f44bc53630912688f71868b5d707aad0351071b8f63552d294a75e3a +test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 7c11478d9d6ab190b3e1ff447f54f0ba680d5f9b70ee1db158d69db272627b41 +test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: d747ca518ce5a9ad01827a87052c6590a6688eb61ada213c012250cfa7e3e762 test_ckf_tracks_example[odd-truth_estimated]__performance_seeding.root: 1a36b7017e59f1c08602ef3c2cb0483c51df248f112e3780c66594110719c575 -test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 11e26017fa19a3ba6e5cd87be2810725764555736613306ce9fb044d6acfaef7 -test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: 28d3200bea541eda197a2310991c7481c198787e94c4796ddd0aca9a5b6a7d24 +test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 37c95a3f1ad5a4b606c0d902e58ac83b814d269a8d614fe0a2a986ec58838a88 +test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: ff43def36609f25318ca901ff1dd58e78a29b8ded8fbe4a6aa0679763a23e14d 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 @@ -83,9 +83,9 @@ test_root_simhits_writer[configPosConstructor]__meas.root: 26b51bbd97cd5f915628e test_root_simhits_writer[configKwConstructor]__meas.root: 26b51bbd97cd5f915628e150aa286a766a30cecdd511f8476e0c6b2ea62227fa test_root_simhits_writer[kwargsConstructor]__meas.root: 26b51bbd97cd5f915628e150aa286a766a30cecdd511f8476e0c6b2ea62227fa test_root_material_writer__material.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -test_root_clusters_writer[configPosConstructor]__clusters.root: 7a6a7927c51dbad1e860f9a6cad45bb66ecf07c63dbecf38196fc16c1d80ea21 -test_root_clusters_writer[configKwConstructor]__clusters.root: 7a6a7927c51dbad1e860f9a6cad45bb66ecf07c63dbecf38196fc16c1d80ea21 -test_root_clusters_writer[kwargsConstructor]__clusters.root: 7a6a7927c51dbad1e860f9a6cad45bb66ecf07c63dbecf38196fc16c1d80ea21 +test_root_clusters_writer[configPosConstructor]__clusters.root: 8938d494bc39245c3554836fb081cebbdd625ef4f1dd1109dda78b5ad251d81d +test_root_clusters_writer[configKwConstructor]__clusters.root: 8938d494bc39245c3554836fb081cebbdd625ef4f1dd1109dda78b5ad251d81d +test_root_clusters_writer[kwargsConstructor]__clusters.root: 8938d494bc39245c3554836fb081cebbdd625ef4f1dd1109dda78b5ad251d81d test_exatrkx[cpu-torch]__performance_track_finding.root: 26dc706b23b693eaf9986d0a6966b38da923cf57e2d99327060090537db27a7b test_exatrkx[gpu-onnx]__performance_track_finding.root: f2c4065008ed1ae5c022800bfdeed5fdfa5f8e23126f4ccb68846556f58ed407 test_exatrkx[gpu-torch]__performance_track_finding.root: 26dc706b23b693eaf9986d0a6966b38da923cf57e2d99327060090537db27a7b diff --git a/Examples/Python/tests/test_algorithms.py b/Examples/Python/tests/test_algorithms.py index 3db13b2c9ff..b6232035819 100644 --- a/Examples/Python/tests/test_algorithms.py +++ b/Examples/Python/tests/test_algorithms.py @@ -1,7 +1,5 @@ import pytest -import acts - from acts.examples import ( TutorialVertexFinderAlgorithm, AdaptiveMultiVertexFinderAlgorithm, diff --git a/Examples/Python/tests/test_detectors.py b/Examples/Python/tests/test_detectors.py index 1a01809e5dd..ee2053a4358 100644 --- a/Examples/Python/tests/test_detectors.py +++ b/Examples/Python/tests/test_detectors.py @@ -1,5 +1,3 @@ -from pathlib import Path - import pytest from helpers import dd4hepEnabled diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index 0f3bbb24c5c..26a526233fb 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -20,8 +20,6 @@ exatrkxEnabled, onnxEnabled, AssertCollectionExistsAlg, - isCI, - doHashChecks, failure_threshold, ) @@ -33,7 +31,6 @@ Sequencer, GenericDetector, AlignedDetector, - RootParticleWriter, ) from acts.examples.odd import getOpenDataDetector @@ -569,7 +566,6 @@ def test_truth_tracking_kalman( root_files = [ ("trackstates_fitter.root", "trackstates", 19), ("tracksummary_fitter.root", "tracksummary", 10), - ("performance_track_finder.root", "track_finder_tracks", 19), ("performance_track_fitter.root", None, -1), ] diff --git a/Examples/Python/tests/test_magnetic_field.py b/Examples/Python/tests/test_magnetic_field.py index 9f2979cccfb..3c1aae5c537 100644 --- a/Examples/Python/tests/test_magnetic_field.py +++ b/Examples/Python/tests/test_magnetic_field.py @@ -1,4 +1,3 @@ -from typing import Type import pytest import acts diff --git a/Examples/Python/tests/test_writer.py b/Examples/Python/tests/test_writer.py index e050195d34f..095b53638a7 100644 --- a/Examples/Python/tests/test_writer.py +++ b/Examples/Python/tests/test_writer.py @@ -1,4 +1,3 @@ -from typing import Type import os import inspect from pathlib import Path @@ -748,33 +747,10 @@ def test_edm4hep_tracks_writer(tmp_path): return from podio.root_io import Reader - from podio.frame import Frame import cppyy reader = Reader(str(out)) - expected = [ - (31.986961364746094, 30, 16), - (28.64777374267578, 30, 16), - (11.607606887817383, 22, 12), - (5.585886001586914, 22, 12), - (20.560943603515625, 20, 11), - (28.742727279663086, 28, 15), - (27.446802139282227, 22, 12), - (30.82959747314453, 24, 13), - (24.671127319335938, 26, 14), - (16.479907989501953, 20, 11), - (10.594233512878418, 22, 12), - (25.174715042114258, 28, 15), - (27.9674072265625, 26, 14), - (4.3012871742248535, 22, 12), - (20.492422103881836, 22, 12), - (27.92759132385254, 24, 13), - (14.514887809753418, 22, 12), - (12.876864433288574, 22, 12), - (12.951473236083984, 26, 14), - ] - actual = [] for frame in reader.get("events"): @@ -805,5 +781,5 @@ def test_edm4hep_tracks_writer(tmp_path): assert rp.x == 0.0 assert rp.y == 0.0 assert rp.z == 0.0 - assert abs(perigee.D0) < 1e-1 - assert abs(perigee.Z0) < 1 + assert abs(perigee.D0) < 1e0 + assert abs(perigee.Z0) < 1e1 diff --git a/Examples/Scripts/MaterialMapping/CMakeLists.txt b/Examples/Scripts/MaterialMapping/CMakeLists.txt index 96006deb72c..f921781cfca 100644 --- a/Examples/Scripts/MaterialMapping/CMakeLists.txt +++ b/Examples/Scripts/MaterialMapping/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(ActsAnalysisMaterialComposition MaterialComposition.cpp) -target_link_libraries(ActsAnalysisMaterialComposition ActsExamplesFramework ROOT::Core ROOT::Hist ROOT::Tree ROOT::TreePlayer Boost::program_options) +target_link_libraries(ActsAnalysisMaterialComposition ActsExamplesFramework ROOT::Core ROOT::Hist ROOT::Tree ROOT::TreePlayer Boost::program_options nlohmann_json::nlohmann_json) install( TARGETS diff --git a/Examples/Scripts/MaterialMapping/MaterialComposition.cpp b/Examples/Scripts/MaterialMapping/MaterialComposition.cpp index ff3f23e749f..b78509484e6 100644 --- a/Examples/Scripts/MaterialMapping/MaterialComposition.cpp +++ b/Examples/Scripts/MaterialMapping/MaterialComposition.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -18,6 +20,7 @@ #include #include +#include #define BOOST_AVAILABLE 1 #if ((BOOST_VERSION / 100) % 1000) <= 71 @@ -62,6 +65,7 @@ int main(int argc, char** argv) { ao("sub-rmax", value(), "Maximal radial restrictions."); ao("sub-zmin", value(), "Minimal z radial restrictions"); ao("sub-zmax", value(), "Maximal z radial restrictions."); + ao("config,c", value(), "Configuration file (json)."); // Set up the variables map variables_map vm; @@ -83,24 +87,57 @@ int main(int argc, char** argv) { // Subdetector configurations std::vector dRegion = {}; - auto snames = vm["sub-names"].as>(); - auto rmins = vm["sub-rmin"].as().values; - auto rmaxs = vm["sub-rmax"].as().values; - auto zmins = vm["sub-zmin"].as().values; - auto zmaxs = vm["sub-zmax"].as().values; - - size_t subs = snames.size(); - - if (subs != rmins.size() or subs != rmaxs.size() or subs != zmins.size() or - subs != zmaxs.size()) { - std::cerr << "Configuration problem." << std::endl; - return 1; - } - // Create the regions - for (unsigned int is = 0; is < subs; ++is) { - dRegion.push_back( - {snames[is], rmins[is], rmaxs[is], zmins[is], zmaxs[is]}); + if (vm.count("config") > 0) { + std::filesystem::path config = vm["config"].as(); + std::cout << "Reading region configuration from JSON: " << config + << std::endl; + + if (!std::filesystem::exists(config)) { + std::cerr << "Configuration file does not exist." << std::endl; + return 1; + } + + std::ifstream ifs(config.string().c_str()); + nlohmann::ordered_json j = nlohmann::ordered_json::parse(ifs); + + for (const auto& [key, regions] : j.items()) { + dRegion.push_back(Region{key, {}}); + auto& reg = dRegion.back(); + std::cout << "Region(" << key << ")" << std::endl; + for (const auto& region : regions) { + float rmin = region["rmin"].template get(); + float rmax = region["rmax"].template get(); + float zmin = region["zmin"].template get(); + float zmax = region["zmax"].template get(); + + reg.boxes.push_back({rmin, rmax, zmin, zmax}); + std::cout << "* " << key << " r/z: " << rmin << "/" << rmax << " " + << zmin << "/" << zmax << std::endl; + } + } + } else { + auto snames = vm["sub-names"].as>(); + auto rmins = vm["sub-rmin"].as().values; + auto rmaxs = vm["sub-rmax"].as().values; + auto zmins = vm["sub-zmin"].as().values; + auto zmaxs = vm["sub-zmax"].as().values; + + size_t subs = snames.size(); + + if (subs != rmins.size() or subs != rmaxs.size() or + subs != zmins.size() or subs != zmaxs.size()) { + std::cerr << "Configuration problem." << std::endl; + return 1; + } + + // Create the regions + for (unsigned int is = 0; is < subs; ++is) { + dRegion.push_back(Region{ + snames[is], + {{static_cast(rmins[is]), static_cast(rmaxs[is]), + static_cast(zmins[is]), static_cast(zmaxs[is])}}}); + } } TApplication* tApp = diff --git a/Examples/Scripts/MaterialMapping/materialComposition.C b/Examples/Scripts/MaterialMapping/materialComposition.C index 3d55040ab1f..0a2a580cc14 100644 --- a/Examples/Scripts/MaterialMapping/materialComposition.C +++ b/Examples/Scripts/MaterialMapping/materialComposition.C @@ -94,7 +94,19 @@ struct MaterialHistograms { } }; -using Region = std::tuple; +struct Region { + std::string name; + std::vector> boxes; + + bool inside(float r, float z) const { + for (const auto& [minR, maxR, minZ, maxZ] : boxes) { + if (minR <= r && r < maxR && minZ <= z && z < maxZ) { + return true; + } + } + return false; + } +}; /// Plot the material composition /// @@ -157,7 +169,7 @@ void materialComposition(const std::string& inFile, const std::string& treeName, // Loop of the regions for (auto& region : regions) { - const auto [rName, minR, maxR, minZ, maxZ] = region; + const auto rName = region.name; // The material histograms ordered by atomic mass std::map mCache; @@ -186,7 +198,7 @@ void materialComposition(const std::string& inFile, const std::string& treeName, float z = stepZ->at(is); float r = std::hypot(x, y); - if (minR > r or minZ > z or maxR < r or maxZ < z) { + if (!region.inside(r, z)) { continue; } @@ -203,7 +215,8 @@ void materialComposition(const std::string& inFile, const std::string& treeName, // The current one auto currentIt = mCache.find(sA); if (currentIt == mCache.end()) { - throw std::runtime_error{"Unknown atomic number " +std::to_string(sA)}; + throw std::runtime_error{"Unknown atomic number " + + std::to_string(sA)}; } auto& current = currentIt->second; current.s_x0 += step / X0; diff --git a/Examples/Scripts/Python/full_chain_itk_FTF.py b/Examples/Scripts/Python/full_chain_itk_FTF.py new file mode 100755 index 00000000000..83f98a666a6 --- /dev/null +++ b/Examples/Scripts/Python/full_chain_itk_FTF.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +import pathlib, acts, acts.examples, acts.examples.itk +from acts.examples.simulation import ( + addParticleGun, + MomentumConfig, + EtaConfig, + ParticleConfig, + addPythia8, + addFatras, + ParticleSelectorConfig, + addDigitization, +) +from acts.examples.reconstruction import ( + addSeeding, + SeedingAlgorithm, + TruthSeedRanges, + addCKFTracks, + TrackSelectorConfig, + addAmbiguityResolution, + AmbiguityResolutionConfig, + addVertexFitting, + VertexFinder, +) + +ttbar_pu200 = False +u = acts.UnitConstants +geo_dir = pathlib.Path("acts-itk") +outputDir = pathlib.Path.cwd() / "itk_output" +# acts.examples.dump_args_calls(locals()) # show acts.examples python binding calls + +detector, trackingGeometry, decorators = acts.examples.itk.buildITkGeometry(geo_dir) +field = acts.examples.MagneticFieldMapXyz(str(geo_dir / "bfield/ATLAS-BField-xyz.root")) +rnd = acts.examples.RandomNumbers(seed=42) + +s = acts.examples.Sequencer(events=100, numThreads=1, outputDir=str(outputDir)) + +if not ttbar_pu200: + addParticleGun( + s, + MomentumConfig(1.0 * u.GeV, 10.0 * u.GeV, transverse=True), + EtaConfig(-4.0, 4.0, uniform=True), + ParticleConfig(2, acts.PdgParticle.eMuon, randomizeCharge=True), + rnd=rnd, + ) +else: + addPythia8( + s, + hardProcess=["Top:qqbar2ttbar=on"], + npileup=200, + vtxGen=acts.examples.GaussianVertexGenerator( + stddev=acts.Vector4(0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns), + mean=acts.Vector4(0, 0, 0, 0), + ), + rnd=rnd, + outputDirRoot=outputDir, + ) + +addFatras( + s, + trackingGeometry, + field, + rnd=rnd, + preSelectParticles=ParticleSelectorConfig( + rho=(0.0 * u.mm, 28.0 * u.mm), + absZ=(0.0 * u.mm, 1.0 * u.m), + eta=(-4.0, 4.0), + pt=(150 * u.MeV, None), + removeNeutral=True, + ) + if ttbar_pu200 + else ParticleSelectorConfig(), + outputDirRoot=outputDir, +) + +addDigitization( + s, + trackingGeometry, + field, + digiConfigFile=geo_dir + / "itk-hgtd/itk-smearing-config.json", # change this file to make it do digitization + outputDirRoot=outputDir, + rnd=rnd, +) + +addSeeding( + 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.FTF, + *acts.examples.itk.itkSeedingAlgConfig( + acts.examples.itk.InputSpacePointsType.PixelSpacePoints + ), + geoSelectionConfigFile=geo_dir / "itk-hgtd/geoSelection-ITk.json", + layerMappingConfigFile=geo_dir / "itk-hgtd/ACTS_FTF_mapinput.csv", + fastrack_inputConfigFile=geo_dir / "itk-hgtd/binTables_ITK_RUN4.txt", + outputDirRoot=outputDir, +) + +s.run() diff --git a/Examples/Scripts/Python/truth_tracking_gsf.py b/Examples/Scripts/Python/truth_tracking_gsf.py index 01565bfdd3a..64328e61abf 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf.py +++ b/Examples/Scripts/Python/truth_tracking_gsf.py @@ -1,36 +1,36 @@ #!/usr/bin/env python3 from pathlib import Path -from typing import Optional, Union - -from acts.examples import Sequencer, GenericDetector, RootParticleReader +from typing import Optional import acts +import acts.examples u = acts.UnitConstants def runTruthTrackingGsf( - trackingGeometry, + trackingGeometry: acts.TrackingGeometry, + field: acts.MagneticFieldProvider, digiConfigFile: Path, - field, outputDir: Path, - outputCsv=True, inputParticlePath: Optional[Path] = None, decorators=[], - s=None, + s: acts.examples.Sequencer = None, ): from acts.examples.simulation import ( addParticleGun, + ParticleConfig, EtaConfig, PhiConfig, - ParticleConfig, + MomentumConfig, addFatras, addDigitization, ) from acts.examples.reconstruction import ( addSeeding, SeedingAlgorithm, + TruthSeedRanges, addTruthTrackingGsf, ) @@ -47,10 +47,15 @@ def runTruthTrackingGsf( if inputParticlePath is None: addParticleGun( s, - EtaConfig(-2.0, 2.0), - ParticleConfig(4, acts.PdgParticle.eElectron, True), + ParticleConfig(num=1, pdg=acts.PdgParticle.eElectron, randomizeCharge=True), + EtaConfig(-3.0, 3.0, uniform=True), + MomentumConfig(1.0 * u.GeV, 100.0 * u.GeV, transverse=True), PhiConfig(0.0, 360.0 * u.degree), - multiplicity=2, + vtxGen=acts.examples.GaussianVertexGenerator( + mean=acts.Vector4(0, 0, 0, 0), + stddev=acts.Vector4(0, 0, 0, 0), + ), + multiplicity=1, rnd=rnd, ) else: @@ -59,7 +64,7 @@ def runTruthTrackingGsf( ) assert inputParticlePath.exists() s.addReader( - RootParticleReader( + acts.examples.RootParticleReader( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), particleCollection="particles_input", @@ -87,22 +92,41 @@ def runTruthTrackingGsf( s, trackingGeometry, field, + rnd=rnd, + inputParticles="particles_input", seedingAlgorithm=SeedingAlgorithm.TruthSmeared, particleHypothesis=acts.ParticleHypothesis.electron, + truthSeedRanges=TruthSeedRanges( + pt=(1 * u.GeV, None), + nHits=(7, None), + ), ) - truthTrkFndAlg = acts.examples.TruthTrackFinder( - level=acts.logging.INFO, - inputParticles="truth_seeds_selected", - inputMeasurementParticlesMap="measurement_particles_map", - outputProtoTracks="prototracks", + addTruthTrackingGsf( + s, + trackingGeometry, + field, ) - s.addAlgorithm(truthTrkFndAlg) - - addTruthTrackingGsf(s, trackingGeometry, field) + s.addAlgorithm( + acts.examples.TrackSelectorAlgorithm( + level=acts.logging.INFO, + inputTracks="tracks", + outputTracks="selected-tracks", + selectorConfig=acts.TrackSelector.Config( + minMeasurements=7, + ), + ) + ) + s.addAlgorithm( + acts.examples.TracksToTrajectories( + level=acts.logging.INFO, + inputTracks="selected-tracks", + outputTrajectories="trajectories-from-tracks", + ) + ) + s.addWhiteboardAlias("trajectories", "trajectories-from-tracks") - # Output s.addWriter( acts.examples.RootTrajectoryStatesWriter( level=acts.logging.INFO, @@ -141,7 +165,7 @@ def runTruthTrackingGsf( if "__main__" == __name__: srcdir = Path(__file__).resolve().parent.parent.parent.parent - detector, trackingGeometry, decorators = GenericDetector.create() + detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) @@ -151,11 +175,9 @@ def runTruthTrackingGsf( runTruthTrackingGsf( trackingGeometry=trackingGeometry, - decorators=decorators, field=field, digiConfigFile=srcdir / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json", - outputCsv=True, inputParticlePath=inputParticlePath, outputDir=Path.cwd(), ).run() diff --git a/Examples/Scripts/Python/truth_tracking_gx2f.py b/Examples/Scripts/Python/truth_tracking_gx2f.py new file mode 100644 index 00000000000..910fc05718a --- /dev/null +++ b/Examples/Scripts/Python/truth_tracking_gx2f.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 + +from pathlib import Path +from typing import Optional + +import acts +import acts.examples + +u = acts.UnitConstants + + +def runTruthTrackingGx2f( + trackingGeometry: acts.TrackingGeometry, + field: acts.MagneticFieldProvider, + outputDir: Path, + digiConfigFile: Path, + s: acts.examples.Sequencer = None, + inputParticlePath: Optional[Path] = None, +): + from acts.examples.simulation import ( + addParticleGun, + MomentumConfig, + EtaConfig, + ParticleConfig, + addFatras, + addDigitization, + ) + from acts.examples.reconstruction import ( + addSeeding, + SeedingAlgorithm, + TruthSeedRanges, + addGx2fTracks, + ) + + s = s or acts.examples.Sequencer( + events=10000, numThreads=-1, logLevel=acts.logging.INFO + ) + + rnd = acts.examples.RandomNumbers() + outputDir = Path(outputDir) + + if inputParticlePath is None: + addParticleGun( + s, + MomentumConfig(100.0 * u.GeV, 100.0 * u.GeV, transverse=True), + EtaConfig(-2.0, 2.0), + ParticleConfig(2, acts.PdgParticle.eMuon, False), + multiplicity=1, + rnd=rnd, + outputDirRoot=outputDir, + ) + else: + acts.logging.getLogger("Truth tracking example").info( + "Reading particles from %s", inputParticlePath.resolve() + ) + assert inputParticlePath.exists() + s.addReader( + RootParticleReader( + level=acts.logging.INFO, + filePath=str(inputParticlePath.resolve()), + particleCollection="particles_input", + orderedEvents=False, + ) + ) + + addFatras( + s, + trackingGeometry, + field, + rnd=rnd, + enableInteractions=True, + ) + + addDigitization( + s, + trackingGeometry, + field, + digiConfigFile=digiConfigFile, + rnd=rnd, + ) + + addSeeding( + s, + trackingGeometry, + field, + seedingAlgorithm=SeedingAlgorithm.TruthSmeared, + rnd=rnd, + truthSeedRanges=TruthSeedRanges( + pt=(1 * u.GeV, None), + nHits=(9, None), + ), + ) + + addGx2fTracks( + s, + trackingGeometry, + field, + # directNavigation, + ) + + # Output + s.addWriter( + acts.examples.RootTrajectoryStatesWriter( + level=acts.logging.INFO, + inputTrajectories="trajectories", + inputParticles="truth_seeds_selected", + inputSimHits="simhits", + inputMeasurementParticlesMap="measurement_particles_map", + inputMeasurementSimHitsMap="measurement_simhits_map", + filePath=str(outputDir / "trackstates_fitter.root"), + ) + ) + + s.addWriter( + acts.examples.RootTrajectorySummaryWriter( + level=acts.logging.INFO, + inputTrajectories="trajectories", + inputParticles="truth_seeds_selected", + inputMeasurementParticlesMap="measurement_particles_map", + filePath=str(outputDir / "tracksummary_fitter.root"), + ) + ) + + # TODO: PerformanceWriters are not tested yet + # s.addWriter( + # acts.examples.TrackFinderPerformanceWriter( + # level=acts.logging.INFO, + # inputProtoTracks="truth_particle_tracks", + # inputParticles="truth_seeds_selected", + # inputMeasurementParticlesMap="measurement_particles_map", + # filePath=str(outputDir / "performance_track_finder.root"), + # ) + # ) + # + # s.addWriter( + # acts.examples.TrackFitterPerformanceWriter( + # level=acts.logging.INFO, + # inputTrajectories="trajectories", + # inputParticles="truth_seeds_selected", + # inputMeasurementParticlesMap="measurement_particles_map", + # filePath=str(outputDir / "performance_track_fitter.root"), + # ) + # ) + + return s + + +if "__main__" == __name__: + srcdir = Path(__file__).resolve().parent.parent.parent.parent + + # detector, trackingGeometry, _ = getOpenDataDetector() + detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + + field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) + + runTruthTrackingGx2f( + trackingGeometry=trackingGeometry, + # decorators=decorators, + field=field, + digiConfigFile=srcdir + / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json", + # "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json", + # outputCsv=True, + # inputParticlePath=inputParticlePath, + outputDir=Path.cwd(), + ).run() diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index 93ed96831e2..b06acfceb34 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -12,17 +12,20 @@ def runTruthTrackingKalman( trackingGeometry: acts.TrackingGeometry, field: acts.MagneticFieldProvider, - outputDir: Path, digiConfigFile: Path, + outputDir: Path, + inputParticlePath: Optional[Path] = None, + decorators=[], directNavigation=False, reverseFilteringMomThreshold=0 * u.GeV, s: acts.examples.Sequencer = None, - inputParticlePath: Optional[Path] = None, ): from acts.examples.simulation import ( addParticleGun, - EtaConfig, ParticleConfig, + EtaConfig, + PhiConfig, + MomentumConfig, addFatras, addDigitization, ) @@ -37,17 +40,25 @@ def runTruthTrackingKalman( events=100, numThreads=-1, logLevel=acts.logging.INFO ) - rnd = acts.examples.RandomNumbers() + for d in decorators: + s.addContextDecorator(d) + + rnd = acts.examples.RandomNumbers(seed=42) outputDir = Path(outputDir) if inputParticlePath is None: addParticleGun( s, - EtaConfig(-2.0, 2.0), - ParticleConfig(2, acts.PdgParticle.eMuon, False), + ParticleConfig(num=1, pdg=acts.PdgParticle.eMuon, randomizeCharge=True), + EtaConfig(-3.0, 3.0, uniform=True), + MomentumConfig(1.0 * u.GeV, 100.0 * u.GeV, transverse=True), + PhiConfig(0.0, 360.0 * u.degree), + vtxGen=acts.examples.GaussianVertexGenerator( + mean=acts.Vector4(0, 0, 0, 0), + stddev=acts.Vector4(0, 0, 0, 0), + ), multiplicity=1, rnd=rnd, - outputDirRoot=outputDir, ) else: acts.logging.getLogger("Truth tracking example").info( @@ -55,7 +66,7 @@ def runTruthTrackingKalman( ) assert inputParticlePath.exists() s.addReader( - RootParticleReader( + acts.examples.RootParticleReader( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), particleCollection="particles_input", @@ -83,11 +94,13 @@ def runTruthTrackingKalman( s, trackingGeometry, field, - seedingAlgorithm=SeedingAlgorithm.TruthSmeared, rnd=rnd, + inputParticles="particles_input", + seedingAlgorithm=SeedingAlgorithm.TruthSmeared, + particleHypothesis=acts.ParticleHypothesis.muon, truthSeedRanges=TruthSeedRanges( - pt=(500 * u.MeV, None), - nHits=(9, None), + pt=(1 * u.GeV, None), + nHits=(7, None), ), ) @@ -99,7 +112,25 @@ def runTruthTrackingKalman( reverseFilteringMomThreshold, ) - # Output + s.addAlgorithm( + acts.examples.TrackSelectorAlgorithm( + level=acts.logging.INFO, + inputTracks="tracks", + outputTracks="selected-tracks", + selectorConfig=acts.TrackSelector.Config( + minMeasurements=7, + ), + ) + ) + s.addAlgorithm( + acts.examples.TracksToTrajectories( + level=acts.logging.INFO, + inputTracks="selected-tracks", + outputTrajectories="trajectories-from-tracks", + ) + ) + s.addWhiteboardAlias("trajectories", "trajectories-from-tracks") + s.addWriter( acts.examples.RootTrajectoryStatesWriter( level=acts.logging.INFO, @@ -122,18 +153,6 @@ def runTruthTrackingKalman( ) ) - s.addWriter( - acts.examples.TrackFinderPerformanceWriter( - level=acts.logging.INFO, - inputProtoTracks="sorted_truth_particle_tracks" - if directNavigation - else "truth_particle_tracks", - inputParticles="truth_seeds_selected", - inputMeasurementParticlesMap="measurement_particles_map", - filePath=str(outputDir / "performance_track_finder.root"), - ) - ) - s.addWriter( acts.examples.TrackFitterPerformanceWriter( level=acts.logging.INFO, @@ -148,7 +167,6 @@ def runTruthTrackingKalman( if "__main__" == __name__: - srcdir = Path(__file__).resolve().parent.parent.parent.parent # detector, trackingGeometry, _ = getOpenDataDetector() diff --git a/Examples/Scripts/TrackingPerformance/TreeReader.h b/Examples/Scripts/TrackingPerformance/TreeReader.h index 3018076e608..fa5a2ce1622 100644 --- a/Examples/Scripts/TrackingPerformance/TreeReader.h +++ b/Examples/Scripts/TrackingPerformance/TreeReader.h @@ -36,7 +36,7 @@ struct ParticleInfo { /// struct TreeReader { // The constructor - TreeReader(TTree* tree_) : tree(tree_){}; + TreeReader(TTree* tree_) : tree(tree_) {} // Get entry void getEntry(unsigned int i) const { diff --git a/Plugins/CMakeLists.txt b/Plugins/CMakeLists.txt index 026df4c7a50..db2151753ec 100644 --- a/Plugins/CMakeLists.txt +++ b/Plugins/CMakeLists.txt @@ -18,5 +18,6 @@ add_component_if(ExaTrkX PluginExaTrkX ACTS_BUILD_PLUGIN_EXATRKX) add_component_if(TGeo PluginTGeo ACTS_BUILD_PLUGIN_TGEO) add_component_if(DD4hep PluginDD4hep ACTS_BUILD_PLUGIN_DD4HEP) add_component_if(EDM4hep PluginEDM4hep ACTS_BUILD_PLUGIN_EDM4HEP) +add_component_if(Podio PluginPodio ACTS_BUILD_PLUGIN_PODIO) propagate_components_to_parent() diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/BoostTrackBuilding.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/BoostTrackBuilding.hpp index f9028492742..6311f4e91cc 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/BoostTrackBuilding.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/BoostTrackBuilding.hpp @@ -18,11 +18,12 @@ namespace Acts { class BoostTrackBuilding final : public Acts::TrackBuildingBase { public: BoostTrackBuilding(std::unique_ptr logger) - : m_logger(std::move(logger)){}; + : m_logger(std::move(logger)) {} - std::vector> operator()( - std::any nodes, std::any edges, std::any edge_weights, - std::vector &spacepointIDs) override; + std::vector> operator()(std::any nodes, std::any edges, + std::any edge_weights, + std::vector &spacepointIDs, + int deviceHint = -1) override; private: std::unique_ptr m_logger; diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/CugraphTrackBuilding.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/CugraphTrackBuilding.hpp index 52a0e588aee..76779016549 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/CugraphTrackBuilding.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/CugraphTrackBuilding.hpp @@ -18,11 +18,12 @@ namespace Acts { class CugraphTrackBuilding final : public Acts::TrackBuildingBase { public: CugraphTrackBuilding(std::unique_ptr logger) - : m_logger(std::move(logger)){}; + : m_logger(std::move(logger)) {} - std::vector> operator()( - std::any nodes, std::any edges, std::any edge_weights, - std::vector &spacepointIDs) override; + std::vector> operator()(std::any nodes, std::any edges, + std::any edge_weights, + std::vector &spacepointIDs, + int deviceHint = -1) override; private: std::unique_ptr m_logger; diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/ExaTrkXPipeline.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/ExaTrkXPipeline.hpp index df706af6b23..e6810eb22ef 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/ExaTrkXPipeline.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/ExaTrkXPipeline.hpp @@ -12,17 +12,26 @@ #include "Acts/Utilities/Logger.hpp" #include +#include #include #include #include -#include +#include namespace Acts { +struct ExaTrkXTiming { + using Duration = std::chrono::duration; + + Duration graphBuildingTime = Duration{0.f}; + boost::container::small_vector classifierTimes; + Duration trackBuildingTime = Duration{0.f}; +}; + class ExaTrkXHook { public: - virtual ~ExaTrkXHook(){}; + virtual ~ExaTrkXHook() {} virtual void operator()(const std::any &, const std::any &) const {}; }; @@ -36,7 +45,9 @@ class ExaTrkXPipeline { std::vector> run(std::vector &features, std::vector &spacepointIDs, - const ExaTrkXHook &hook = {}) const; + int deviceHint = -1, + const ExaTrkXHook &hook = {}, + ExaTrkXTiming *timing = nullptr) const; private: std::unique_ptr m_logger; diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxEdgeClassifier.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxEdgeClassifier.hpp index a7b9de364d0..cb1d84f2713 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxEdgeClassifier.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxEdgeClassifier.hpp @@ -31,8 +31,8 @@ class OnnxEdgeClassifier final : public Acts::EdgeClassificationBase { OnnxEdgeClassifier(const Config &cfg, std::unique_ptr logger); ~OnnxEdgeClassifier(); - std::tuple operator()(std::any nodes, - std::any edges) override; + std::tuple operator()( + std::any nodes, std::any edges, int deviceHint = -1) override; Config config() const { return m_cfg; } diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxMetricLearning.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxMetricLearning.hpp index 644581adfec..26af6fb3619 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxMetricLearning.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/OnnxMetricLearning.hpp @@ -35,7 +35,8 @@ class OnnxMetricLearning final : public Acts::GraphConstructionBase { ~OnnxMetricLearning(); std::tuple operator()(std::vector& inputValues, - std::size_t numNodes) override; + std::size_t numNodes, + int deviceHint = -1) override; Config config() const { return m_cfg; } diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/Stages.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/Stages.hpp index 82847d06860..048f56bfd3c 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/Stages.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/Stages.hpp @@ -23,12 +23,14 @@ class GraphConstructionBase { /// Perform the graph construction /// /// @param inputValues Flattened input data - /// @param numNodes number of nodes. inputValues.size() / numNodes + /// @param numNodes Number of nodes. inputValues.size() / numNodes /// then gives the number of features + /// @param deviceHint Which GPU to pick. Not relevant for CPU-only builds /// /// @return (node_tensor, edge_tensore) virtual std::tuple operator()( - std::vector &inputValues, std::size_t numNodes) = 0; + std::vector &inputValues, std::size_t numNodes, + int deviceHint = -1) = 0; virtual ~GraphConstructionBase() = default; }; @@ -39,10 +41,11 @@ class EdgeClassificationBase { /// /// @param nodes Node tensor with shape (n_nodes, n_node_features) /// @param edges Edge-index tensor with shape (2, n_edges) + /// @param deviceHint Which GPU to pick. Not relevant for CPU-only builds /// /// @return (node_tensor, edge_tensor, score_tensor) virtual std::tuple operator()( - std::any nodes, std::any edges) = 0; + std::any nodes, std::any edges, int deviceHint = -1) = 0; virtual ~EdgeClassificationBase() = default; }; @@ -55,11 +58,12 @@ class TrackBuildingBase { /// @param edges Edge-index tensor with shape (2, n_edges) /// @param edgeWeights Edge-weights of the previous edge classification phase /// @param spacepointIDs IDs of the nodes (must have size=n_nodes) + /// @param deviceHint Which GPU to pick. Not relevant for CPU-only builds /// /// @return tracks (as vectors of node-IDs) virtual std::vector> operator()( std::any nodes, std::any edges, std::any edgeWeights, - std::vector &spacepointIDs) = 0; + std::vector &spacepointIDs, int deviceHint = -1) = 0; virtual ~TrackBuildingBase() = default; }; diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchEdgeClassifier.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchEdgeClassifier.hpp index 73bf7d80918..bfa7c9054f4 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchEdgeClassifier.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchEdgeClassifier.hpp @@ -36,8 +36,8 @@ class TorchEdgeClassifier final : public Acts::EdgeClassificationBase { TorchEdgeClassifier(const Config &cfg, std::unique_ptr logger); ~TorchEdgeClassifier(); - std::tuple operator()(std::any nodes, - std::any edges) override; + std::tuple operator()( + std::any nodes, std::any edges, int deviceHint = -1) override; Config config() const { return m_cfg; } diff --git a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchMetricLearning.hpp b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchMetricLearning.hpp index b8a45a79188..886687787cf 100644 --- a/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchMetricLearning.hpp +++ b/Plugins/ExaTrkX/include/Acts/Plugins/ExaTrkX/TorchMetricLearning.hpp @@ -38,7 +38,8 @@ class TorchMetricLearning final : public Acts::GraphConstructionBase { ~TorchMetricLearning(); std::tuple operator()(std::vector &inputValues, - std::size_t numNodes) override; + std::size_t numNodes, + int deviceHint = -1) override; Config config() const { return m_cfg; } diff --git a/Plugins/ExaTrkX/src/BoostTrackBuilding.cpp b/Plugins/ExaTrkX/src/BoostTrackBuilding.cpp index 421d85d5560..01e80930960 100644 --- a/Plugins/ExaTrkX/src/BoostTrackBuilding.cpp +++ b/Plugins/ExaTrkX/src/BoostTrackBuilding.cpp @@ -46,8 +46,8 @@ auto weaklyConnectedComponents(vertex_t numNodes, namespace Acts { std::vector> BoostTrackBuilding::operator()( - std::any, std::any edges, std::any weights, - std::vector& spacepointIDs) { + std::any nodes, std::any edges, std::any weights, + std::vector& spacepointIDs, int) { ACTS_DEBUG("Start track building"); const auto edgeTensor = std::any_cast(edges).to(torch::kCPU); const auto edgeWeightTensor = diff --git a/Plugins/ExaTrkX/src/CugraphTrackBuilding.cpp b/Plugins/ExaTrkX/src/CugraphTrackBuilding.cpp index 17f3c395f98..062701341ba 100644 --- a/Plugins/ExaTrkX/src/CugraphTrackBuilding.cpp +++ b/Plugins/ExaTrkX/src/CugraphTrackBuilding.cpp @@ -18,7 +18,7 @@ namespace Acts { std::vector> CugraphTrackBuilding::operator()( std::any, std::any edges, std::any edge_weights, - std::vector &spacepointIDs) { + std::vector &spacepointIDs, int) { auto numSpacepoints = spacepointIDs.size(); auto edgesAfterFiltering = std::any_cast>(edges); auto numEdgesAfterF = edgesAfterFiltering.size() / 2; diff --git a/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp b/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp index 15d6e0080cc..8c408413c16 100644 --- a/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp +++ b/Plugins/ExaTrkX/src/ExaTrkXPipeline.cpp @@ -34,16 +34,31 @@ ExaTrkXPipeline::ExaTrkXPipeline( std::vector> ExaTrkXPipeline::run( std::vector &features, std::vector &spacepointIDs, - const ExaTrkXHook &hook) const { - auto [nodes, edges] = (*m_graphConstructor)(features, spacepointIDs.size()); + int deviceHint, const ExaTrkXHook &hook, ExaTrkXTiming *timing) const { + auto t0 = std::chrono::high_resolution_clock::now(); + auto [nodes, edges] = + (*m_graphConstructor)(features, spacepointIDs.size(), deviceHint); + auto t1 = std::chrono::high_resolution_clock::now(); + + if (timing != nullptr) { + timing->graphBuildingTime = t1 - t0; + } hook(nodes, edges); std::any edge_weights; + timing->classifierTimes.clear(); for (auto edgeClassifier : m_edgeClassifiers) { + t0 = std::chrono::high_resolution_clock::now(); auto [newNodes, newEdges, newWeights] = - (*edgeClassifier)(std::move(nodes), std::move(edges)); + (*edgeClassifier)(std::move(nodes), std::move(edges), deviceHint); + t1 = std::chrono::high_resolution_clock::now(); + + if (timing != nullptr) { + timing->classifierTimes.push_back(t1 - t0); + } + nodes = std::move(newNodes); edges = std::move(newEdges); edge_weights = std::move(newWeights); @@ -51,8 +66,17 @@ std::vector> ExaTrkXPipeline::run( hook(nodes, edges); } - return (*m_trackBuilder)(std::move(nodes), std::move(edges), - std::move(edge_weights), spacepointIDs); + t0 = std::chrono::high_resolution_clock::now(); + auto res = + (*m_trackBuilder)(std::move(nodes), std::move(edges), + std::move(edge_weights), spacepointIDs, deviceHint); + t1 = std::chrono::high_resolution_clock::now(); + + if (timing != nullptr) { + timing->trackBuildingTime = t1 - t0; + } + + return res; } } // namespace Acts diff --git a/Plugins/ExaTrkX/src/OnnxEdgeClassifier.cpp b/Plugins/ExaTrkX/src/OnnxEdgeClassifier.cpp index 2a629f370cd..ccdaff82fd9 100644 --- a/Plugins/ExaTrkX/src/OnnxEdgeClassifier.cpp +++ b/Plugins/ExaTrkX/src/OnnxEdgeClassifier.cpp @@ -44,7 +44,7 @@ OnnxEdgeClassifier::OnnxEdgeClassifier(const Config &cfg, OnnxEdgeClassifier::~OnnxEdgeClassifier() {} std::tuple OnnxEdgeClassifier::operator()( - std::any inputNodes, std::any inputEdges) { + std::any inputNodes, std::any inputEdges, int) { Ort::AllocatorWithDefaultOptions allocator; auto memoryInfo = Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); diff --git a/Plugins/ExaTrkX/src/OnnxMetricLearning.cpp b/Plugins/ExaTrkX/src/OnnxMetricLearning.cpp index 680f36b14fa..9e59d5639ab 100644 --- a/Plugins/ExaTrkX/src/OnnxMetricLearning.cpp +++ b/Plugins/ExaTrkX/src/OnnxMetricLearning.cpp @@ -57,7 +57,7 @@ void OnnxMetricLearning::buildEdgesWrapper(std::vector& embedFeatures, } std::tuple OnnxMetricLearning::operator()( - std::vector& inputValues, std::size_t) { + std::vector& inputValues, std::size_t, int) { Ort::AllocatorWithDefaultOptions allocator; auto memoryInfo = Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); diff --git a/Plugins/ExaTrkX/src/TorchEdgeClassifier.cpp b/Plugins/ExaTrkX/src/TorchEdgeClassifier.cpp index 3acc354a221..8e9a1c46d7b 100644 --- a/Plugins/ExaTrkX/src/TorchEdgeClassifier.cpp +++ b/Plugins/ExaTrkX/src/TorchEdgeClassifier.cpp @@ -43,10 +43,10 @@ TorchEdgeClassifier::TorchEdgeClassifier(const Config& cfg, TorchEdgeClassifier::~TorchEdgeClassifier() {} std::tuple TorchEdgeClassifier::operator()( - std::any inputNodes, std::any inputEdges) { + std::any inputNodes, std::any inputEdges, int deviceHint) { ACTS_DEBUG("Start edge classification"); c10::InferenceMode guard(true); - const torch::Device device(m_deviceType); + const torch::Device device(m_deviceType, deviceHint); auto nodes = std::any_cast(inputNodes).to(device); auto edgeList = std::any_cast(inputEdges).to(device); diff --git a/Plugins/ExaTrkX/src/TorchMetricLearning.cpp b/Plugins/ExaTrkX/src/TorchMetricLearning.cpp index 786e6d8268f..6ffda448587 100644 --- a/Plugins/ExaTrkX/src/TorchMetricLearning.cpp +++ b/Plugins/ExaTrkX/src/TorchMetricLearning.cpp @@ -46,10 +46,10 @@ TorchMetricLearning::TorchMetricLearning(const Config &cfg, TorchMetricLearning::~TorchMetricLearning() {} std::tuple TorchMetricLearning::operator()( - std::vector &inputValues, std::size_t numNodes) { + std::vector &inputValues, std::size_t numNodes, int deviceHint) { ACTS_DEBUG("Start graph construction"); c10::InferenceMode guard(true); - const torch::Device device(m_deviceType); + const torch::Device device(m_deviceType, deviceHint); const int64_t numAllFeatures = inputValues.size() / numNodes; diff --git a/Plugins/ExaTrkX/src/buildEdges.cpp b/Plugins/ExaTrkX/src/buildEdges.cpp index f45cb548ef4..1b133291d7f 100644 --- a/Plugins/ExaTrkX/src/buildEdges.cpp +++ b/Plugins/ExaTrkX/src/buildEdges.cpp @@ -66,7 +66,7 @@ torch::Tensor Acts::detail::buildEdgesFRNN(torch::Tensor &embedFeatures, float rVal, int kVal, bool flipDirections) { #ifndef ACTS_EXATRKX_CPUONLY - torch::Device device(torch::kCUDA); + const auto device = embedFeatures.device(); const int64_t numSpacepoints = embedFeatures.size(0); const int dim = embedFeatures.size(1); diff --git a/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp b/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp index cfeff029fa0..24cf17db84d 100644 --- a/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp +++ b/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp @@ -29,7 +29,7 @@ class AmbiguityTrackClassifier { /// @param modelPath path to the model file AmbiguityTrackClassifier(const char* modelPath) : m_env(ORT_LOGGING_LEVEL_WARNING, "MLClassifier"), - m_duplicateClassifier(m_env, modelPath){}; + m_duplicateClassifier(m_env, modelPath) {} /// Compute a score for each track to be used in the track selection /// diff --git a/Plugins/Podio/.gitignore b/Plugins/Podio/.gitignore new file mode 100644 index 00000000000..2012a8af579 --- /dev/null +++ b/Plugins/Podio/.gitignore @@ -0,0 +1,4 @@ +podio_generated_files.cmake +src/*.cc +src/selection.xml +ActsPodioEdm/* diff --git a/Plugins/Podio/CMakeLists.txt b/Plugins/Podio/CMakeLists.txt new file mode 100644 index 00000000000..4ca3e49544f --- /dev/null +++ b/Plugins/Podio/CMakeLists.txt @@ -0,0 +1,79 @@ +add_library( + ActsPluginPodio SHARED + src/PodioUtil.cpp +) + +target_include_directories( + ActsPluginPodio + PUBLIC + $ + $) +target_link_libraries( + ActsPluginPodio + PUBLIC ActsCore ActsPluginIdentification) + +# target_link_libraries(ActsPluginEDM4hep PUBLIC EDM4HEP::edm4hep) + + +# message(STATUS "IO HANDLERS: ${PODIO_IO_HANDLERS}") + +PODIO_GENERATE_DATAMODEL( + ActsPodioEdm + ${CMAKE_CURRENT_LIST_DIR}/edm.yml + headers + sources + IO_BACKEND_HANDLERS ${PODIO_IO_HANDLERS} +) + +PODIO_ADD_DATAMODEL_CORE_LIB(ActsPodioEdm "${headers}" "${sources}") + +target_link_libraries(ActsPluginPodio PUBLIC + ActsPodioEdm + ROOT::Core + podio::podio + podio::podioRootIO +) + +PODIO_ADD_ROOT_IO_DICT(ActsPodioEdmDict ActsPodioEdm "${headers}" src/selection.xml) +add_library(Acts::ActsPodioEdmDict ALIAS ActsPodioEdmDict) + +install( + TARGETS ActsPodioEdm + EXPORT ActsPodioEdmTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ActsPodioEdm) + +install( + DIRECTORY ActsPodioEdm + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ActsPodioEdm) + +set(install_package_config_dir "${CMAKE_INSTALL_LIBDIR}/cmake/Acts") +install( + EXPORT ActsPodioEdmTargets + DESTINATION ${install_package_config_dir}) + +install( + TARGETS ActsPluginPodio + EXPORT ActsPluginPodioTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install( + DIRECTORY include/Acts + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(TARGETS ActsPodioEdmDict + EXPORT ActsPodioEdmTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ActsPodioEdm" + COMPONENT dev) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/ActsPodioEdmDictDict.rootmap" + DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT dev) + +if (${ROOT_VERSION} GREATER 6) + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/libActsPodioEdmDict_rdict.pcm" + DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT dev) +endif() + diff --git a/Plugins/Podio/edm.yml b/Plugins/Podio/edm.yml new file mode 100644 index 00000000000..18b6f83d6dc --- /dev/null +++ b/Plugins/Podio/edm.yml @@ -0,0 +1,120 @@ +--- +options: + getSyntax: True + exposePODMembers: False + includeSubfolder: True + +components: + + ActsPodioEdm::Vector3f : + Members: + - float x + - float y + - float z + ExtraCode: + declaration: " + constexpr Vector3f() : x(0),y(0),z(0) {}\n + constexpr Vector3f(float xx, float yy, float zz) : x(xx),y(yy),z(zz) {}\n + constexpr Vector3f(const float* v) : x(v[0]),y(v[1]),z(v[2]) {}\n + constexpr bool operator==(const Vector3f& v) const { return (x==v.x&&y==v.y&&z==v.z) ; }\n + constexpr float operator[](unsigned i) const { return *( &x + i ) ; }\n + " + + ActsPodioEdm::Surface: + Members: + - int surfaceType + - int boundsType + - uint64_t geometryId + - uint64_t identifier + - std::array boundValues + - uint32_t boundValuesSize + - std::array transform + + ActsPodioEdm::TrackInfo: + Members: + - std::array parameters + - std::array covariance // local covariance + - uint32_t tipIndex // index of the outermost track state + - unsigned int nMeasurements // number of measurements + - unsigned int nHoles // number of holes + - float chi2 // chi squared of the track + - unsigned int ndf // number of degrees of freedom + - unsigned int nOutliers // number of outliers + - unsigned int nSharedHits // number of shared hits + + ActsPodioEdm::TrackStateInfo: + Members: + - uint32_t previous + + - uint32_t ipredicted; + - uint32_t ifiltered; + - uint32_t ismoothed; + + - std::array measurement + - std::array measurementCovariance + - uint32_t measdim; + + - uint32_t ijacobian + - uint64_t projector + - bool hasProjector + + - double chi2 + - double pathLength + - uint64_t typeFlags + + - uint64_t uncalibratedIdentifier + + ActsPodioEdm::BoundParametersInfo: + Members: + - std::array values + - std::array covariance + + ActsPodioEdm::JacobianInfo: + Members: + - std::array values + +datatypes: + ActsPodioEdm::Track: + Description: "Reconstructed track" + Author : "Paul Gessinger, CERN" + Members: + - ActsPodioEdm::TrackInfo data // local parameters and covariance + + - ActsPodioEdm::Surface referenceSurface // reference surface + + ActsPodioEdm::TrackState: + Description: "Local state on a track" + Author : "Paul Gessinger, CERN" + Members: + - ActsPodioEdm::TrackStateInfo data // local information + - ActsPodioEdm::Surface referenceSurface // reference surface + + ActsPodioEdm::BoundParameters: + Description: "Local track parameters" + Author : "Paul Gessinger, CERN" + Members: + - ActsPodioEdm::BoundParametersInfo data // parameter information + + ActsPodioEdm::Jacobian: + Description: "Jacobian matrix from the previous track state to the current one" + Author : "Paul Gessinger, CERN" + Members: + - ActsPodioEdm::JacobianInfo data // the jacobian data + + # - int32_t type //flagword that defines the type of track.Bits 16-31 are used internally + # - float chi2 //Chi^2 of the track fit + # - int32_t ndf //number of degrees of freedom of the track fit + # - float dEdx //dEdx of the track. + # - float dEdxError //error of dEdx. + # - float radiusOfInnermostHit //radius of the innermost hit that has been used in the track fit + # VectorMembers: + # - int32_t subDetectorHitNumbers //number of hits in particular subdetectors.Check/set collection variable TrackSubdetectorNames for decoding the indices + # - edm4hep::TrackState trackStates //track states + # - edm4hep::Quantity dxQuantities // different measurements of dx quantities + # OneToManyRelations: + # - edm4hep::TrackerHit trackerHits //hits that have been used to create this track + # - edm4hep::Track tracks //tracks (segments) that have been combined to create this track + # ExtraCode: + # declaration: > + # auto data() { return &m_obj->data; } + diff --git a/Plugins/Podio/include/Acts/Plugins/Podio/PodioDynamicColumns.hpp b/Plugins/Podio/include/Acts/Plugins/Podio/PodioDynamicColumns.hpp new file mode 100644 index 00000000000..3e2b2086711 --- /dev/null +++ b/Plugins/Podio/include/Acts/Plugins/Podio/PodioDynamicColumns.hpp @@ -0,0 +1,105 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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 +#include + +#include +#include + +namespace Acts::podio_detail { + +struct ConstDynamicColumnBase { + ConstDynamicColumnBase(const std::string& name) : m_name{name} {} + + virtual ~ConstDynamicColumnBase() = default; + + virtual std::any get(size_t i) const = 0; + + virtual size_t size() const = 0; + + protected: + std::string m_name; +}; + +struct DynamicColumnBase : public ConstDynamicColumnBase { + DynamicColumnBase(const std::string& name) : ConstDynamicColumnBase{name} {} + + virtual std::any get(size_t i) = 0; + std::any get(size_t i) const override = 0; + + virtual void add() = 0; + virtual void clear() = 0; + virtual void erase(size_t i) = 0; + virtual void copyFrom(size_t dstIdx, const DynamicColumnBase& src, + size_t srcIdx) = 0; + + virtual std::unique_ptr clone( + bool empty = false) const = 0; + + virtual void releaseInto(podio::Frame& frame, const std::string& prefix) = 0; +}; + +template +struct DynamicColumn : public DynamicColumnBase { + DynamicColumn(const std::string& name, + podio::UserDataCollection collection = {}) + : DynamicColumnBase(name), m_collection{std::move(collection)} {} + + std::any get(size_t i) override { return &m_collection.vec().at(i); } + + std::any get(size_t i) const override { return &m_collection.vec().at(i); } + + void add() override { m_collection.vec().emplace_back(); } + void clear() override { m_collection.clear(); } + void erase(size_t i) override { + m_collection.vec().erase(m_collection.vec().begin() + i); + } + size_t size() const override { return m_collection.size(); } + + std::unique_ptr clone(bool empty) const override { + if (empty) { + return std::make_unique>(m_name); + } + podio::UserDataCollection copy; + copy.vec().reserve(m_collection.size()); + for (const T& v : m_collection) { + copy.push_back(v); + } + return std::make_unique>(m_name, std::move(copy)); + } + + void copyFrom(size_t dstIdx, const DynamicColumnBase& src, + size_t srcIdx) override { + const auto* other = dynamic_cast*>(&src); + assert(other != nullptr && + "Source column is not of same type as destination"); + m_collection.vec().at(dstIdx) = other->m_collection.vec().at(srcIdx); + } + + void releaseInto(podio::Frame& frame, const std::string& prefix) override { + frame.put(std::move(m_collection), prefix + m_name); + } + + podio::UserDataCollection m_collection; +}; + +template +struct ConstDynamicColumn : public ConstDynamicColumnBase { + ConstDynamicColumn(const std::string& name, + const podio::UserDataCollection& collection) + : ConstDynamicColumnBase(name), m_collection{collection} {} + + std::any get(size_t i) const override { return &m_collection.vec().at(i); } + size_t size() const override { return m_collection.size(); } + + const podio::UserDataCollection& m_collection; +}; +} // namespace Acts::podio_detail diff --git a/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackContainer.hpp b/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackContainer.hpp new file mode 100644 index 00000000000..75ffacd313c --- /dev/null +++ b/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackContainer.hpp @@ -0,0 +1,375 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/detail/DynamicColumn.hpp" +#include "Acts/Plugins/Podio/PodioDynamicColumns.hpp" +#include "Acts/Plugins/Podio/PodioUtil.hpp" +#include "ActsPodioEdm/Track.h" +#include "ActsPodioEdm/TrackCollection.h" +#include "ActsPodioEdm/TrackInfo.h" + +#include +#include +#include + +#include + +namespace Acts { + +class MutablePodioTrackContainer; +class ConstPodioTrackContainer; + +template <> +struct IsReadOnlyTrackContainer : std::false_type { +}; + +template <> +struct IsReadOnlyTrackContainer : std::true_type {}; + +class PodioTrackContainerBase { + public: + using IndexType = MultiTrajectoryTraits::IndexType; + static constexpr auto kInvalid = MultiTrajectoryTraits::kInvalid; + static constexpr auto MeasurementSizeMax = + MultiTrajectoryTraits::MeasurementSizeMax; + + using Parameters = + typename detail_lt::Types::CoefficientsMap; + using Covariance = + typename detail_lt::Types::CovarianceMap; + + using ConstParameters = + typename detail_lt::Types::CoefficientsMap; + using ConstCovariance = + typename detail_lt::Types::CovarianceMap; + + protected: + PodioTrackContainerBase(const PodioUtil::ConversionHelper& helper) + : m_helper{helper} {} + + template + static std::any component_impl(T& instance, HashedString key, + IndexType itrack) { + using namespace Acts::HashedStringLiteral; + if constexpr (EnsureConst) { + static_assert(std::is_const_v>, + "Is not const"); + } + + using namespace Acts::HashedStringLiteral; + auto track = instance.m_collection->at(itrack); + std::conditional_t + dataPtr; + if constexpr (EnsureConst) { + dataPtr = &track.getData(); + } else { + dataPtr = &track.data(); + } + auto& data = *dataPtr; + switch (key) { + case "tipIndex"_hash: + return &data.tipIndex; + case "params"_hash: + return data.parameters.data(); + case "cov"_hash: + return data.covariance.data(); + case "nMeasurements"_hash: + return &data.nMeasurements; + case "nHoles"_hash: + return &data.nHoles; + case "chi2"_hash: + return &data.chi2; + case "ndf"_hash: + return &data.ndf; + case "nOutliers"_hash: + return &data.nOutliers; + case "nSharedHits"_hash: + return &data.nSharedHits; + default: + auto it = instance.m_dynamic.find(key); + if (it == instance.m_dynamic.end()) { + throw std::runtime_error("Unable to handle this component"); + } + + std::conditional_t + col = it->second.get(); + assert(col && "Dynamic column is null"); + return col->get(itrack); + } + } + + static void populateSurfaceBuffer( + const PodioUtil::ConversionHelper& helper, + const ActsPodioEdm::TrackCollection& collection, + std::vector>& surfaces) noexcept { + surfaces.reserve(collection.size()); + for (ActsPodioEdm::Track track : collection) { + surfaces.push_back(PodioUtil::convertSurfaceFromPodio( + helper, track.getReferenceSurface())); + } + } + + std::reference_wrapper m_helper; + std::vector> m_surfaces; +}; + +class MutablePodioTrackContainer : public PodioTrackContainerBase { + public: + MutablePodioTrackContainer(const PodioUtil::ConversionHelper& helper) + : PodioTrackContainerBase{helper}, + m_collection{std::make_unique()} { + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + } + + MutablePodioTrackContainer(const MutablePodioTrackContainer& other); + MutablePodioTrackContainer(MutablePodioTrackContainer&& other) = default; + + MutablePodioTrackContainer(const ConstPodioTrackContainer& other); + + // BEGIN INTERFACE HELPER + + private: + std::shared_ptr getOrCreateSurface(IndexType itrack) { + std::shared_ptr& ptr = m_surfaces.at(itrack); + if (!ptr) { + ActsPodioEdm::Track track = m_collection->at(itrack); + ptr = PodioUtil::convertSurfaceFromPodio(m_helper, + track.getReferenceSurface()); + } + return ptr; + } + + public: + std::any component_impl(HashedString key, IndexType itrack) { + return PodioTrackContainerBase::component_impl(*this, key, itrack); + } + + std::any component_impl(HashedString key, IndexType itrack) const { + return PodioTrackContainerBase::component_impl(*this, key, itrack); + } + + bool hasColumn_impl(HashedString key) const { + return m_dynamic.find(key) != m_dynamic.end(); + } + + std::size_t size_impl() const { return m_collection->size(); } + // END INTERFACE HELPER + + const Surface* referenceSurface_impl(IndexType itrack) const { + return m_surfaces.at(itrack).get(); + } + + void setReferenceSurface_impl(IndexType itrack, + std::shared_ptr surface) { + auto track = m_collection->at(itrack); + track.setReferenceSurface( + PodioUtil::convertSurfaceToPodio(m_helper, *surface)); + m_surfaces.at(itrack) = std::move(surface); + } + + public: + // BEGIN INTERFACE + + IndexType addTrack_impl() { + auto track = m_collection->create(); + track.referenceSurface().surfaceType = PodioUtil::kNoSurface; + m_surfaces.emplace_back(); + for (const auto& [key, vec] : m_dynamic) { + vec->add(); + } + return m_collection->size() - 1; + }; + + void removeTrack_impl(IndexType itrack); + + template + constexpr void addColumn_impl(const std::string& key) { + m_dynamic.insert({hashString(key), + std::make_unique>(key)}); + } + + Parameters parameters(IndexType itrack) { + return Parameters{m_collection->at(itrack).data().parameters.data()}; + } + + ConstParameters parameters(IndexType itrack) const { + return ConstParameters{ + m_collection->at(itrack).getData().parameters.data()}; + } + + Covariance covariance(IndexType itrack) { + return Covariance{m_collection->at(itrack).data().covariance.data()}; + } + + ConstCovariance covariance(IndexType itrack) const { + return ConstCovariance{ + m_collection->at(itrack).getData().covariance.data()}; + } + + // @TODO What's the equivalent of this? + void copyDynamicFrom_impl(IndexType dstIdx, + const MutablePodioTrackContainer& src, + IndexType srcIdx); + + void ensureDynamicColumns_impl(const MutablePodioTrackContainer& other); + + void reserve(IndexType /*size*/) {} + + ActsPodioEdm::TrackCollection& trackCollection() { return *m_collection; } + + void releaseInto(podio::Frame& frame, const std::string& suffix = "") { + std::string s = suffix; + if (!s.empty()) { + s = "_" + s; + } + frame.put(std::move(m_collection), "tracks" + s); + m_surfaces.clear(); + + for (const auto& [key, col] : m_dynamic) { + col->releaseInto(frame, "tracks" + s + "_extra__"); + } + } + + // END INTERFACE + + private: + friend PodioTrackContainerBase; + + std::unique_ptr m_collection; + std::unordered_map> + m_dynamic; +}; + +ACTS_STATIC_CHECK_CONCEPT(TrackContainerBackend, MutablePodioTrackContainer); + +class ConstPodioTrackContainer : public PodioTrackContainerBase { + public: + ConstPodioTrackContainer(const PodioUtil::ConversionHelper& helper, + const ActsPodioEdm::TrackCollection& collection) + : PodioTrackContainerBase{helper}, m_collection{&collection} { + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + } + + ConstPodioTrackContainer(const PodioUtil::ConversionHelper& helper, + const podio::Frame& frame, + const std::string& suffix = "") + : PodioTrackContainerBase{helper} { + std::string s = suffix.empty() ? suffix : "_" + suffix; + std::string tracksKey = "tracks" + s; + + std::vector available = frame.getAvailableCollections(); + if (std::find(available.begin(), available.end(), tracksKey) == + available.end()) { + throw std::runtime_error{"Track collection '" + tracksKey + + "'not found in frame"}; + } + + const auto* collection = frame.get(tracksKey); + + if (const auto* d = + dynamic_cast(collection); + d != nullptr) { + m_collection = d; + } else { + throw std::runtime_error{"Unable to get collection " + tracksKey}; + } + + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + + // let's find dynamic columns + using types = + std::tuple; + + for (const auto& col : available) { + std::string prefix = tracksKey + "_extra__"; + std::size_t p = col.find(prefix); + if (p == std::string::npos) { + continue; + } + std::string dynName = col.substr(prefix.size()); + const podio::CollectionBase* coll = frame.get(col); + + std::unique_ptr up; + + std::apply( + [&](auto... args) { + auto inner = [&](auto arg) { + if (up) { + return; + } + using T = decltype(arg); + const auto* dyn = + dynamic_cast*>(coll); + if (dyn == nullptr) { + return; + } + up = std::make_unique>( + dynName, *dyn); + }; + + ((inner(args)), ...); + }, + types{}); + + if (!up) { + throw std::runtime_error{"Dynamic column '" + dynName + + "' is not of allowed type"}; + } + + m_dynamic.insert({hashString(dynName), std::move(up)}); + } + } + + std::any component_impl(HashedString key, IndexType itrack) const { + return PodioTrackContainerBase::component_impl(*this, key, itrack); + } + + bool hasColumn_impl(HashedString key) const { + return m_dynamic.find(key) != m_dynamic.end(); + } + + std::size_t size_impl() const { return m_collection->size(); } + + const Surface* referenceSurface_impl(IndexType itrack) const { + return m_surfaces.at(itrack).get(); + } + + ConstParameters parameters(IndexType itrack) const { + return ConstParameters{ + m_collection->at(itrack).getData().parameters.data()}; + } + + ConstCovariance covariance(IndexType itrack) const { + return ConstCovariance{ + m_collection->at(itrack).getData().covariance.data()}; + } + + const ActsPodioEdm::TrackCollection& trackCollection() { + return *m_collection; + } + + private: + friend PodioTrackContainerBase; + + const ActsPodioEdm::TrackCollection* m_collection; + std::unordered_map> + m_dynamic; +}; + +ACTS_STATIC_CHECK_CONCEPT(ConstTrackContainerBackend, ConstPodioTrackContainer); + +} // namespace Acts diff --git a/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackStateContainer.hpp b/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackStateContainer.hpp new file mode 100644 index 00000000000..1dc8c505b28 --- /dev/null +++ b/Plugins/Podio/include/Acts/Plugins/Podio/PodioTrackStateContainer.hpp @@ -0,0 +1,705 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/MultiTrajectoryBackendConcept.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackStatePropMask.hpp" +#include "Acts/EventData/Types.hpp" +#include "Acts/Plugins/Podio/PodioDynamicColumns.hpp" +#include "Acts/Plugins/Podio/PodioTrackContainer.hpp" +#include "Acts/Plugins/Podio/PodioUtil.hpp" +#include "Acts/Utilities/HashedString.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "ActsPodioEdm/BoundParametersCollection.h" +#include "ActsPodioEdm/JacobianCollection.h" +#include "ActsPodioEdm/TrackStateCollection.h" +#include "ActsPodioEdm/TrackStateInfo.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "podio/UserDataCollection.h" + +namespace Acts { + +class MutablePodioTrackStateContainer; +class ConstPodioTrackStateContainer; + +class PodioTrackStateContainerBase { + public: + using Parameters = + typename detail_lt::Types::CoefficientsMap; + using Covariance = + typename detail_lt::Types::CovarianceMap; + + using ConstParameters = + typename detail_lt::Types::CoefficientsMap; + using ConstCovariance = + typename detail_lt::Types::CovarianceMap; + + protected: + template + static constexpr bool has_impl(T& instance, HashedString key, + TrackIndexType istate) { + constexpr auto kInvalid = MultiTrajectoryTraits::kInvalid; + using namespace Acts::HashedStringLiteral; + auto trackState = instance.m_collection->at(istate); + const auto& data = trackState.getData(); + switch (key) { + case "predicted"_hash: + return data.ipredicted != kInvalid; + case "filtered"_hash: + return data.ifiltered != kInvalid; + case "smoothed"_hash: + return data.ismoothed != kInvalid; + case "calibrated"_hash: + return data.measdim != 0; + case "calibratedCov"_hash: + return data.measdim != 0; + case "jacobian"_hash: + return data.ijacobian != kInvalid; + case "projector"_hash: + return data.hasProjector; + case "uncalibratedSourceLink"_hash: + return data.uncalibratedIdentifier != PodioUtil::kNoIdentifier; + case "previous"_hash: + case "measdim"_hash: + case "referenceSurface"_hash: + case "chi2"_hash: + case "pathLength"_hash: + case "typeFlags"_hash: + return true; + default: + return instance.m_dynamic.find(key) != instance.m_dynamic.end(); + } + + return false; + } + + template + static std::any component_impl(T& instance, HashedString key, + TrackIndexType istate) { + if constexpr (EnsureConst) { + static_assert(std::is_const_v>, + "Is not const"); + } + using namespace Acts::HashedStringLiteral; + auto trackState = instance.m_collection->at(istate); + std::conditional_t + dataPtr; + if constexpr (EnsureConst) { + dataPtr = &trackState.getData(); + } else { + dataPtr = &trackState.data(); + } + auto& data = *dataPtr; + switch (key) { + case "previous"_hash: + return &data.previous; + case "predicted"_hash: + return &data.ipredicted; + case "filtered"_hash: + return &data.ifiltered; + case "smoothed"_hash: + return &data.ismoothed; + case "projector"_hash: + return &data.projector; + case "measdim"_hash: + return &data.measdim; + case "chi2"_hash: + return &data.chi2; + case "pathLength"_hash: + return &data.pathLength; + case "typeFlags"_hash: + return &data.typeFlags; + default: + auto it = instance.m_dynamic.find(key); + if (it == instance.m_dynamic.end()) { + throw std::runtime_error("Unable to handle this component"); + } + std::conditional_t + col = it->second.get(); + assert(col && "Dynamic column is null"); + return col->get(istate); + } + } + + template + static constexpr bool hasColumn_impl(T& instance, HashedString key) { + using namespace Acts::HashedStringLiteral; + switch (key) { + case "predicted"_hash: + case "filtered"_hash: + case "smoothed"_hash: + case "jacobian"_hash: + case "projector"_hash: + case "previous"_hash: + case "uncalibratedSourceLink"_hash: + case "referenceSurface"_hash: + case "measdim"_hash: + case "chi2"_hash: + case "pathLength"_hash: + case "typeFlags"_hash: + return true; + default: + return instance.m_dynamic.find(key) != instance.m_dynamic.end(); + } + } + + static void populateSurfaceBuffer( + const PodioUtil::ConversionHelper& helper, + const ActsPodioEdm::TrackStateCollection& collection, + std::vector>& surfaces) noexcept { + surfaces.reserve(collection.size()); + for (ActsPodioEdm::TrackState trackState : collection) { + surfaces.push_back(PodioUtil::convertSurfaceFromPodio( + helper, trackState.getReferenceSurface())); + } + } +}; + +template <> +struct IsReadOnlyMultiTrajectory + : std::true_type {}; + +class ConstPodioTrackStateContainer final + : public PodioTrackStateContainerBase, + public MultiTrajectory { + public: + ConstPodioTrackStateContainer( + const PodioUtil::ConversionHelper& helper, + const ActsPodioEdm::TrackStateCollection& trackStates, + const ActsPodioEdm::BoundParametersCollection& params, + const ActsPodioEdm::JacobianCollection& jacs) + : m_helper{helper}, + m_collection{&trackStates}, + m_params{¶ms}, + m_jacs{&jacs} { + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + } + + ConstPodioTrackStateContainer(const PodioUtil::ConversionHelper& helper, + const podio::Frame& frame, + const std::string& suffix = "") + : m_helper{helper}, + m_collection{nullptr}, + m_params{nullptr}, + m_jacs{nullptr} { + std::string s = suffix.empty() ? suffix : "_" + suffix; + + std::vector available = frame.getAvailableCollections(); + + std::string trackStatesKey = "trackStates" + s; + std::string paramsKey = "trackStateParameters" + s; + std::string jacsKey = "trackStateJacobians" + s; + + if (std::find(available.begin(), available.end(), trackStatesKey) == + available.end()) { + throw std::runtime_error{"Track state collection '" + trackStatesKey + + "'not found in frame"}; + } + + if (std::find(available.begin(), available.end(), paramsKey) == + available.end()) { + throw std::runtime_error{"Track state parameters collection '" + + paramsKey + "'not found in frame"}; + } + + if (std::find(available.begin(), available.end(), jacsKey) == + available.end()) { + throw std::runtime_error{"Track state jacobian collection '" + jacsKey + + "'not found in frame"}; + } + + loadCollection(m_collection, frame, + trackStatesKey); + loadCollection(m_params, frame, + paramsKey); + loadCollection(m_jacs, frame, jacsKey); + + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + + // let's find dynamic columns + + using load_type = std::unique_ptr (*)( + const podio::CollectionBase*); + + using types = + std::tuple; + + for (const auto& col : available) { + std::string prefix = trackStatesKey + "_extra__"; + std::size_t p = col.find(prefix); + if (p == std::string::npos) { + continue; + } + std::string dynName = col.substr(prefix.size()); + const podio::CollectionBase* coll = frame.get(col); + + std::unique_ptr up; + + std::apply( + [&](auto... args) { + auto inner = [&](auto arg) { + if (up) { + return; + } + using T = decltype(arg); + const auto* dyn = + dynamic_cast*>(coll); + if (dyn == nullptr) { + return; + } + up = std::make_unique>( + dynName, *dyn); + }; + + ((inner(args)), ...); + }, + types{}); + + if (!up) { + throw std::runtime_error{"Dynamic column '" + dynName + + "' is not of allowed type"}; + } + + m_dynamic.insert({hashString(dynName), std::move(up)}); + } + } + + private: + template + static void loadCollection(collection_t const*& dest, + const podio::Frame& frame, + const std::string& key) { + const auto* collection = frame.get(key); + + if (const auto* d = dynamic_cast(collection); + d != nullptr) { + dest = d; + } else { + throw std::runtime_error{"Unable to get collection " + key}; + } + } + + public: + ConstParameters parameters_impl(IndexType istate) const { + return ConstParameters{m_params->at(istate).getData().values.data()}; + } + + ConstCovariance covariance_impl(IndexType istate) const { + return ConstCovariance{m_params->at(istate).getData().covariance.data()}; + } + + ConstCovariance jacobian_impl(IndexType istate) const { + IndexType ijacobian = m_collection->at(istate).getData().ijacobian; + return ConstCovariance{m_jacs->at(ijacobian).getData().values.data()}; + } + + template + ConstTrackStateProxy::Measurement measurement_impl( + IndexType index) const { + return ConstTrackStateProxy::Measurement{ + m_collection->at(index).getData().measurement.data()}; + } + + template + ConstTrackStateProxy::MeasurementCovariance + measurementCovariance_impl(IndexType index) const { + return ConstTrackStateProxy::MeasurementCovariance{ + m_collection->at(index).getData().measurementCovariance.data()}; + } + + IndexType size_impl() const { return m_collection->size(); } + + std::any component_impl(HashedString key, IndexType istate) const { + return PodioTrackStateContainerBase::component_impl(*this, key, + istate); + } + + constexpr bool hasColumn_impl(HashedString key) const { + return PodioTrackStateContainerBase::hasColumn_impl(*this, key); + } + + constexpr bool has_impl(HashedString key, IndexType istate) const { + return PodioTrackStateContainerBase::has_impl(*this, key, istate); + } + + MultiTrajectoryTraits::IndexType calibratedSize_impl(IndexType istate) const { + return m_collection->at(istate).getData().measdim; + } + + SourceLink getUncalibratedSourceLink_impl(IndexType istate) const { + return m_helper.get().identifierToSourceLink( + m_collection->at(istate).getData().uncalibratedIdentifier); + } + + const Surface* referenceSurface_impl(IndexType istate) const { + return m_surfaces.at(istate).get(); + } + + private: + friend class PodioTrackStateContainerBase; + + std::reference_wrapper m_helper; + const ActsPodioEdm::TrackStateCollection* m_collection; + const ActsPodioEdm::BoundParametersCollection* m_params; + const ActsPodioEdm::JacobianCollection* m_jacs; + std::vector> m_surfaces; + + std::unordered_map> + m_dynamic; +}; + +static_assert(IsReadOnlyMultiTrajectory::value, + "MutablePodioTrackStateContainer should not be read-only"); + +ACTS_STATIC_CHECK_CONCEPT(ConstMultiTrajectoryBackend, + ConstPodioTrackStateContainer); + +template <> +struct IsReadOnlyMultiTrajectory + : std::false_type {}; + +class MutablePodioTrackStateContainer final + : public PodioTrackStateContainerBase, + public MultiTrajectory { + public: + MutablePodioTrackStateContainer(PodioUtil::ConversionHelper& helper) + : m_helper{helper} { + m_collection = std::make_unique(); + m_jacs = std::make_unique(); + m_params = std::make_unique(); + + populateSurfaceBuffer(m_helper, *m_collection, m_surfaces); + } + + ConstParameters parameters_impl(IndexType istate) const { + return ConstParameters{m_params->at(istate).getData().values.data()}; + } + + Parameters parameters_impl(IndexType istate) { + return Parameters{m_params->at(istate).data().values.data()}; + } + + ConstCovariance covariance_impl(IndexType istate) const { + return ConstCovariance{m_params->at(istate).getData().covariance.data()}; + } + + Covariance covariance_impl(IndexType istate) { + return Covariance{m_params->at(istate).data().covariance.data()}; + } + + ConstCovariance jacobian_impl(IndexType istate) const { + IndexType ijacobian = m_collection->at(istate).getData().ijacobian; + return ConstCovariance{m_jacs->at(ijacobian).getData().values.data()}; + } + + Covariance jacobian_impl(IndexType istate) { + IndexType ijacobian = m_collection->at(istate).getData().ijacobian; + return Covariance{m_jacs->at(ijacobian).data().values.data()}; + } + + template + ConstTrackStateProxy::Measurement measurement_impl( + IndexType index) const { + return ConstTrackStateProxy::Measurement{ + m_collection->at(index).getData().measurement.data()}; + } + + template + TrackStateProxy::Measurement measurement_impl(IndexType index) { + return TrackStateProxy::Measurement{ + m_collection->at(index).data().measurement.data()}; + } + + template + ConstTrackStateProxy::MeasurementCovariance + measurementCovariance_impl(IndexType index) const { + return ConstTrackStateProxy::MeasurementCovariance{ + m_collection->at(index).getData().measurementCovariance.data()}; + } + + template + TrackStateProxy::MeasurementCovariance measurementCovariance_impl( + IndexType index) { + return TrackStateProxy::MeasurementCovariance{ + m_collection->at(index).data().measurementCovariance.data()}; + } + + IndexType size_impl() const { return m_collection->size(); } + + std::any component_impl(HashedString key, IndexType istate) const { + return PodioTrackStateContainerBase::component_impl(*this, key, + istate); + } + + std::any component_impl(HashedString key, IndexType istate) { + return PodioTrackStateContainerBase::component_impl(*this, key, + istate); + } + + constexpr bool hasColumn_impl(HashedString key) const { + return PodioTrackStateContainerBase::hasColumn_impl(*this, key); + } + + constexpr bool has_impl(HashedString key, IndexType istate) const { + return PodioTrackStateContainerBase::has_impl(*this, key, istate); + } + + IndexType addTrackState_impl( + TrackStatePropMask mask = TrackStatePropMask::All, + TrackIndexType iprevious = kTrackIndexInvalid) { + auto trackState = m_collection->create(); + auto& data = trackState.data(); + data.previous = iprevious; + data.ipredicted = kInvalid; + data.ifiltered = kInvalid; + data.ismoothed = kInvalid; + data.ijacobian = kInvalid; + trackState.referenceSurface().surfaceType = PodioUtil::kNoSurface; + + if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Predicted)) { + m_params->create(); + data.ipredicted = m_params->size() - 1; + } + if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Filtered)) { + m_params->create(); + data.ifiltered = m_params->size() - 1; + } + if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Smoothed)) { + m_params->create(); + data.ismoothed = m_params->size() - 1; + } + if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Jacobian)) { + m_jacs->create(); + data.ijacobian = m_jacs->size() - 1; + } + data.measdim = 0; + data.hasProjector = false; + if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Calibrated)) { + data.hasProjector = true; + } + m_surfaces.emplace_back(); + + data.uncalibratedIdentifier = PodioUtil::kNoIdentifier; + assert(m_collection->size() == m_surfaces.size() && + "Inconsistent surface buffer"); + + for (const auto& [key, vec] : m_dynamic) { + vec->add(); + } + + return m_collection->size() - 1; + } + + void shareFrom_impl(TrackIndexType iself, TrackIndexType iother, + TrackStatePropMask shareSource, + TrackStatePropMask shareTarget) { + auto& self = m_collection->at(iself).data(); + auto& other = m_collection->at(iother).data(); + + assert(ACTS_CHECK_BIT(getTrackState(iother).getMask(), shareSource) && + "Source has incompatible allocation"); + + using PM = TrackStatePropMask; + + IndexType sourceIndex{kInvalid}; + switch (shareSource) { + case PM::Predicted: + sourceIndex = other.ipredicted; + break; + case PM::Filtered: + sourceIndex = other.ifiltered; + break; + case PM::Smoothed: + sourceIndex = other.ismoothed; + break; + case PM::Jacobian: + sourceIndex = other.ijacobian; + break; + default: + throw std::domain_error{"Unable to share this component"}; + } + + assert(sourceIndex != kInvalid); + + switch (shareTarget) { + case PM::Predicted: + assert(shareSource != PM::Jacobian); + self.ipredicted = sourceIndex; + break; + case PM::Filtered: + assert(shareSource != PM::Jacobian); + self.ifiltered = sourceIndex; + break; + case PM::Smoothed: + assert(shareSource != PM::Jacobian); + self.ismoothed = sourceIndex; + break; + case PM::Jacobian: + assert(shareSource == PM::Jacobian); + self.ijacobian = sourceIndex; + break; + default: + throw std::domain_error{"Unable to share this component"}; + } + } + + void unset_impl(TrackStatePropMask target, TrackIndexType istate) { + auto& data = m_collection->at(istate).data(); + switch (target) { + case TrackStatePropMask::Predicted: + data.ipredicted = kInvalid; + break; + case TrackStatePropMask::Filtered: + data.ifiltered = kInvalid; + break; + case TrackStatePropMask::Smoothed: + data.ismoothed = kInvalid; + break; + case TrackStatePropMask::Jacobian: + data.ijacobian = kInvalid; + break; + case TrackStatePropMask::Calibrated: + data.measdim = 0; + break; + default: + throw std::domain_error{"Unable to unset this component"}; + } + } + + void clear_impl() { + m_collection->clear(); + m_params->clear(); + m_surfaces.clear(); + for (const auto& [key, vec] : m_dynamic) { + vec->clear(); + } + } + + template + constexpr void addColumn_impl(const std::string& key) { + m_dynamic.insert({hashString(key), + std::make_unique>(key)}); + } + + void allocateCalibrated_impl(IndexType istate, size_t measdim) { + assert(measdim > 0 && "Zero measdim not supported"); + auto& data = m_collection->at(istate).data(); + data.measdim = measdim; + } + + void setUncalibratedSourceLink_impl(IndexType istate, + const SourceLink& sourceLink) { + PodioUtil::Identifier id = + m_helper.get().sourceLinkToIdentifier(sourceLink); + m_collection->at(istate).data().uncalibratedIdentifier = id; + } + + void setReferenceSurface_impl(IndexType istate, + std::shared_ptr surface) { + auto trackState = m_collection->at(istate); + trackState.setReferenceSurface( + PodioUtil::convertSurfaceToPodio(m_helper, *surface)); + m_surfaces.at(istate) = std::move(surface); + } + + MultiTrajectoryTraits::IndexType calibratedSize_impl(IndexType istate) const { + return m_collection->at(istate).getData().measdim; + } + + SourceLink getUncalibratedSourceLink_impl(IndexType istate) const { + return m_helper.get().identifierToSourceLink( + m_collection->at(istate).getData().uncalibratedIdentifier); + } + + const Surface* referenceSurface_impl(IndexType istate) const { + return m_surfaces.at(istate).get(); + } + + void releaseInto(podio::Frame& frame, const std::string& suffix = "") { + std::string s = suffix; + if (!s.empty()) { + s = "_" + s; + } + frame.put(std::move(m_collection), "trackStates" + s); + frame.put(std::move(m_params), "trackStateParameters" + s); + frame.put(std::move(m_jacs), "trackStateJacobians" + s); + m_surfaces.clear(); + + for (const auto& [key, col] : m_dynamic) { + col->releaseInto(frame, "trackStates" + s + "_extra__"); + } + } + + private: + friend class PodioTrackStateContainerBase; + friend class ConstPodioTrackStateContainer; + + std::reference_wrapper m_helper; + std::unique_ptr m_collection; + std::unique_ptr m_params; + std::unique_ptr m_jacs; + std::vector> m_surfaces; + + std::unordered_map> + m_dynamic; +}; + +static_assert( + !IsReadOnlyMultiTrajectory::value, + "MutablePodioTrackStateContainer should not be read-only"); + +static_assert(!MutablePodioTrackStateContainer::ReadOnly, + "MutablePodioTrackStateContainer should not be read-only"); + +ACTS_STATIC_CHECK_CONCEPT(MutableMultiTrajectoryBackend, + MutablePodioTrackStateContainer); + +// ConstPodioTrackStateContainer::ConstPodioTrackStateContainer( +// MutablePodioTrackStateContainer&& other) +// : m_helper{other.m_helper}, +// m_collection{std::move(other.m_collection)}, +// m_params{std::move(other.m_params)}, +// m_jacs{std::move(other.m_jacs)}, +// m_surfaces{std::move(other.m_surfaces)} {} + +// ConstPodioTrackStateContainer::ConstPodioTrackStateContainer( +// const MutablePodioTrackStateContainer& other) +// : m_helper{other.m_helper}, +// m_surfaces{other.m_surfaces.begin(), other.m_surfaces.end()} { +// for (auto src : *other.m_collection) { +// auto dst = m_collection->create(); +// dst = src.clone(); +// } +// for (auto src : *other.m_params) { +// auto dst = m_params->create(); +// dst = src.clone(); +// } +// for (auto src : *other.m_jacs) { +// auto dst = m_jacs->create(); +// dst = src.clone(); +// } +// } + +} // namespace Acts diff --git a/Plugins/Podio/include/Acts/Plugins/Podio/PodioUtil.hpp b/Plugins/Podio/include/Acts/Plugins/Podio/PodioUtil.hpp new file mode 100644 index 00000000000..328529898cd --- /dev/null +++ b/Plugins/Podio/include/Acts/Plugins/Podio/PodioUtil.hpp @@ -0,0 +1,46 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/EventData/SourceLink.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Utilities/Helpers.hpp" + +#include +#include + +namespace ActsPodioEdm { +class Surface; +} + +namespace Acts::PodioUtil { + +using Identifier = uint64_t; +constexpr Identifier kNoIdentifier = std::numeric_limits::max(); +constexpr int kNoSurface = -1; + +// @TODO: We might want to consider making this a type erased type that's not an interface +class ConversionHelper { + public: + virtual std::optional surfaceToIdentifier( + const Surface& surface) const = 0; + virtual const Surface* identifierToSurface(Identifier identifier) const = 0; + + virtual Identifier sourceLinkToIdentifier(const SourceLink& sl) = 0; + virtual SourceLink identifierToSourceLink(Identifier identifier) const = 0; +}; + +std::shared_ptr convertSurfaceFromPodio( + const ConversionHelper& helper, const ActsPodioEdm::Surface& surface); + +ActsPodioEdm::Surface convertSurfaceToPodio(const ConversionHelper& helper, + const Acts::Surface& surface); + +} // namespace Acts::PodioUtil diff --git a/Plugins/Podio/src/PodioUtil.cpp b/Plugins/Podio/src/PodioUtil.cpp new file mode 100644 index 00000000000..cfa3f5e358b --- /dev/null +++ b/Plugins/Podio/src/PodioUtil.cpp @@ -0,0 +1,206 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Podio//PodioUtil.hpp" + +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Plugins/Identification/Identifier.hpp" +#include "Acts/Surfaces/AnnulusBounds.hpp" +#include "Acts/Surfaces/ConeSurface.hpp" +#include "Acts/Surfaces/ConvexPolygonBounds.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiamondBounds.hpp" +#include "Acts/Surfaces/DiscBounds.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/DiscTrapezoidBounds.hpp" +#include "Acts/Surfaces/EllipseBounds.hpp" +#include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Surfaces/PlanarBounds.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/StrawSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/ThrowAssert.hpp" +#include "ActsPodioEdm/Surface.h" + +#include +#include + +namespace Acts::PodioUtil { + +namespace { +template +std::shared_ptr createBounds( + const ActsPodioEdm::Surface& surface) { + constexpr size_t S = bounds_t::eSize; + throw_assert(surface.boundValuesSize == S, + "Unexpected number of bound values"); + + std::array values{}; + for (size_t i = 0; i < S; i++) { + values.at(i) = surface.boundValues.at(i); + } + return std::make_shared(values); +} +} // namespace + +ActsPodioEdm::Surface convertSurfaceToPodio(const ConversionHelper& helper, + const Acts::Surface& surface) { + ActsPodioEdm::Surface result; + + std::optional identifier = helper.surfaceToIdentifier(surface); + if (identifier.has_value()) { + result.identifier = identifier.value(); + } else { + result.identifier = kNoIdentifier; + assert(surface.associatedDetectorElement() == nullptr && + "Unidentified surface does not have detector element"); + // @TODO: Surface type is not well-defined for curvilinear surface: looks like any plane surface + result.surfaceType = surface.type(); + // @TODO: Test line bounds, does not have bounds, so nullptr + result.boundsType = surface.bounds().type(); + result.geometryId = surface.geometryId().value(); + auto values = surface.bounds().values(); + + if (values.size() > result.boundValues.size()) { + throw std::runtime_error{"Too many bound values to store"}; + } + + for (size_t i = 0; i < values.size(); i++) { + result.boundValues.at(i) = values.at(i); + } + result.boundValuesSize = values.size(); + + Eigen::Map> trf{result.transform.data()}; + + // This is safe ONLY(!) if there is no associated detector element, since + // the surface will not inspect the geometry context at all by itself. + Acts::GeometryContext gctx; + trf = surface.transform(gctx).matrix(); + } + + return result; +} + +std::shared_ptr convertSurfaceFromPodio( + const ConversionHelper& helper, const ActsPodioEdm::Surface& surface) { + if (surface.surfaceType == kNoSurface) { + return nullptr; + } + + Eigen::Map> mat{surface.transform.data()}; + Transform3 transform{mat}; + + using T = Surface::SurfaceType; + using B = SurfaceBounds; + + std::shared_ptr result; + + if (const Surface* srf = helper.identifierToSurface(surface.identifier); + srf != nullptr) { + result = srf->getSharedPtr(); + } + + if (result) { + return result; + } + + switch (surface.surfaceType) { + default: + throw std::runtime_error{"Invalid surface type encountered"}; + + case T::Cone: + throw_assert(surface.boundsType == B::eCone, "Unexpected bounds type"); + result = Acts::Surface::makeShared( + transform, createBounds(surface)); + break; + + case T::Cylinder: + throw_assert(surface.boundsType == B::eCylinder, + "Unexpected bounds type"); + result = Acts::Surface::makeShared( + transform, createBounds(surface)); + break; + + case T::Disc: { + std::shared_ptr dBounds; + switch (surface.boundsType) { + default: + throw std::runtime_error{"Invalid bounds type encountered"}; + + case B::eDisc: + dBounds = createBounds(surface); + break; + + case B::eAnnulus: + dBounds = createBounds(surface); + break; + + case B::eDiscTrapezoid: + dBounds = createBounds(surface); + break; + } + result = Acts::Surface::makeShared(transform, dBounds); + break; + } + + case T::Perigee: + throw_assert(surface.boundsType == B::eBoundless, + "Unexpected bounds type"); + result = Acts::Surface::makeShared(transform); + break; + + case T::Plane: { + std::shared_ptr pBounds; + switch (surface.boundsType) { + default: + throw std::runtime_error{"Invalid bounds type encountered"}; + + case B::eDiamond: + pBounds = createBounds(surface); + break; + case B::eEllipse: + pBounds = createBounds(surface); + break; + case B::eRectangle: + pBounds = createBounds(surface); + break; + case B::eConvexPolygon: + template_switch_lambda<6, 32>(surface.boundValuesSize, [&](auto N) { + constexpr size_t nValues = decltype(N)::value; + constexpr size_t nVertices = nValues / 2; + pBounds = createBounds>(surface); + }); + // @TODO: Maybe handle dynamic convex polygons? + break; + } + assert(pBounds && "No PlanarBounds"); + result = Acts::Surface::makeShared(transform, pBounds); + + break; + } + + case T::Straw: + throw_assert(surface.boundsType == B::eLine, "Unexpected bounds type"); + result = Acts::Surface::makeShared( + transform, createBounds(surface)); + break; + + case T::Curvilinear: + throw_assert(surface.boundsType == B::eBoundless, + "Unexpected bounds type"); + result = Acts::Surface::makeShared(transform); + break; + } + + return result; +} + +} // namespace Acts::PodioUtil diff --git a/Plugins/Sycl/src/Utilities/DeviceSelector.cpp b/Plugins/Sycl/src/Utilities/DeviceSelector.cpp index 442136cfbe7..5c2a70a7a5d 100644 --- a/Plugins/Sycl/src/Utilities/DeviceSelector.cpp +++ b/Plugins/Sycl/src/Utilities/DeviceSelector.cpp @@ -15,7 +15,7 @@ namespace Acts::Sycl { DeviceSelector::DeviceSelector(const std::string& deviceName) : m_defaultSelector(cl::sycl::default_selector()), - m_deviceName(deviceName){}; + m_deviceName(deviceName) {} int DeviceSelector::operator()(const cl::sycl::device& d) const { // Under no circumstances do we accept any NVidia OpenCL devices. diff --git a/Tests/DownstreamProject/CMakeLists.txt b/Tests/DownstreamProject/CMakeLists.txt index e34282ec8c9..da142389dc0 100644 --- a/Tests/DownstreamProject/CMakeLists.txt +++ b/Tests/DownstreamProject/CMakeLists.txt @@ -37,3 +37,17 @@ if(DD4HEP) find_package(Acts CONFIG REQUIRED COMPONENTS PluginDD4hep) target_link_libraries( ShowActsVersion PRIVATE ActsPluginDD4hep) endif() + +option(PODIO "Build with podio" ON) +if(PODIO) + message(STATUS "Adding podio plugin") + find_package(Acts CONFIG REQUIRED COMPONENTS PluginPodio) + target_link_libraries( ShowActsVersion PRIVATE ActsPluginPodio) +endif() + +option(EDM4HEP "Build with EDM4hep" ON) +if(EDM4HEP) + message(STATUS "Adding EDM4hep plugin") + find_package(Acts CONFIG REQUIRED COMPONENTS PluginEDM4hep) + target_link_libraries( ShowActsVersion PRIVATE ActsPluginEDM4hep) +endif() diff --git a/Tests/UnitTests/Core/Detector/BlueprintHelperTests.cpp b/Tests/UnitTests/Core/Detector/BlueprintHelperTests.cpp new file mode 100644 index 00000000000..e2b1c8a37e9 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/BlueprintHelperTests.cpp @@ -0,0 +1,302 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Detector/Blueprint.hpp" +#include "Acts/Detector/detail/BlueprintHelper.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { +class IInternalStructureBuilder {}; +} // namespace Experimental +} // namespace Acts + +BOOST_AUTO_TEST_SUITE(Experimental) + +BOOST_AUTO_TEST_CASE(BlueprintHelperSorting) { + // Create root node + std::vector detectorBinning = {Acts::binR}; + std::vector detectorBoundaries = {0., 50., 100.}; + auto detector = std::make_unique( + "detector", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + detectorBoundaries, detectorBinning); + + BOOST_CHECK(detector->parent == nullptr); + BOOST_CHECK(detector->children.empty()); + BOOST_CHECK(detector->name == "detector"); + + std::vector pixelsBinning = {Acts::binZ}; + std::vector pixelsBoundaries = {20., 50., 100.}; + + auto pixels = std::make_unique( + "pixels", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + pixelsBoundaries, pixelsBinning); + + std::vector beamPipeBoundaries = {0., 20., 100.}; + auto beamPipe = std::make_unique( + "beam_pipe", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + beamPipeBoundaries); + + std::vector gapBoundaries = {20., 50., 10.}; + auto gap0 = std::make_unique( + "gap0", Acts::Transform3::Identity() * Acts::Translation3(0., 0., -90.), + Acts::VolumeBounds::eCylinder, gapBoundaries); + + std::vector layerBoundaries = {20., 50., 80.}; + auto layer = std::make_unique( + "layer", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + layerBoundaries, + std::make_shared()); + + auto gap1 = std::make_unique( + "gap1", Acts::Transform3::Identity() * Acts::Translation3(0., 0., 90.), + Acts::VolumeBounds::eCylinder, gapBoundaries); + + // Add the nodes in a random fashion + pixels->add(std::move(gap1)); + pixels->add(std::move(gap0)); + pixels->add(std::move(layer)); + // Add pixels and beam pipe in reverse order + detector->add(std::move(pixels)); + detector->add(std::move(beamPipe)); + + std::ofstream fs("detector_unordered.dot"); + detector->dotStream(fs); + fs.close(); + + // Sort the detector + Acts::Experimental::detail::BlueprintHelper::sort(*detector); + + // Test the recursive sort worked + BOOST_CHECK(detector->children.front()->name == "beam_pipe"); + BOOST_CHECK(detector->children.back()->name == "pixels"); + BOOST_CHECK(detector->children.back()->children.front()->name == "gap0"); + BOOST_CHECK(detector->children.back()->children[1u]->name == "layer"); + BOOST_CHECK(detector->children.back()->children.back()->name == "gap1"); + + std::ofstream fs2("detector_ordered.dot"); + detector->dotStream(fs2); + fs2.close(); +} + +BOOST_AUTO_TEST_CASE(BlueprintCylindricalGapFilling) { + // Detector dimensions + Acts::ActsScalar detectorIr = 0.; + Acts::ActsScalar detectorOr = 120.; + Acts::ActsScalar detectorHz = 400.; + + // Beam pipe + Acts::ActsScalar beamPipeOr = 20.; + + // Pixel system + Acts::ActsScalar pixelIr = 25; + Acts::ActsScalar pixelOr = 115; + Acts::ActsScalar pixelEcHz = 50; + + auto innerBuilder = + std::make_shared(); + + // Create root node + std::vector detectorBinning = {Acts::binR}; + std::vector detectorBoundaries = {detectorIr, detectorOr, + detectorHz}; + + // The root node - detector + auto detector = std::make_unique( + "detector", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + detectorBoundaries, detectorBinning); + + // The beam pipe + std::vector beamPipeBoundaries = {detectorIr, beamPipeOr, + detectorHz}; + auto beamPipe = std::make_unique( + "beam_pipe", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + beamPipeBoundaries, innerBuilder); + detector->add(std::move(beamPipe)); + + // A pixel system + std::vector pixelBoundaries = {pixelIr, pixelOr, + detectorHz}; + std::vector pixelBinning = {Acts::binZ}; + auto pixel = std::make_unique( + "pixel", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + pixelBoundaries, pixelBinning); + + // Nec: Small differences to check if the adjustments are made + std::vector pixelEcBoundaries = {pixelIr, pixelOr - 5., + pixelEcHz}; + std::vector pixelEcBinning = {Acts::binZ}; + + auto pixelNec = std::make_unique( + "pixelNec", + Acts::Transform3::Identity() * + Acts::Translation3(0., 0., -detectorHz + pixelEcHz), + Acts::VolumeBounds::eCylinder, pixelEcBoundaries, pixelEcBinning); + + // Add a single encap layer + std::vector pixelNecBoundaries = {pixelIr + 2, pixelOr - 7., + 10.}; + auto pixelNecLayer = std::make_unique( + "pixelNecLayer", + Acts::Transform3::Identity() * + Acts::Translation3(0., 0., -detectorHz + pixelEcHz), + Acts::VolumeBounds::eCylinder, pixelNecBoundaries, innerBuilder); + + pixelNec->add(std::move(pixelNecLayer)); + + // Barrel + std::vector pixelBarrelBoundaries = { + pixelIr + 1, pixelOr - 1., detectorHz - 2 * pixelEcHz}; + std::vector pixelBarrelBinning = {Acts::binR}; + + auto pixelBarrel = std::make_unique( + "pixelBarrel", Acts::Transform3::Identity(), + Acts::VolumeBounds::eCylinder, pixelBarrelBoundaries, pixelBarrelBinning); + + std::vector pixelBarrelL0Boundaries = { + 60, 65., detectorHz - 2 * pixelEcHz}; + auto pixelBarrelL0 = std::make_unique( + "pixelBarrelL0", Acts::Transform3::Identity(), + Acts::VolumeBounds::eCylinder, pixelBarrelL0Boundaries, innerBuilder); + + std::vector pixelBarrelL1Boundaries = { + 100, 105., detectorHz - 2 * pixelEcHz}; + auto pixelBarrelL1 = std::make_unique( + "pixelBarrelL1", Acts::Transform3::Identity(), + Acts::VolumeBounds::eCylinder, pixelBarrelL1Boundaries, innerBuilder); + pixelBarrel->add(std::move(pixelBarrelL0)); + pixelBarrel->add(std::move(pixelBarrelL1)); + + auto pixelPec = std::make_unique( + "pixelPec", + Acts::Transform3::Identity() * + Acts::Translation3(0., 0., +detectorHz - pixelEcHz), + Acts::VolumeBounds::eCylinder, pixelEcBoundaries, pixelEcBinning); + + std::vector pixelPecBoundaries = {pixelIr + 2, pixelOr - 7., + 10.}; + auto pixelPecLayer = std::make_unique( + "pixelPecLayer", + Acts::Transform3::Identity() * + Acts::Translation3(0., 0., detectorHz - pixelEcHz), + Acts::VolumeBounds::eCylinder, pixelPecBoundaries, innerBuilder); + + pixelPec->add(std::move(pixelPecLayer)); + + // Adding pixel + pixel->add(std::move(pixelNec)); + pixel->add(std::move(pixelPec)); + pixel->add(std::move(pixelBarrel)); + + detector->add(std::move(pixel)); + + std::ofstream fs("detector_with_gaps.dot"); + detector->dotStream(fs); + fs.close(); + + // Simple test + BOOST_CHECK(detector->children.size() == 2u); + BOOST_CHECK(detector->children[0u]->name == "beam_pipe"); + BOOST_CHECK(detector->children[1u]->name == "pixel"); + + // Now fill the gaps + Acts::Experimental::detail::BlueprintHelper::fillGaps(*detector); + + // Do the tests again + BOOST_CHECK(detector->children.size() == 4u); + BOOST_CHECK(detector->children[0u]->name == "beam_pipe"); + BOOST_CHECK(detector->children[1u]->name == "detector_gap_0"); + BOOST_CHECK(detector->children[2u]->name == "pixel"); + BOOST_CHECK(detector->children[3u]->name == "detector_gap_1"); + + // Adjustment of gap parameters + BOOST_CHECK(detector->children[1u]->boundaryValues[0] == beamPipeOr); + BOOST_CHECK(detector->children[1u]->boundaryValues[1] == pixelIr); + BOOST_CHECK(detector->children[1u]->boundaryValues[2] == detectorHz); + + BOOST_CHECK(detector->children[3u]->boundaryValues[0] == pixelOr); + BOOST_CHECK(detector->children[3u]->boundaryValues[1] == detectorOr); + BOOST_CHECK(detector->children[3u]->boundaryValues[2] == detectorHz); + + // Check the pixel system: Nec / Barrel / Pec + BOOST_CHECK(detector->children[2u]->children.size() == 3u); + BOOST_CHECK(detector->children[2u]->children[0u]->children.size() == 3u); + BOOST_CHECK(detector->children[2u]->children[1u]->children.size() == 5u); + BOOST_CHECK(detector->children[2u]->children[2u]->children.size() == 3u); + + // Nec test + BOOST_CHECK( + detector->children[2u]->children[0u]->children[0]->boundaryValues[0] == + pixelIr); + BOOST_CHECK( + detector->children[2u]->children[0u]->children[0]->boundaryValues[1] == + pixelOr); + + BOOST_CHECK( + detector->children[2u]->children[0u]->children[1]->boundaryValues[0] == + pixelIr); + BOOST_CHECK( + detector->children[2u]->children[0u]->children[1]->boundaryValues[1] == + pixelOr); + + BOOST_CHECK( + detector->children[2u]->children[0u]->children[2]->boundaryValues[0] == + pixelIr); + BOOST_CHECK( + detector->children[2u]->children[0u]->children[2]->boundaryValues[1] == + pixelOr); + + std::ofstream fs2("detector_without_gaps.dot"); + detector->dotStream(fs2); + fs2.close(); +} + +BOOST_AUTO_TEST_CASE(BlueprintCylindricalGapException) { + auto innerBuilder = + std::make_shared(); + + // The root node - detector + std::vector detectorBoundaries = {0., 50., 100.}; + std::vector detectorBinning = {Acts::binX}; + auto detector = std::make_unique( + "detector", Acts::Transform3::Identity(), Acts::VolumeBounds::eCuboid, + detectorBoundaries, detectorBinning); + + std::vector cubeOneBoundaries = {0., 20., 100.}; + auto cubeOne = std::make_unique( + "cubeOne", Acts::Transform3::Identity(), Acts::VolumeBounds::eCuboid, + cubeOneBoundaries, innerBuilder); + detector->add(std::move(cubeOne)); + + // Throw because the detector is not cylindrical (cube not yet implemented) + BOOST_CHECK_THROW( + Acts::Experimental::detail::BlueprintHelper::fillGaps(*detector), + std::runtime_error); + + // Let's change both from a cuboid to a cylinder + detector->boundsType = Acts::VolumeBounds::eCylinder; + detector->children.front()->boundsType = Acts::VolumeBounds::eCylinder; + + // Add a second volume + std::vector volTwoBoundaries = {0., 20., 100.}; + auto volTwo = std::make_unique( + "volTwo", Acts::Transform3::Identity(), Acts::VolumeBounds::eCylinder, + volTwoBoundaries, innerBuilder); + detector->add(std::move(volTwo)); + + // Throw because cylinders can not be binned in x + BOOST_CHECK_THROW( + Acts::Experimental::detail::BlueprintHelper::fillGaps(*detector), + std::runtime_error); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/BlueprintTests.cpp b/Tests/UnitTests/Core/Detector/BlueprintTests.cpp new file mode 100644 index 00000000000..25bb5cae6f0 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/BlueprintTests.cpp @@ -0,0 +1,88 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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/Detector/Blueprint.hpp" + +#include + +namespace Acts { +namespace Experimental { +class IInternalStructureBuilder {}; +} // namespace Experimental +} // namespace Acts + +BOOST_AUTO_TEST_SUITE(Experimental) + +BOOST_AUTO_TEST_CASE(BlueprintTest) { + std::vector bValues = {0., 10., 100.}; + + // Create root node + std::vector binning = {Acts::binR}; + auto root = std::make_unique( + "detector", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + bValues, binning); + // Check the root node + BOOST_CHECK(root->isRoot()); + BOOST_CHECK(root->parent == nullptr); + BOOST_CHECK(root->children.empty()); + BOOST_CHECK(root->name == "detector"); + + auto leaf0 = std::make_unique( + "volume_0", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + bValues); + BOOST_CHECK(leaf0->isLeaf()); + + auto branch = std::make_unique( + "container_0", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + bValues, binning); + + auto leaf1 = std::make_unique( + "volume_1", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + bValues); + + auto leaf2 = std::make_unique( + "volume_2", Acts::Transform3::Identity(), Acts::VolumeBounds::eOther, + bValues, + std::make_shared()); + + // Keep around the pointers of the branch & leaves + auto* leaf0Ptr = leaf0.get(); + auto* leaf1Ptr = leaf1.get(); + auto* leaf2Ptr = leaf2.get(); + auto* branchPtr = branch.get(); + + branch->add(std::move(leaf1)); + branch->add(std::move(leaf2)); + + // Branch has two children + BOOST_CHECK(branch->children.size() == 2u); + + // Parent of the leaves is the branch + BOOST_CHECK(leaf1Ptr->parent == branchPtr); + BOOST_CHECK(leaf2Ptr->parent == branchPtr); + + root->add(std::move(branch)); + + // Root stays root + BOOST_CHECK(root->isRoot()); + // Parent of the branch is the root + BOOST_CHECK(branchPtr->parent == root.get()); + + root->add(std::move(leaf0)); + + // Parent of the leaf is the root + BOOST_CHECK(leaf0Ptr->parent == root.get()); + + std::ofstream fs("blueprint.dot"); + root->dotStream(fs); + fs.close(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index 62e534ef637..2f3c8768b60 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -1,3 +1,5 @@ +add_unittest(Blueprint BlueprintTests.cpp) +add_unittest(BlueprintHelper BlueprintHelperTests.cpp) add_unittest(CylindricalContainerBuilder CylindricalContainerBuilderTests.cpp) add_unittest(CylindricalDetectorHelper CylindricalDetectorHelperTests.cpp) add_unittest(GridAxisGenerators GridAxisGeneratorsTests.cpp) diff --git a/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp index 4345a2ec102..0aa5c815f76 100644 --- a/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp @@ -16,7 +16,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/Surface.hpp" #include -#include +#include #include #include diff --git a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp index b4223f5d433..74b3a848555 100644 --- a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp @@ -596,7 +596,7 @@ BOOST_AUTO_TEST_CASE(StepSizeSurface) { BOOST_CHECK_EQUAL(state.stepSize.value(), distance); // start with a different step size - state.stepSize.setValue(navDir * stepSize); + state.stepSize.setUser(navDir * stepSize); stepper.updateStepSize( state, target diff --git a/Tests/UnitTests/Core/Propagator/ConstrainedStepTests.cpp b/Tests/UnitTests/Core/Propagator/ConstrainedStepTests.cpp index 922f31747da..75b2e14856c 100644 --- a/Tests/UnitTests/Core/Propagator/ConstrainedStepTests.cpp +++ b/Tests/UnitTests/Core/Propagator/ConstrainedStepTests.cpp @@ -28,8 +28,7 @@ BOOST_AUTO_TEST_CASE(ConstrainedStepTest) { ConstrainedStep stepSize_p(0.25); // All of the types should be 0.25 now - BOOST_CHECK_EQUAL(stepSize_p.value(ConstrainedStep::accuracy), - std::numeric_limits::max()); + BOOST_CHECK_EQUAL(stepSize_p.accuracy(), std::numeric_limits::max()); BOOST_CHECK_EQUAL(stepSize_p.value(ConstrainedStep::actor), std::numeric_limits::max()); BOOST_CHECK_EQUAL(stepSize_p.value(ConstrainedStep::aborter), @@ -39,8 +38,8 @@ BOOST_AUTO_TEST_CASE(ConstrainedStepTest) { // Check the cast operation to double BOOST_CHECK_EQUAL(stepSize_p.value(), 0.25); - // now we update the accuracy - stepSize_p.update(0.1, ConstrainedStep::accuracy); + // now we set the accuracy + stepSize_p.setAccuracy(0.1); BOOST_CHECK_EQUAL(stepSize_p.value(), 0.1); // now we update the actor to smaller @@ -56,13 +55,12 @@ BOOST_AUTO_TEST_CASE(ConstrainedStepTest) { // now set two and update them stepSize_p.update(0.05, ConstrainedStep::user); - stepSize_p.update(0.03, ConstrainedStep::accuracy); + stepSize_p.setAccuracy(0.03); BOOST_CHECK_EQUAL(stepSize_p.value(), 0.03); // now we release the accuracy - to the highest available value - stepSize_p.release(ConstrainedStep::accuracy); - BOOST_CHECK_EQUAL(stepSize_p.value(ConstrainedStep::accuracy), - std::numeric_limits::max()); + stepSize_p.releaseAccuracy(); + BOOST_CHECK_EQUAL(stepSize_p.accuracy(), std::numeric_limits::max()); BOOST_CHECK_EQUAL(stepSize_p.value(), 0.05); } diff --git a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp index 1a6b8c1c5f1..2285b53c080 100644 --- a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp @@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { .closest(), navDir, false); CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps); - esState.stepSize.setValue(navDir * stepSize); + esState.stepSize.setUser(navDir * stepSize); es.updateStepSize(esState, targetSurface ->intersect(esState.geoContext, es.position(esState), diff --git a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp index 9f7f88cdb55..0aa37fe9c82 100644 --- a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp @@ -149,7 +149,7 @@ BOOST_DATA_TEST_CASE( auto initialLimit = pathLimit.internalLimit; detail::setupLoopProtection( - pState, pStepper, pathLimit, + pState, pStepper, pathLimit, false, *Acts::getDefaultLogger("LoopProt", Logging::INFO)); auto updatedLimit = diff --git a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp index 19bf75cb9a1..746fc4d6df9 100644 --- a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp @@ -13,7 +13,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" #include "Acts/EventData/GenericBoundTrackParameters.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/Geometry/GeometryContext.hpp" diff --git a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp index 95a33f88b83..f12dbcf2e9f 100644 --- a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp @@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { .closest(), navDir, false); CHECK_CLOSE_ABS(slsState.stepSize.value(), 2, 1e-6); - slsState.stepSize.setValue(navDir * stepSize); + slsState.stepSize.setUser(navDir * stepSize); sls.updateStepSize( slsState, targetSurface diff --git a/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp b/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp index 57ad2d176b4..636d2c2bc54 100644 --- a/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp @@ -16,7 +16,7 @@ #include "Acts/EventData/Charge.hpp" #include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackParameters.hpp" diff --git a/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp b/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp index b16b637bde8..fb54f97c26d 100644 --- a/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/Gx2fTests.cpp @@ -29,7 +29,6 @@ #include "Acts/Tests/CommonHelpers/MeasurementsCreator.hpp" #include "Acts/Tests/CommonHelpers/PredefinedMaterials.hpp" #include "Acts/Tests/CommonHelpers/TestSourceLink.hpp" -#include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/TrackFitting/GlobalChiSquareFitter.hpp" #include "Acts/Utilities/Logger.hpp" @@ -66,18 +65,6 @@ Acts::CurvilinearTrackParameters makeParameters( Acts::ParticleHypothesis::pion()); } -// Construct a straight-line propagator. -auto makeStraightPropagator(std::shared_ptr geo) { - Acts::Navigator::Config cfg{std::move(geo)}; - cfg.resolvePassive = false; - cfg.resolveMaterial = true; - cfg.resolveSensitive = true; - Acts::Navigator navigator(cfg); - Acts::StraightLineStepper stepper; - return Acts::Propagator( - stepper, std::move(navigator)); -} - static std::vector prepareSourceLinks( const std::vector& sourceLinks) { std::vector result; @@ -136,31 +123,24 @@ std::shared_ptr makeToyDetector( for (auto& sCfg : surfaceConfig) { CuboidVolumeBuilder::LayerConfig cfg; cfg.surfaceCfg = {sCfg}; + cfg.active = true; + cfg.envelopeX = {-0.1_mm, 0.1_mm}; + cfg.envelopeY = {-0.1_mm, 0.1_mm}; + cfg.envelopeZ = {-0.1_mm, 0.1_mm}; layerConfig.push_back(cfg); } - for (auto& cfg : layerConfig) { - cfg.surfaces = {}; - } - // Inner Volume - Build volume configuration CuboidVolumeBuilder::VolumeConfig volumeConfig; - volumeConfig.position = {nSurfaces / 2. * 1_m, 0., 0.}; - volumeConfig.length = {nSurfaces * 1_m, 1_m, 1_m}; + volumeConfig.length = {(nSurfaces + 1) * 1_m, 1_m, 1_m}; + volumeConfig.position = {volumeConfig.length.x() / 2, 0., 0.}; volumeConfig.layerCfg = layerConfig; volumeConfig.name = "Test volume"; - volumeConfig.volumeMaterial = - std::make_shared(makeBeryllium()); - - volumeConfig.layers.clear(); - for (auto& lay : volumeConfig.layerCfg) { - lay.active = true; - } // Outer volume - Build TrackingGeometry configuration CuboidVolumeBuilder::Config config; - config.position = {nSurfaces / 2. * 1_m, 0., 0.}; - config.length = {nSurfaces * 1_m, 1_m, 1_m}; + config.length = {(nSurfaces + 1) * 1_m, 1_m, 1_m}; + config.position = {volumeConfig.length.x() / 2, 0., 0.}; config.volumeCfg = {volumeConfig}; cvb.setConfig(config); @@ -203,8 +183,8 @@ BOOST_AUTO_TEST_CASE(NoFit) { detector.geometry = makeToyDetector(geoCtx, nSurfaces); auto parametersMeasurements = makeParameters(); - auto startParametersFit = makeParameters(0.1_m, 0.1_m, 0.1_m, 42_ns, - 10_degree, 80_degree, 1_GeV, 1_e); + auto startParametersFit = makeParameters(7_mm, 11_mm, 15_mm, 42_ns, 10_degree, + 80_degree, 1_GeV, 1_e); MeasurementResolution resPixel = {MeasurementType::eLoc01, {25_um, 50_um}}; MeasurementResolutionMap resolutions = { @@ -220,7 +200,7 @@ BOOST_AUTO_TEST_CASE(NoFit) { using Gx2Fitter = Experimental::Gx2Fitter; - Gx2Fitter Fitter(simPropagator, gx2fLogger->clone()); + Gx2Fitter fitter(simPropagator, gx2fLogger->clone()); const Surface* rSurface = ¶metersMeasurements.referenceSurface(); @@ -239,14 +219,14 @@ BOOST_AUTO_TEST_CASE(NoFit) { Acts::VectorMultiTrajectory{}}; // Fit the track - auto res = Fitter.fit(sourceLinks.begin(), sourceLinks.end(), + auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(), startParametersFit, gx2fOptions, tracks); BOOST_REQUIRE(res.ok()); auto& track = *res; BOOST_CHECK_EQUAL(track.tipIndex(), Acts::MultiTrajectoryTraits::kInvalid); - BOOST_CHECK(!track.hasReferenceSurface()); + BOOST_CHECK(track.hasReferenceSurface()); BOOST_CHECK_EQUAL(track.nMeasurements(), 0u); BOOST_CHECK_EQUAL(track.nHoles(), 0u); BOOST_CHECK_EQUAL(track.parameters(), startParametersFit.parameters()); @@ -269,9 +249,9 @@ BOOST_AUTO_TEST_CASE(Fit5Iterations) { ACTS_DEBUG("Go to propagator"); auto parametersMeasurements = makeParameters(); - auto startParametersFit = makeParameters(10_mm, 10_mm, 10_mm, 42_ns, - 10_degree, 80_degree, 1_GeV, 1_e); - // auto startParametersFit = parametersMeasurements; + auto startParametersFit = makeParameters(7_mm, 11_mm, 15_mm, 42_ns, 10_degree, + 80_degree, 1_GeV, 1_e); + // Context objects Acts::GeometryContext geoCtx; Acts::MagneticFieldContext magCtx; @@ -279,8 +259,6 @@ BOOST_AUTO_TEST_CASE(Fit5Iterations) { std::default_random_engine rng(42); MeasurementResolution resPixel = {MeasurementType::eLoc01, {25_um, 50_um}}; - // MeasurementResolution resStrip0 = {MeasurementType::eLoc0, {100_um}}; - // MeasurementResolution resStrip1 = {MeasurementType::eLoc1, {150_um}}; MeasurementResolutionMap resolutions = { {Acts::GeometryIdentifier().setVolume(0), resPixel}}; @@ -301,21 +279,14 @@ BOOST_AUTO_TEST_CASE(Fit5Iterations) { const Surface* rSurface = ¶metersMeasurements.referenceSurface(); - Navigator::Config cfg{detector.geometry}; - cfg.resolvePassive = false; - cfg.resolveMaterial = true; - cfg.resolveSensitive = true; - Navigator rNavigator(cfg); - // Configure propagation with deactivated B-field - auto bField = std::make_shared(Vector3(0., 0., 0.)); using RecoStepper = EigenStepper<>; - RecoStepper rStepper(bField); - using RecoPropagator = Propagator; - RecoPropagator rPropagator(rStepper, rNavigator); + const auto recoPropagator = + makeConstantFieldPropagator(detector.geometry, 0_T); + using RecoPropagator = decltype(recoPropagator); using Gx2Fitter = Experimental::Gx2Fitter; - Gx2Fitter Fitter(rPropagator, gx2fLogger->clone()); + Gx2Fitter fitter(recoPropagator, gx2fLogger->clone()); Experimental::Gx2FitterExtensions extensions; extensions.calibrator @@ -335,20 +306,20 @@ BOOST_AUTO_TEST_CASE(Fit5Iterations) { Acts::VectorMultiTrajectory{}}; // Fit the track - auto res = Fitter.fit(sourceLinks.begin(), sourceLinks.end(), + auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(), startParametersFit, gx2fOptions, tracks); BOOST_REQUIRE(res.ok()); auto& track = *res; - BOOST_CHECK_EQUAL(track.tipIndex(), Acts::MultiTrajectoryTraits::kInvalid); - BOOST_CHECK(!track.hasReferenceSurface()); - BOOST_CHECK_EQUAL(track.nMeasurements(), 0u); + BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1); + BOOST_CHECK(track.hasReferenceSurface()); + BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces); BOOST_CHECK_EQUAL(track.nHoles(), 0u); // We need quite coarse checks here, since on different builds // the created measurements differ in the randomness - BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -10., 6e0); - BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -10., 6e0); + BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0); + BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0); BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3); BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], M_PI / 2, 1e-3); BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1); @@ -372,9 +343,9 @@ BOOST_AUTO_TEST_CASE(MixedDetector) { ACTS_DEBUG("Go to propagator"); auto parametersMeasurements = makeParameters(); - auto startParametersFit = makeParameters(10_mm, 10_mm, 10_mm, 42_ns, - 10_degree, 80_degree, 1_GeV, 1_e); - // auto startParametersFit = parametersMeasurements; + auto startParametersFit = makeParameters(7_mm, 11_mm, 15_mm, 42_ns, 10_degree, + 80_degree, 1_GeV, 1_e); + // Context objects Acts::GeometryContext geoCtx; Acts::MagneticFieldContext magCtx; @@ -411,21 +382,14 @@ BOOST_AUTO_TEST_CASE(MixedDetector) { const Surface* rSurface = ¶metersMeasurements.referenceSurface(); - Navigator::Config cfg{detector.geometry}; - cfg.resolvePassive = false; - cfg.resolveMaterial = true; - cfg.resolveSensitive = true; - Navigator rNavigator(cfg); - // Configure propagation with deactivated B-field - auto bField = std::make_shared(Vector3(0., 0., 0.)); using RecoStepper = EigenStepper<>; - RecoStepper rStepper(bField); - using RecoPropagator = Propagator; - RecoPropagator rPropagator(rStepper, rNavigator); + const auto recoPropagator = + makeConstantFieldPropagator(detector.geometry, 0_T); + using RecoPropagator = decltype(recoPropagator); using Gx2Fitter = Experimental::Gx2Fitter; - Gx2Fitter Fitter(rPropagator, gx2fLogger->clone()); + Gx2Fitter fitter(recoPropagator, gx2fLogger->clone()); Experimental::Gx2FitterExtensions extensions; extensions.calibrator @@ -445,20 +409,20 @@ BOOST_AUTO_TEST_CASE(MixedDetector) { Acts::VectorMultiTrajectory{}}; // Fit the track - auto res = Fitter.fit(sourceLinks.begin(), sourceLinks.end(), + auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(), startParametersFit, gx2fOptions, tracks); BOOST_REQUIRE(res.ok()); auto& track = *res; - BOOST_CHECK_EQUAL(track.tipIndex(), Acts::MultiTrajectoryTraits::kInvalid); - BOOST_CHECK(!track.hasReferenceSurface()); - BOOST_CHECK_EQUAL(track.nMeasurements(), 0u); + BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1); + BOOST_CHECK(track.hasReferenceSurface()); + BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces); BOOST_CHECK_EQUAL(track.nHoles(), 0u); // We need quite coarse checks here, since on different builds // the created measurements differ in the randomness - BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -10., 6e0); - BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -10., 6e0); + BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0); + BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0); BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3); BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], M_PI / 2, 1e-3); BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1); diff --git a/Tests/UnitTests/Core/Utilities/FiniteStateMachineTests.cpp b/Tests/UnitTests/Core/Utilities/FiniteStateMachineTests.cpp index da8b9ae9272..4f24f9acd85 100644 --- a/Tests/UnitTests/Core/Utilities/FiniteStateMachineTests.cpp +++ b/Tests/UnitTests/Core/Utilities/FiniteStateMachineTests.cpp @@ -39,7 +39,7 @@ struct Disconnect {}; struct fsm : FiniteStateMachine { - fsm() : fsm_base(states::Disconnected{}){}; + fsm() : fsm_base(states::Disconnected{}) {} event_return on_event(const states::Disconnected& /*unused*/, const events::Connect& /*unused*/) { @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(Terminted) { struct fsm2 : FiniteStateMachine { - fsm2() : fsm_base(states::Disconnected{}){}; + fsm2() : fsm_base(states::Disconnected{}) {} event_return on_event(const states::Disconnected& /*unused*/, const events::Connect& /*unused*/, double f) { diff --git a/Tests/UnitTests/Core/Utilities/ResultTests.cpp b/Tests/UnitTests/Core/Utilities/ResultTests.cpp index 0a853d0f60f..e003278007a 100644 --- a/Tests/UnitTests/Core/Utilities/ResultTests.cpp +++ b/Tests/UnitTests/Core/Utilities/ResultTests.cpp @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(TestErrorCodes) { } struct NoCopy { - NoCopy(int i) : num(i){}; + NoCopy(int i) : num(i) {} NoCopy(const NoCopy&) = delete; NoCopy& operator=(const NoCopy&) = delete; NoCopy(NoCopy&&) = default; diff --git a/Tests/UnitTests/Plugins/CMakeLists.txt b/Tests/UnitTests/Plugins/CMakeLists.txt index 6bf5098f243..0bbeeaa5f51 100644 --- a/Tests/UnitTests/Plugins/CMakeLists.txt +++ b/Tests/UnitTests/Plugins/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory_if(Sycl ACTS_BUILD_PLUGIN_SYCL) add_subdirectory_if(TGeo ACTS_BUILD_PLUGIN_TGEO) add_subdirectory_if(EDM4hep ACTS_BUILD_PLUGIN_EDM4HEP) add_subdirectory_if(FpeMonitoring ACTS_BUILD_PLUGIN_FPEMON) +add_subdirectory_if(Podio ACTS_BUILD_PLUGIN_PODIO) diff --git a/Tests/UnitTests/Plugins/Podio/CMakeLists.txt b/Tests/UnitTests/Plugins/Podio/CMakeLists.txt new file mode 100644 index 00000000000..7ea2534c517 --- /dev/null +++ b/Tests/UnitTests/Plugins/Podio/CMakeLists.txt @@ -0,0 +1,3 @@ +set(unittest_extra_libraries ActsPluginPodio) +add_unittest(ConvertTrackPodio ConvertTrackPodioTest.cpp) +add_unittest(PodioTrackStateContainer PodioTrackStateContainerTest.cpp) diff --git a/Tests/UnitTests/Plugins/Podio/ConvertTrackPodioTest.cpp b/Tests/UnitTests/Plugins/Podio/ConvertTrackPodioTest.cpp new file mode 100644 index 00000000000..a94a894f396 --- /dev/null +++ b/Tests/UnitTests/Plugins/Podio/ConvertTrackPodioTest.cpp @@ -0,0 +1,306 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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 +#include +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Plugins/Podio/PodioTrackContainer.hpp" +#include "Acts/Plugins/Podio/PodioTrackStateContainer.hpp" +#include "Acts/Plugins/Podio/PodioUtil.hpp" +#include "Acts/Surfaces/AnnulusBounds.hpp" +#include "Acts/Surfaces/ConeSurface.hpp" +#include "Acts/Surfaces/ConvexPolygonBounds.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiamondBounds.hpp" +#include "Acts/Surfaces/DiscBounds.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/DiscTrapezoidBounds.hpp" +#include "Acts/Surfaces/EllipseBounds.hpp" +#include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Surfaces/PlanarBounds.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/StrawSurface.hpp" +#include "Acts/Surfaces/SurfaceBounds.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "ActsPodioEdm/Surface.h" +#include + +#include +#include +#include +#include +#include + +using namespace Acts; +using namespace Acts::UnitLiterals; +using namespace Acts::HashedStringLiteral; +BOOST_AUTO_TEST_SUITE(PodioTrackConversion) + +class NullHelper : public PodioUtil::ConversionHelper { + public: + std::optional surfaceToIdentifier( + const Surface& /*surface*/) const override { + return {}; + } + const Surface* identifierToSurface( + PodioUtil::Identifier /*identifier*/) const override { + return nullptr; + } + + SourceLink identifierToSourceLink( + PodioUtil::Identifier /*identifier*/) const override { + return SourceLink{0}; + } + + PodioUtil::Identifier sourceLinkToIdentifier( + const SourceLink& /*sourceLink*/) override { + return 0; + } +}; + +struct MapHelper : public NullHelper { + std::optional surfaceToIdentifier( + const Surface& surface) const override { + for (auto&& [id, srf] : surfaces) { + if (srf == &surface) { + return id; + } + } + return {}; + } + const Surface* identifierToSurface(PodioUtil::Identifier id) const override { + auto it = surfaces.find(id); + if (it == surfaces.end()) { + return nullptr; + } + + return it->second; + } + + std::unordered_map surfaces; +}; + +BOOST_AUTO_TEST_CASE(ConvertSurface) { + auto rBounds = std::make_shared(15, 20); + + auto trf = Transform3::Identity(); + trf.translation().setRandom(); + + auto free = Acts::Surface::makeShared(trf, rBounds); + + NullHelper helper; + auto surface = PodioUtil::convertSurfaceToPodio(helper, *free); + + auto free2 = PodioUtil::convertSurfaceFromPodio(helper, surface); + + Acts::GeometryContext gctx; + + BOOST_REQUIRE(free2); + BOOST_CHECK_EQUAL(free->type(), free2->type()); + BOOST_CHECK_EQUAL(free->bounds().type(), free2->bounds().type()); + BOOST_CHECK_EQUAL(free->center(gctx), free2->center(gctx)); + + const auto* rBounds2 = dynamic_cast(&free2->bounds()); + BOOST_REQUIRE_NE(rBounds2, nullptr); + + BOOST_CHECK_EQUAL(rBounds2->halfLengthX(), rBounds->halfLengthX()); + BOOST_CHECK_EQUAL(rBounds2->halfLengthY(), rBounds->halfLengthY()); + + // this could probably use some more complete checks +} + +BOOST_AUTO_TEST_CASE(ConvertTrack) { + auto rBounds = std::make_shared(15, 20); + auto trf = Transform3::Identity(); + trf.translation().setRandom(); + auto free = Acts::Surface::makeShared(trf, rBounds); + + MapHelper helper; + + auto refCov = BoundMatrix::Random().eval(); + + podio::Frame frame; + + { + Acts::MutablePodioTrackStateContainer tsc{helper}; + Acts::MutablePodioTrackContainer ptc{helper}; + ActsPodioEdm::TrackCollection& tracks = ptc.trackCollection(); + + Acts::TrackContainer tc{ptc, tsc}; + + BOOST_CHECK(!tc.hasColumn("int_column"_hash)); + BOOST_CHECK(!tc.hasColumn("float_column"_hash)); + tc.addColumn("int_column"); + tc.addColumn("float_column"); + BOOST_CHECK(tc.hasColumn("int_column"_hash)); + BOOST_CHECK(tc.hasColumn("float_column"_hash)); + + BOOST_CHECK_EQUAL(tc.size(), 0); + + auto t = tc.getTrack(tc.addTrack()); + BOOST_CHECK_EQUAL(t.tipIndex(), MultiTrajectoryTraits::kInvalid); + + BOOST_CHECK_EQUAL(tsc.size(), 0); + auto ts1 = t.appendTrackState(); + auto ts2 = t.appendTrackState(); + auto ts3 = t.appendTrackState(); + BOOST_CHECK_EQUAL(tsc.size(), 3); + BOOST_CHECK_EQUAL(ts1.index(), 0); + BOOST_CHECK_EQUAL(ts2.index(), 1); + BOOST_CHECK_EQUAL(ts3.index(), 2); + + BOOST_CHECK_EQUAL(t.nTrackStates(), 3); + BOOST_CHECK_EQUAL(t.tipIndex(), 2); + + BOOST_CHECK_EQUAL(tc.size(), 1); + + auto pTrack = tracks.at(0); + BOOST_CHECK_EQUAL(pTrack.data().tipIndex, 2); + + t.parameters() << 1, 2, 3, 4, 5, 6; + Eigen::Map pars{pTrack.data().parameters.data()}; + BoundVector bv; + bv << 1, 2, 3, 4, 5, 6; + BOOST_CHECK_EQUAL(pars, bv); + + t.covariance() = refCov; + + Eigen::Map cov{pTrack.data().covariance.data()}; + BOOST_CHECK_EQUAL(refCov, cov); + + t.nMeasurements() = 17; + BOOST_CHECK_EQUAL(pTrack.data().nMeasurements, 17); + + t.nHoles() = 34; + BOOST_CHECK_EQUAL(pTrack.data().nHoles, 34); + + t.chi2() = 882.3f; + BOOST_CHECK_EQUAL(pTrack.data().chi2, 882.3f); + + t.nDoF() = 9; + BOOST_CHECK_EQUAL(pTrack.data().ndf, 9); + + t.nOutliers() = 77; + BOOST_CHECK_EQUAL(pTrack.data().nOutliers, 77); + + t.nSharedHits() = 99; + BOOST_CHECK_EQUAL(pTrack.data().nSharedHits, 99); + + Acts::GeometryContext gctx; + t.setReferenceSurface(free); + const auto& free2 = t.referenceSurface(); + BOOST_CHECK_EQUAL(free->center(gctx), free2.center(gctx)); + + const auto* rBounds2 = + dynamic_cast(&free2.bounds()); + BOOST_REQUIRE_NE(rBounds2, nullptr); + + BOOST_CHECK_EQUAL(rBounds2->halfLengthX(), rBounds->halfLengthX()); + BOOST_CHECK_EQUAL(rBounds2->halfLengthY(), rBounds->halfLengthY()); + + BOOST_CHECK_EQUAL(pTrack.getReferenceSurface().identifier, + PodioUtil::kNoIdentifier); + + auto t2 = tc.getTrack(tc.addTrack()); + auto t3 = tc.getTrack(tc.addTrack()); + BOOST_CHECK_EQUAL(tc.size(), 3); + + // Register surface "with the detector" + helper.surfaces[666] = free.get(); + t2.setReferenceSurface(free); + auto pTrack2 = tracks.at(1); + BOOST_CHECK_EQUAL(pTrack2.getReferenceSurface().identifier, 666); + + t.component() = -11; + t2.component() = 42; + t3.component() = -98; + + t.component() = -11.2f; + t2.component() = 42.4f; + t3.component() = -98.9f; + + ptc.releaseInto(frame); + tsc.releaseInto(frame); + + BOOST_REQUIRE_NE(frame.get("tracks"), nullptr); + BOOST_CHECK_EQUAL(frame.get("tracks")->size(), 3); + BOOST_REQUIRE_NE(frame.get("tracks_extra__int_column"), nullptr); + BOOST_REQUIRE_NE(frame.get("tracks_extra__float_column"), nullptr); + + BOOST_REQUIRE_NE(frame.get("trackStates"), nullptr); + BOOST_CHECK_EQUAL(frame.get("trackStates")->size(), 3); + } + + { + Acts::ConstPodioTrackStateContainer tsc{helper, frame}; + Acts::ConstPodioTrackContainer ptc{helper, frame}; + // const ActsPodioEdm::TrackCollection& tracks = ptc.trackCollection(); + + Acts::TrackContainer tc{ptc, tsc}; + + BOOST_CHECK(tc.hasColumn("int_column"_hash)); + BOOST_CHECK(tc.hasColumn("float_column"_hash)); + + BOOST_CHECK_EQUAL(tc.size(), 3); + + auto t = tc.getTrack(0); + const auto& freeRecreated = t.referenceSurface(); + // Not the exact same surface, it's recreated from values + BOOST_CHECK_NE(free.get(), &freeRecreated); + + BOOST_CHECK_EQUAL(t.nMeasurements(), 17); + + BOOST_CHECK_EQUAL(t.nHoles(), 34); + + BOOST_CHECK_EQUAL(t.chi2(), 882.3f); + + BOOST_CHECK_EQUAL(t.nDoF(), 9); + + BOOST_CHECK_EQUAL(t.nOutliers(), 77); + + BOOST_CHECK_EQUAL(t.nSharedHits(), 99); + + BOOST_CHECK_EQUAL(t.tipIndex(), 2); + BOOST_CHECK_EQUAL(t.nTrackStates(), 3); + + auto t2 = tc.getTrack(1); + // Is the exact same surface, because it's looked up in the "detector" + BOOST_CHECK_EQUAL(free.get(), &t2.referenceSurface()); + BoundVector bv; + bv << 1, 2, 3, 4, 5, 6; + BOOST_CHECK_EQUAL(t.parameters(), bv); + + BOOST_CHECK_EQUAL(t.covariance(), refCov); + + auto t3 = tc.getTrack(2); + BOOST_CHECK(!t3.hasReferenceSurface()); + + BOOST_CHECK_EQUAL((t.component()), -11); + BOOST_CHECK_EQUAL((t2.component()), 42); + BOOST_CHECK_EQUAL((t3.component()), -98); + + BOOST_CHECK_EQUAL((t.component()), -11.2f); + BOOST_CHECK_EQUAL((t2.component()), 42.4f); + BOOST_CHECK_EQUAL((t3.component()), -98.9f); + } +} + +// @TODO: Add ensure dynamic columns + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/Podio/PodioTrackStateContainerTest.cpp b/Tests/UnitTests/Plugins/Podio/PodioTrackStateContainerTest.cpp new file mode 100644 index 00000000000..929d3661494 --- /dev/null +++ b/Tests/UnitTests/Plugins/Podio/PodioTrackStateContainerTest.cpp @@ -0,0 +1,333 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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 +#include +#include + +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/EventData/TrackStatePropMask.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Plugins/Podio/PodioTrackStateContainer.hpp" +#include "Acts/Plugins/Podio/PodioUtil.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Tests/CommonHelpers/MultiTrajectoryTestsCommon.hpp" +#include "ActsPodioEdm/BoundParametersCollection.h" +#include "ActsPodioEdm/JacobianCollection.h" +#include "ActsPodioEdm/TrackStateCollection.h" + +#include + +#include +#include +#include + +namespace { + +using namespace Acts; +using namespace Acts::UnitLiterals; +using namespace Acts::Test; +namespace bd = boost::unit_test::data; + +std::default_random_engine rng(31415); + +class NullHelper : public PodioUtil::ConversionHelper { + public: + std::optional surfaceToIdentifier( + const Surface& /*surface*/) const override { + return {}; + } + const Surface* identifierToSurface( + PodioUtil::Identifier /*identifier*/) const override { + return nullptr; + } + + SourceLink identifierToSourceLink( + PodioUtil::Identifier /*identifier*/) const override { + return SourceLink{0}; + } + + PodioUtil::Identifier sourceLinkToIdentifier( + const SourceLink& /*sourceLink*/) override { + return 0; + } +}; + +struct MapHelper : public NullHelper { + std::optional surfaceToIdentifier( + const Surface& surface) const override { + for (auto&& [id, srf] : surfaces) { + if (srf == &surface) { + return id; + } + } + return {}; + } + const Surface* identifierToSurface(PodioUtil::Identifier id) const override { + auto it = surfaces.find(id); + if (it == surfaces.end()) { + return nullptr; + } + + return it->second; + } + + PodioUtil::Identifier sourceLinkToIdentifier(const SourceLink& sl) override { + sourceLinks.push_back(sl); + return sourceLinks.size() - 1; + } + + SourceLink identifierToSourceLink(PodioUtil::Identifier id) const override { + return sourceLinks.at(id); + } + + std::unordered_map surfaces; + std::vector sourceLinks; +}; + +struct Factory { + using trajectory_t = MutablePodioTrackStateContainer; + using const_trajectory_t = ConstPodioTrackStateContainer; + + // list used to have stable addresses, irrelevant for testing + std::list m_collections; + std::list m_params; + std::list m_jacs; + MapHelper m_helper; + + MutablePodioTrackStateContainer create() { return {m_helper}; } +}; + +using CommonTests = MultiTrajectoryTestsCommon; + +} // namespace + +BOOST_AUTO_TEST_SUITE(PodioTrackStateContainerTest) + +BOOST_AUTO_TEST_CASE(Build) { + CommonTests ct; + ct.testBuild(); +} + +BOOST_AUTO_TEST_CASE(ConstCorrectness) { + // @TODO: Const version can only be non-owning! +} + +BOOST_AUTO_TEST_CASE(Clear) { + CommonTests ct; + ct.testClear(); +} + +BOOST_AUTO_TEST_CASE(ApplyWithAbort) { + CommonTests ct; + ct.testApplyWithAbort(); +} + +BOOST_AUTO_TEST_CASE(AddTrackStateWithBitMask) { + CommonTests ct; + ct.testAddTrackStateWithBitMask(); +} + +// assert expected "cross-talk" between trackstate proxies +BOOST_AUTO_TEST_CASE(TrackStateProxyCrossTalk) { + CommonTests ct; + ct.testTrackStateProxyCrossTalk(rng); +} + +BOOST_AUTO_TEST_CASE(TrackStateReassignment) { + CommonTests ct; + ct.testTrackStateReassignment(rng); +} + +BOOST_DATA_TEST_CASE(TrackStateProxyStorage, bd::make({1u, 2u}), + nMeasurements) { + CommonTests ct; + ct.testTrackStateProxyStorage(rng, nMeasurements); +} + +BOOST_AUTO_TEST_CASE(TrackStateProxyAllocations) { + CommonTests ct; + ct.testTrackStateProxyAllocations(rng); +} + +BOOST_AUTO_TEST_CASE(TrackStateProxyGetMask) { + CommonTests ct; + ct.testTrackStateProxyGetMask(); +} + +BOOST_AUTO_TEST_CASE(TrackStateProxyCopy) { + CommonTests ct; + ct.testTrackStateProxyCopy(rng); +} + +BOOST_AUTO_TEST_CASE(TrackStateProxyCopyDiffMTJ) { + CommonTests ct; + ct.testTrackStateProxyCopyDiffMTJ(); +} + +BOOST_AUTO_TEST_CASE(ProxyAssignment) { + CommonTests ct; + ct.testProxyAssignment(); +} + +BOOST_AUTO_TEST_CASE(CopyFromConst) { + CommonTests ct; + ct.testCopyFromConst(); +} + +BOOST_AUTO_TEST_CASE(TrackStateProxyShare) { + CommonTests ct; + ct.testTrackStateProxyShare(rng); +} + +BOOST_AUTO_TEST_CASE(MultiTrajectoryExtraColumns) { + CommonTests ct; + ct.testMultiTrajectoryExtraColumns(); +} + +BOOST_AUTO_TEST_CASE(MultiTrajectoryExtraColumnsRuntime) { + CommonTests ct; + ct.testMultiTrajectoryExtraColumnsRuntime(); +} + +BOOST_AUTO_TEST_CASE(WriteToPodioFrame) { + using namespace HashedStringLiteral; + + MapHelper helper; + + // auto tmp_path = std::filesystem::temp_directory_path(); + auto tmp_path = std::filesystem::current_path(); + auto outfile = tmp_path / "trackstates.root"; + + BoundVector tv1; + tv1 << 1, 1, 1, 1, 1, 1; + + BoundVector tv2 = tv1 * 2; + BoundVector tv3 = tv1 * 3; + BoundVector tv4 = tv1 * 4; + + BoundMatrix cov1; + cov1.setOnes(); + + BoundMatrix cov2 = cov1 * 2; + BoundMatrix cov3 = cov1 * 3; + BoundMatrix cov4 = cov1 * 4; + + auto rBounds = std::make_shared(15, 20); + auto trf = Transform3::Identity(); + trf.translation().setRandom(); + auto free = Acts::Surface::makeShared(trf, rBounds); + auto reg = Acts::Surface::makeShared(trf, rBounds); + + helper.surfaces[666] = reg.get(); + + podio::Frame frame; + + MutablePodioTrackStateContainer c{helper}; + BOOST_CHECK(!c.hasColumn("int_column"_hash)); + BOOST_CHECK(!c.hasColumn("float_column"_hash)); + c.addColumn("int_column"); + c.addColumn("float_column"); + BOOST_CHECK(c.hasColumn("int_column"_hash)); + BOOST_CHECK(c.hasColumn("float_column"_hash)); + + { + auto t1 = c.getTrackState(c.addTrackState(TrackStatePropMask::Predicted)); + t1.predicted() = tv1; + t1.predictedCovariance() = cov1; + + t1.setReferenceSurface(free); + + auto t2 = + c.getTrackState(c.addTrackState(TrackStatePropMask::All, t1.index())); + t2.predicted() = tv2; + t2.predictedCovariance() = cov2; + + t2.filtered() = tv3; + t2.filteredCovariance() = cov3; + + t2.smoothed() = tv4; + t2.smoothedCovariance() = cov4; + + t2.jacobian() = cov2; + + auto t3 = c.getTrackState(c.addTrackState()); + t3.setReferenceSurface(reg); + + t1.component() = -11; + t2.component() = 42; + t3.component() = -98; + + t1.component() = -11.2f; + t2.component() = 42.4f; + t3.component() = -98.9f; + } + + c.releaseInto(frame, "test"); + + BOOST_CHECK_EQUAL(frame.get("trackStates_test")->size(), 3); + BOOST_CHECK_EQUAL(frame.get("trackStateParameters_test")->size(), 7); + BOOST_CHECK_EQUAL(frame.get("trackStateJacobians_test")->size(), 2); + BOOST_CHECK_NE(frame.get("trackStates_test_extra__int_column"), nullptr); + BOOST_CHECK_NE(frame.get("trackStates_test_extra__float_column"), nullptr); + + ConstPodioTrackStateContainer cc{helper, frame, "test"}; + + BOOST_CHECK_EQUAL(cc.size(), 3); + BOOST_CHECK(cc.hasColumn("int_column"_hash)); + BOOST_CHECK(cc.hasColumn("float_column"_hash)); + + auto t1 = cc.getTrackState(0); + auto t2 = cc.getTrackState(1); + auto t3 = cc.getTrackState(2); + + BOOST_CHECK_EQUAL(t2.previous(), 0); + + BOOST_CHECK(t1.hasReferenceSurface()); + BOOST_CHECK(!t2.hasReferenceSurface()); + BOOST_CHECK(t3.hasReferenceSurface()); + + Acts::GeometryContext gctx; + + const auto& ext = t1.referenceSurface(); + BOOST_CHECK_NE(&ext, free.get()); + BOOST_CHECK_EQUAL(trf.matrix(), ext.transform(gctx).matrix()); + BOOST_CHECK_EQUAL(free->bounds().type(), ext.bounds().type()); + BOOST_CHECK_EQUAL(free->type(), ext.type()); + const auto* rBounds2 = dynamic_cast(&ext.bounds()); + BOOST_REQUIRE_NE(rBounds2, nullptr); + BOOST_CHECK_EQUAL(rBounds->halfLengthX(), rBounds2->halfLengthX()); + BOOST_CHECK_EQUAL(rBounds->halfLengthY(), rBounds2->halfLengthY()); + + BOOST_CHECK_EQUAL(t1.predicted(), tv1); + BOOST_CHECK_EQUAL(t1.predictedCovariance(), cov1); + + BOOST_CHECK_EQUAL(t2.predicted(), tv2); + BOOST_CHECK_EQUAL(t2.predictedCovariance(), cov2); + BOOST_CHECK_EQUAL(t2.filtered(), tv3); + BOOST_CHECK_EQUAL(t2.filteredCovariance(), cov3); + BOOST_CHECK_EQUAL(t2.smoothed(), tv4); + BOOST_CHECK_EQUAL(t2.smoothedCovariance(), cov4); + + BOOST_CHECK_EQUAL(t2.jacobian(), cov2); + + BOOST_CHECK_EQUAL(&t3.referenceSurface(), reg.get()); + + BOOST_CHECK_EQUAL((t1.component()), -11); + BOOST_CHECK_EQUAL((t2.component()), 42); + BOOST_CHECK_EQUAL((t3.component()), -98); + + BOOST_CHECK_EQUAL((t1.component()), -11.2f); + BOOST_CHECK_EQUAL((t2.component()), 42.4f); + BOOST_CHECK_EQUAL((t3.component()), -98.9f); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/Sycl/Seeding/SpacePoint.hpp b/Tests/UnitTests/Plugins/Sycl/Seeding/SpacePoint.hpp index dc128f77eb4..f268bbef709 100644 --- a/Tests/UnitTests/Plugins/Sycl/Seeding/SpacePoint.hpp +++ b/Tests/UnitTests/Plugins/Sycl/Seeding/SpacePoint.hpp @@ -20,7 +20,7 @@ struct SpacePoint { m_x(p_x), m_y(p_y), m_z(p_z), - m_r(p_r){}; + m_r(p_r) {} float x() const { return m_x; } float y() const { return m_y; } diff --git a/cmake/ActsConfig.cmake.in b/cmake/ActsConfig.cmake.in index 7c93bdecbb1..78cbaa08ffa 100644 --- a/cmake/ActsConfig.cmake.in +++ b/cmake/ActsConfig.cmake.in @@ -73,6 +73,12 @@ endif() if(PluginActSVG IN_LIST Acts_COMPONENTS) find_dependency(actsvg @actsvg_VERSION@ CONFIG EXACT) endif() +if(PluginEDM4hep IN_LIST Acts_COMPONENTS) + find_dependency(EDM4HEP @EDM4HEP_VERSION@ CONFIG EXACT) +endif() +if(PluginPodio IN_LIST Acts_COMPONENTS) + find_dependency(podio @podio_VERSION@ CONFIG EXACT) +endif() # dependencies that we have built ourselves but cannot be # straightforwardly handed to cmake @@ -90,6 +96,10 @@ if(PluginSycl IN_LIST Acts_COMPONENTS) find_package(SYCL REQUIRED) endif() +if(PluginPodio IN_LIST Acts_COMPONENTS) + include(${CMAKE_CURRENT_LIST_DIR}/ActsPodioEdmTargets.cmake) +endif() + # load **all** available components. we can not just include the requested # components since there can be interdependencies between them. if(NOT Acts_FIND_QUIETLY) @@ -102,3 +112,4 @@ foreach(_component ${Acts_COMPONENTS}) # include the targets file to create the imported targets for the user include(${CMAKE_CURRENT_LIST_DIR}/Acts${_component}Targets.cmake) endforeach() + diff --git a/docs/getting_started.md b/docs/getting_started.md index 19c9a4381f2..6775cd9e19f 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -273,6 +273,7 @@ components. | ACTS_BUILD_PLUGIN_ACTSVG | Build SVG display plugin
type: `bool`, default: `OFF` | | ACTS_BUILD_PLUGIN_CUDA | Build CUDA plugin
type: `bool`, default: `OFF` | | ACTS_BUILD_PLUGIN_DD4HEP | Build DD4hep plugin
type: `bool`, default: `OFF` | +| ACTS_BUILD_PLUGIN_PODIO | Build Podio plugin
type: `bool`, default: `OFF` | | ACTS_BUILD_PLUGIN_EDM4HEP | Build EDM4hep plugin
type: `bool`, default: `OFF` | | ACTS_BUILD_PLUGIN_FPEMON | Build FPE monitoring plugin
type: `bool`, default: `OFF` | | ACTS_BUILD_PLUGIN_GEANT4 | Build Geant4 plugin
type: `bool`, default: `OFF` |