diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e84044776a8..59ba76bb00a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -38,51 +38,6 @@ jobs: - name: Run pre-commit run: pre-commit run --all-files --show-diff-on-failure - license: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Check - run: > - sudo apt-get install -y git - && CI/check_license.py . --exclude "*thirdparty/*" - include_guards: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Check - run: > - CI/check_include_guards.py . --fail-global --exclude "*thirdparty/*" - pragma_once: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check - run: > - CI/check_pragma_once.sh - type_t: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Check - run: > - CI/check_type_t.py . --exclude "thirdparty/*" - boost_test_macro: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check - run: > - CI/check_boost_test_macro.sh smearing_config: runs-on: ubuntu-latest steps: @@ -93,39 +48,7 @@ jobs: - name: Check run: > CI/check_smearing_config.py . - cmake_options: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Check - run: > - docs/parse_cmake_options.py CMakeLists.txt --write docs/getting_started.md --verify - spelling: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install codespell - run: > - pip install codespell==2.2.5 - - name: Check - run: > - CI/check_spelling - math_macros: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Check - run: > - CI/check_math_macros.py . --exclude "thirdparty/*" + missing_includes: runs-on: ubuntu-latest steps: @@ -136,6 +59,7 @@ jobs: - name: Check run: > CI/missing_include_check.sh + fpe_masks: runs-on: ubuntu-latest steps: @@ -149,6 +73,7 @@ jobs: - name: Check run: > CI/check_fpe_masks.py --token ${{ secrets.GITHUB_TOKEN }} + unused_files: runs-on: ubuntu-latest steps: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c897c947010..25aff340111 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -142,33 +142,49 @@ build_exatrkx: - cmake --build build -- -j6 - ccache -s -# test_exatrkx_unittests: -# stage: test -# needs: -# - build_exatrkx -# image: ghcr.io/acts-project/ubuntu2204_exatrkx:63 -# tags: -# - docker-gpu-nvidia -# script: -# - ctest --test-dir build -R ExaTrkX -# -# test_exatrkx_python: -# stage: test -# needs: -# - build_exatrkx -# image: ghcr.io/acts-project/ubuntu2204_exatrkx:63 -# tags: -# - docker-gpu-nvidia -# script: -# - apt-get update -y -# - apt-get install -y python3 libxxhash0 -# - source build/this_acts_withdeps.sh -# - git clone $CLONE_URL src -# - cd src -# - git checkout $HEAD_SHA -# - pip3 install -r Examples/Python/tests/requirements.txt -# - nvidia-smi -# - pytest -rFsv -k test_exatrkx +test_exatrkx_unittests: + stage: test + needs: + - build_exatrkx + image: ghcr.io/acts-project/ubuntu2204_exatrkx:63 + variables: + DEPENDENCY_URL: https://acts.web.cern.ch/ACTS/ci/ubuntu-22.04/deps.$DEPENDENCY_TAG.tar.zst + tags: + - docker-gpu-nvidia + script: + + - apt-get update -y + - git clone $CLONE_URL src + - cd src + - git checkout $HEAD_SHA + - source CI/dependencies.sh + - cd .. + - ctest --test-dir build -R ExaTrkX + +test_exatrkx_python: + stage: test + needs: + - build_exatrkx + image: ghcr.io/acts-project/ubuntu2204_exatrkx:63 + variables: + DEPENDENCY_URL: https://acts.web.cern.ch/ACTS/ci/ubuntu-22.04/deps.$DEPENDENCY_TAG.tar.zst + tags: + - docker-gpu-nvidia + script: + - apt-get update -y + - git clone $CLONE_URL src + - cd src + - git checkout $HEAD_SHA + - nvidia-smi + - source CI/dependencies.sh + - source ../build/this_acts_withdeps.sh + - python3 -m pip install -r Examples/Python/tests/requirements.txt + - echo $PYTHONPATH + - which python3 + - python3 --version + - python3 -c "import acts" + - pytest -rFsv -k torch --collect-only + - pytest -rFsv -k gpu-torch # For now only test torch GPU pipeline build_linux_ubuntu: stage: build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4158678a3e0..d4c224bf3c7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,8 @@ repos: - id: codespell args: [ "-S", "*.ipynb,*.onnx,_build,*.svg", - "-I", "./CI/codespell_ignore.txt" + "-I", "./CI/codespell_ignore.txt", + "-w" ] exclude: ^CI/.*$ diff --git a/CI/codespell_ignore.txt b/CI/codespell_ignore.txt index 13a3328440f..e6ab8123413 100644 --- a/CI/codespell_ignore.txt +++ b/CI/codespell_ignore.txt @@ -5,14 +5,12 @@ coner dthe iself sortings -fime gaus te parm writet localy lastr -digitial exprot pring aline diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index c2944f0dd06..8482f92c4b6 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -265,6 +265,15 @@ function trackfinding() { $path/performance_finding_ckf_ambi.html \ $path/performance_finding_ckf_ambi fi + + if [ -f $refdir/$path/performance_finding_ckf_ml_solver.root ]; then + run_histcmp \ + $outdir/data/$path/performance_finding_ckf_ml_solver.root \ + $refdir/$path/performance_finding_ckf_ml_solver.root \ + "ML Ambisolver | ${name}" \ + $path/performance_finding_ckf_ml_solver.html \ + $path/performance_finding_ckf_ml_solver + fi } function vertexing() { diff --git a/CI/physmon/physmon_common.py b/CI/physmon/physmon_common.py index 32c056e2820..a451dee943b 100644 --- a/CI/physmon/physmon_common.py +++ b/CI/physmon/physmon_common.py @@ -33,7 +33,9 @@ def makeSetup() -> PhysmonSetup: level=acts.logging.INFO, ) - detector, trackingGeometry, decorators = getOpenDataDetector(matDeco) + detector = getOpenDataDetector(matDeco) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() setup = PhysmonSetup( detector=detector, trackingGeometry=trackingGeometry, diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_finding_ckf_ml_solver.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_finding_ckf_ml_solver.root new file mode 100644 index 00000000000..db22a916d9e Binary files /dev/null and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_finding_ckf_ml_solver.root differ diff --git a/CI/physmon/reference/trackfinding_ttbar_pu200/performance_fitting_ckf_ml_solver.root b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_fitting_ckf_ml_solver.root new file mode 100644 index 00000000000..0a40506d1bb Binary files /dev/null and b/CI/physmon/reference/trackfinding_ttbar_pu200/performance_fitting_ckf_ml_solver.root differ diff --git a/CI/physmon/workflows/physmon_trackfinding_1muon.py b/CI/physmon/workflows/physmon_trackfinding_1muon.py index 90305112099..028b30ce5e5 100755 --- a/CI/physmon/workflows/physmon_trackfinding_1muon.py +++ b/CI/physmon/workflows/physmon_trackfinding_1muon.py @@ -74,7 +74,7 @@ def run_ckf_tracking(label, seeding): rnd=rnd, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) diff --git a/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py b/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py index 1a119c8ed6c..85debced2b4 100755 --- a/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py +++ b/CI/physmon/workflows/physmon_trackfinding_4muon_50vertices.py @@ -71,7 +71,7 @@ rnd=rnd, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) diff --git a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py index 99f12d0170d..d26e5e78873 100755 --- a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py +++ b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py @@ -19,7 +19,9 @@ CkfConfig, addCKFTracks, addAmbiguityResolution, + addAmbiguityResolutionML, AmbiguityResolutionConfig, + AmbiguityResolutionMLConfig, addVertexFitting, VertexFinder, TrackSelectorConfig, @@ -33,9 +35,10 @@ with tempfile.TemporaryDirectory() as temp: + # Running with a single thread to avoid rance conditions with Pythia8, see https://github.com/acts-project/acts/issues/3963 s = acts.examples.Sequencer( events=3, - numThreads=-1, + numThreads=1, # run with single thread logLevel=acts.logging.INFO, ) @@ -69,7 +72,7 @@ ), postSelectParticles=ParticleSelectorConfig( pt=(0.5 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) @@ -134,6 +137,17 @@ outputDirRoot=tp, ) + addAmbiguityResolutionML( + s, + AmbiguityResolutionMLConfig( + maximumSharedHits=3, maximumIterations=1000000, nMeasurementsMin=6 + ), + tracks="ckf_tracks", + outputDirRoot=tp, + onnxModelFile=Path(__file__).resolve().parent.parent.parent.parent + / "thirdparty/OpenDataDetector/data/duplicateClassifier.onnx", + ) + addAmbiguityResolution( s, AmbiguityResolutionConfig( @@ -141,6 +155,7 @@ maximumIterations=100000, nMeasurementsMin=6, ), + tracks="ckf_tracks", outputDirRoot=tp, ) @@ -187,6 +202,17 @@ tp / "performance_fitting_ambi.root", tp / "performance_fitting_ckf_ambi.root", ) + + shutil.move( + tp / "performance_finding_ambiML.root", + tp / "performance_finding_ckf_ml_solver.root", + ) + + shutil.move( + tp / "performance_fitting_ambiML.root", + tp / "performance_fitting_ckf_ml_solver.root", + ) + for vertexing in ["amvf_gauss_notime", "amvf_grid_time"]: shutil.move( tp / f"{vertexing}/performance_vertexing.root", @@ -200,6 +226,8 @@ "performance_fitting_ckf.root", "performance_finding_ckf_ambi.root", "performance_fitting_ckf_ambi.root", + "performance_finding_ckf_ml_solver.root", + "performance_fitting_ckf_ml_solver.root", "performance_vertexing_amvf_gauss_notime.root", "performance_vertexing_amvf_grid_time.root", ]: diff --git a/Core/include/Acts/AmbiguityResolution/AmbiguityNetworkConcept.hpp b/Core/include/Acts/AmbiguityResolution/AmbiguityNetworkConcept.hpp new file mode 100644 index 00000000000..a69e1fee9fe --- /dev/null +++ b/Core/include/Acts/AmbiguityResolution/AmbiguityNetworkConcept.hpp @@ -0,0 +1,51 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackContainerFrontendConcept.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/EventData/VectorTrackContainer.hpp" +#include "Acts/Utilities/Concepts.hpp" + +namespace Acts { + +/// @brief Concept for the ambiguity network used in the ambiguity resolution +/// +/// The ambiguity network correspond to the AmbiguityTrackClassifier found in +/// the Onnx plugin. It is used to score the tracks and select the best ones. +/// +/// The constructor of the Ambiguity Solver network should take string as input +/// corresponding to the path of the ONNX model. +/// The implementation of the Ambiguity Solver network should have two methods: +/// - inferScores: takes clusters (a list of track ID associated with a cluster +/// ID) and the track container and return an outputTensor (list of scores for +/// each track in the clusters). +/// - trackSelection: Takes clusters and the output tensor from the inferScores +/// method and return the list of track ID to keep. +/// +/// @tparam N the type of the network +template +concept AmbiguityNetworkConcept = requires( + TrackContainer &tracks, + std::unordered_map> &clusters, + std::vector> &outputTensor, const char *modelPath, + network_t &n) { + { network_t(modelPath) } -> std::same_as; + + { + n.inferScores(clusters, tracks) + } -> std::same_as>>; + { + n.trackSelection(clusters, outputTensor) + } -> std::same_as>; +}; + +} // namespace Acts diff --git a/Core/include/Acts/AmbiguityResolution/AmbiguityResolutionML.hpp b/Core/include/Acts/AmbiguityResolution/AmbiguityResolutionML.hpp new file mode 100644 index 00000000000..66717e3f8ee --- /dev/null +++ b/Core/include/Acts/AmbiguityResolution/AmbiguityResolutionML.hpp @@ -0,0 +1,136 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/AmbiguityResolution/AmbiguityNetworkConcept.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include +#include +#include +#include + +namespace Acts { + +/// Generic implementation of the machine learning ambiguity resolution +/// Contains method for data preparations +template +class AmbiguityResolutionML { + public: + struct Config { + /// Path to the model file for the duplicate neural network + std::string inputDuplicateNN = ""; + /// Minimum number of measurement to form a track. + std::size_t nMeasurementsMin = 7; + }; + /// Construct the ambiguity resolution algorithm. + /// + /// @param cfg is the algorithm configuration + /// @param logger is the logging instance + AmbiguityResolutionML(const Config& cfg, + std::unique_ptr logger = getDefaultLogger( + "AmbiguityResolutionML", Logging::INFO)) + : m_cfg{cfg}, + m_duplicateClassifier(m_cfg.inputDuplicateNN.c_str()), + m_logger{std::move(logger)} {} + + /// Associate the hits to the tracks + /// + /// This algorithm performs the mapping of hits ID to track ID. Our final goal + /// is too loop over all the tracks (and their associated hits) by order of + /// decreasing number hits for this we use a multimap where the key is the + /// number of hits as this will automatically perform the sorting. + /// + /// @param tracks is the input track container + /// @param sourceLinkHash is the hash function for the source link, will be used to associate to tracks + /// @param sourceLinkEquality is the equality function for the source link used used to associated hits to tracks + /// @return an ordered list containing pairs of track ID and associated measurement ID + template + std::multimap>> + mapTrackHits(const track_container_t& tracks, + const source_link_hash_t& sourceLinkHash, + const source_link_equality_t& sourceLinkEquality) const { + // A map to store (and generate) the measurement index for each source link + auto measurementIndexMap = + std::unordered_map(0, sourceLinkHash, + sourceLinkEquality); + + // A map to store the track Id and their associated measurements ID, a + // multimap is used to automatically sort the tracks by the number of + // measurements + std::multimap>> + trackMap; + std::size_t trackIndex = 0; + std::vector measurements; + // Loop over all the trajectories in the events + for (const auto& track : tracks) { + // Kick out tracks that do not fulfill our initial requirements + if (track.nMeasurements() < m_cfg.nMeasurementsMin) { + continue; + } + measurements.clear(); + for (auto ts : track.trackStatesReversed()) { + if (ts.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) { + SourceLink sourceLink = ts.getUncalibratedSourceLink(); + // assign a new measurement index if the source link was not seen yet + auto emplace = measurementIndexMap.try_emplace( + sourceLink, measurementIndexMap.size()); + measurements.push_back(emplace.first->second); + } + } + trackMap.emplace(track.nMeasurements(), + std::make_pair(trackIndex, measurements)); + ++trackIndex; + } + return trackMap; + } + + /// Select the track associated with each cluster + /// + /// In this algorithm the call the neural network to score the tracks and then + /// select the track with the highest score in each cluster + /// + /// @param clusters is a map of clusters, each cluster correspond to a vector of track ID + /// @param tracks is the input track container + /// @return a vector of trackID corresponding tho the good tracks + template + std::vector solveAmbiguity( + std::unordered_map>& clusters, + const track_container_t& tracks) const { + std::vector> outputTensor = + m_duplicateClassifier.inferScores(clusters, tracks); + std::vector goodTracks = + m_duplicateClassifier.trackSelection(clusters, outputTensor); + + return goodTracks; + } + + private: + // Configuration + Config m_cfg; + + // The neural network for duplicate classification, the network + // implementation is chosen with the AmbiguityNetwork template parameter + AmbiguityNetwork m_duplicateClassifier; + + /// Logging instance + std::unique_ptr m_logger = nullptr; + + /// Private access to logging instance + const Logger& logger() const { return *m_logger; } +}; + +} // namespace Acts diff --git a/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp index cf932fc0470..5e079af0df7 100644 --- a/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp +++ b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp @@ -129,11 +129,11 @@ class MultiComponentBoundTrackParameters { /// Get the weight and a GenericBoundTrackParameters object for one component std::pair operator[](std::size_t i) const { - return std::make_pair( + return { std::get(m_components[i]), Parameters(m_surface, std::get(m_components[i]), std::get>(m_components[i]), - m_particleHypothesis)); + m_particleHypothesis)}; } /// Parameters vector. diff --git a/Core/include/Acts/Geometry/Blueprint.hpp b/Core/include/Acts/Geometry/Blueprint.hpp new file mode 100644 index 00000000000..db96654218e --- /dev/null +++ b/Core/include/Acts/Geometry/Blueprint.hpp @@ -0,0 +1,105 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" + +namespace Acts { + +class GeometryContext; + +/// This class is the top-level entry point to build a tracking geometry using +/// the blueprint building mechanism. It forms the root of a tree of nodes where +/// each node performs a portion of the construction. This top-level class has +/// the main construction methods that execute the construction of the geometry. +/// +/// ``` +/// +---------------+ +-----------+ +/// | | | | +/// | Root | | v +/// | | | +---------------+ +/// +---------------+ | | | +/// | | | Child 1 | +/// +----------+ | | | +/// v +----------+ +---------------+ +/// +---------------+ | +/// | | +--------------+ +/// | Child 2 | v +----------+ +/// | | .---------. | | +/// +---------------+ / \ | v +/// ( Proc node ) | +---------------+ +/// `. ,' | | | +/// `-------' | | Child 3 | +/// | | | | +/// | | +---------------+ +/// +---------+ +/// ``` +/// +/// The construction phases are documented in @c BlueprintNode, which is the +/// base class for all nodes in the tree. +/// @note This class inherits from @c BlueprintNode, but hides the main +/// blueprint construction phase overloads. The @c Blueprint class is +/// only ever intended to be the top-level node, and not anywhere else +/// in the tree. +class Blueprint : public BlueprintNode { + public: + struct Config { + /// Determine how much envelope space to produce from the highest volume + /// in the geometry hierarchy and the world volume. + ExtentEnvelope envelope = ExtentEnvelope::Zero(); + + /// The geometry identifier hook, passed through the `TrackingGeometry` + /// constructor. This will be superseded by a more integrated approach to + /// the identification scheme + GeometryIdentifierHook geometryIdentifierHook = {}; + }; + + /// Constructor from a config object + /// @param config The configuration object + explicit Blueprint(const Config& config); + + /// Construct the tracking geometry from the blueprint tree + /// @param options The construction options, see @c BlueprintOptions + /// @param gctx The geometry context for construction. In almost all cases, + /// this should be the *nominal* geometry context + /// @param logger The logger to use for output during construction + std::unique_ptr construct( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()); + + protected: + /// The name of the blueprint node, always "Root" + /// @return The name + const std::string& name() const override; + + /// @copydoc BlueprintNode::build + Volume& build(const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// @copydoc BlueprintNode::connect + PortalShellBase& connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// @copydoc BlueprintNode::finalize + void finalize(const BlueprintOptions& options, const GeometryContext& gctx, + TrackingVolume& parent, + const Logger& logger = Acts::getDummyLogger()) override; + + /// @copydoc BlueprintNode::addToGraphviz + void addToGraphviz(std::ostream& os) const override; + + private: + Config m_cfg; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/BlueprintNode.hpp b/Core/include/Acts/Geometry/BlueprintNode.hpp new file mode 100644 index 00000000000..e159c463067 --- /dev/null +++ b/Core/include/Acts/Geometry/BlueprintNode.hpp @@ -0,0 +1,295 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/BlueprintOptions.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/NavigationPolicyFactory.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/TransformRange.hpp" + +#include +#include +#include +#include + +namespace Acts { + +class Volume; +class TrackingVolume; +class VolumeBounds; +class PortalShellBase; +class CylinderContainerBlueprintNode; +class MaterialDesignatorBlueprintNode; +class StaticBlueprintNode; +class LayerBlueprintNode; + +/// Base class for all nodes in the blueprint tree. This class defines the +/// three-phase construction process. The three phases are +/// +/// -# **Build**: Construct volume representation + compute final sizing +/// -# **Connect**: Create and connect portals at volume boundaries +/// -# **Finalize**: Register portals with volumes + create acceleration +/// structures +/// +/// During the *build* phase, the `build` method of all nodes in the tree are +/// called recursively. Some nodes, like @ref Acts::CylinderContainerBlueprintNode, +/// will take action on the volumes returns from its children, and perform +/// sizing to connect them. See the @ref Acts::CylinderContainerBlueprintNode and @ref +/// Acts::CylinderVolumeStack documentation for details on how the sizing is +/// carried out. +class BlueprintNode { + public: + /// Virtual destructor to ensure correct cleanup + virtual ~BlueprintNode() = default; + + /// Get the name of this node + virtual const std::string& name() const = 0; + + /// @anchor construction + /// @name Construction methods + /// These methods constitute the primary interface of the node that + /// participates in the geometry construction. + /// @{ + + /// This method is called during the *build* phase of the blueprint tree + /// construction. It returns a single @ref Acts::Volume which represents transform + /// and bounds of the entire subtree. This does not have to correspond to the + /// final @ref Acts::TrackingVolume, some node types will produce temporary volume + /// representations. Lifetime of the returned volume is managed by the source + /// node! + /// Nodes higher in the hierarchy will issue resizes down the tree hierarchy. + /// This is not done through a direct hierarchy, but coordinated by the + /// respective node type, by internally consulting its children. + /// + /// @note Generally, you should not need to to call this method directly. + /// The construction should usually be done through the special + /// @ref Acts::Blueprint class. + /// + /// @param options The global construction options + /// @param gctx The geometry context for construction (usually nominal) + /// @param logger The logger to use for output during construction + /// @return The volume used for communicating transform and size up the hierarchy + virtual Volume& build(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) = 0; + + /// This method is called during the *connect* phase. This phase handles the + /// creation and connection of *portals* (instances of @ref Acts::PortalLinkBase). + /// After the build-phase has completed, the volume sizes are **final**. Each + /// node will consult its fully sized volume to produce *boundary surfaces*. + /// Each boundary surface is then turned into a @ref Acts::TrivialPortalLink, which + /// in turn produces a one-sided portal (see @ref Acts::Portal documentation) + /// + /// Some nodes (like @ref Acts::CylinderContainerBlueprintNode) will take action on + /// their children, and unify the connected portals. + /// + /// After a node's processing has completed, it returns a reference to a @ref + /// Acts::PortalShellBase, which represents a set of portals in a specific + /// geometry arrangement. The returned object lifetime is managed by the + /// returning node. + /// + /// @param options The global construction options + /// @param gctx The geometry context for construction (usually nominal) + /// @param logger The logger to use for output during construction + virtual PortalShellBase& connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) = 0; + + /// This method is called during the *finalize* phase. This phase handles: + /// + /// - Registering portals into their final volumes + /// - Registering volumes into their parents + /// - Creating navigation policies + /// - (In future) Handle geometry identification assignment + /// + /// At the end of this phase, each node will have transferred any temporary + /// resources created during the build, that need to be retained, into the + /// final @ref Acts::TrackingGeometry, and can be safely destroyed. + /// + /// @note The @p parent for volumes, portals, etc to be registered in is passed in **as an + /// argument**, rather than being implicitly determined from the + /// **parent node**. This is done so that nodes can remove themselves + /// from the final volume hierarchy, like container nodes or the + /// @ref Acts::MaterialDesignatorBlueprintNode. + /// + /// @param options The global construction options + /// @param gctx The geometry context for construction (usually nominal) + /// @param parent The parent volume to register in + /// @param logger The logger to use for output during construction + virtual void finalize(const BlueprintOptions& options, + const GeometryContext& gctx, TrackingVolume& parent, + const Logger& logger = Acts::getDummyLogger()) = 0; + + /// @} + + /// @anchor convenience + /// @name Convenience methods + /// These methods are meant to make the construction of a blueprint tree in + /// code more ergonomic. + /// They usually take an optional `callback` parameter. The primary use for + /// this parameter is structural, as it facilitates introducing scopes to + /// indicate in code that objects are nested. + /// + /// ```cpp + /// Blueprint::Config cfg; + /// auto root = std::make_unique(cfg); + /// root->addStaticVolume( + /// base, std::make_shared(50_mm, 400_mm, 1000_mm), + /// "PixelWrapper", [&](auto& wrapper) { + /// // This scope can be used to equip `wrapper` + /// }); + /// ``` + /// + /// Alternatively, they can also be used without a callback, as the newly + /// created node is also returned by reference: + /// + /// ``` + /// auto& wrapper = root->addStaticVolume( + /// base, std::make_shared(50_mm, 400_mm, 1000_mm), + /// "PixelWrapper"); + /// ``` + /// + /// In both cases, it's not necessary to register the newly created node + /// with a parent node. + /// + /// @{ + + /// This method creates a new @ref Acts::StaticBlueprintNode wrapping @p + /// volume and adds it to this node as a child. + /// @param volume The volume to add + /// @param callback An optional callback that receives the node as an argument + /// @return A reference to the created node + StaticBlueprintNode& addStaticVolume( + std::unique_ptr volume, + const std::function& callback = {}); + + /// Alternative overload for creating a @ref Acts::StaticBlueprintNode. This + /// overload will invoke the constructor of @ref Acts::TrackingVolume and use + /// that volume to create the node. + /// @param transform The transform of the volume + /// @param volumeBounds The bounds of the volume + /// @param volumeName The name of the volume + /// @param callback An optional callback that receives the node as an argument + StaticBlueprintNode& addStaticVolume( + const Transform3& transform, std::shared_ptr volumeBounds, + const std::string& volumeName = "undefined", + const std::function& callback = {}); + + /// Convenience method for creating a @ref Acts::CylinderContainerBlueprintNode. + /// @param name The name of the container node. This name is only reflected + /// in the node tree for debugging, as no extra volumes is created + /// for the container. + /// @param direction The direction of the stack configuration. See + /// @ref Acts::CylinderVolumeStack for details. + /// @param callback An optional callback that receives the node as an argument + CylinderContainerBlueprintNode& addCylinderContainer( + const std::string& name, BinningValue direction, + const std::function& + callback = {}); + + /// Convenience method for creating a @ref Acts::MaterialDesignatorBlueprintNode. + /// @param name The name of the material designator node. Used for debugging + /// the node tree only. + /// @param callback An optional callback that receives the node as an argument + MaterialDesignatorBlueprintNode& addMaterial( + const std::string& name, + const std::function& + callback = {}); + + /// Convenience method for creating a @ref Acts::LayerBlueprintNode. + /// @param name The name of the layer node. + /// @param callback An optional callback that receives the node as an argument + LayerBlueprintNode& addLayer( + const std::string& name, + const std::function& callback = {}); + + /// @} + + /// Register a @p child to this node. + /// @warning This method throws if adding the child would create a + /// cycle in the blueprint tree! + /// @param child The child node to add + /// @return A reference this node (not the child!) + BlueprintNode& addChild(std::shared_ptr child); + + /// A range-like object that allows range based for loops and index access. + /// This type's iterators and accessors return mutable references when + /// dereferenced. + using MutableChildRange = + detail::TransformRange>>; + + /// A range-like object that allows range based for loops and index access. + /// This type's iterators and accessors return const references when + /// dereferenced. + using ChildRange = + detail::TransformRange>>; + + /// Return a @ref MutableChildRange to the children of this node. + /// @return A range-like object to the children + MutableChildRange children(); + + /// Return a @ref ChildRange to the children of this node. + /// @return A range-like object to the children + ChildRange children() const; + + /// Remove all children from this node + void clearChildren(); + + /// Return the depth of this node in the blueprint tree. A depth of zero means + /// this node does not have a parent. + /// @return The depth of this node + std::size_t depth() const; + + /// Print the node tree starting from this node to graphviz format + /// @param os The stream to print to + void graphviz(std::ostream& os) const; + + /// Method that writes a representatiohn of **this node only** to graphviz. + /// This should generally not be called on its own, but through the @ref + /// BlueprintNode::graphviz method. + /// @param os The stream to print to + virtual void addToGraphviz(std::ostream& os) const; + + /// Print a representation of this node to the stream + /// @param os The stream to print to + /// @param node The node to print + /// @return The output stream + friend std::ostream& operator<<(std::ostream& os, const BlueprintNode& node) { + node.toStream(os); + return os; + } + + protected: + /// Virtual method to determine stream representation. + /// @note This method is called by the stream operator. + virtual void toStream(std::ostream& os) const; + + /// Set the depth to @p depth and update children recursively + void setDepth(std::size_t depth); + + /// Printing helper returning a prefix including an indent depending on the + /// depth. + /// @return The prefix string + std::string prefix() const; + + /// An indentation depending on the depth of this node. + /// @return The indentation string + std::string indent() const; + + private: + std::size_t m_depth{0}; + std::vector> m_children{}; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/BlueprintOptions.hpp b/Core/include/Acts/Geometry/BlueprintOptions.hpp new file mode 100644 index 00000000000..305ff5d399d --- /dev/null +++ b/Core/include/Acts/Geometry/BlueprintOptions.hpp @@ -0,0 +1,28 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/NavigationPolicyFactory.hpp" + +#include + +namespace Acts { + +struct BlueprintOptions { + std::shared_ptr defaultNavigationPolicyFactory{ + makeDefaultNavigationPolicyFactory()}; + + void validate() const; + + private: + static std::unique_ptr + makeDefaultNavigationPolicyFactory(); +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/CompositePortalLink.hpp b/Core/include/Acts/Geometry/CompositePortalLink.hpp index 6586d875b79..231af047b6c 100644 --- a/Core/include/Acts/Geometry/CompositePortalLink.hpp +++ b/Core/include/Acts/Geometry/CompositePortalLink.hpp @@ -23,6 +23,7 @@ class Surface; /// Composite portal links can graft together other portal link instances, for /// example grids that could not be merged due to invalid binnings. /// +/// ``` /// +-------+ +-------+ /// | | | | /// | | | | @@ -36,6 +37,7 @@ class Surface; /// | | +-------+ /// | | | | /// +-------+ +-------+ +/// ``` /// /// During resolution, it will consult each of it's children and return /// the result on the first surface where the lookup position is within diff --git a/Core/include/Acts/Geometry/CylinderContainerBlueprintNode.hpp b/Core/include/Acts/Geometry/CylinderContainerBlueprintNode.hpp new file mode 100644 index 00000000000..637f1594c0c --- /dev/null +++ b/Core/include/Acts/Geometry/CylinderContainerBlueprintNode.hpp @@ -0,0 +1,157 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include + +namespace Acts { + +/// This class handles the case of wrapping a set of cylinder-shaped children +/// and stacking them in a configured direction. +/// The stacking is done using @ref CylinderVolumeStack. +/// The container does not result in an extra volume in the hierarchy, as all +/// input volumes and any gap volumes produced are directly registered in the +/// volume of the parent of this node. +/// @note This node assumes all children produce only cylinder volumes! It throws +/// if this is not the case. +class CylinderContainerBlueprintNode final : public BlueprintNode { + public: + /// Main constructor for the cylinder container node. + /// @param name The name of the node (for debug only) + /// @param direction The stacking direction + /// @param attachmentStrategy The attachment strategy for the stack + /// @param resizeStrategy The resize strategy + /// @note The parameters are passed through to @ref CylinderVolumeStack, + /// see documentation of that class for more information + CylinderContainerBlueprintNode( + const std::string& name, BinningValue direction, + CylinderVolumeStack::AttachmentStrategy attachmentStrategy = + CylinderVolumeStack::AttachmentStrategy::Midpoint, + CylinderVolumeStack::ResizeStrategy resizeStrategy = + CylinderVolumeStack::ResizeStrategy::Expand); + + /// @copydoc BlueprintNode::name + const std::string& name() const override; + + /// This participates in the construction of the geometry via the blueprint + /// tree. The steps are approximately as follows: + /// -# Collect all child volumes + /// -# Package them into a @ref Acts::CylinderVolumeStack, which performs + /// sizing and/or gap creation + /// -# Return the @ref Acts::CylinderVolumeStack as a volume up the tree + /// + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param logger The logger to use + /// @return The combined @ref Acts::CylinderVolumeStack + Volume& build(const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// This participates in the construction of the geometry via the blueprint + /// tree. The steps are approximately as follows: + /// -# Walk through all volumes that were created by the build phase + /// -# Check if they are: *real* child volumes or gap volumes + /// - If gap volume: produce a @ref Acts::TrackingVolume, and wrap it in a single use shell + /// - If child volume: locate the right child node it came from, call + /// ` connect` and collect the returned shell + /// -# Produce a combined @ref Acts::CylinderStackPortalShell from all the shells + /// -# Return that shell representation + /// + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param logger The logger to use + /// @return The combined @ref Acts::CylinderStackPortalShell + CylinderStackPortalShell& connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// This participates in the construction of the geometry via the blueprint + /// tree. The steps are approximately as follows: + /// -# Register portals created for gap volumes, as they're not handled by + /// dedicated nodes + /// -# Register gap volumes in the @p parent volume + /// -# Create a configured @ref Acts::INavigationPolicy for the gap + /// -# Call `finalize` on all children while passing through @p parent. + /// + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param parent The parent volume + /// @param logger The logger to use + void finalize(const BlueprintOptions& options, const GeometryContext& gctx, + TrackingVolume& parent, const Logger& logger) override; + + /// Setter for the stacking direction + /// @param direction The stacking direction + /// @return This node for chaining + CylinderContainerBlueprintNode& setDirection(BinningValue direction); + + /// Setter for the attachment strategy + /// @param attachmentStrategy The attachment strategy + /// @return This node for chaining + CylinderContainerBlueprintNode& setAttachmentStrategy( + CylinderVolumeStack::AttachmentStrategy attachmentStrategy); + + /// Setter for the resize strategy + /// @param resizeStrategy The resize strategy + /// @return This node for chaining + CylinderContainerBlueprintNode& setResizeStrategy( + CylinderVolumeStack::ResizeStrategy resizeStrategy); + + /// Accessor to the stacking direction + /// @return The stacking direction + BinningValue direction() const; + + /// Accessor to the attachment strategy + /// @return The attachment strategy + CylinderVolumeStack::AttachmentStrategy attachmentStrategy() const; + + /// Accessor to the resize strategy + /// @return The resize strategy + CylinderVolumeStack::ResizeStrategy resizeStrategy() const; + + private: + /// @copydoc BlueprintNode::addToGraphviz + void addToGraphviz(std::ostream& os) const override; + + /// Helper function to check if a volume was created as a gap volume. + /// @param volume The volume to check + /// @return True if the volume is a gap volume, false otherwise + bool isGapVolume(const Volume& volume) const; + + std::vector collectChildShells( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger); + + std::string m_name; + + BinningValue m_direction = BinningValue::binZ; + + CylinderVolumeStack::AttachmentStrategy m_attachmentStrategy{ + CylinderVolumeStack::AttachmentStrategy::Midpoint}; + + CylinderVolumeStack::ResizeStrategy m_resizeStrategy{ + CylinderVolumeStack::ResizeStrategy::Expand}; + + // Is only initialized during `build` + std::vector m_childVolumes; + std::unique_ptr m_stack{nullptr}; + std::map m_volumeToNode; + std::vector, + std::unique_ptr>> + m_gaps; + std::unique_ptr m_shell{nullptr}; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/GridPortalLink.hpp b/Core/include/Acts/Geometry/GridPortalLink.hpp index 8bf1d06ee08..94e98867905 100644 --- a/Core/include/Acts/Geometry/GridPortalLink.hpp +++ b/Core/include/Acts/Geometry/GridPortalLink.hpp @@ -111,6 +111,7 @@ class GridPortalLink : public PortalLinkBase { /// /// 1D merge scenarios: /// + /// ``` /// +----------------------------------------------------------+ /// |Colinear | /// | | @@ -126,9 +127,11 @@ class GridPortalLink : public PortalLinkBase { /// | +-------+-------+-------+-------+-------+-------+ | /// | | /// +----------------------------------------------------------+ + /// ``` /// /// Two grid along a shared direction are merged along their shared direction /// + /// ``` /// +-------------------------------------------------+ /// |Parallel | /// | | @@ -147,10 +150,12 @@ class GridPortalLink : public PortalLinkBase { /// | +-------+ +-------+ +-------+-------+ | /// | | /// +-------------------------------------------------+ + /// ``` /// /// Two grids along a shared direction a merged in the direction that is /// orthogonal to their shared direction. /// + /// ``` /// +-------------------------------------------+ /// |Perpendicular | /// | | @@ -180,6 +185,7 @@ class GridPortalLink : public PortalLinkBase { /// | +-------+-------+-------+ | /// | | /// +-------------------------------------------+ + /// ``` /// /// Two grids whose directions are not shared are merged (ordering does not /// matter here). The routine will expand one of the grids to match the @@ -192,6 +198,7 @@ class GridPortalLink : public PortalLinkBase { /// side. The 1D grid is expanded to match the binning in the as-of-yet /// unbinned direction with the binning taken from the 2D grid. /// + /// ``` /// +-----------------------------------------+ /// |2D + 1D | /// | | @@ -215,7 +222,9 @@ class GridPortalLink : public PortalLinkBase { /// | | | | | /// | +-------+-------+ | /// +-----------------------------------------+ + /// ``` /// + /// ``` /// +--------------------------------------------------------------+ /// |2D + 1D | /// | | @@ -234,6 +243,7 @@ class GridPortalLink : public PortalLinkBase { /// | +-------+-------+ +-------+ +-------+-------+-------+ | /// | | /// +--------------------------------------------------------------+ + /// ``` /// /// 2D merges /// The grids need to already share a common axis. If that is not the case, @@ -241,6 +251,7 @@ class GridPortalLink : public PortalLinkBase { /// merging as a fallback if needed. /// Ordering and direction does not matter here. /// + /// ``` /// +-----------------------------------------+ /// |2D + 2D | /// | | @@ -273,6 +284,7 @@ class GridPortalLink : public PortalLinkBase { /// | +-------+-------+-------+-------+ | /// | | /// +-----------------------------------------+ + /// ``` /// /// @param a The first grid portal link /// @param b The second grid portal link diff --git a/Core/include/Acts/Geometry/LayerBlueprintNode.hpp b/Core/include/Acts/Geometry/LayerBlueprintNode.hpp new file mode 100644 index 00000000000..5eb2e747df8 --- /dev/null +++ b/Core/include/Acts/Geometry/LayerBlueprintNode.hpp @@ -0,0 +1,141 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/StaticBlueprintNode.hpp" + +#include + +namespace Acts { + +/// The layer node is essentially an auto-sizing wrapper around a set of +/// surfaces. +/// @note This implementation is **preliminary** and will likely change +/// in the future. +/// It defers most of the functionality to @ref Acts::StaticBlueprintNode, +/// after the initial volume creation is completed. +/// +/// The layer volume is created to wrap around the surfaces registered with +/// this node. The orientation of the resulting volume defaults to the identity +/// matrix. If another orientation is desired, this can be set with the @ref +/// Acts::LayerBlueprintNode::setTransform. See @ref Acts::ProtoLayer for +/// details on the auto-sizing from surfaces. +/// +class LayerBlueprintNode : public StaticBlueprintNode { + public: + /// Enum that lists out the supported layer types. + enum class LayerType { + /// A cylinder layer + Cylinder, + + /// A disc layer + Disc, + + /// A plane layer + /// @note This is not yet implemented + Plane + }; + + /// Constructor for a layer node. + /// @param name The name of the layer + explicit LayerBlueprintNode(const std::string& name) + : StaticBlueprintNode{nullptr}, m_name(name) {} + + /// @copydoc BlueprintNode::name + const std::string& name() const override; + + /// This function participates in the geometry construction. + /// It will: + /// -# Analyze the surfaces provided and produce a wrapping volume + /// -# Register the surfaces with the volume + /// -# Return the volume + /// @note At least one surfaces needs to be registered via + /// @ref Acts::LayerBlueprintNode::setSurfaces before + /// geometry construction. + Volume& build(const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// Register a set of surfaces with the layer node. + /// @param surfaces The surfaces to register + /// @return Reference to this node for chaining + LayerBlueprintNode& setSurfaces( + std::vector> surfaces); + + /// Access the registered surfaces. + /// @return The registered surfaces + const std::vector>& surfaces() const; + + /// Set the transformation of the layer node. + /// This can be used to specifically orient the resulting layer volume. + /// @param transform The transformation to set + /// @return Reference to this node for chaining + LayerBlueprintNode& setTransform(const Transform3& transform); + + /// Access the transformation of the layer node. + /// @return The transformation + const Transform3& transform() const; + + /// Set the envelope of the layer node. This configures the amount of space to + /// add around the contained surfaces. + /// @param envelope The envelope to set + /// @return Reference to this node for chaining + LayerBlueprintNode& setEnvelope(const ExtentEnvelope& envelope); + + /// Access the envelope of the layer node. + /// @return The envelope + const ExtentEnvelope& envelope() const; + + /// Set the layer type of the layer node. + /// @param layerType The layer type to set + /// @return Reference to this node for chaining + LayerBlueprintNode& setLayerType(LayerType layerType); + + /// Access the layer type of the layer node. + /// @return The layer type + const LayerType& layerType() const; + + /// Output operator for the layer type enum. + /// @param os The output stream + /// @param type The layer type + friend std::ostream& operator<<(std::ostream& os, + LayerBlueprintNode::LayerType type) { + switch (type) { + using enum LayerBlueprintNode::LayerType; + case Cylinder: + os << "Cylinder"; + break; + case Disc: + os << "Disc"; + break; + case Plane: + os << "Plane"; + break; + } + return os; + } + + private: + /// @copydoc Acts::BlueprintNode::addToGraphviz + void addToGraphviz(std::ostream& os) const override; + + /// Helper method that performs the volume creation from the configured + /// surfaces. It converts from an @p extent object to an instance of @ref + /// Acts::VolumeBounds. + /// @param extent The extent to use for the volume creation + /// @param logger The logger to use + void buildVolume(const Extent& extent, const Logger& logger); + + std::string m_name; + std::vector> m_surfaces{}; + Transform3 m_transform = Transform3::Identity(); + ExtentEnvelope m_envelope = ExtentEnvelope::Zero(); + LayerType m_layerType = LayerType::Cylinder; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/MaterialDesignatorBlueprintNode.hpp b/Core/include/Acts/Geometry/MaterialDesignatorBlueprintNode.hpp new file mode 100644 index 00000000000..dbb4663fe71 --- /dev/null +++ b/Core/include/Acts/Geometry/MaterialDesignatorBlueprintNode.hpp @@ -0,0 +1,101 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/ProtoBinning.hpp" +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/PortalShell.hpp" + +#include + +namespace Acts { + +/// This node type registers material proxies into its child volume during the +/// blueprint construction. It is configured ahead of time which volume faces to +/// mark up, and how do to so. +/// @note This node can only have a single child. This is not an error during +/// tree building, but during geometry construction. +/// @note This currently only supports a cylinder volume child +class MaterialDesignatorBlueprintNode final : public BlueprintNode { + public: + // @TODO: This needs cuboid volume storage as well + // @TODO: I don't love the type + using BinningConfig = std::variant>>; + + /// Main constructor for the material designator node. + /// @param name The name of the node (for debug only) + explicit MaterialDesignatorBlueprintNode(const std::string& name) + : m_name(name) {} + + /// @copydoc BlueprintNode::name + const std::string& name() const override; + + /// @copydoc BlueprintNode::toStream + void toStream(std::ostream& os) const override; + + /// This method participates in the geometry construction. + /// It checks that this node only has a single child, is correctly configured, + /// and forwards the call. + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param logger The logger to use + /// @return The child volume + Volume& build(const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// This method participates in the geometry construction. + /// It receives the populated portal shell from its only child and attaches + /// material proxies by consulting the configuration stored in the node. + /// @note Currently, this node will unconditionally attach + /// @ref Acts::ProtoGridSurfaceMaterial + /// + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param logger The logger to use + /// @return The portal shell with material proxies attached + PortalShellBase& connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// This method participates in the geometry construction. + /// Passes through the call to its only child. + /// @param options The global blueprint options + /// @param gctx The geometry context (nominal usually) + /// @param parent The parent volume + /// @param logger The logger to use during construction + void finalize(const BlueprintOptions& options, const GeometryContext& gctx, + TrackingVolume& parent, const Logger& logger) override; + + /// Retrieve the binning configuration + /// @return The binning configuration + const std::optional& binning() const; + + /// Set the binning configuration + /// @param binning The binning configuration + MaterialDesignatorBlueprintNode& setBinning(BinningConfig binning); + + private: + /// @copydoc BlueprintNode::addToGraphviz + void addToGraphviz(std::ostream& os) const override; + + void handleCylinderBinning( + CylinderPortalShell& cylShell, + const std::vector< + std::tuple>& binning, + const Logger& logger); + + std::string m_name{}; + + std::optional m_binning{}; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/Portal.hpp b/Core/include/Acts/Geometry/Portal.hpp index 722b433d036..3d1a065026f 100644 --- a/Core/include/Acts/Geometry/Portal.hpp +++ b/Core/include/Acts/Geometry/Portal.hpp @@ -110,6 +110,7 @@ class Portal { /// precision). The resulting portal will have one portal along the shared /// surface's normal vector, and one opposite that vector. /// + /// ``` /// portal1 portal2 /// +---+ +---+ /// | | | | @@ -118,6 +119,7 @@ class Portal { /// | | | | /// | | | | /// +---+ +---+ + /// ``` /// /// @note The input portals need to have compatible link loadaout, e.g. one /// portal needs to have the *along normal* slot filled, and the @@ -140,6 +142,7 @@ class Portal { /// relative to one another (e.g. one along one opposite), the function will /// throw an exception. /// + /// ``` /// ^ ^ /// | | /// portal1| portal2| @@ -149,6 +152,7 @@ class Portal { /// | | /// | | /// v v + /// ``` /// /// @note This is a destructive operation on both portals, their /// links will be moved to produce merged links, which can fail diff --git a/Core/include/Acts/Geometry/StaticBlueprintNode.hpp b/Core/include/Acts/Geometry/StaticBlueprintNode.hpp new file mode 100644 index 00000000000..f1b2b9cbd0a --- /dev/null +++ b/Core/include/Acts/Geometry/StaticBlueprintNode.hpp @@ -0,0 +1,67 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" + +namespace Acts { + +/// The static blueprint node wraps a single already-constructred @c TrackingVolume. +/// The node will present this volume to its hierarchy. The volume is given as +/// mutable, and will be potentially enlarged in order to connect to neighboring +/// volumes. +/// - In case the volume already has child volumes, they will be retained. +/// - In case the volume already has a registered navigation policy, it will be +/// overwritten with the one configured on this node, regardless of content. +class StaticBlueprintNode : public BlueprintNode { + public: + /// Construct the static node from an existing volume + /// @param volume The volume to wrap + explicit StaticBlueprintNode(std::unique_ptr volume); + + /// Get the name of this node. It is automatically taken from the wrapped + /// volume + /// @return The name of the volume + const std::string& name() const override; + + /// @copydoc BlueprintNode::build + /// Build-phase of the blueprint construction. Returns the wrapped volume for + /// sizing. + Volume& build(const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// @copydoc BlueprintNode::connect + PortalShellBase& connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger = Acts::getDummyLogger()) override; + + /// @copydoc BlueprintNode::finalize + void finalize(const BlueprintOptions& options, const GeometryContext& gctx, + TrackingVolume& parent, + const Logger& logger = Acts::getDummyLogger()) override; + + virtual StaticBlueprintNode& setNavigationPolicyFactory( + std::shared_ptr navigationPolicyFactory); + + const NavigationPolicyFactory* navigationPolicyFactory() const; + + protected: + void addToGraphviz(std::ostream& os) const override; + + std::unique_ptr m_volume; + + std::unique_ptr m_shell; + + std::shared_ptr m_navigationPolicyFactory; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Material/ProtoVolumeMaterial.hpp b/Core/include/Acts/Material/ProtoVolumeMaterial.hpp index 38206e8f6dc..dd302e31ccb 100644 --- a/Core/include/Acts/Material/ProtoVolumeMaterial.hpp +++ b/Core/include/Acts/Material/ProtoVolumeMaterial.hpp @@ -72,12 +72,11 @@ class ProtoVolumeMaterial : public IVolumeMaterial { Material m_material; }; -/// Return the material inline const Acts::Material Acts::ProtoVolumeMaterial::material( const Acts::Vector3& /*position*/) const { return m_material; } -/// Return the bin Utility + inline const Acts::BinUtility& Acts::ProtoVolumeMaterial::binUtility() const { return m_binUtility; } diff --git a/Core/include/Acts/Seeding/BinnedGroupIterator.ipp b/Core/include/Acts/Seeding/BinnedGroupIterator.ipp index 70cd691f5db..8bcd0ac04b8 100644 --- a/Core/include/Acts/Seeding/BinnedGroupIterator.ipp +++ b/Core/include/Acts/Seeding/BinnedGroupIterator.ipp @@ -64,7 +64,7 @@ Acts::BinnedGroupIterator::operator*() const { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overread" #endif - return std::make_tuple(std::move(bottoms), global_index, std::move(tops)); + return {std::move(bottoms), global_index, std::move(tops)}; #if defined(__GNUC__) && __GNUC__ >= 12 && !defined(__clang__) #pragma GCC diagnostic pop #endif diff --git a/Core/include/Acts/Seeding/PathSeeder.hpp b/Core/include/Acts/Seeding/PathSeeder.hpp index 87ceda365c8..f619f1a0ab4 100644 --- a/Core/include/Acts/Seeding/PathSeeder.hpp +++ b/Core/include/Acts/Seeding/PathSeeder.hpp @@ -108,8 +108,6 @@ class PathSeeder { /// @param gctx The geometry context /// @param sourceLinkGridLookup The lookup table for the source links /// @param seedCollection The collection of seeds to fill - /// - /// @return The vector of seeds template void findSeeds(const GeometryContext& gctx, const std::unordered_map& diff --git a/Core/include/Acts/Seeding/SeedFinder.ipp b/Core/include/Acts/Seeding/SeedFinder.ipp index e6ccb9f589f..b61947bb634 100644 --- a/Core/include/Acts/Seeding/SeedFinder.ipp +++ b/Core/include/Acts/Seeding/SeedFinder.ipp @@ -839,7 +839,7 @@ std::pair SeedFinder:: const external_spacepoint_t& spM, const Acts::Range1D& rMiddleSPRange) const { if (m_config.useVariableMiddleSPRange) { - return std::make_pair(rMiddleSPRange.min(), rMiddleSPRange.max()); + return {rMiddleSPRange.min(), rMiddleSPRange.max()}; } if (!m_config.rRangeMiddleSP.empty()) { /// get zBin position of the middle SP @@ -848,10 +848,9 @@ std::pair SeedFinder:: int zBin = std::distance(m_config.zBinEdges.begin(), pVal); /// protects against zM at the limit of zBinEdges zBin == 0 ? zBin : --zBin; - return std::make_pair(m_config.rRangeMiddleSP[zBin][0], - m_config.rRangeMiddleSP[zBin][1]); + return {m_config.rRangeMiddleSP[zBin][0], m_config.rRangeMiddleSP[zBin][1]}; } - return std::make_pair(m_config.rMinMiddle, m_config.rMaxMiddle); + return {m_config.rMinMiddle, m_config.rMaxMiddle}; } } // namespace Acts diff --git a/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp b/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp index 6bc565f80eb..644a0fa34fd 100644 --- a/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp +++ b/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp @@ -15,7 +15,15 @@ namespace Acts::detail { -/// Clusterise tracks based on shared hits +/// Cluster tracks based on shared hits. +/// +/// In this algorithm we will loop through all the tracks by decreasing number +/// of measurements. Cluster are created when a new track is encountered that +/// doesn't share hits with the leading track of a previous cluster (with the +/// leading track defined as the track that lead to the cluster creation). If a +/// track shares hits with the leading track of a cluster, it is added to that +/// cluster. If a track shares hits with multiple clusters, it is associated to +/// the cluster with the leading track with the most hits. /// /// @param trackMap : Multimap storing pair of track ID and vector of measurement ID. The keys are the number of measurement and are just there to facilitate the ordering. /// @return an unordered map representing the clusters, the keys the ID of the primary track of each cluster and the store a vector of track IDs. diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index dc0bc221e9a..5e46bc0dba3 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -361,6 +361,29 @@ struct Gx2fSystem { std::size_t m_ndf = 0u; }; +/// @brief Adds a measurement to the GX2F equation system in a modular backend function. +/// +/// This function processes measurement data and integrates it into the GX2F +/// system. +/// +/// @param extendedSystem All parameters of the current equation system to update. +/// @param jacobianFromStart The Jacobian matrix from the start to the current state. +/// @param covarianceMeasurement The covariance matrix of the measurement. +/// @param predicted The predicted state vector based on the track state. +/// @param measurement The measurement vector. +/// @param projector The projection matrix. +/// @param logger A logger instance. +/// +/// @note The dynamic Eigen matrices are suboptimal. We could think of +/// templating again in the future on kMeasDims. We currently use dynamic +/// matrices to reduce the memory during compile time. +void addMeasurementToGx2fSumsBackend( + Gx2fSystem& extendedSystem, + const std::vector& jacobianFromStart, + const Eigen::MatrixXd& covarianceMeasurement, const BoundVector& predicted, + const Eigen::VectorXd& measurement, const Eigen::MatrixXd& projector, + const Logger& logger); + /// @brief Process measurements and fill the aMatrix and bVector /// /// The function processes each measurement for the GX2F Actor fitting process. @@ -370,7 +393,7 @@ struct Gx2fSystem { /// @tparam kMeasDim Number of dimensions of the measurement /// @tparam track_state_t The type of the track state /// -/// @param extendedSystem All parameters of the current equation system +/// @param extendedSystem All parameters of the current equation system to update /// @param jacobianFromStart The Jacobian matrix from start to the current state /// @param trackState The track state to analyse /// @param logger A logger instance @@ -379,44 +402,9 @@ void addMeasurementToGx2fSums(Gx2fSystem& extendedSystem, const std::vector& jacobianFromStart, const track_state_t& trackState, const Logger& logger) { - // First we get back the covariance and try to invert it. If the inversion - // fails, we can already abort. const ActsSquareMatrix covarianceMeasurement = trackState.template calibratedCovariance(); - const auto safeInvCovMeasurement = safeInverse(covarianceMeasurement); - if (!safeInvCovMeasurement) { - ACTS_WARNING("addMeasurementToGx2fSums: safeInvCovMeasurement failed."); - ACTS_VERBOSE(" covarianceMeasurement:\n" << covarianceMeasurement); - return; - } - - // Create an extended Jacobian. This one contains only eBoundSize rows, - // because the rest is irrelevant. We fill it in the next steps. - // TODO make dimsExtendedParams template with unrolling - Eigen::MatrixXd extendedJacobian = - Eigen::MatrixXd::Zero(eBoundSize, extendedSystem.nDims()); - - // This part of the Jacobian comes from the material-less propagation - extendedJacobian.topLeftCorner() = - jacobianFromStart[0]; - - // If we have material, loop here over all Jacobians. We add extra columns for - // their phi-theta projections. These parts account for the propagation of the - // scattering angles. - for (std::size_t matSurface = 1; matSurface < jacobianFromStart.size(); - matSurface++) { - const BoundMatrix jac = jacobianFromStart[matSurface]; - - const ActsMatrix jacPhiTheta = - jac * Gx2fConstants::phiThetaProjector; - - // The position, where we need to insert the values in the extended Jacobian - const std::size_t deltaPosition = eBoundSize + 2 * (matSurface - 1); - - extendedJacobian.block(0, deltaPosition) = jacPhiTheta; - } - const BoundVector predicted = trackState.smoothed(); const ActsVector measurement = @@ -425,54 +413,9 @@ void addMeasurementToGx2fSums(Gx2fSystem& extendedSystem, const ActsMatrix projector = trackState.template projectorSubspaceHelper().projector(); - const Eigen::MatrixXd projJacobian = projector * extendedJacobian; - - const ActsMatrix projPredicted = projector * predicted; - - const ActsVector residual = measurement - projPredicted; - - // Finally contribute to chi2sum, aMatrix, and bVector - extendedSystem.chi2() += - (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0); - - extendedSystem.aMatrix() += - (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian) - .eval(); - - extendedSystem.bVector() += - (residual.transpose() * (*safeInvCovMeasurement) * projJacobian) - .eval() - .transpose(); - - ACTS_VERBOSE( - "Contributions in addMeasurementToGx2fSums:\n" - << " kMeasDim: " << kMeasDim << "\n" - << " predicted: " << predicted.transpose() << "\n" - << " measurement: " << measurement.transpose() << "\n" - << " covarianceMeasurement:\n" - << covarianceMeasurement << "\n" - << " projector:\n" - << projector.eval() << "\n" - << " projJacobian:\n" - << projJacobian.eval() << "\n" - << " projPredicted: " << (projPredicted.transpose()).eval() << "\n" - << " residual: " << (residual.transpose()).eval() << "\n" - << " extendedJacobian:\n" - << extendedJacobian << "\n" - << " aMatrix contribution:\n" - << (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian) - .eval() - << "\n" - << " bVector contribution: " - << (residual.transpose() * (*safeInvCovMeasurement) * projJacobian).eval() - << "\n" - << " chi2sum contribution: " - << (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0) - << "\n" - << " safeInvCovMeasurement:\n" - << (*safeInvCovMeasurement)); - - return; + addMeasurementToGx2fSumsBackend(extendedSystem, jacobianFromStart, + covarianceMeasurement, predicted, measurement, + projector, logger); } /// @brief Process material and fill the aMatrix and bVector @@ -694,6 +637,15 @@ std::size_t countMaterialStates( return nMaterialSurfaces; } +/// @brief Solve the gx2f system to get the delta parameters for the update +/// +/// This function computes the delta parameters for the GX2F Actor fitting +/// process by solving the linear equation system [a] * delta = b. It uses the +/// column-pivoting Householder QR decomposition for numerical stability. +/// +/// @param extendedSystem All parameters of the current equation system +Eigen::VectorXd computeGx2fDeltaParams(const Gx2fSystem& extendedSystem); + /// @brief Update parameters (and scattering angles if applicable) /// /// @param params Parameters to be updated @@ -1391,10 +1343,8 @@ class Gx2Fitter { return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements; } - // calculate delta params [a] * delta = b Eigen::VectorXd deltaParamsExtended = - extendedSystem.aMatrix().colPivHouseholderQr().solve( - extendedSystem.bVector()); + computeGx2fDeltaParams(extendedSystem); ACTS_VERBOSE("aMatrix:\n" << extendedSystem.aMatrix() << "\n" @@ -1558,10 +1508,8 @@ class Gx2Fitter { return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements; } - // calculate delta params [a] * delta = b Eigen::VectorXd deltaParamsExtended = - extendedSystem.aMatrix().colPivHouseholderQr().solve( - extendedSystem.bVector()); + computeGx2fDeltaParams(extendedSystem); ACTS_VERBOSE("aMatrix:\n" << extendedSystem.aMatrix() << "\n" diff --git a/Core/include/Acts/TrackFitting/detail/KalmanGlobalCovariance.hpp b/Core/include/Acts/TrackFitting/detail/KalmanGlobalCovariance.hpp index cf9a3f36b01..66dfe439fd8 100644 --- a/Core/include/Acts/TrackFitting/detail/KalmanGlobalCovariance.hpp +++ b/Core/include/Acts/TrackFitting/detail/KalmanGlobalCovariance.hpp @@ -95,7 +95,7 @@ globalTrackParametersCovariance(const traj_t& multiTraj, prev_ts = ts; }); - return std::make_pair(fullGlobalTrackParamsCov, stateRowIndices); + return {fullGlobalTrackParamsCov, stateRowIndices}; } } // namespace Acts::detail diff --git a/Core/include/Acts/Utilities/Delegate.hpp b/Core/include/Acts/Utilities/Delegate.hpp index a0ef2cf7252..8cac473abb9 100644 --- a/Core/include/Acts/Utilities/Delegate.hpp +++ b/Core/include/Acts/Utilities/Delegate.hpp @@ -50,7 +50,6 @@ class Delegate { /// Alias of the return type using return_type = R; using holder_type = H; - /// Alias to the function pointer type this class will store using function_type = return_type (*)(const holder_type *, Args...); using function_ptr_type = return_type (*)(Args...); using signature_type = R(Args...); @@ -81,12 +80,14 @@ class Delegate { Delegate(const Delegate &) noexcept = default; Delegate &operator=(const Delegate &) noexcept = default; + /// @cond /// Constructor with an explicit runtime callable /// @param callable The runtime value of the callable /// @note The function signature requires the first argument of the callable is `const void*`. /// i.e. if the signature of the delegate is `void(int)`, the /// callable's signature has to be `void(const void*, int)`. explicit Delegate(function_type callable) { connect(callable); } + /// @endcond /// Constructor with a possibly stateful function object. /// @tparam Callable Type of the callable @@ -129,6 +130,7 @@ class Delegate { requires(isNoFunPtr::value) = delete; + /// @cond /// Assignment operator with an explicit runtime callable /// @param callable The runtime value of the callable /// @note The function signature requires the first argument of the callable is `const void*`. @@ -147,6 +149,7 @@ class Delegate { { connect(callable); } + /// @endcond /// Assignment operator from rvalue reference is deleted, should catch /// assignment from temporary objects and thus invalid pointers @@ -155,6 +158,7 @@ class Delegate { requires(isNoFunPtr::value) = delete; + /// @cond /// Connect a free function pointer. /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it /// @tparam Callable The compile-time free function pointer @@ -175,6 +179,7 @@ class Delegate { return std::invoke(Callable, std::forward(args)...); }; } + /// @endcond /// Assignment operator with possibly stateful function object. /// @tparam Callable Type of the callable @@ -195,6 +200,7 @@ class Delegate { requires(isNoFunPtr::value) = delete; + /// @cond /// Connect anything that is assignable to the function pointer /// @param callable The runtime value of the callable /// @note The function signature requires the first argument of the callable is `const void*`. @@ -206,6 +212,7 @@ class Delegate { } m_function = callable; } + /// @endcond template void connect(function_type callable, const Type *instance) diff --git a/Core/include/Acts/Utilities/GridBinFinder.ipp b/Core/include/Acts/Utilities/GridBinFinder.ipp index 8b06748ffec..37db6082bb0 100644 --- a/Core/include/Acts/Utilities/GridBinFinder.ipp +++ b/Core/include/Acts/Utilities/GridBinFinder.ipp @@ -45,9 +45,9 @@ std::array, DIM> Acts::GridBinFinder::getSizePerAxis( using value_t = typename std::decay_t; if constexpr (std::is_same_v) { assert(val >= 0); - return std::make_pair(-val, val); + return {-val, val}; } else if constexpr (std::is_same_v, value_t>) { - return std::make_pair(-val.first, val.second); + return {-val.first, val.second}; } else { assert(locPosition.size() > i); assert(locPosition[i] > 0ul); diff --git a/Core/include/Acts/Utilities/Logger.hpp b/Core/include/Acts/Utilities/Logger.hpp index 83dc989ca85..b9e499b74f3 100644 --- a/Core/include/Acts/Utilities/Logger.hpp +++ b/Core/include/Acts/Utilities/Logger.hpp @@ -7,11 +7,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. #pragma once + // STL include(s) -#include #include -#include -#include #include #include #include diff --git a/Core/include/Acts/Utilities/Result.hpp b/Core/include/Acts/Utilities/Result.hpp index 9410fa0edfe..475019fc99b 100644 --- a/Core/include/Acts/Utilities/Result.hpp +++ b/Core/include/Acts/Utilities/Result.hpp @@ -30,6 +30,9 @@ class Result { Result(std::variant&& var) : m_var(std::move(var)) {} public: + using ValueType = T; + using ErrorType = E; + /// Default construction is disallowed. Result() = delete; @@ -172,6 +175,144 @@ class Result { return std::move(std::get(m_var)); } + /// Retrieves the valid value from the result object, or returns a default + /// value if no valid value exists. + /// + /// @param[in] v The default value to use if no valid value exists. + /// @note This is the lvalue version. + /// @note This function always returns by value. + /// @return Either the valid value, or the given substitute. + template + std::conditional_t, const T&, T> value_or(U&& v) const& + requires(std::same_as, T>) + { + if (ok()) { + return value(); + } else { + return std::forward(v); + } + } + + /// Retrieves the valid value from the result object, or returns a default + /// value if no valid value exists. + /// + /// @param[in] v The default value to use if no valid value exists. + /// @note This is the rvalue version which moves the value out. + /// @note This function always returns by value. + /// @return Either the valid value, or the given substitute. + template + T value_or(U&& v) && + requires(std::same_as, T>) + { + if (ok()) { + return std::move(*this).value(); + } else { + return std::forward(v); + } + } + + /// Transforms the value contained in this result. + /// + /// Applying a function `f` to a valid value `x` returns `f(x)`, while + /// applying `f` to an invalid value returns another invalid value. + /// + /// @param[in] callable The transformation function to apply. + /// @note This is the lvalue version. + /// @note This functions is `fmap` on the functor in `A` of `Result`. + /// @return The modified valid value if exists, or an error otherwise. + template + auto transform(C&& callable) const& + requires std::invocable + { + using CallableReturnType = decltype(std::declval()(std::declval())); + using R = Result, E>; + if (ok()) { + return R::success(callable(value())); + } else { + return R::failure(error()); + } + } + + /// Transforms the value contained in this result. + /// + /// Applying a function `f` to a valid value `x` returns `f(x)`, while + /// applying `f` to an invalid value returns another invalid value. + /// + /// @param[in] callable The transformation function to apply. + /// @note This is the rvalue version. + /// @note This functions is `fmap` on the functor in `A` of `Result`. + /// @return The modified valid value if exists, or an error otherwise. + template + auto transform(C&& callable) && + requires std::invocable + { + using CallableReturnType = decltype(std::declval()(std::declval())); + using R = Result, E>; + if (ok()) { + return R::success(callable(std::move(*this).value())); + } else { + return R::failure(std::move(*this).error()); + } + } + + /// Bind a function to this result monadically. + /// + /// This function takes a function `f` and, if this result contains a valid + /// value `x`, returns `f(x)`. If the type of `x` is `T`, then `f` is + /// expected to accept type `T` and return `Result`. In this case, + /// `transform` would return the unhelpful type `Result>`, so + /// `and_then` strips away the outer layer to return `Result`. If the + /// value is invalid, this returns an invalid value in `Result`. + /// + /// @param[in] callable The transformation function to apply. + /// @note This is the lvalue version. + /// @note This functions is `>>=` on the functor in `A` of `Result`. + /// @return The modified valid value if exists, or an error otherwise. + template + auto and_then(C&& callable) const& + requires std::invocable + { + using R = decltype(std::declval()(std::declval())); + + static_assert(std::same_as, + "bind must take a callable with the same error type"); + + if (ok()) { + return callable(value()); + } else { + return R::failure(error()); + } + } + + /// Bind a function to this result monadically. + /// + /// This function takes a function `f` and, if this result contains a valid + /// value `x`, returns `f(x)`. If the type of `x` is `T`, then `f` is + /// expected to accept type `T` and return `Result`. In this case, + /// `transform` would return the unhelpful type `Result>`, so + /// `and_then` strips away the outer layer to return `Result`. If the + /// value is invalid, this returns an invalid value in `Result`. + /// + /// @param[in] callable The transformation function to apply. + /// @note This is the rvalue version. + /// @note This functions is `>>=` on the functor in `A` of `Result`. + /// @return The modified valid value if exists, or an error otherwise. + template + auto and_then(C&& callable) && + requires std::invocable + { + using R = decltype(std::declval()(std::declval())); + + static_assert(std::same_as, + "bind must take a callable with the same error type"); + + if (ok()) { + return callable(std::move(*this).value()); + } else { + return R::failure(std::move(*this).error()); + } + } + private: std::variant m_var; diff --git a/Core/include/Acts/Utilities/TrackHelpers.hpp b/Core/include/Acts/Utilities/TrackHelpers.hpp index 36d37fab714..dc3765be7f6 100644 --- a/Core/include/Acts/Utilities/TrackHelpers.hpp +++ b/Core/include/Acts/Utilities/TrackHelpers.hpp @@ -209,7 +209,7 @@ findTrackStateForExtrapolation( } ACTS_VERBOSE("found intersection at " << intersection.pathLength()); - return std::make_pair(*first, intersection.pathLength()); + return std::pair(*first, intersection.pathLength()); } case TrackExtrapolationStrategy::last: { @@ -229,7 +229,7 @@ findTrackStateForExtrapolation( } ACTS_VERBOSE("found intersection at " << intersection.pathLength()); - return std::make_pair(*last, intersection.pathLength()); + return std::pair(*last, intersection.pathLength()); } case TrackExtrapolationStrategy::firstOrLast: { @@ -256,13 +256,13 @@ findTrackStateForExtrapolation( if (intersectionFirst.isValid() && absDistanceFirst <= absDistanceLast) { ACTS_VERBOSE("using first track state with intersection at " << intersectionFirst.pathLength()); - return std::make_pair(*first, intersectionFirst.pathLength()); + return std::pair(*first, intersectionFirst.pathLength()); } if (intersectionLast.isValid() && absDistanceLast <= absDistanceFirst) { ACTS_VERBOSE("using last track state with intersection at " << intersectionLast.pathLength()); - return std::make_pair(*last, intersectionLast.pathLength()); + return std::pair(*last, intersectionLast.pathLength()); } ACTS_ERROR("no intersection found"); @@ -531,7 +531,7 @@ calculatePredictedResidual(track_state_proxy_t trackState) { MeasurementMatrix residualCovariance = measurementCovariance + predictedCovariance; - return std::pair(residual, residualCovariance); + return {residual, residualCovariance}; } /// Helper function to calculate the filtered residual and its covariance @@ -568,7 +568,7 @@ calculateFilteredResidual(track_state_proxy_t trackState) { MeasurementMatrix residualCovariance = measurementCovariance + filteredCovariance; - return std::pair(residual, residualCovariance); + return {residual, residualCovariance}; } /// Helper function to calculate the smoothed residual and its covariance @@ -605,7 +605,7 @@ calculateSmoothedResidual(track_state_proxy_t trackState) { MeasurementMatrix residualCovariance = measurementCovariance + smoothedCovariance; - return std::pair(residual, residualCovariance); + return {residual, residualCovariance}; } /// Helper function to calculate the predicted chi2 diff --git a/Core/include/Acts/Utilities/TransformRange.hpp b/Core/include/Acts/Utilities/TransformRange.hpp index 4c7653c49e2..58e30b4f8c4 100644 --- a/Core/include/Acts/Utilities/TransformRange.hpp +++ b/Core/include/Acts/Utilities/TransformRange.hpp @@ -169,6 +169,8 @@ struct TransformRangeIterator { /// Construct an iterator from an underlying iterator explicit TransformRangeIterator(iterator_t iterator) : m_iterator(iterator) {} + TransformRangeIterator() = default; + /// Return a reference to the value that is transformed by the callable /// @return Reference to the transformed value reference operator*() { return Callable::apply(*m_iterator); } @@ -184,6 +186,14 @@ struct TransformRangeIterator { return *this; } + /// Advance the iterator + /// @return Reference to the iterator + TransformRangeIterator operator++(int) { + auto tmp = *this; + ++m_iterator; + return tmp; + } + /// Compare two iterators for equality /// @param other The other iterator to compare to bool operator==(const TransformRangeIterator& other) const { @@ -219,3 +229,9 @@ struct DotGet { }; } // namespace Acts::detail + +/// @cond +template +constexpr bool std::ranges::enable_borrowed_range< + Acts::detail::TransformRange> = true; +/// @endcond diff --git a/Core/include/Acts/Visualization/Interpolation3D.hpp b/Core/include/Acts/Visualization/Interpolation3D.hpp new file mode 100644 index 00000000000..ea8e649869e --- /dev/null +++ b/Core/include/Acts/Visualization/Interpolation3D.hpp @@ -0,0 +1,96 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" + +#include + +namespace Acts::Interpolation3D { + +/// @brief Helper function to interpolate points using a spline +/// from Eigen +/// +/// The only requirement is that the input trajectory type has +/// a method empty() and size() and that the elements can be +/// accessed with operator[] and have themselves a operator[] to +/// access the coordinates. +/// +/// @tparam input_trajectory_type input trajectory type +/// +/// @param inputsRaw input vector points +/// @param nPoints number of interpolation points +/// @param keepOriginalHits keep the original hits in the trajectory +/// +/// @return std::vector interpolated points +template +trajectory_type spline(const trajectory_type& inputsRaw, std::size_t nPoints, + bool keepOriginalHits = false) { + trajectory_type output; + if (inputsRaw.empty()) { + return output; + } + + using InputVectorType = typename trajectory_type::value_type; + + std::vector inputs; + // If input type is a vector of Vector3 we can use it directly + if constexpr (std::is_same_v>) { + inputs = inputsRaw; + } else { + inputs.reserve(inputsRaw.size()); + for (const auto& input : inputsRaw) { + inputs.push_back(Vector3(input[0], input[1], input[2])); + } + } + + // Don't do anything if we have less than 3 points or less interpolation + // points than input points + if (inputsRaw.size() < 3 || nPoints <= inputsRaw.size()) { + return inputsRaw; + } else { + Eigen::MatrixXd points(3, inputs.size()); + for (std::size_t i = 0; i < inputs.size(); ++i) { + points.col(i) = inputs[i].transpose(); + } + Eigen::Spline spline3D = + Eigen::SplineFitting>::Interpolate(points, 2); + + double step = 1. / (nPoints - 1); + for (std::size_t i = 0; i < nPoints; ++i) { + double t = i * step; + InputVectorType point; + point[0] = spline3D(t)[0]; + point[1] = spline3D(t)[1]; + point[2] = spline3D(t)[2]; + output.push_back(point); + } + } + // If we want to keep the original hits, we add them to the output + // (first and last are there anyway) + if (keepOriginalHits) { + output.insert(output.begin(), inputsRaw.begin() + 1, inputsRaw.end() - 1); + // We need to sort the output in distance to first + std::sort(output.begin(), output.end(), + [&inputs](const auto& a, const auto& b) { + const auto ifront = inputs.front(); + double da2 = (a[0] - ifront[0]) * (a[0] - ifront[0]) + + (a[1] - ifront[1]) * (a[1] - ifront[1]) + + (a[2] - ifront[2]) * (a[2] - ifront[2]); + double db2 = (b[0] - ifront[0]) * (b[0] - ifront[0]) + + (b[1] - ifront[1]) * (b[1] - ifront[1]) + + (b[2] - ifront[2]) * (b[2] - ifront[2]); + return da2 < db2; + }); + } + + return output; +} + +} // namespace Acts::Interpolation3D diff --git a/Core/src/Detector/detail/IndexedGridFiller.cpp b/Core/src/Detector/detail/IndexedGridFiller.cpp index 6e2f8cfd114..88e6dd608b0 100644 --- a/Core/src/Detector/detail/IndexedGridFiller.cpp +++ b/Core/src/Detector/detail/IndexedGridFiller.cpp @@ -47,14 +47,10 @@ std::vector Acts::Experimental::detail::binSequence( rBins.reserve(bmax - bmin + 1u + 2 * expand); // handle bmin:/max expand it down (for bound, don't fill underflow) if (type == Acts::AxisBoundaryType::Bound) { - bmin = (static_cast(bmin) - static_cast(expand) > 0) - ? bmin - expand - : 1u; + bmin = bmin > expand ? bmin - expand : 1u; bmax = (bmax + expand <= nBins) ? bmax + expand : nBins; } else if (type == Acts::AxisBoundaryType::Open) { - bmin = (static_cast(bmin) - static_cast(expand) >= 0) - ? bmin - expand - : 0u; + bmin = bmin >= expand ? bmin - expand : 0u; bmax = (bmax + expand <= nBins + 1u) ? bmax + expand : nBins + 1u; } fill_linear(bmin, bmax); @@ -62,14 +58,11 @@ std::vector Acts::Experimental::detail::binSequence( // Close case std::size_t span = bmax - bmin + 1u + 2 * expand; // Safe with respect to the closure point, treat as bound - if (2 * span < nBins && (bmax + expand <= nBins) && - (static_cast(bmin) - static_cast(expand) > 0)) { + if (2 * span < nBins && (bmax + expand <= nBins) && (bmin > expand)) { return binSequence({bmin, bmax}, expand, nBins, Acts::AxisBoundaryType::Bound); } else if (2 * span < nBins) { - bmin = static_cast(bmin) - static_cast(expand) > 0 - ? bmin - expand - : 1u; + bmin = bmin > expand ? bmin - expand : 1u; bmax = bmax + expand <= nBins ? bmax + expand : nBins; fill_linear(bmin, bmax); // deal with expansions over the phi boundary @@ -77,9 +70,8 @@ std::vector Acts::Experimental::detail::binSequence( std::size_t overstep = (bmax + expand - nBins); fill_linear(1u, overstep); } - if (static_cast(bmin) - static_cast(expand) < 1) { - std::size_t understep = - abs(static_cast(bmin) - static_cast(expand)); + if (bmin <= expand) { + std::size_t understep = expand - bmin; fill_linear(nBins - understep, nBins); } std::ranges::sort(rBins); diff --git a/Core/src/Geometry/Blueprint.cpp b/Core/src/Geometry/Blueprint.cpp new file mode 100644 index 00000000000..f4531acaa2f --- /dev/null +++ b/Core/src/Geometry/Blueprint.cpp @@ -0,0 +1,157 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/Blueprint.hpp" + +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Utilities/GraphViz.hpp" + +namespace { +const std::string s_rootName = "Root"; +} + +namespace Acts { + +Blueprint::Blueprint(const Config &config) : m_cfg(config) {} + +const std::string &Blueprint::name() const { + return s_rootName; +} + +Volume &Blueprint::build(const BlueprintOptions & /*options*/, + const GeometryContext & /*gctx*/, + const Logger & /*logger*/) { + throw std::logic_error("Root node cannot be built"); +} + +PortalShellBase &Blueprint::connect(const BlueprintOptions & /*options*/, + const GeometryContext & /*gctx*/, + const Logger & /*logger*/) { + throw std::logic_error("Root node cannot be connected"); +} + +void Blueprint::finalize(const BlueprintOptions & /*options*/, + const GeometryContext & /*gctx*/, + TrackingVolume & /*parent*/, + const Logger & /*logger*/) { + throw std::logic_error("Root node cannot be finalized"); +} + +void Blueprint::addToGraphviz(std::ostream &os) const { + GraphViz::Node node{ + .id = name(), .label = "World", .shape = GraphViz::Shape::House}; + + os << node; + BlueprintNode::addToGraphviz(os); +} + +std::unique_ptr Blueprint::construct( + const BlueprintOptions &options, const GeometryContext &gctx, + const Logger &logger) { + using enum BinningValue; + + ACTS_INFO(prefix() << "Building tracking geometry from blueprint tree"); + + options.validate(); + + if (m_cfg.envelope == ExtentEnvelope::Zero()) { + ACTS_WARNING(prefix() << "Root node is configured with zero envelope. This " + "might lead to navigation issues"); + } + + if (children().size() != 1) { + ACTS_ERROR(prefix() << "Root node must have exactly one child"); + throw std::logic_error("Root node must have exactly one child"); + } + + auto &child = children().at(0); + + ACTS_DEBUG(prefix() << "Executing building on tree"); + Volume &topVolume = child.build(options, gctx, logger); + const auto &bounds = topVolume.volumeBounds(); + + std::stringstream ss; + bounds.toStream(ss); + ACTS_DEBUG(prefix() << "have top volume: " << ss.str() << "\n" + << topVolume.transform().matrix()); + + std::shared_ptr worldBounds; + + if (const auto *cyl = dynamic_cast(&bounds); + cyl != nullptr) { + using enum CylinderVolumeBounds::BoundValues; + + // Make a copy that we'll modify + auto newBounds = std::make_shared(*cyl); + + const auto &zEnv = m_cfg.envelope[binZ]; + if (zEnv[0] != zEnv[1]) { + ACTS_ERROR( + prefix() << "Root node cylinder envelope for z must be symmetric"); + throw std::logic_error( + "Root node cylinder envelope for z must be " + "symmetric"); + } + + const auto &rEnv = m_cfg.envelope[binR]; + + newBounds->set({ + {eHalfLengthZ, newBounds->get(eHalfLengthZ) + zEnv[0]}, + {eMinR, std::max(0.0, newBounds->get(eMinR) - rEnv[0])}, + {eMaxR, newBounds->get(eMaxR) + rEnv[1]}, + }); + + worldBounds = std::move(newBounds); + + } else if (const auto *box = + dynamic_cast(&bounds); + box != nullptr) { + throw std::logic_error{"Not implemented"}; + } else { + throw std::logic_error{"Unsupported volume bounds type"}; + } + + ACTS_DEBUG(prefix() << "New root volume bounds are: " << *worldBounds); + + auto world = std::make_unique( + topVolume.transform(), std::move(worldBounds), "World"); + + // @TODO: This needs to become configurable + world->setNavigationPolicy( + options.defaultNavigationPolicyFactory->build(gctx, *world, logger)); + + // Need one-sided portal shell that connects outwards to nullptr + SingleCylinderPortalShell worldShell{*world}; + worldShell.applyToVolume(); + + auto &shell = child.connect(options, gctx, logger); + + shell.fill(*world); + + child.finalize(options, gctx, *world, logger); + + std::set> names; + + world->visitVolumes([&names, &logger, this](const auto *volume) { + if (names.contains(volume->volumeName())) { + ACTS_ERROR(prefix() << "Duplicate volume name: " << volume->volumeName()); + throw std::logic_error("Duplicate volume name"); + } + names.insert(volume->volumeName()); + }); + + return std::make_unique( + std::move(world), nullptr, m_cfg.geometryIdentifierHook, logger); +} + +} // namespace Acts diff --git a/Core/src/Geometry/BlueprintNode.cpp b/Core/src/Geometry/BlueprintNode.cpp new file mode 100644 index 00000000000..13c03495494 --- /dev/null +++ b/Core/src/Geometry/BlueprintNode.cpp @@ -0,0 +1,173 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/BlueprintNode.hpp" + +#include "Acts/Geometry/Blueprint.hpp" +#include "Acts/Geometry/CylinderContainerBlueprintNode.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" +#include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp" +#include "Acts/Geometry/StaticBlueprintNode.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/TryAllNavigationPolicy.hpp" + +#include +#include + +namespace Acts { + +namespace { +bool hasDescendent(const BlueprintNode& descendent, + const BlueprintNode& ancestor) { + if (&descendent == &ancestor) { + return true; + } + + return std::ranges::any_of(ancestor.children(), + [&](const auto& child) -> bool { + return hasDescendent(descendent, child); + }); +} +} // namespace + +void BlueprintNode::toStream(std::ostream& os) const { + os << "BlueprintNode(" << name() << ")"; +} + +BlueprintNode& BlueprintNode::addChild(std::shared_ptr child) { + if (!child) { + throw std::invalid_argument("Child is nullptr"); + } + + if (dynamic_cast(child.get()) != nullptr) { + throw std::invalid_argument("Cannot add a Blueprint as a child"); + } + + if (child->depth() != 0) { + throw std::invalid_argument("Child has already been added to another node"); + } + + if (hasDescendent(*this, *child)) { + throw std::invalid_argument("Adding child would create a cycle"); + } + + child->setDepth(m_depth + 1); + m_children.push_back(std::move(child)); + return *this; +} + +BlueprintNode::MutableChildRange BlueprintNode::children() { + return MutableChildRange{m_children}; +} + +BlueprintNode::ChildRange BlueprintNode::children() const { + return ChildRange{m_children}; +} + +std::size_t BlueprintNode::depth() const { + return m_depth; +} + +void BlueprintNode::setDepth(std::size_t depth) { + m_depth = depth; + for (auto& child : children()) { + child.setDepth(depth + 1); + } +} + +std::string BlueprintNode::indent() const { + return std::string(m_depth * 2, ' '); +} + +std::string BlueprintNode::prefix() const { + return indent() + "[" + name() + "]: "; +} + +StaticBlueprintNode& BlueprintNode::addStaticVolume( + std::unique_ptr volume, + const std::function& callback) { + if (!volume) { + throw std::invalid_argument("Volume is nullptr"); + } + + auto child = std::make_shared(std::move(volume)); + addChild(child); + + if (callback) { + callback(*child); + } + return *child; +} + +StaticBlueprintNode& BlueprintNode::addStaticVolume( + const Transform3& transform, std::shared_ptr volumeBounds, + const std::string& volumeName, + const std::function& callback) { + return addStaticVolume(std::make_unique( + transform, std::move(volumeBounds), volumeName), + callback); +} + +CylinderContainerBlueprintNode& BlueprintNode::addCylinderContainer( + const std::string& name, BinningValue direction, + const std::function& + callback) { + auto cylinder = + std::make_shared(name, direction); + addChild(cylinder); + if (callback) { + callback(*cylinder); + } + return *cylinder; +} + +MaterialDesignatorBlueprintNode& BlueprintNode::addMaterial( + const std::string& name, + const std::function& + callback) { + auto material = std::make_shared(name); + addChild(material); + if (callback) { + callback(*material); + } + return *material; +} + +LayerBlueprintNode& BlueprintNode::addLayer( + const std::string& name, + const std::function& callback) { + auto layer = std::make_shared(name); + addChild(layer); + if (callback) { + callback(*layer); + } + return *layer; +} + +void BlueprintNode::clearChildren() { + for (auto& child : children()) { + child.setDepth(0); + } + m_children.clear(); +} + +void BlueprintNode::graphviz(std::ostream& os) const { + os << "digraph BlueprintNode {" << std::endl; + addToGraphviz(os); + os << "}" << std::endl; +} + +void BlueprintNode::addToGraphviz(std::ostream& os) const { + for (const auto& child : children()) { + os << indent() << "\"" << name() << "\" -> \"" << child.name() << "\";" + << std::endl; + child.addToGraphviz(os); + } +} + +} // namespace Acts diff --git a/Core/src/Geometry/BlueprintOptions.cpp b/Core/src/Geometry/BlueprintOptions.cpp new file mode 100644 index 00000000000..07913665861 --- /dev/null +++ b/Core/src/Geometry/BlueprintOptions.cpp @@ -0,0 +1,29 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/BlueprintOptions.hpp" + +#include "Acts/Geometry/NavigationPolicyFactory.hpp" +#include "Acts/Navigation/TryAllNavigationPolicy.hpp" + +namespace Acts { + +void BlueprintOptions::validate() const { + if (!defaultNavigationPolicyFactory) { + throw std::invalid_argument("Navigation policy factory is nullptr"); + } +} + +std::unique_ptr +BlueprintOptions::makeDefaultNavigationPolicyFactory() { + return NavigationPolicyFactory::make() + .add() + .asUniquePtr(); +} + +} // namespace Acts diff --git a/Core/src/Geometry/CMakeLists.txt b/Core/src/Geometry/CMakeLists.txt index 74f6e0e126d..189c4396112 100644 --- a/Core/src/Geometry/CMakeLists.txt +++ b/Core/src/Geometry/CMakeLists.txt @@ -43,4 +43,11 @@ target_sources( PortalLinkBase.cpp PortalError.cpp PortalShell.cpp + BlueprintNode.cpp + Blueprint.cpp + BlueprintOptions.cpp + CylinderContainerBlueprintNode.cpp + StaticBlueprintNode.cpp + LayerBlueprintNode.cpp + MaterialDesignatorBlueprintNode.cpp ) diff --git a/Core/src/Geometry/CylinderContainerBlueprintNode.cpp b/Core/src/Geometry/CylinderContainerBlueprintNode.cpp new file mode 100644 index 00000000000..284b30949ac --- /dev/null +++ b/Core/src/Geometry/CylinderContainerBlueprintNode.cpp @@ -0,0 +1,257 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/CylinderContainerBlueprintNode.hpp" + +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Utilities/GraphViz.hpp" +#include "Acts/Visualization/GeometryView3D.hpp" + +#include +#include + +namespace Acts { + +CylinderContainerBlueprintNode::CylinderContainerBlueprintNode( + const std::string& name, BinningValue direction, + CylinderVolumeStack::AttachmentStrategy attachmentStrategy, + CylinderVolumeStack::ResizeStrategy resizeStrategy) + : m_name(name), + m_direction(direction), + m_attachmentStrategy(attachmentStrategy), + m_resizeStrategy(resizeStrategy) {} + +const std::string& CylinderContainerBlueprintNode::name() const { + return m_name; +} + +Volume& CylinderContainerBlueprintNode::build(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger) { + ACTS_DEBUG(prefix() << "cylinder container build (dir=" << m_direction + << ")"); + + if (m_stack != nullptr) { + ACTS_ERROR(prefix() << "Volume is already built"); + throw std::runtime_error("Volume is already built"); + } + + for (auto& child : children()) { + Volume& volume = child.build(options, gctx, logger); + m_childVolumes.push_back(&volume); + // We need to remember which volume we got from which child, so we can + // assemble a correct portal shell later + m_volumeToNode[&volume] = &child; + } + ACTS_VERBOSE(prefix() << "-> Collected " << m_childVolumes.size() + << " child volumes"); + + ACTS_VERBOSE(prefix() << "-> Building the stack"); + m_stack = std::make_unique(m_childVolumes, m_direction, + m_attachmentStrategy, + m_resizeStrategy, logger); + ACTS_DEBUG(prefix() << "-> Stack bounds are: " << m_stack->volumeBounds()); + + ACTS_DEBUG(prefix() << " *** build complete ***"); + + return *m_stack; +} + +std::vector +CylinderContainerBlueprintNode::collectChildShells( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger) { + std::vector shells; + ACTS_DEBUG(prefix() << "Have " << m_childVolumes.size() << " child volumes"); + for (Volume* volume : m_childVolumes) { + if (isGapVolume(*volume)) { + // We need to create a TrackingVolume from the gap and put it in the shell + auto gap = std::make_unique(*volume); + gap->setVolumeName(name() + "::Gap" + std::to_string(m_gaps.size() + 1)); + ACTS_DEBUG(prefix() << " ~> Gap volume (" << gap->volumeName() + << "): " << gap->volumeBounds()); + auto shell = std::make_unique(*gap); + assert(shell->isValid()); + shells.push_back(shell.get()); + + m_gaps.emplace_back(std::move(shell), std::move(gap)); + + } else { + // Figure out which child we got this volume from + auto it = m_volumeToNode.find(volume); + if (it == m_volumeToNode.end()) { + throw std::runtime_error("Volume not found in child volumes"); + } + BlueprintNode& child = *it->second; + + ACTS_DEBUG(prefix() << " ~> Child (" << child.name() + << ") volume: " << volume->volumeBounds()); + + auto* shell = dynamic_cast( + &child.connect(options, gctx, logger)); + if (shell == nullptr) { + ACTS_ERROR(prefix() + << "Child volume of cylinder stack is not a cylinder"); + throw std::runtime_error( + "Child volume of cylinder stack is not a cylinder"); + } + assert(shell->isValid()); + + shells.push_back(shell); + } + } + return shells; +} + +CylinderStackPortalShell& CylinderContainerBlueprintNode::connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger) { + ACTS_DEBUG(prefix() << "Cylinder container connect"); + if (m_stack == nullptr) { + ACTS_ERROR(prefix() << "Volume is not built"); + throw std::runtime_error("Volume is not built"); + } + + ACTS_DEBUG(prefix() << "Collecting child shells from " << children().size() + << " children"); + + // We have child volumes and gaps as bare Volumes in `m_childVolumes` after + // `build()` has completed. For the stack shell, we need TrackingVolumes in + // the right order. + + std::vector shells = + collectChildShells(options, gctx, logger); + + // Sanity checks + throw_assert(shells.size() == m_childVolumes.size(), + "Number of shells does not match number of child volumes"); + + throw_assert(std::ranges::none_of( + shells, [](const auto* shell) { return shell == nullptr; }), + "Invalid shell pointer"); + + throw_assert(std::ranges::all_of( + shells, [](const auto* shell) { return shell->isValid(); }), + "Invalid shell"); + + ACTS_DEBUG(prefix() << "Producing merged cylinder stack shell in " + << m_direction << " direction from " << shells.size() + << " shells"); + m_shell = std::make_unique(gctx, std::move(shells), + m_direction, logger); + + assert(m_shell != nullptr && "No shell was built at the end of connect"); + assert(m_shell->isValid() && "Shell is not valid at the end of connect"); + return *m_shell; +} + +void CylinderContainerBlueprintNode::finalize(const BlueprintOptions& options, + const GeometryContext& gctx, + TrackingVolume& parent, + const Logger& logger) { + ACTS_DEBUG(prefix() << "Finalizing cylinder container"); + + if (m_stack == nullptr) { + ACTS_ERROR(prefix() << "Volume is not built"); + throw std::runtime_error("Volume is not built"); + } + + if (m_shell == nullptr) { + ACTS_ERROR(prefix() << "Volume is not connected"); + throw std::runtime_error("Volume is not connected"); + } + + const auto* policyFactory = options.defaultNavigationPolicyFactory.get(); + + ACTS_DEBUG(prefix() << "Registering " << m_gaps.size() + << " gap volumes with parent"); + for (auto& [shell, gap] : m_gaps) { + auto* gapPtr = gap.get(); + parent.addVolume(std::move(gap)); + shell->applyToVolume(); + auto policy = policyFactory->build(gctx, *gapPtr, logger); + gapPtr->setNavigationPolicy(std::move(policy)); + } + + ACTS_DEBUG(prefix() << "Finalizing " << children().size() << " children"); + + for (auto& child : children()) { + child.finalize(options, gctx, parent, logger); + } +} + +bool CylinderContainerBlueprintNode::isGapVolume(const Volume& volume) const { + assert(m_stack != nullptr); + return std::ranges::any_of( + m_stack->gaps(), [&](const auto& gap) { return gap.get() == &volume; }); +} + +CylinderContainerBlueprintNode& CylinderContainerBlueprintNode::setDirection( + BinningValue direction) { + if (m_stack != nullptr) { + throw std::runtime_error("Cannot change direction after build"); + } + m_direction = direction; + return *this; +} + +CylinderContainerBlueprintNode& +CylinderContainerBlueprintNode::setAttachmentStrategy( + CylinderVolumeStack::AttachmentStrategy attachmentStrategy) { + if (m_stack != nullptr) { + throw std::runtime_error("Cannot change direction after build"); + } + m_attachmentStrategy = attachmentStrategy; + return *this; +} + +CylinderContainerBlueprintNode& +CylinderContainerBlueprintNode::setResizeStrategy( + CylinderVolumeStack::ResizeStrategy resizeStrategy) { + if (m_stack != nullptr) { + throw std::runtime_error("Cannot change direction after build"); + } + m_resizeStrategy = resizeStrategy; + return *this; +} + +void CylinderContainerBlueprintNode::addToGraphviz(std::ostream& os) const { + std::stringstream ss; + ss << "" + name() + ""; + ss << "
CylinderContainer"; + ss << "
dir: " << m_direction; + GraphViz::Node node{ + .id = name(), .label = ss.str(), .shape = GraphViz::Shape::DoubleOctagon}; + os << node << std::endl; + for (const auto& child : children()) { + os << indent() << GraphViz::Edge{{.id = name()}, {.id = child.name()}} + << std::endl; + child.addToGraphviz(os); + } +} + +BinningValue CylinderContainerBlueprintNode::direction() const { + return m_direction; +} + +CylinderVolumeStack::AttachmentStrategy +CylinderContainerBlueprintNode::attachmentStrategy() const { + return m_attachmentStrategy; +} + +CylinderVolumeStack::ResizeStrategy +CylinderContainerBlueprintNode::resizeStrategy() const { + return m_resizeStrategy; +} + +} // namespace Acts diff --git a/Core/src/Geometry/LayerBlueprintNode.cpp b/Core/src/Geometry/LayerBlueprintNode.cpp new file mode 100644 index 00000000000..de2e2cdb8cd --- /dev/null +++ b/Core/src/Geometry/LayerBlueprintNode.cpp @@ -0,0 +1,149 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/LayerBlueprintNode.hpp" + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/ProtoLayer.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Utilities/GraphViz.hpp" + +namespace Acts { + +Volume& LayerBlueprintNode::build(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger) { + if (m_surfaces.empty()) { + ACTS_ERROR("LayerBlueprintNode: no surfaces provided"); + throw std::invalid_argument("LayerBlueprintNode: no surfaces provided"); + } + + ACTS_DEBUG(prefix() << "Building Layer " << name() << " from " + << m_surfaces.size() << " surfaces"); + ACTS_VERBOSE(prefix() << " -> layer type: " << m_layerType); + ACTS_VERBOSE(prefix() << " -> transform:\n" << m_transform.matrix()); + + Extent extent; + + ProtoLayer protoLayer{gctx, m_surfaces, m_transform.inverse()}; + ACTS_VERBOSE(prefix() << "Built proto layer: " << protoLayer); + + extent.addConstrain(protoLayer.extent, m_envelope); + + ACTS_VERBOSE(prefix() << " -> layer extent: " << extent); + + buildVolume(extent, logger); + assert(m_volume != nullptr && "Volume not built from proto layer"); + + for (auto& surface : m_surfaces) { + m_volume->addSurface(surface); + } + + return StaticBlueprintNode::build(options, gctx, logger); +} + +void LayerBlueprintNode::buildVolume(const Extent& extent, + const Logger& logger) { + ACTS_VERBOSE(prefix() << "Building volume for layer " << name()); + using enum BinningValue; + using enum LayerType; + + std::shared_ptr bounds; + switch (m_layerType) { + case Cylinder: + case Disc: { + double minR = extent.min(binR); + double maxR = extent.max(binR); + double hlZ = extent.interval(binZ) / 2.0; + bounds = std::make_shared(minR, maxR, hlZ); + break; + } + case Plane: { + double hlX = extent.interval(binX) / 2.0; + double hlY = extent.interval(binY) / 2.0; + double hlZ = extent.interval(binZ) / 2.0; + bounds = std::make_shared(hlX, hlY, hlZ); + break; + } + } + + assert(bounds != nullptr); + + ACTS_VERBOSE(prefix() << " -> bounds: " << *bounds); + + Transform3 transform = m_transform; + transform.translation() = + Vector3{extent.medium(binX), extent.medium(binY), extent.medium(binZ)}; + + ACTS_VERBOSE(prefix() << " -> adjusted transform:\n" << transform.matrix()); + + m_volume = + std::make_unique(transform, std::move(bounds), m_name); +} + +const std::string& LayerBlueprintNode::name() const { + return m_name; +} + +LayerBlueprintNode& LayerBlueprintNode::setSurfaces( + std::vector> surfaces) { + m_surfaces = std::move(surfaces); + return *this; +} + +const std::vector>& LayerBlueprintNode::surfaces() + const { + return m_surfaces; +} + +LayerBlueprintNode& LayerBlueprintNode::setTransform( + const Transform3& transform) { + m_transform = transform; + return *this; +} + +const Transform3& LayerBlueprintNode::transform() const { + return m_transform; +} + +LayerBlueprintNode& LayerBlueprintNode::setEnvelope( + const ExtentEnvelope& envelope) { + m_envelope = envelope; + return *this; +} + +const ExtentEnvelope& LayerBlueprintNode::envelope() const { + return m_envelope; +} + +LayerBlueprintNode& LayerBlueprintNode::setLayerType(LayerType layerType) { + m_layerType = layerType; + return *this; +} + +const LayerBlueprintNode::LayerType& LayerBlueprintNode::layerType() const { + return m_layerType; +} + +void LayerBlueprintNode::addToGraphviz(std::ostream& os) const { + std::stringstream ss; + ss << "" << name() << ""; + ss << "
"; + ss << m_layerType; + + GraphViz::Node node{ + .id = name(), .label = ss.str(), .shape = GraphViz::Shape::Diamond}; + + os << node; + + BlueprintNode::addToGraphviz(os); +} + +} // namespace Acts diff --git a/Core/src/Geometry/MaterialDesignatorBlueprintNode.cpp b/Core/src/Geometry/MaterialDesignatorBlueprintNode.cpp new file mode 100644 index 00000000000..0e89b65d6e3 --- /dev/null +++ b/Core/src/Geometry/MaterialDesignatorBlueprintNode.cpp @@ -0,0 +1,191 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp" + +#include "Acts/Detector/ProtoBinning.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/Portal.hpp" +#include "Acts/Material/ProtoSurfaceMaterial.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/GraphViz.hpp" +#include "Acts/Utilities/Helpers.hpp" + +namespace Acts { + +const std::string& MaterialDesignatorBlueprintNode::name() const { + return m_name; +} + +void MaterialDesignatorBlueprintNode::toStream(std::ostream& os) const { + os << "MaterialDesignatorBlueprintNode(" << name() << ")"; +} + +Volume& MaterialDesignatorBlueprintNode::build(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger) { + if (children().size() != 1) { + ACTS_ERROR(prefix() << "MaterialDesignatorBlueprintNode must have exactly " + "one child, but has " + << children().size()); + throw std::runtime_error( + "MaterialDesignatorBlueprintNode must have exactly one child"); + } + + if (!m_binning) { + ACTS_ERROR(prefix() << "Binning is not set"); + throw std::runtime_error("Binning is not set"); + } + + return children().at(0).build(options, gctx, logger); +} + +void MaterialDesignatorBlueprintNode::handleCylinderBinning( + CylinderPortalShell& cylShell, + const std::vector< + std::tuple>& binning, + const Logger& logger) { + ACTS_DEBUG(prefix() << "Binning is set to compatible type"); + using enum CylinderVolumeBounds::Face; + + for (auto& [face, loc0, loc1] : binning) { + if (face == OuterCylinder || face == InnerCylinder) { + if (loc0.binValue != BinningValue::binRPhi) { + ACTS_ERROR(prefix() << "Binning is not in RPhi"); + throw std::runtime_error("Binning is not in RPhi"); + } + + if (loc1.binValue != BinningValue::binZ) { + ACTS_ERROR(prefix() << "Binning is not in Z"); + throw std::runtime_error("Binning is not in Z"); + } + } + + if (face == PositiveDisc || face == NegativeDisc) { + if (loc0.binValue != BinningValue::binR) { + ACTS_ERROR(prefix() << "Binning is not in R"); + throw std::runtime_error("Binning is not in R"); + } + if (loc1.binValue != BinningValue::binPhi) { + ACTS_ERROR(prefix() << "Binning is not in Phi"); + throw std::runtime_error("Binning is not in Phi"); + } + } + + Experimental::BinningDescription desc{.binning = {loc0, loc1}}; + ACTS_DEBUG(prefix() << "~> Assigning proto binning " << desc.toString() + << " to face " << face); + + auto material = std::make_shared(std::move(desc)); + + auto portal = cylShell.portal(face); + if (portal == nullptr) { + ACTS_ERROR(prefix() << "Portal is nullptr"); + throw std::runtime_error("Portal is nullptr"); + } + portal->surface().assignSurfaceMaterial(std::move(material)); + } +} + +PortalShellBase& MaterialDesignatorBlueprintNode::connect( + const BlueprintOptions& options, const GeometryContext& gctx, + const Logger& logger) { + ACTS_DEBUG(prefix() << "MaterialDesignatorBlueprintNode::connect"); + if (children().size() != 1) { + ACTS_ERROR(prefix() << "MaterialDesignatorBlueprintNode must have exactly " + "one child, but has " + << children().size()); + throw std::runtime_error( + "MaterialDesignatorBlueprintNode must have exactly one child"); + } + if (!m_binning) { + ACTS_ERROR(prefix() << "Binning is not set"); + throw std::runtime_error("Binning is not set"); + } + + auto& shell = children().at(0).connect(options, gctx, logger); + + ACTS_DEBUG(prefix() << "Received shell from child " + << children().at(0).name()); + + if (auto* cylShell = dynamic_cast(&shell)) { + ACTS_DEBUG(prefix() << "Connecting cylinder shell"); + + if (const auto* binning = std::get_if>>(&m_binning.value()); + binning != nullptr) { + handleCylinderBinning(*cylShell, *binning, logger); + } else { + ACTS_ERROR(prefix() << "Binning is set to unknown type"); + throw std::runtime_error("Unknown binning type"); + } + + } + // @TODO: Handle cuboid volume shell here + else { + ACTS_ERROR(prefix() << "Shell is not supported"); + throw std::runtime_error("Shell is not supported"); + } + + return shell; +} + +void MaterialDesignatorBlueprintNode::finalize(const BlueprintOptions& options, + const GeometryContext& gctx, + TrackingVolume& parent, + const Logger& logger) { + if (children().size() != 1) { + throw std::runtime_error( + "MaterialDesignatorBlueprintNode must have exactly one child"); + } + return children().at(0).finalize(options, gctx, parent, logger); +} + +void MaterialDesignatorBlueprintNode::addToGraphviz(std::ostream& os) const { + if (!m_binning) { + throw std::runtime_error("Binning is not set"); + } + + std::stringstream ss; + ss << "" + name() + ""; + ss << "
CylinderContainer"; + + std::visit( + overloaded{ + [&](const std::vector< + std::tuple>& binning) { + for (const auto& [face, loc0, loc1] : binning) { + ss << "
" << face; + ss << ": " << loc0.binValue << "=" << loc0.bins(); + ss << ", " << loc1.binValue << "=" << loc1.bins(); + } + }, + [](const auto& /*binning*/) { + // No output in all other cases + }}, + m_binning.value()); + os << GraphViz::Node{ + .id = name(), .label = ss.str(), .shape = GraphViz::Shape::Hexagon}; + BlueprintNode::addToGraphviz(os); +} + +const std::optional& +MaterialDesignatorBlueprintNode::binning() const { + return m_binning; +} + +MaterialDesignatorBlueprintNode& MaterialDesignatorBlueprintNode::setBinning( + BinningConfig binning) { + m_binning = std::move(binning); + return *this; +} + +} // namespace Acts diff --git a/Core/src/Geometry/PortalLinkBase.cpp b/Core/src/Geometry/PortalLinkBase.cpp index bdc65f8a552..f558fe19238 100644 --- a/Core/src/Geometry/PortalLinkBase.cpp +++ b/Core/src/Geometry/PortalLinkBase.cpp @@ -109,7 +109,7 @@ std::unique_ptr PortalLinkBase::merge( } else if (const auto* bTrivial = dynamic_cast(b.get()); bTrivial != nullptr) { - ACTS_VERBOSE( + ACTS_WARNING( "Merging a grid portal with a trivial portal (via composite)"); return std::make_unique(std::move(a), std::move(b), direction); @@ -128,7 +128,7 @@ std::unique_ptr PortalLinkBase::merge( aTrivial != nullptr) { if (const auto* bGrid = dynamic_cast(b.get()); bGrid) { - ACTS_VERBOSE( + ACTS_WARNING( "Merging a trivial portal with a grid portal (via composite)"); return std::make_unique(std::move(a), std::move(b), direction); @@ -141,7 +141,7 @@ std::unique_ptr PortalLinkBase::merge( direction); } else if (dynamic_cast(b.get()) != nullptr) { - ACTS_WARNING("Merging a trivial portal with a composite portal"); + ACTS_VERBOSE("Merging a trivial portal with a composite portal"); return std::make_unique(std::move(a), std::move(b), direction); @@ -156,12 +156,12 @@ std::unique_ptr PortalLinkBase::merge( direction); } else if (dynamic_cast(b.get()) != nullptr) { - ACTS_WARNING("Merging a composite portal with a trivial portal"); + ACTS_VERBOSE("Merging a composite portal with a trivial portal"); return std::make_unique(std::move(a), std::move(b), direction); } else if (dynamic_cast(b.get()) != nullptr) { - ACTS_WARNING("Merging two composite portals"); + ACTS_VERBOSE("Merging two composite portals"); return std::make_unique(std::move(a), std::move(b), direction); diff --git a/Core/src/Geometry/StaticBlueprintNode.cpp b/Core/src/Geometry/StaticBlueprintNode.cpp new file mode 100644 index 00000000000..12defdcc2eb --- /dev/null +++ b/Core/src/Geometry/StaticBlueprintNode.cpp @@ -0,0 +1,170 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/StaticBlueprintNode.hpp" + +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/PortalShell.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Utilities/GraphViz.hpp" +#include "Acts/Visualization/GeometryView3D.hpp" + +namespace Acts { + +StaticBlueprintNode::StaticBlueprintNode(std::unique_ptr volume) + : m_volume(std::move(volume)) {} + +Volume& StaticBlueprintNode::build(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger) { + ACTS_DEBUG(prefix() << "static build"); + if (!m_volume) { + throw std::runtime_error("Volume is not built"); + } + + ACTS_DEBUG(prefix() << "Building volume (" << name() << ") with " + << children().size() << " children"); + for (auto& child : children()) { + child.build(options, gctx, logger); + } + + ACTS_DEBUG(prefix() << "-> returning volume " << *m_volume); + return *m_volume; +} + +PortalShellBase& StaticBlueprintNode::connect(const BlueprintOptions& options, + const GeometryContext& gctx, + const Logger& logger) { + ACTS_DEBUG(prefix() << "Static connect"); + if (m_volume == nullptr) { + throw std::runtime_error("Volume is not present"); + } + + ACTS_DEBUG(prefix() << "Connecting parent volume (" << name() << ") with " + << children().size() << " children"); + + for (auto& child : children()) { + auto& shell = child.connect(options, gctx, logger); + // Register ourselves on the outside of the shell + shell.fill(*m_volume); + } + + VolumeBounds::BoundsType type = m_volume->volumeBounds().type(); + if (type == VolumeBounds::eCylinder) { + m_shell = std::make_unique(*m_volume); + + } else if (type == VolumeBounds::eCuboid) { + throw std::logic_error("Cuboid is not implemented yet"); + + } else { + throw std::logic_error("Volume type is not supported"); + } + + assert(m_shell != nullptr && + "No shell was built at the end of StaticBlueprintNode::connect"); + assert(m_shell->isValid() && + "Shell is not valid at the end of StaticBlueprintNode::connect"); + return *m_shell; +} + +void StaticBlueprintNode::finalize(const BlueprintOptions& options, + const GeometryContext& gctx, + TrackingVolume& parent, + const Logger& logger) { + ACTS_DEBUG(prefix() << "Finalizing static volume"); + + if (!m_volume) { + ACTS_ERROR(prefix() << "Volume is not built"); + throw std::runtime_error("Volume is not built"); + } + + if (!m_shell) { + ACTS_ERROR(prefix() << "Shell is not built"); + throw std::runtime_error("Shell is not built"); + } + + for (auto& child : children()) { + child.finalize(options, gctx, *m_volume, logger); + } + + ACTS_DEBUG(prefix() << "Registering " << m_shell->size() + << " portals into volume " << m_volume->volumeName()); + m_shell->applyToVolume(); + + ACTS_DEBUG(prefix() << " Adding volume (" << m_volume->volumeName() + << ") to parent volume (" << parent.volumeName() << ")"); + + const auto* policyFactory = options.defaultNavigationPolicyFactory.get(); + + if (m_navigationPolicyFactory) { + policyFactory = m_navigationPolicyFactory.get(); + } + m_volume->setNavigationPolicy(policyFactory->build(gctx, *m_volume, logger)); + + parent.addVolume(std::move(m_volume)); +} + +const std::string& StaticBlueprintNode::name() const { + static const std::string uninitialized = "uninitialized"; + if (m_volume == nullptr) { + return uninitialized; + } + return m_volume->volumeName(); +} + +StaticBlueprintNode& StaticBlueprintNode::setNavigationPolicyFactory( + std::shared_ptr navigationPolicyFactory) { + m_navigationPolicyFactory = std::move(navigationPolicyFactory); + return *this; +} + +const NavigationPolicyFactory* StaticBlueprintNode::navigationPolicyFactory() + const { + return m_navigationPolicyFactory.get(); +} + +void StaticBlueprintNode::addToGraphviz(std::ostream& os) const { + std::stringstream ss; + ss << "" << name() << ""; + ss << "
"; + if (m_volume == nullptr) { + throw std::runtime_error("Volume is not built"); + } + switch (m_volume->volumeBounds().type()) { + case VolumeBounds::eCylinder: + ss << "Cylinder"; + break; + case VolumeBounds::eCuboid: + ss << "Cuboid"; + break; + case VolumeBounds::eCone: + ss << "Cone"; + break; + case VolumeBounds::eCutoutCylinder: + ss << "CutoutCylinder"; + break; + case VolumeBounds::eGenericCuboid: + ss << "GenericCuboid"; + break; + case VolumeBounds::eTrapezoid: + ss << "Trapezoid"; + break; + default: + ss << "Other"; + } + + GraphViz::Node node{ + .id = name(), .label = ss.str(), .shape = GraphViz::Shape::Rectangle}; + + os << node; + + BlueprintNode::addToGraphviz(os); +} + +} // namespace Acts diff --git a/Core/src/Propagator/detail/CovarianceEngine.cpp b/Core/src/Propagator/detail/CovarianceEngine.cpp index a89016bce3c..ca2f57b88cf 100644 --- a/Core/src/Propagator/detail/CovarianceEngine.cpp +++ b/Core/src/Propagator/detail/CovarianceEngine.cpp @@ -97,8 +97,7 @@ CurvilinearState detail::curvilinearState( pos4, direction, freeParameters[eFreeQOverP], std::move(cov), particleHypothesis); // Create the curvilinear state - return std::make_tuple(std::move(curvilinearParams), fullTransportJacobian, - accumulatedPath); + return {std::move(curvilinearParams), fullTransportJacobian, accumulatedPath}; } void detail::transportCovarianceToBound( diff --git a/Core/src/Propagator/detail/SympyCovarianceEngine.cpp b/Core/src/Propagator/detail/SympyCovarianceEngine.cpp index f10171cc5ec..488a2d69ec9 100644 --- a/Core/src/Propagator/detail/SympyCovarianceEngine.cpp +++ b/Core/src/Propagator/detail/SympyCovarianceEngine.cpp @@ -86,8 +86,7 @@ CurvilinearState sympy::curvilinearState( pos4, direction, freeParameters[eFreeQOverP], std::move(cov), particleHypothesis); // Create the curvilinear state - return std::make_tuple(std::move(curvilinearParams), fullTransportJacobian, - accumulatedPath); + return {std::move(curvilinearParams), fullTransportJacobian, accumulatedPath}; } void sympy::transportCovarianceToBound( diff --git a/Core/src/Surfaces/detail/AlignmentHelper.cpp b/Core/src/Surfaces/detail/AlignmentHelper.cpp index 92c1fb7d2a1..b5308d60c84 100644 --- a/Core/src/Surfaces/detail/AlignmentHelper.cpp +++ b/Core/src/Surfaces/detail/AlignmentHelper.cpp @@ -87,6 +87,6 @@ Acts::detail::RotationToAxes Acts::detail::rotationToLocalAxesDerivative( rotToCompositeLocalYAxis * relRotation(1, 2) + rotToCompositeLocalZAxis * relRotation(2, 2); - return std::make_tuple(std::move(rotToLocalXAxis), std::move(rotToLocalYAxis), - std::move(rotToLocalZAxis)); + return {std::move(rotToLocalXAxis), std::move(rotToLocalYAxis), + std::move(rotToLocalZAxis)}; } diff --git a/Core/src/Surfaces/detail/AnnulusBoundsHelper.cpp b/Core/src/Surfaces/detail/AnnulusBoundsHelper.cpp index b46bfaf5b8a..4ceab6d594b 100644 --- a/Core/src/Surfaces/detail/AnnulusBoundsHelper.cpp +++ b/Core/src/Surfaces/detail/AnnulusBoundsHelper.cpp @@ -63,5 +63,5 @@ Acts::detail::AnnulusBoundsHelper::create(const Transform3& transform, auto annulusBounds = std::make_shared( rMin, rMax, phiMin, phiMax, originShift, phiShift); - return std::make_tuple(annulusBounds, boundsTransform); + return {annulusBounds, boundsTransform}; } diff --git a/Core/src/TrackFinding/AmbiguityTrackClustering.cpp b/Core/src/TrackFinding/AmbiguityTrackClustering.cpp index ff0e95cbb3d..8586f571aec 100644 --- a/Core/src/TrackFinding/AmbiguityTrackClustering.cpp +++ b/Core/src/TrackFinding/AmbiguityTrackClustering.cpp @@ -21,8 +21,9 @@ Acts::detail::clusterDuplicateTracks( // different clusters. std::unordered_map hitToTrack; - // Loop over all the tracks - for (const auto& [_, trackValue] : trackMap) { + // Loop backward over all the tracks + for (auto track = trackMap.rbegin(); track != trackMap.rend(); ++track) { + const auto& trackValue = track->second; std::vector hits = trackValue.second; auto matchedTrack = hitToTrack.end(); // Loop over all the hits in the track diff --git a/Core/src/TrackFitting/GlobalChiSquareFitter.cpp b/Core/src/TrackFitting/GlobalChiSquareFitter.cpp index 50b8b4cab8c..4062124496e 100644 --- a/Core/src/TrackFitting/GlobalChiSquareFitter.cpp +++ b/Core/src/TrackFitting/GlobalChiSquareFitter.cpp @@ -50,3 +50,98 @@ void Acts::Experimental::updateGx2fCovarianceParams( return; } + +void Acts::Experimental::addMeasurementToGx2fSumsBackend( + Gx2fSystem& extendedSystem, + const std::vector& jacobianFromStart, + const Eigen::MatrixXd& covarianceMeasurement, const BoundVector& predicted, + const Eigen::VectorXd& measurement, const Eigen::MatrixXd& projector, + const Logger& logger) { + // First, w try to invert the covariance matrix. If the inversion fails, we + // can already abort. + const auto safeInvCovMeasurement = safeInverse(covarianceMeasurement); + if (!safeInvCovMeasurement) { + ACTS_WARNING("addMeasurementToGx2fSums: safeInvCovMeasurement failed."); + ACTS_VERBOSE(" covarianceMeasurement:\n" << covarianceMeasurement); + return; + } + + // Create an extended Jacobian. This one contains only eBoundSize rows, + // because the rest is irrelevant. We fill it in the next steps. + // TODO make dimsExtendedParams template with unrolling + Eigen::MatrixXd extendedJacobian = + Eigen::MatrixXd::Zero(eBoundSize, extendedSystem.nDims()); + + // This part of the Jacobian comes from the material-less propagation + extendedJacobian.topLeftCorner() = + jacobianFromStart[0]; + + // If we have material, loop here over all Jacobians. We add extra columns for + // their phi-theta projections. These parts account for the propagation of the + // scattering angles. + for (std::size_t matSurface = 1; matSurface < jacobianFromStart.size(); + matSurface++) { + const BoundMatrix jac = jacobianFromStart[matSurface]; + + const ActsMatrix jacPhiTheta = + jac * Gx2fConstants::phiThetaProjector; + + // The position, where we need to insert the values in the extended Jacobian + const std::size_t deltaPosition = eBoundSize + 2 * (matSurface - 1); + + extendedJacobian.template block(0, deltaPosition) = + jacPhiTheta; + } + + const Eigen::MatrixXd projJacobian = projector * extendedJacobian; + + const Eigen::VectorXd projPredicted = projector * predicted; + + const Eigen::VectorXd residual = measurement - projPredicted; + + // Finally contribute to chi2sum, aMatrix, and bVector + extendedSystem.chi2() += + (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0); + + extendedSystem.aMatrix() += + (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian) + .eval(); + + extendedSystem.bVector() += + (residual.transpose() * (*safeInvCovMeasurement) * projJacobian) + .eval() + .transpose(); + + ACTS_VERBOSE( + "Contributions in addMeasurementToGx2fSums:\n" + << " predicted: " << predicted.transpose() << "\n" + << " measurement: " << measurement.transpose() << "\n" + << " covarianceMeasurement:\n" + << covarianceMeasurement << "\n" + << " projector:\n" + << projector.eval() << "\n" + << " projJacobian:\n" + << projJacobian.eval() << "\n" + << " projPredicted: " << (projPredicted.transpose()).eval() << "\n" + << " residual: " << (residual.transpose()).eval() << "\n" + << " extendedJacobian:\n" + << extendedJacobian << "\n" + << " aMatrix contribution:\n" + << (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian) + .eval() + << "\n" + << " bVector contribution: " + << (residual.transpose() * (*safeInvCovMeasurement) * projJacobian).eval() + << "\n" + << " chi2sum contribution: " + << (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0) + << "\n" + << " safeInvCovMeasurement:\n" + << (*safeInvCovMeasurement)); +} + +Eigen::VectorXd Acts::Experimental::computeGx2fDeltaParams( + const Acts::Experimental::Gx2fSystem& extendedSystem) { + return extendedSystem.aMatrix().colPivHouseholderQr().solve( + extendedSystem.bVector()); +} diff --git a/Core/src/Utilities/SpacePointUtility.cpp b/Core/src/Utilities/SpacePointUtility.cpp index 32470e1ebb9..6f662fe20bd 100644 --- a/Core/src/Utilities/SpacePointUtility.cpp +++ b/Core/src/Utilities/SpacePointUtility.cpp @@ -99,7 +99,7 @@ SpacePointUtility::globalCoords( tcov = std::nullopt; } - return std::make_tuple(globalPos, globalTime, gcov, tcov); + return {globalPos, globalTime, gcov, tcov}; } Vector2 SpacePointUtility::calcRhoZVars( diff --git a/Core/src/Vertexing/AdaptiveGridTrackDensity.cpp b/Core/src/Vertexing/AdaptiveGridTrackDensity.cpp index d08b9db4913..31daae4b039 100644 --- a/Core/src/Vertexing/AdaptiveGridTrackDensity.cpp +++ b/Core/src/Vertexing/AdaptiveGridTrackDensity.cpp @@ -156,7 +156,7 @@ AdaptiveGridTrackDensity::getMaxZTPosition(DensityMap& densityMap) const { double maxZ = getSpatialBinCenter(bin.first); double maxT = getTemporalBinCenter(bin.second); - return std::make_pair(maxZ, maxT); + return std::pair(maxZ, maxT); } Result diff --git a/Core/src/Vertexing/ImpactPointEstimator.cpp b/Core/src/Vertexing/ImpactPointEstimator.cpp index df9f75fd9fa..51f0ebe24db 100644 --- a/Core/src/Vertexing/ImpactPointEstimator.cpp +++ b/Core/src/Vertexing/ImpactPointEstimator.cpp @@ -217,7 +217,7 @@ Result> getDistanceAndMomentumImpl( Vector4 deltaRStraightTrack{Vector4::Zero()}; deltaRStraightTrack.head() = pcaStraightTrack - vtxPos; - return std::make_pair(deltaRStraightTrack, momDirStraightTrack); + return std::pair(deltaRStraightTrack, momDirStraightTrack); } // Charged particles in a constant B field follow a helical trajectory. In @@ -291,7 +291,7 @@ Result> getDistanceAndMomentumImpl( Vector4 deltaR{Vector4::Zero()}; deltaR.head() = pca - vtxPos; - return std::make_pair(deltaR, momDir); + return std::pair(deltaR, momDir); } } // namespace diff --git a/Core/src/Vertexing/Vertex.cpp b/Core/src/Vertexing/Vertex.cpp index 7c2c54cc7ac..4368f3b2faa 100644 --- a/Core/src/Vertexing/Vertex.cpp +++ b/Core/src/Vertexing/Vertex.cpp @@ -74,7 +74,7 @@ const std::vector& Vertex::tracks() const { } std::pair Vertex::fitQuality() const { - return std::pair(m_chiSquared, m_numberDoF); + return {m_chiSquared, m_numberDoF}; } void Vertex::setPosition(const Vector3& position) { diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp index 331408faa9a..a7e9b31bb7d 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp @@ -48,6 +48,10 @@ class DigitizationAlgorithm final : public IAlgorithm { std::string outputMeasurementParticlesMap = "measurement_particles_map"; /// Output collection to map measured hits to simulated hits. std::string outputMeasurementSimHitsMap = "measurement_simhits_map"; + /// Output collection to map particles to measurements. + std::string outputParticleMeasurementsMap = "particle_measurements_map"; + /// Output collection to map particles to simulated hits. + std::string outputSimHitMeasurementsMap = "simhit_measurements_map"; /// Map of surface by identifier to allow local - to global std::unordered_map @@ -122,7 +126,7 @@ class DigitizationAlgorithm final : public IAlgorithm { Config m_cfg; /// Digitizers within geometry hierarchy Acts::GeometryHierarchyMap m_digitizers; - /// Geometric digtizer + /// Geometric digitizer ActsFatras::Channelizer m_channelizer; using CellsMap = @@ -140,6 +144,11 @@ class DigitizationAlgorithm final : public IAlgorithm { WriteDataHandle> m_outputMeasurementSimHitsMap{ this, "OutputMeasurementSimHitsMap"}; + WriteDataHandle> m_outputParticleMeasurementsMap{ + this, "OutputParticleMeasurementsMap"}; + WriteDataHandle> m_outputSimHitMeasurementsMap{ + this, "OutputSimHitMeasurementsMap"}; + /// Construct a fixed-size smearer from a configuration. /// /// It's templated on the smearing dimension given by @tparam kSmearDIM @@ -153,7 +162,7 @@ class DigitizationAlgorithm final : public IAlgorithm { // Copy the geometric configuration impl.geometric = cfg.geometricDigiConfig; // Prepare the smearing configuration - for (int i = 0; i < static_cast(kSmearDIM); ++i) { + for (std::size_t i = 0; i < kSmearDIM; ++i) { impl.smearing.indices[i] = cfg.smearingDigiConfig.at(i).index; impl.smearing.smearFunctions[i] = cfg.smearingDigiConfig.at(i).smearFunction; diff --git a/Examples/Algorithms/Digitization/scripts/smearing-config.py b/Examples/Algorithms/Digitization/scripts/smearing-config.py index 5c176d86318..64dc1b1e3e9 100644 --- a/Examples/Algorithms/Digitization/scripts/smearing-config.py +++ b/Examples/Algorithms/Digitization/scripts/smearing-config.py @@ -137,7 +137,7 @@ def block_to_json(args): data["range"] = ps[1:] data["type"] = "GaussClipped" elif t in [3, 4]: - data["type"] = "Uniform" if t == 3 else "Digitial" + data["type"] = "Uniform" if t == 3 else "Digital" pitch = ps[0] low = ps[1] diff --git a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp index d7b4754cb3a..e55ac39d09b 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp @@ -16,12 +16,9 @@ #include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" -#include "ActsExamples/Utilities/Range.hpp" -#include "ActsFatras/EventData/Barcode.hpp" #include #include -#include #include #include #include @@ -61,12 +58,23 @@ DigitizationAlgorithm::DigitizationAlgorithm(Config config, throw std::invalid_argument( "Missing hit-to-simulated-hits map output collection"); } + if (m_cfg.outputParticleMeasurementsMap.empty()) { + throw std::invalid_argument( + "Missing particle-to-measurements map output collection"); + } + if (m_cfg.outputSimHitMeasurementsMap.empty()) { + throw std::invalid_argument( + "Missing particle-to-simulated-hits map output collection"); + } m_outputMeasurements.initialize(m_cfg.outputMeasurements); m_outputClusters.initialize(m_cfg.outputClusters); m_outputMeasurementParticlesMap.initialize( m_cfg.outputMeasurementParticlesMap); m_outputMeasurementSimHitsMap.initialize(m_cfg.outputMeasurementSimHitsMap); + m_outputParticleMeasurementsMap.initialize( + m_cfg.outputParticleMeasurementsMap); + m_outputSimHitMeasurementsMap.initialize(m_cfg.outputSimHitMeasurementsMap); } if (m_cfg.doOutputCells) { @@ -141,7 +149,7 @@ ProcessCode DigitizationAlgorithm::execute(const AlgorithmContext& ctx) const { MeasurementContainer measurements; ClusterContainer clusters; - IndexMultimap measurementParticlesMap; + IndexMultimap measurementParticlesMap; IndexMultimap measurementSimHitsMap; measurements.reserve(simHits.size()); measurementParticlesMap.reserve(simHits.size()); @@ -302,6 +310,12 @@ ProcessCode DigitizationAlgorithm::execute(const AlgorithmContext& ctx) const { m_outputMeasurements(ctx, std::move(measurements)); m_outputClusters(ctx, std::move(clusters)); + // invert them before they are moved + m_outputParticleMeasurementsMap( + ctx, invertIndexMultimap(measurementParticlesMap)); + m_outputSimHitMeasurementsMap(ctx, + invertIndexMultimap(measurementSimHitsMap)); + m_outputMeasurementParticlesMap(ctx, std::move(measurementParticlesMap)); m_outputMeasurementSimHitsMap(ctx, std::move(measurementSimHitsMap)); } diff --git a/Examples/Algorithms/Geant4/CMakeLists.txt b/Examples/Algorithms/Geant4/CMakeLists.txt index a7dd786bccf..cb056036f00 100644 --- a/Examples/Algorithms/Geant4/CMakeLists.txt +++ b/Examples/Algorithms/Geant4/CMakeLists.txt @@ -1,8 +1,6 @@ add_library( ActsExamplesGeant4 SHARED - src/GdmlDetectorConstruction.cpp - src/TelescopeG4DetectorConstruction.cpp src/Geant4Simulation.cpp src/MagneticFieldWrapper.cpp src/MaterialPhysicsList.cpp @@ -32,43 +30,9 @@ target_link_libraries( PUBLIC ActsCore ActsExamplesFramework - ActsExamplesDetectorTelescope + ActsExamplesDetectorsCommon Boost::headers ${Geant4_LIBRARIES} ) -if(ACTS_BUILD_EXAMPLES_DD4HEP) - if(${DD4hep_VERSION} VERSION_LESS 1.11) - target_include_directories( - ActsExamplesGeant4 - PRIVATE ${DD4hep_INCLUDE_DIRS} - ) - target_link_libraries( - ActsExamplesGeant4 - PRIVATE ${DD4hep_DDCORE_LIBRARY} ${DD4hep_DDG4_LIBRARY} - ) - else() - target_link_libraries( - ActsExamplesGeant4 - PUBLIC ActsExamplesDetectorDD4hep DD4hep::DDCore DD4hep::DDG4 - ) - endif() - - target_sources(ActsExamplesGeant4 PUBLIC src/DDG4DetectorConstruction.cpp) -endif() - -if(ACTS_BUILD_PLUGIN_GEOMODEL) - target_sources( - ActsExamplesGeant4 - PUBLIC src/GeoModelDetectorConstruction.cpp - ) - - find_library(GeoModel2G4_LIBRARY GeoModel2G4 REQUIRED) - - target_link_libraries( - ActsExamplesGeant4 - PUBLIC ActsPluginGeoModel ${GeoModel2G4_LIBRARY} - ) -endif() - install(TARGETS ActsExamplesGeant4 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/DDG4/DDG4DetectorConstruction.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/DDG4/DDG4DetectorConstruction.hpp deleted file mode 100644 index 6706b5c20e4..00000000000 --- a/Examples/Algorithms/Geant4/include/ActsExamples/DDG4/DDG4DetectorConstruction.hpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" - -#include - -#include - -class G4VPhysicalVolume; - -namespace dd4hep { -class Detector; -} - -namespace ActsExamples { - -struct DD4hepDetector; - -/// Construct the Geant4 detector from a DD4hep description. -class DDG4DetectorConstruction final : public G4VUserDetectorConstruction { - public: - DDG4DetectorConstruction( - std::shared_ptr detector, - std::vector> regionCreators = {}); - ~DDG4DetectorConstruction() final; - - /// Convert the stored DD4hep detector to a Geant4 description. - /// - /// Transfers ownership of the created object as all volumes (including world) - /// are deleted in ~G4PhysicalVolumeStore(). - /// - /// @note for facilitating configuration within the ACTS framework the world - /// volume is cached - G4VPhysicalVolume* Construct() final; - - private: - /// The Acts DD4hep detector instance - std::shared_ptr m_detector; - /// Region creators - std::vector> m_regionCreators; - /// The world volume - G4VPhysicalVolume* m_world = nullptr; - - /// The DD4hep detector instance - dd4hep::Detector& dd4hepDetector() const; -}; - -class DDG4DetectorConstructionFactory final - : public Geant4::DetectorConstructionFactory { - public: - DDG4DetectorConstructionFactory( - std::shared_ptr detector, - std::vector> regionCreators = {}); - ~DDG4DetectorConstructionFactory() final; - - std::unique_ptr factorize() const override; - - private: - /// The Acts DD4hep detector instance - std::shared_ptr m_detector; - /// Region creators - std::vector> m_regionCreators; -}; - -} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/DetectorConstructionFactory.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4ConstructionOptions.hpp similarity index 53% rename from Examples/Algorithms/Geant4/include/ActsExamples/Geant4/DetectorConstructionFactory.hpp rename to Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4ConstructionOptions.hpp index 16cb2c3eae6..a767227ae3e 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/DetectorConstructionFactory.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4ConstructionOptions.hpp @@ -9,18 +9,16 @@ #pragma once #include - -#include +#include namespace ActsExamples::Geant4 { +class RegionCreator; +} // namespace ActsExamples::Geant4 -/// Silly Geant4 will destroy the detector construction after the run manager is -/// destructed. This class works around it by factorizing a factory. -class DetectorConstructionFactory { - public: - virtual ~DetectorConstructionFactory() = default; +namespace ActsExamples { - virtual std::unique_ptr factorize() const = 0; +struct Geant4ConstructionOptions { + std::vector> regionCreators; }; -} // namespace ActsExamples::Geant4 +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp index 917b87a7a27..df9dd637eca 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp @@ -10,6 +10,7 @@ #include "Acts/Material/MaterialInteraction.hpp" #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/EventData/PropagationSummary.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" @@ -17,6 +18,7 @@ #include "ActsExamples/Framework/IAlgorithm.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/RandomNumbers.hpp" +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp" #include @@ -44,10 +46,8 @@ namespace ActsExamples { struct Geant4Handle; namespace Geant4 { -class DetectorConstructionFactory; class SensitiveSurfaceMapper; struct EventStore; -class RegionCreator; } // namespace Geant4 /// Abstracts common Geant4 Acts algorithm behaviour. @@ -61,10 +61,11 @@ class Geant4SimulationBase : public IAlgorithm { /// Random number service. std::shared_ptr randomNumbers; - /// Detector construction object. - /// G4RunManager will take care of deletion - std::shared_ptr - detectorConstructionFactory; + /// Geant4 construction options. + Geant4ConstructionOptions constructionOptions; + + /// Detector instance to access Geant4 geometry construction. + std::shared_ptr detector; /// Optional Geant4 instance overwrite. std::shared_ptr geant4Handle; @@ -81,8 +82,7 @@ class Geant4SimulationBase : public IAlgorithm { /// Algorithm execute method, called once per event with context /// /// @param ctx the AlgorithmContext for this event - ActsExamples::ProcessCode execute( - const ActsExamples::AlgorithmContext& ctx) const override; + ProcessCode execute(const ActsExamples::AlgorithmContext& ctx) const override; /// Readonly access to the configuration virtual const Config& config() const = 0; @@ -166,8 +166,7 @@ class Geant4Simulation final : public Geant4SimulationBase { /// Algorithm execute method, called once per event with context /// /// @param ctx the AlgorithmContext for this event - ActsExamples::ProcessCode execute( - const ActsExamples::AlgorithmContext& ctx) const final; + ProcessCode execute(const ActsExamples::AlgorithmContext& ctx) const final; /// Readonly access to the configuration const Config& config() const final { return m_cfg; } @@ -209,8 +208,7 @@ class Geant4MaterialRecording final : public Geant4SimulationBase { /// Algorithm execute method, called once per event with context /// /// @param ctx the AlgorithmContext for this event - ActsExamples::ProcessCode execute( - const ActsExamples::AlgorithmContext& ctx) const final; + ProcessCode execute(const ActsExamples::AlgorithmContext& ctx) const override; /// Readonly access to the configuration const Config& config() const final { return m_cfg; } diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/RegionCreator.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/RegionCreator.hpp index 5327e4f37eb..8977449eff8 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/RegionCreator.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/RegionCreator.hpp @@ -13,6 +13,8 @@ #include #include +class G4Region; + namespace ActsExamples::Geant4 { /// Geant4 Region Creator @@ -24,6 +26,9 @@ class RegionCreator { public: /// Nested configuration struct for the Geant4 region creator struct Config { + /// Region name + std::string name; + /// Process cut to be applied for gammas, in mm double gammaCut{}; @@ -37,35 +42,25 @@ class RegionCreator { double protonCut{}; /// Volume list to be included in this region - std::vector volumes{}; + std::vector volumes; }; /// Region creator constructor /// /// @param cfg is the configuration struct - /// @param name is the region name - /// @param level is the logging level to be used - RegionCreator(const Config& cfg, std::string name, - Acts::Logging::Level level); + explicit RegionCreator(const Config& cfg); /// Construct the region - void construct(); + /// @note The lifetime of the returned region is managed by Geant4 + G4Region* buildRegion( + const Acts::Logger& logger = Acts::getDummyLogger()) const; /// Readonly access to the configuration const Config& config() const { return m_cfg; } private: - /// Region name - std::string m_name; - /// Config instance Config m_cfg; - - /// Private access method to the logging instance - const Acts::Logger& logger() const { return *m_logger; } - - /// The looging instance - std::unique_ptr m_logger; }; } // namespace ActsExamples::Geant4 diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp deleted file mode 100644 index dba44638152..00000000000 --- a/Examples/Algorithms/Geant4/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" -#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" - -#include "G4VUserDetectorConstruction.hh" - -class G4VPhysicalVolume; -class G4LogicalVolume; - -namespace ActsExamples { - -class TelescopeG4DetectorConstruction final - : public G4VUserDetectorConstruction { - public: - TelescopeG4DetectorConstruction( - const TelescopeDetector::Config& cfg, - std::vector> regionCreators = {}); - - G4VPhysicalVolume* Construct() final; - - private: - /// The configuration of the telescope detector - TelescopeDetector::Config m_cfg; - /// Region creators - std::vector> m_regionCreators; - /// The world volume - G4VPhysicalVolume* m_world{}; -}; - -class TelescopeG4DetectorConstructionFactory final - : public Geant4::DetectorConstructionFactory { - public: - TelescopeG4DetectorConstructionFactory( - const TelescopeDetector::Config& cfg, - std::vector> regionCreators = {}); - - std::unique_ptr factorize() const override; - - private: - /// The configuration of the telescope detector - TelescopeDetector::Config m_cfg; - /// Region creators - std::vector> m_regionCreators; -}; - -} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/src/DDG4DetectorConstruction.cpp b/Examples/Algorithms/Geant4/src/DDG4DetectorConstruction.cpp deleted file mode 100644 index 1b24da8f273..00000000000 --- a/Examples/Algorithms/Geant4/src/DDG4DetectorConstruction.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/DDG4/DDG4DetectorConstruction.hpp" - -#include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include - -class G4VPhysicalVolume; - -namespace ActsExamples { - -DDG4DetectorConstruction::DDG4DetectorConstruction( - std::shared_ptr detector, - std::vector> regionCreators) - : G4VUserDetectorConstruction(), - m_detector(std::move(detector)), - m_regionCreators(std::move(regionCreators)) {} - -DDG4DetectorConstruction::~DDG4DetectorConstruction() = default; - -dd4hep::Detector& DDG4DetectorConstruction::dd4hepDetector() const { - return m_detector->geometryService->detector(); -} - -// See DD4hep::Simulation::Geant4DetectorConstruction::Construct() -G4VPhysicalVolume* DDG4DetectorConstruction::Construct() { - if (m_world == nullptr) { - dd4hep::sim::Geant4Mapping& g4map = dd4hep::sim::Geant4Mapping::instance(); - auto conv = dd4hep::sim::Geant4Converter(dd4hepDetector(), - dd4hep::PrintLevel::VERBOSE); - dd4hep::sim::Geant4GeometryInfo* geoInfo = - conv.create(dd4hepDetector().world()).detach(); - g4map.attach(geoInfo); - // All volumes are deleted in ~G4PhysicalVolumeStore() - m_world = geoInfo->world(); - // Create Geant4 volume manager - g4map.volumeManager(); - - // Create regions - for (const auto& regionCreator : m_regionCreators) { - regionCreator->construct(); - } - } - return m_world; -} - -DDG4DetectorConstructionFactory::DDG4DetectorConstructionFactory( - std::shared_ptr detector, - std::vector> regionCreators) - : m_detector(std::move(detector)), - m_regionCreators(std::move(regionCreators)) {} - -DDG4DetectorConstructionFactory::~DDG4DetectorConstructionFactory() = default; - -std::unique_ptr -DDG4DetectorConstructionFactory::factorize() const { - return std::make_unique(m_detector, - m_regionCreators); -} - -} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/src/GdmlDetectorConstruction.cpp b/Examples/Algorithms/Geant4/src/GdmlDetectorConstruction.cpp deleted file mode 100644 index 242597ac175..00000000000 --- a/Examples/Algorithms/Geant4/src/GdmlDetectorConstruction.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/Geant4/GdmlDetectorConstruction.hpp" - -#include - -#include - -class G4VPhysicalVolume; - -using namespace ActsExamples; - -GdmlDetectorConstruction::GdmlDetectorConstruction( - std::string path, - std::vector> regionCreators) - : G4VUserDetectorConstruction(), - m_path(std::move(path)), - m_regionCreators(std::move(regionCreators)) {} - -G4VPhysicalVolume* GdmlDetectorConstruction::Construct() { - if (m_world == nullptr) { - G4GDMLParser parser; - // TODO how to handle errors - parser.Read(m_path); - m_world = parser.GetWorldVolume(); - - // Create regions - for (const auto& regionCreator : m_regionCreators) { - regionCreator->construct(); - } - } - return m_world; -} - -GdmlDetectorConstructionFactory::GdmlDetectorConstructionFactory( - std::string path, - std::vector> regionCreators) - : m_path(std::move(path)), m_regionCreators(std::move(regionCreators)) {} - -std::unique_ptr -GdmlDetectorConstructionFactory::factorize() const { - return std::make_unique(m_path, m_regionCreators); -} diff --git a/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp b/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp index 0f26f8037b1..9ae93e97087 100644 --- a/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp +++ b/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp @@ -15,7 +15,6 @@ #include "ActsExamples/Framework/IAlgorithm.hpp" #include "ActsExamples/Framework/RandomNumbers.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" #include "ActsExamples/Geant4/EventStore.hpp" #include "ActsExamples/Geant4/Geant4Manager.hpp" #include "ActsExamples/Geant4/MagneticFieldWrapper.hpp" @@ -54,7 +53,7 @@ Geant4SimulationBase::Geant4SimulationBase(const Config& cfg, std::string name, if (cfg.inputParticles.empty()) { throw std::invalid_argument("Missing input particle collection"); } - if (cfg.detectorConstructionFactory == nullptr) { + if (cfg.detector == nullptr) { throw std::invalid_argument("Missing detector construction factory"); } if (cfg.randomNumbers == nullptr) { @@ -82,7 +81,10 @@ void Geant4SimulationBase::commonInitialization() { } // G4RunManager will take care of deletion m_detectorConstruction = - config().detectorConstructionFactory->factorize().release(); + config() + .detector + ->buildGeant4DetectorConstruction(config().constructionOptions) + .release(); runManager().SetUserInitialization(m_detectorConstruction); runManager().InitializeGeometry(); } diff --git a/Examples/Algorithms/Geant4/src/RegionCreator.cpp b/Examples/Algorithms/Geant4/src/RegionCreator.cpp index c38dcf83078..f3561293035 100644 --- a/Examples/Algorithms/Geant4/src/RegionCreator.cpp +++ b/Examples/Algorithms/Geant4/src/RegionCreator.cpp @@ -8,6 +8,8 @@ #include "ActsExamples/Geant4/RegionCreator.hpp" +#include "Acts/Utilities/Logger.hpp" + #include #include #include @@ -15,15 +17,11 @@ namespace ActsExamples::Geant4 { -RegionCreator::RegionCreator(const Config& cfg, std::string name, - Acts::Logging::Level level) - : m_name(std::move(name)), - m_cfg(cfg), - m_logger(Acts::getDefaultLogger(m_name, level)) {} +RegionCreator::RegionCreator(const Config& cfg) : m_cfg(cfg) {} -void RegionCreator::construct() { +G4Region* RegionCreator::buildRegion(const Acts::Logger& logger) const { // create a new G4Region - G4Region* region = new G4Region(m_name); + G4Region* region = new G4Region(m_cfg.name); // loop over volumes and find the ones in the list std::size_t nVolumes{0}; @@ -43,12 +41,13 @@ void RegionCreator::construct() { if (nVolumesCurrent == 0) { ACTS_WARNING("No volumes matching \"" << volumeName << "\" found in G4 LogicalVolumeStore. " - << m_name << " G4PhysicsRegion may not behave as intended."); + << m_cfg.name + << " G4PhysicsRegion may not behave as intended."); } nVolumes += nVolumesCurrent; } - ACTS_INFO("Created region " << m_name); + ACTS_INFO("Created region " << m_cfg.name); ACTS_INFO("A total of " << nVolumes << " volumes were assigned"); // create a G4ProductionCuts object and set appropriate values @@ -66,6 +65,8 @@ void RegionCreator::construct() { // assign cuts to the region region->SetProductionCuts(cuts); + + return region; } } // namespace ActsExamples::Geant4 diff --git a/Examples/Algorithms/Geant4HepMC/include/ActsExamples/Geant4HepMC/EventRecording.hpp b/Examples/Algorithms/Geant4HepMC/include/ActsExamples/Geant4HepMC/EventRecording.hpp index 6f45940182c..fead7f18d97 100644 --- a/Examples/Algorithms/Geant4HepMC/include/ActsExamples/Geant4HepMC/EventRecording.hpp +++ b/Examples/Algorithms/Geant4HepMC/include/ActsExamples/Geant4HepMC/EventRecording.hpp @@ -9,11 +9,13 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/SequenceElement.hpp" +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" #include #include @@ -38,8 +40,11 @@ class EventRecording final : public ActsExamples::IAlgorithm { /// The recorded events output std::string outputHepMcTracks = "geant-outcome-tracks"; - std::shared_ptr - detectorConstructionFactory; + /// Geant4 construction options. + Geant4ConstructionOptions constructionOptions; + + /// Detector instance to access Geant4 geometry construction. + std::shared_ptr detector; /// random number seed 1 int seed1 = 12345; diff --git a/Examples/Algorithms/Geant4HepMC/src/EventRecording.cpp b/Examples/Algorithms/Geant4HepMC/src/EventRecording.cpp index 93c27b0c1c1..51826af837a 100644 --- a/Examples/Algorithms/Geant4HepMC/src/EventRecording.cpp +++ b/Examples/Algorithms/Geant4HepMC/src/EventRecording.cpp @@ -10,7 +10,6 @@ #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" #include @@ -41,7 +40,7 @@ EventRecording::EventRecording(const EventRecording::Config& config, if (m_cfg.outputHepMcTracks.empty()) { throw std::invalid_argument("Missing output event collection"); } - if (m_cfg.detectorConstructionFactory == nullptr) { + if (m_cfg.detector == nullptr) { throw std::invalid_argument("Missing detector construction object"); } @@ -52,7 +51,8 @@ EventRecording::EventRecording(const EventRecording::Config& config, // G4RunManager deals with the lifetime of these objects m_runManager->SetUserInitialization( - m_cfg.detectorConstructionFactory->factorize().release()); + m_cfg.detector->buildGeant4DetectorConstruction(m_cfg.constructionOptions) + .release()); m_runManager->SetUserInitialization(new FTFP_BERT); m_runManager->SetUserAction(new Geant4::HepMC3::RunAction()); m_runManager->SetUserAction( diff --git a/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.cpp b/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.cpp index 9d5c1b873d3..7c1b629430e 100644 --- a/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.cpp +++ b/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.cpp @@ -8,19 +8,14 @@ #include "ActsExamples/Generators/EventGenerator.hpp" -#include "Acts/Surfaces/PerigeeSurface.hpp" #include "ActsExamples/EventData/SimVertex.hpp" -#include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include #include -#include #include #include -#include namespace ActsExamples { diff --git a/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.hpp b/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.hpp index 7a76ac03d71..1690b203d2e 100644 --- a/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.hpp +++ b/Examples/Algorithms/Generators/ActsExamples/Generators/EventGenerator.hpp @@ -24,7 +24,6 @@ #include namespace ActsExamples { -struct AlgorithmContext; /// Event generator based on separate particles and vertex generators. /// diff --git a/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp b/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp index 00411fc64c3..9da5e1a6e1a 100644 --- a/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp +++ b/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp @@ -13,7 +13,6 @@ #include "Acts/Utilities/AngleHelpers.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include #include diff --git a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp index 1b774a7bdf0..916addff0ae 100644 --- a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp +++ b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp @@ -11,10 +11,8 @@ #include "Acts/Utilities/MathHelpers.hpp" #include "ActsExamples/EventData/SimVertex.hpp" #include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include -#include #include #include #include diff --git a/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt b/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt index 85866466ec6..64255b89b14 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt +++ b/Examples/Algorithms/TrackFindingExaTrkX/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( src/PrototracksToParameters.cpp src/TrackFindingFromPrototrackAlgorithm.cpp src/TruthGraphBuilder.cpp + src/createFeatures.cpp ) target_include_directories( diff --git a/Examples/Algorithms/TrackFindingML/CMakeLists.txt b/Examples/Algorithms/TrackFindingML/CMakeLists.txt index d826b224f2c..80f55f60579 100644 --- a/Examples/Algorithms/TrackFindingML/CMakeLists.txt +++ b/Examples/Algorithms/TrackFindingML/CMakeLists.txt @@ -1,7 +1,5 @@ set(SOURCES - src/AmbiguityResolutionML.cpp src/AmbiguityResolutionMLAlgorithm.cpp - src/AmbiguityResolutionMLDBScanAlgorithm.cpp src/SeedFilterMLAlgorithm.cpp ) diff --git a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityDBScanClustering.hpp b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityDBScanClustering.hpp deleted file mode 100644 index 45fe4cb5480..00000000000 --- a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityDBScanClustering.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/EventData/TrackContainer.hpp" -#include "Acts/EventData/TrackContainerFrontendConcept.hpp" -#include "Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp" -#include "Acts/Utilities/DBScan.hpp" - -#include -#include -#include - -namespace Acts { - -/// Clusterise tracks based on shared hits -/// -/// @param trackMap Multimap storing pair of track ID and vector of measurement ID. The keys are the number of measurement and are just there to facilitate the ordering. -/// @param tracks Track container with all the track to be clustered -/// @param epsilon Maximum distance between 2 tracks to be clustered -/// @param minPoints Minimum number of tracks to create a cluster -/// @return an unordered map representing the clusters, the keys the ID of the primary track of each cluster and the store a vector of track IDs. -template -std::unordered_map> dbscanTrackClustering( - std::multimap>>& - trackMap, - const track_container_t& tracks, float epsilon = 0.07, int minPoints = 2) { - // Unordered map associating a vector with all the track ID of a cluster to - // the ID of the first track of the cluster - std::unordered_map> cluster; - // Unordered map associating hits to the ID of the first track of the - // different clusters. - std::unordered_map hitToTrack; - - // Initialize a DBScan of dimension 4 (phi, eta, z, Pt) - using DBSCAN = Acts::DBScan<4, double, 4>; - DBSCAN dbscan(epsilon, minPoints, true); - - std::vector> data; - std::size_t trackID = 0; - std::vector clusterAssignments; - - // Get the input feature of the network for all the tracks - for (const auto& [key, val] : trackMap) { - auto traj = tracks.getTrack(val.first); - data.push_back({Acts::VectorHelpers::eta(traj.momentum()), - Acts::VectorHelpers::phi(traj.momentum())}); - } - std::size_t clusterNb = dbscan.cluster(data, clusterAssignments); - - // Cluster track with DBScan - std::vector< - std::multimap>>> - dbscanClusters(clusterNb); - for (const auto& [key, val] : trackMap) { - std::size_t clusterID = clusterAssignments[trackID]; - dbscanClusters[clusterID].emplace(key, val); - trackID++; - } - - // Perform a subClustering of the DBScan cluster using the measurement ID - // clustering - for (const auto& dbscanCluster : dbscanClusters) { - auto subCluster = Acts::detail::clusterDuplicateTracks(dbscanCluster); - cluster.merge(subCluster); - if (!subCluster.empty()) { - std::cout << "Overlapping track ID, there must be an error" << std::endl; - } - } - return cluster; -} - -} // namespace Acts diff --git a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp deleted file mode 100644 index ea967a23c22..00000000000 --- a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "ActsExamples/EventData/Track.hpp" -#include "ActsExamples/Framework/IAlgorithm.hpp" - -#include -#include -#include - -namespace ActsExamples { - -/// Generic implementation of the machine learning ambiguity resolution -/// Contains method for data preparations -class AmbiguityResolutionML : public IAlgorithm { - public: - /// Construct the ambiguity resolution algorithm. - /// - /// @param name name of the algorithm - /// @param lvl is the logging level - AmbiguityResolutionML(std::string name, Acts::Logging::Level lvl); - - protected: - /// Associated measurements ID to Tracks ID - /// - /// @param tracks is the input track container - /// @param nMeasurementsMin minimum number of measurement per track - /// @return an ordered list containing pairs of track ID and associated measurement ID - std::multimap>> - mapTrackHits(const ConstTrackContainer& tracks, int nMeasurementsMin) const; - - /// Prepare the output track container to be written - /// - /// @param tracks is the input track container - /// @param goodTracks is list of the IDs of all the tracks we want to keep - ConstTrackContainer prepareOutputTrack( - const ConstTrackContainer& tracks, - std::vector& goodTracks) const; -}; - -} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp index 2e5d22ac01d..e591b2e201a 100644 --- a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp +++ b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp @@ -8,10 +8,11 @@ #pragma once +#include "Acts/AmbiguityResolution/AmbiguityResolutionML.hpp" #include "Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/DataHandle.hpp" -#include "ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" #include @@ -23,7 +24,10 @@ namespace ActsExamples { /// 1) Cluster together nearby tracks using shared hits /// 2) For each track use a neural network to compute a score /// 3) In each cluster keep the track with the highest score -class AmbiguityResolutionMLAlgorithm final : public AmbiguityResolutionML { +class AmbiguityResolutionMLAlgorithm final : public IAlgorithm { + using AmbiguityResolution = + Acts::AmbiguityResolutionML; + public: struct Config { /// Input track collection. @@ -33,7 +37,11 @@ class AmbiguityResolutionMLAlgorithm final : public AmbiguityResolutionML { /// Output track collection. std::string outputTracks; /// Minimum number of measurement to form a track. - int nMeasurementsMin = 7; + std::size_t nMeasurementsMin = 7; + /// Construct the ML ambiguity resolution configuration. + AmbiguityResolution::Config toAmbiguityResolutionMLConfig() const { + return {inputDuplicateNN, nMeasurementsMin}; + } }; /// Construct the ambiguity resolution algorithm. @@ -53,8 +61,7 @@ class AmbiguityResolutionMLAlgorithm final : public AmbiguityResolutionML { private: Config m_cfg; - // ONNX model for track selection - Acts::AmbiguityTrackClassifier m_duplicateClassifier; + AmbiguityResolution m_ambiML; ReadDataHandle m_inputTracks{this, "InputTracks"}; WriteDataHandle m_outputTracks{this, "OutputTracks"}; }; diff --git a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLDBScanAlgorithm.hpp b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLDBScanAlgorithm.hpp deleted file mode 100644 index 6ad387ffdc4..00000000000 --- a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/AmbiguityResolutionMLDBScanAlgorithm.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp" -#include "ActsExamples/EventData/Track.hpp" -#include "ActsExamples/Framework/DataHandle.hpp" -#include "ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp" - -#include - -namespace ActsExamples { - -/// Evicts tracks that seem to be duplicated and fake. -/// -/// The implementation works as follows: -/// 1) Cluster together nearby tracks using a DBScan -/// 2) Create subcluster based on tracks with shared hits -/// 3) For each track use a neural network to compute a score -/// 4) In each cluster keep the track with the highest score -class AmbiguityResolutionMLDBScanAlgorithm final - : public AmbiguityResolutionML { - public: - struct Config { - /// Input trajectories collection. - std::string inputTracks; - /// Path to the ONNX model for the duplicate neural network - std::string inputDuplicateNN; - /// Output trajectories collection. - std::string outputTracks; - /// Minimum number of measurement to form a track. - int nMeasurementsMin = 7; - /// Maximum distance between 2 tracks to be clustered in the DBScan - float epsilonDBScan = 0.07; - /// Minimum number of tracks to create a cluster in the DBScan - int minPointsDBScan = 2; - }; - - /// Construct the ambiguity resolution algorithm. - /// - /// @param cfg is the algorithm configuration - /// @param lvl is the logging level - AmbiguityResolutionMLDBScanAlgorithm(Config cfg, Acts::Logging::Level lvl); - - /// Run the ambiguity resolution algorithm. - /// - /// @param cxt 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; - // ONNX model for track selection - Acts::AmbiguityTrackClassifier m_duplicateClassifier; - ReadDataHandle m_inputTracks{this, "InputTracks"}; - WriteDataHandle m_outputTracks{this, "OutputTracks"}; -}; - -} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/SeedFilterMLAlgorithm.hpp b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/SeedFilterMLAlgorithm.hpp index 6472a6e8d2b..53659a33e4f 100644 --- a/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/SeedFilterMLAlgorithm.hpp +++ b/Examples/Algorithms/TrackFindingML/include/ActsExamples/TrackFindingML/SeedFilterMLAlgorithm.hpp @@ -12,7 +12,7 @@ #include "ActsExamples/EventData/SimSeed.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/DataHandle.hpp" -#include "ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" #include diff --git a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionML.cpp b/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionML.cpp deleted file mode 100644 index f3282a15d94..00000000000 --- a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionML.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/TrackFindingML/AmbiguityResolutionML.hpp" - -#include "ActsExamples/EventData/IndexSourceLink.hpp" -#include "ActsExamples/EventData/Measurement.hpp" - -ActsExamples::AmbiguityResolutionML::AmbiguityResolutionML( - std::string name, Acts::Logging::Level lvl) - : ActsExamples::IAlgorithm(name, lvl) {} - -std::multimap>> -ActsExamples::AmbiguityResolutionML::mapTrackHits( - const ActsExamples::ConstTrackContainer& tracks, - int nMeasurementsMin) const { - std::multimap>> trackMap; - // Loop over all the trajectories in the events - for (const auto& track : tracks) { - std::vector hits; - int nbMeasurements = 0; - // Store the hits id for the trajectory and compute the number of - // measurement - tracks.trackStateContainer().visitBackwards( - track.tipIndex(), [&](const auto& state) { - if (state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) { - std::size_t indexHit = - state.getUncalibratedSourceLink() - .template get() - .index(); - hits.emplace_back(indexHit); - ++nbMeasurements; - } - }); - if (nbMeasurements < nMeasurementsMin) { - continue; - } - trackMap.emplace(nbMeasurements, std::make_pair(track.index(), hits)); - } - return trackMap; -} - -ActsExamples::ConstTrackContainer -ActsExamples::AmbiguityResolutionML::prepareOutputTrack( - const ActsExamples::ConstTrackContainer& tracks, - std::vector& goodTracks) const { - std::shared_ptr trackStateContainer = - tracks.trackStateContainerHolder(); - auto trackContainer = std::make_shared(); - trackContainer->reserve(goodTracks.size()); - // Temporary empty track state container: we don't change the original one, - // but we need one for filtering - auto tempTrackStateContainer = - std::make_shared(); - - TrackContainer solvedTracks{trackContainer, tempTrackStateContainer}; - solvedTracks.ensureDynamicColumns(tracks); - - for (auto&& iTrack : goodTracks) { - auto destProxy = solvedTracks.makeTrack(); - auto srcProxy = tracks.getTrack(iTrack); - destProxy.copyFrom(srcProxy, false); - destProxy.tipIndex() = srcProxy.tipIndex(); - } - - ConstTrackContainer outputTracks{ - std::make_shared( - std::move(*trackContainer)), - trackStateContainer}; - return outputTracks; -} diff --git a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLAlgorithm.cpp b/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLAlgorithm.cpp index ba2e9abeb98..805913e60ae 100644 --- a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLAlgorithm.cpp +++ b/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLAlgorithm.cpp @@ -8,18 +8,30 @@ #include "ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include #include +static std::size_t sourceLinkHash(const Acts::SourceLink& a) { + return static_cast( + a.get().index()); +} + +static bool sourceLinkEquality(const Acts::SourceLink& a, + const Acts::SourceLink& b) { + return a.get().index() == + b.get().index(); +} + ActsExamples::AmbiguityResolutionMLAlgorithm::AmbiguityResolutionMLAlgorithm( ActsExamples::AmbiguityResolutionMLAlgorithm::Config cfg, Acts::Logging::Level lvl) - : ActsExamples::AmbiguityResolutionML("AmbiguityResolutionMLAlgorithm", - lvl), + : ActsExamples::IAlgorithm("AmbiguityResolutionMLAlgorithm", lvl), m_cfg(std::move(cfg)), - m_duplicateClassifier(m_cfg.inputDuplicateNN.c_str()) { + m_ambiML(m_cfg.toAmbiguityResolutionMLConfig(), logger().clone()) { if (m_cfg.inputTracks.empty()) { throw std::invalid_argument("Missing trajectories input collection"); } @@ -34,15 +46,32 @@ ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionMLAlgorithm::execute( const AlgorithmContext& ctx) const { // Read input data const auto& tracks = m_inputTracks(ctx); - // Associate measurement to their respective tracks + // Associate measurement to their respective tracks to prepare the track + // shared hits based clustering std::multimap>> - trackMap = mapTrackHits(tracks, m_cfg.nMeasurementsMin); + trackMap = + m_ambiML.mapTrackHits(tracks, &sourceLinkHash, &sourceLinkEquality); + // Cluster the tracks based on the shared hits auto cluster = Acts::detail::clusterDuplicateTracks(trackMap); // Select the ID of the track we want to keep std::vector goodTracks = - m_duplicateClassifier.solveAmbiguity(cluster, tracks); + m_ambiML.solveAmbiguity(cluster, tracks); // Prepare the output track collection from the IDs - auto outputTracks = prepareOutputTrack(tracks, goodTracks); + TrackContainer solvedTracks{std::make_shared(), + std::make_shared()}; + solvedTracks.ensureDynamicColumns(tracks); + for (auto iTrack : goodTracks) { + auto destProxy = solvedTracks.makeTrack(); + auto srcProxy = tracks.getTrack(iTrack); + destProxy.copyFrom(srcProxy, false); + destProxy.tipIndex() = srcProxy.tipIndex(); + } + + ActsExamples::ConstTrackContainer outputTracks{ + std::make_shared( + std::move(solvedTracks.container())), + tracks.trackStateContainerHolder()}; + m_outputTracks(ctx, std::move(outputTracks)); return ActsExamples::ProcessCode::SUCCESS; diff --git a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLDBScanAlgorithm.cpp b/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLDBScanAlgorithm.cpp deleted file mode 100644 index 6f0c7529208..00000000000 --- a/Examples/Algorithms/TrackFindingML/src/AmbiguityResolutionMLDBScanAlgorithm.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/TrackFindingML/AmbiguityResolutionMLDBScanAlgorithm.hpp" - -#include "ActsExamples/Framework/ProcessCode.hpp" -#include "ActsExamples/Framework/WhiteBoard.hpp" -#include "ActsExamples/TrackFindingML/AmbiguityDBScanClustering.hpp" - -#include -#include - -ActsExamples::AmbiguityResolutionMLDBScanAlgorithm:: - AmbiguityResolutionMLDBScanAlgorithm( - ActsExamples::AmbiguityResolutionMLDBScanAlgorithm::Config cfg, - Acts::Logging::Level lvl) - : ActsExamples::AmbiguityResolutionML( - "AmbiguityResolutionMLDBScanAlgorithm", lvl), - m_cfg(std::move(cfg)), - m_duplicateClassifier(m_cfg.inputDuplicateNN.c_str()) { - if (m_cfg.inputTracks.empty()) { - throw std::invalid_argument("Missing trajectories input collection"); - } - if (m_cfg.outputTracks.empty()) { - throw std::invalid_argument("Missing trajectories output collection"); - } - m_inputTracks.initialize(m_cfg.inputTracks); - m_outputTracks.initialize(m_cfg.outputTracks); -} - -ActsExamples::ProcessCode -ActsExamples::AmbiguityResolutionMLDBScanAlgorithm::execute( - const AlgorithmContext& ctx) const { - // Read input data - const auto& tracks = m_inputTracks(ctx); - // Associate measurement to their respective tracks - std::multimap>> - trackMap = mapTrackHits(tracks, m_cfg.nMeasurementsMin); - // Cluster the tracks using DBscan - auto cluster = Acts::dbscanTrackClustering( - trackMap, tracks, m_cfg.epsilonDBScan, m_cfg.minPointsDBScan); - // Select the ID of the track we want to keep - std::vector goodTracks = - m_duplicateClassifier.solveAmbiguity(cluster, tracks); - // Prepare the output track collection from the IDs - auto outputTracks = prepareOutputTrack(tracks, goodTracks); - m_outputTracks(ctx, std::move(outputTracks)); - - return ActsExamples::ProcessCode::SUCCESS; -} diff --git a/Examples/Algorithms/TrackFitting/CMakeLists.txt b/Examples/Algorithms/TrackFitting/CMakeLists.txt index 07e26091384..a6a9a5afe3b 100644 --- a/Examples/Algorithms/TrackFitting/CMakeLists.txt +++ b/Examples/Algorithms/TrackFitting/CMakeLists.txt @@ -2,7 +2,6 @@ add_library( ActsExamplesTrackFitting SHARED src/RefittingCalibrator.cpp - src/SurfaceSortingAlgorithm.cpp src/TrackFittingAlgorithm.cpp src/KalmanFitterFunction.cpp src/RefittingAlgorithm.cpp diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp deleted file mode 100644 index 02f85bff2e1..00000000000 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" -#include "ActsExamples/EventData/Measurement.hpp" -#include "ActsExamples/EventData/ProtoTrack.hpp" -#include "ActsExamples/EventData/SimHit.hpp" -#include "ActsExamples/EventData/Track.hpp" -#include "ActsExamples/Framework/DataHandle.hpp" -#include "ActsExamples/Framework/IAlgorithm.hpp" -#include "ActsExamples/Framework/ProcessCode.hpp" - -#include -#include -#include -#include - -namespace ActsExamples { -struct AlgorithmContext; - -using TrackHitList = std::map; - -class SurfaceSortingAlgorithm final : public IAlgorithm { - public: - struct Config { - /// Input proto track collection - std::string inputProtoTracks; - /// Input simulated hit collection - std::string inputSimHits; - /// Input measurement to simulated hit map for truth position - std::string inputMeasurementSimHitsMap; - /// Output proto track collection - std::string outputProtoTracks; - }; - - SurfaceSortingAlgorithm(Config cfg, Acts::Logging::Level level); - - ActsExamples::ProcessCode execute(const AlgorithmContext& ctx) const final; - - /// Get readonly access to the config parameters - const Config& config() const { return m_cfg; } - - private: - Config m_cfg; - - ReadDataHandle m_inputProtoTracks{this, - "InputProtoTracks"}; - ReadDataHandle m_inputSimHits{this, "InputSimHits"}; - ReadDataHandle m_inputMeasurementSimHitsMap{ - this, "InputMeasurementSimHitsMap"}; - WriteDataHandle m_outputProtoTracks{this, - "OutputProtoTracks"}; -}; - -} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp index 03aa74fa58f..79eb367589b 100644 --- a/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp @@ -64,7 +64,8 @@ ActsExamples::ProcessCode ActsExamples::RefittingAlgorithm::execute( auto itrack = 0ul; for (const auto& track : inputTracks) { // Check if you are not in picking mode - if (m_cfg.pickTrack > -1 && m_cfg.pickTrack != static_cast(itrack++)) { + if (m_cfg.pickTrack > -1 && + static_cast(m_cfg.pickTrack) != itrack++) { continue; } diff --git a/Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp deleted file mode 100644 index 040634efbe9..00000000000 --- a/Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp" - -#include "ActsExamples/EventData/ProtoTrack.hpp" -#include "ActsExamples/EventData/SimHit.hpp" -#include "ActsFatras/EventData/Hit.hpp" - -#include -#include -#include -#include -#include - -namespace ActsExamples { -struct AlgorithmContext; -} // namespace ActsExamples - -ActsExamples::SurfaceSortingAlgorithm::SurfaceSortingAlgorithm( - Config cfg, Acts::Logging::Level level) - : ActsExamples::IAlgorithm("SurfaceSortingAlgorithm", level), - m_cfg(std::move(cfg)) { - if (m_cfg.inputProtoTracks.empty()) { - throw std::invalid_argument("Missing input proto track collection"); - } - if (m_cfg.inputSimHits.empty()) { - throw std::invalid_argument("Missing input simulated hits collection"); - } - if (m_cfg.inputMeasurementSimHitsMap.empty()) { - throw std::invalid_argument("Missing input measurement sim hits map"); - } - if (m_cfg.outputProtoTracks.empty()) { - throw std::invalid_argument("Missing output proto track collection"); - } - - m_inputProtoTracks.initialize(m_cfg.inputProtoTracks); - m_inputSimHits.initialize(m_cfg.inputSimHits); - m_inputMeasurementSimHitsMap.initialize(m_cfg.inputMeasurementSimHitsMap); - m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); -} - -ActsExamples::ProcessCode ActsExamples::SurfaceSortingAlgorithm::execute( - const ActsExamples::AlgorithmContext& ctx) const { - const auto& protoTracks = m_inputProtoTracks(ctx); - const auto& simHits = m_inputSimHits(ctx); - const auto& simHitsMap = m_inputMeasurementSimHitsMap(ctx); - - ProtoTrackContainer sortedTracks; - sortedTracks.reserve(protoTracks.size()); - TrackHitList trackHitList; - - for (std::size_t itrack = 0; itrack < protoTracks.size(); ++itrack) { - const auto& protoTrack = protoTracks[itrack]; - - ProtoTrack sortedProtoTrack; - sortedProtoTrack.reserve(protoTrack.size()); - trackHitList.clear(); - - if (protoTrack.empty()) { - continue; - } - - for (const auto hit : protoTrack) { - const auto simHitIndex = simHitsMap.find(hit)->second; - auto simHit = simHits.nth(simHitIndex); - auto simHitTime = simHit->time(); - trackHitList.insert(std::make_pair(simHitTime, hit)); - } - - /// Map will now be sorted by truth hit time - for (auto const& [time, hit] : trackHitList) { - sortedProtoTrack.emplace_back(hit); - } - - sortedTracks.emplace_back(std::move(sortedProtoTrack)); - } - - m_outputProtoTracks(ctx, std::move(sortedTracks)); - - return ActsExamples::ProcessCode::SUCCESS; -} diff --git a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp index 50360e3d3b4..d79a864bc73 100644 --- a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp @@ -103,7 +103,8 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingAlgorithm::execute( std::vector trackSourceLinks; for (std::size_t itrack = 0; itrack < protoTracks.size(); ++itrack) { // Check if you are not in picking mode - if (m_cfg.pickTrack > -1 && m_cfg.pickTrack != static_cast(itrack)) { + if (m_cfg.pickTrack > -1 && + static_cast(m_cfg.pickTrack) != itrack) { continue; } diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp index b4ef0928da0..6896763f32c 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.cpp @@ -8,8 +8,8 @@ #include "ActsExamples/TruthTracking/ParticleSelector.hpp" -#include "Acts/Definitions/Common.hpp" #include "Acts/Utilities/VectorHelpers.hpp" +#include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" @@ -17,8 +17,10 @@ #include #include -ActsExamples::ParticleSelector::ParticleSelector(const Config& config, - Acts::Logging::Level level) +namespace ActsExamples { + +ParticleSelector::ParticleSelector(const Config& config, + Acts::Logging::Level level) : IAlgorithm("ParticleSelector", level), m_cfg(config) { if (m_cfg.inputParticles.empty()) { throw std::invalid_argument("Missing input particles collection"); @@ -28,8 +30,17 @@ ActsExamples::ParticleSelector::ParticleSelector(const Config& config, } m_inputParticles.initialize(m_cfg.inputParticles); + m_inputParticleMeasurementsMap.maybeInitialize( + m_cfg.inputParticleMeasurementsMap); m_outputParticles.initialize(m_cfg.outputParticles); + if (!m_inputParticleMeasurementsMap.isInitialized() && + (m_cfg.measurementsMin > 0 || + m_cfg.measurementsMax < std::numeric_limits::max())) { + throw std::invalid_argument( + "Measurement-based cuts require the inputMeasurementParticlesMap"); + } + ACTS_DEBUG("selection particle rho [" << m_cfg.rhoMin << "," << m_cfg.rhoMax << ")"); ACTS_DEBUG("selection particle |z| [" << m_cfg.absZMin << "," << m_cfg.absZMax @@ -46,6 +57,8 @@ ActsExamples::ParticleSelector::ParticleSelector(const Config& config, << ")"); ACTS_DEBUG("selection particle m [" << m_cfg.mMin << "," << m_cfg.mMax << ")"); + ACTS_DEBUG("selection particle hits [" << m_cfg.hitsMin << "," + << m_cfg.hitsMax << ")"); ACTS_DEBUG("selection particle measurements [" << m_cfg.measurementsMin << "," << m_cfg.measurementsMax << ")"); ACTS_DEBUG("remove charged particles " << m_cfg.removeCharged); @@ -59,12 +72,18 @@ ActsExamples::ParticleSelector::ParticleSelector(const Config& config, << m_cfg.maxPrimaryVertexId << ")"); } -ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( - const AlgorithmContext& ctx) const { +ProcessCode ParticleSelector::execute(const AlgorithmContext& ctx) const { // prepare input/ output types const SimParticleContainer& inputParticles = m_inputParticles(ctx); + const static InverseMultimap emptyMeasurementParticlesMap; + const InverseMultimap& inputMeasurementParticlesMap = + m_inputParticleMeasurementsMap.isInitialized() + ? m_inputParticleMeasurementsMap(ctx) + : emptyMeasurementParticlesMap; + std::size_t nInvalidCharge = 0; + std::size_t nInvalidHitCount = 0; std::size_t nInvalidMeasurementCount = 0; // helper functions to select tracks @@ -87,9 +106,14 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( nInvalidCharge += static_cast(!validCharge); - bool validMeasurementCount = - within(p.numberOfHits(), m_cfg.measurementsMin, m_cfg.measurementsMax); + const bool validHitCount = + within(p.numberOfHits(), m_cfg.hitsMin, m_cfg.hitsMax); + nInvalidHitCount += static_cast(!validHitCount); + const std::size_t measurementCount = + inputMeasurementParticlesMap.count(p.particleId()); + const bool validMeasurementCount = + within(measurementCount, m_cfg.measurementsMin, m_cfg.measurementsMax); nInvalidMeasurementCount += static_cast(!validMeasurementCount); @@ -103,7 +127,7 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( } return validPdg && validCharge && validSecondary && validPrimaryVertexId && - validMeasurementCount && + validHitCount && validMeasurementCount && within(p.transverseMomentum(), m_cfg.ptMin, m_cfg.ptMax) && within(std::abs(eta), m_cfg.absEtaMin, m_cfg.absEtaMax) && within(eta, m_cfg.etaMin, m_cfg.etaMax) && @@ -132,6 +156,7 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( << outputParticles.size() << " from " << inputParticles.size() << " particles"); ACTS_DEBUG("filtered out because of charge: " << nInvalidCharge); + ACTS_DEBUG("filtered out because of hit count: " << nInvalidHitCount); ACTS_DEBUG("filtered out because of measurement count: " << nInvalidMeasurementCount); @@ -139,3 +164,5 @@ ActsExamples::ProcessCode ActsExamples::ParticleSelector::execute( return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp index 4bd465d1b4d..4c57c5cbeef 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSelector.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" @@ -26,6 +27,9 @@ class ParticleSelector final : public IAlgorithm { struct Config { /// The input particles collection. std::string inputParticles; + /// (Optionally) The input particle measurements map. Only required for + /// measurement-based cuts. + std::string inputParticleMeasurementsMap; /// The output particles collection. std::string outputParticles; @@ -51,7 +55,10 @@ class ParticleSelector final : public IAlgorithm { // Rest mass cuts double mMin = 0; double mMax = std::numeric_limits::infinity(); - /// Measurement number cuts + // Hit count cuts + std::size_t hitsMin = 0; + std::size_t hitsMax = std::numeric_limits::max(); + // Measurement number cuts std::size_t measurementsMin = 0; std::size_t measurementsMax = std::numeric_limits::max(); /// Remove charged particles. @@ -81,6 +88,8 @@ class ParticleSelector final : public IAlgorithm { Config m_cfg; ReadDataHandle m_inputParticles{this, "InputParticles"}; + ReadDataHandle> m_inputParticleMeasurementsMap{ + this, "InputParticleMeasurementsMap"}; WriteDataHandle m_outputParticles{this, "OutputParticles"}; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackTruthMatcher.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackTruthMatcher.hpp index 5f8d89a98b0..6c11d4e0c43 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackTruthMatcher.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackTruthMatcher.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/EventData/TruthMatching.hpp" @@ -21,8 +21,6 @@ namespace ActsExamples { -struct AlgorithmContext; - /// Matches tracks to truth particles and vice versa class TrackTruthMatcher final : public IAlgorithm { public: @@ -56,7 +54,7 @@ class TrackTruthMatcher final : public IAlgorithm { ReadDataHandle m_inputTracks{this, "InputTracks"}; ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMap"}; WriteDataHandle m_outputTrackParticleMatching{ this, "OutputTrackParticleMatching"}; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp index 6ea0225e200..2e5d1c4cd15 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp @@ -12,7 +12,6 @@ #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Utilities/Range.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include #include @@ -25,18 +24,16 @@ #include namespace ActsExamples { -struct AlgorithmContext; -} // namespace ActsExamples -ActsExamples::TruthSeedingAlgorithm::TruthSeedingAlgorithm( - ActsExamples::TruthSeedingAlgorithm::Config cfg, Acts::Logging::Level lvl) - : ActsExamples::IAlgorithm("TruthSeedingAlgorithm", lvl), - m_cfg(std::move(cfg)) { +TruthSeedingAlgorithm::TruthSeedingAlgorithm(Config cfg, + Acts::Logging::Level lvl) + : IAlgorithm("TruthSeedingAlgorithm", lvl), m_cfg(std::move(cfg)) { if (m_cfg.inputParticles.empty()) { throw std::invalid_argument("Missing input truth particles collection"); } - if (m_cfg.inputMeasurementParticlesMap.empty()) { - throw std::invalid_argument("Missing input hit-particles map collection"); + if (m_cfg.inputParticleMeasurementsMap.empty()) { + throw std::invalid_argument( + "Missing input particle-measurements map collection"); } if (m_cfg.inputSpacePoints.empty()) { throw std::invalid_argument("Missing seeds or space point collection"); @@ -65,20 +62,16 @@ ActsExamples::TruthSeedingAlgorithm::TruthSeedingAlgorithm( } m_inputParticles.initialize(m_cfg.inputParticles); - m_inputMeasurementParticlesMap.initialize(m_cfg.inputMeasurementParticlesMap); + m_inputParticleMeasurementsMap.initialize(m_cfg.inputParticleMeasurementsMap); m_outputParticles.initialize(m_cfg.outputParticles); m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); m_outputSeeds.initialize(m_cfg.outputSeeds); } -ActsExamples::ProcessCode ActsExamples::TruthSeedingAlgorithm::execute( - const ActsExamples::AlgorithmContext& ctx) const { +ProcessCode TruthSeedingAlgorithm::execute(const AlgorithmContext& ctx) const { // prepare input collections const auto& particles = m_inputParticles(ctx); - const auto& hitParticlesMap = m_inputMeasurementParticlesMap(ctx); - // compute particle_id -> {hit_id...} map from the - // hit_id -> {particle_id...} map on the fly. - const auto& particleHitsMap = invertIndexMultimap(hitParticlesMap); + const auto& particleMeasurementsMap = m_inputParticleMeasurementsMap(ctx); // construct the combined input container of space point pointers from all // configured input sources. @@ -120,27 +113,28 @@ ActsExamples::ProcessCode ActsExamples::TruthSeedingAlgorithm::execute( } for (const auto& particle : particles) { - // find the corresponding hits for this particle - const auto& hits = - makeRange(particleHitsMap.equal_range(particle.particleId())); - // fill hit indices to create the proto track + // find the corresponding measurements for this particle + const auto& measurements = + makeRange(particleMeasurementsMap.equal_range(particle.particleId())); + // fill measurement indices to create the proto track ProtoTrack track; - track.reserve(hits.size()); - for (const auto& hit : hits) { - track.push_back(hit.second); + track.reserve(measurements.size()); + for (const auto& measurement : measurements) { + track.push_back(measurement.second); } - // The list of hits and the initial start parameters + // The list of measurements and the initial start parameters if (track.size() < 3) { - ACTS_WARNING("Particle " << particle << " has less than 3 hits"); + ACTS_WARNING("Particle " << particle << " has less than 3 measurements"); continue; } // Space points on the proto track std::vector spacePointsOnTrack; spacePointsOnTrack.reserve(track.size()); - // Loop over the hit index on the proto track to find the space points - for (const auto& hitIndex : track) { - auto it = spMap.find(hitIndex); + // Loop over the measurement index on the proto track to find the space + // points + for (const auto& measurementIndex : track) { + auto it = spMap.find(measurementIndex); if (it != spMap.end()) { spacePointsOnTrack.push_back(it->second); } @@ -198,5 +192,7 @@ ActsExamples::ProcessCode ActsExamples::TruthSeedingAlgorithm::execute( m_outputProtoTracks(ctx, std::move(tracks)); m_outputSeeds(ctx, std::move(seeds)); - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp index a6cb0416160..5807ef54cd0 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" -#include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/SimSeed.hpp" #include "ActsExamples/EventData/SimSpacePoint.hpp" @@ -23,16 +22,7 @@ #include #include -namespace ActsFatras { -class Barcode; -} // namespace ActsFatras - -namespace Acts { -class TrackingGeometry; -} - namespace ActsExamples { -struct AlgorithmContext; /// Construct track seeds from particles. class TruthSeedingAlgorithm final : public IAlgorithm { @@ -40,8 +30,8 @@ class TruthSeedingAlgorithm final : public IAlgorithm { struct Config { /// The input truth particles that should be used for truth seeding. std::string inputParticles; - /// The input hit-particles map collection. - std::string inputMeasurementParticlesMap; + /// The input particle-measurements map collection. + std::string inputParticleMeasurementsMap; /// Input space point collections. /// /// We allow multiple space point collections to allow different parts of @@ -80,8 +70,8 @@ class TruthSeedingAlgorithm final : public IAlgorithm { Config m_cfg; ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle m_inputMeasurementParticlesMap{ - this, "InputMeasurementParticlesMaps"}; + ReadDataHandle> m_inputParticleMeasurementsMap{ + this, "InputParticleMeasurementsMap"}; std::vector>> m_inputSpacePoints{}; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.cpp index 54953ce7c2b..84b08693635 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.cpp @@ -8,23 +8,15 @@ #include "ActsExamples/TruthTracking/TruthTrackFinder.hpp" -#include "Acts/Utilities/MultiIndex.hpp" -#include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Utilities/Range.hpp" -#include "ActsFatras/EventData/Particle.hpp" -#include #include #include #include namespace ActsExamples { -struct AlgorithmContext; -} // namespace ActsExamples - -using namespace ActsExamples; TruthTrackFinder::TruthTrackFinder(const Config& config, Acts::Logging::Level level) @@ -32,7 +24,7 @@ TruthTrackFinder::TruthTrackFinder(const Config& config, if (m_cfg.inputParticles.empty()) { throw std::invalid_argument("Missing input truth particles collection"); } - if (m_cfg.inputMeasurementParticlesMap.empty()) { + if (m_cfg.inputParticleMeasurementsMap.empty()) { throw std::invalid_argument("Missing input hit-particles map collection"); } if (m_cfg.outputProtoTracks.empty()) { @@ -40,17 +32,14 @@ TruthTrackFinder::TruthTrackFinder(const Config& config, } m_inputParticles.initialize(m_cfg.inputParticles); - m_inputMeasurementParticlesMap.initialize(m_cfg.inputMeasurementParticlesMap); + m_inputParticleMeasurementsMap.initialize(m_cfg.inputParticleMeasurementsMap); m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); } ProcessCode TruthTrackFinder::execute(const AlgorithmContext& ctx) const { // prepare input collections const auto& particles = m_inputParticles(ctx); - const auto& hitParticlesMap = m_inputMeasurementParticlesMap(ctx); - // compute particle_id -> {hit_id...} map from the - // hit_id -> {particle_id...} map on the fly. - const auto& particleHitsMap = invertIndexMultimap(hitParticlesMap); + const auto& particleMeasurementsMap = m_inputParticleMeasurementsMap(ctx); // prepare output collection ProtoTrackContainer tracks; @@ -59,14 +48,15 @@ ProcessCode TruthTrackFinder::execute(const AlgorithmContext& ctx) const { ACTS_VERBOSE("Create prototracks for " << particles.size() << " particles"); for (const auto& particle : particles) { // find the corresponding hits for this particle - const auto& hits = - makeRange(particleHitsMap.equal_range(particle.particleId())); - ACTS_VERBOSE(" - Prototrack from " << hits.size() << " hits"); + const auto& measurements = + makeRange(particleMeasurementsMap.equal_range(particle.particleId())); + ACTS_VERBOSE(" - Prototrack from " << measurements.size() + << " measurements"); // fill hit indices to create the proto track ProtoTrack track; - track.reserve(hits.size()); - for (const auto& hit : hits) { - track.emplace_back(hit.second); + track.reserve(measurements.size()); + for (const auto& measurement : measurements) { + track.emplace_back(measurement.second); } // add proto track to the output collection tracks.emplace_back(std::move(track)); @@ -75,3 +65,5 @@ ProcessCode TruthTrackFinder::execute(const AlgorithmContext& ctx) const { m_outputProtoTracks(ctx, std::move(tracks)); return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.hpp index 8f7affb79c3..39c42f651b3 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthTrackFinder.hpp @@ -10,7 +10,6 @@ #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" -#include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" @@ -18,12 +17,7 @@ #include -namespace ActsFatras { -class Barcode; -} // namespace ActsFatras - namespace ActsExamples { -struct AlgorithmContext; /// Convert true particle tracks into "reconstructed" proto tracks. /// @@ -37,8 +31,8 @@ class TruthTrackFinder final : public IAlgorithm { struct Config { /// The input truth particles that should be used to create proto tracks. std::string inputParticles; - /// The input hit-particles map collection. - std::string inputMeasurementParticlesMap; + /// The input particle-measurements map collection. + std::string inputParticleMeasurementsMap; /// The output proto tracks collection. std::string outputProtoTracks; }; @@ -55,8 +49,8 @@ class TruthTrackFinder final : public IAlgorithm { ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle m_inputMeasurementParticlesMap{ - this, "InputMeasurementParticlesMap"}; + ReadDataHandle> m_inputParticleMeasurementsMap{ + this, "InputParticleMeasurementsMap"}; WriteDataHandle m_outputProtoTracks{this, "OutputProtoTracks"}; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthVertexFinder.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthVertexFinder.hpp index 2ea7d401866..bbbefa2dd96 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthVertexFinder.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthVertexFinder.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoVertex.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/Track.hpp" @@ -19,13 +20,10 @@ #include namespace ActsExamples { -struct AlgorithmContext; /// Group particles into proto vertices using truth information. class TruthVertexFinder final : public IAlgorithm { public: - using HitParticlesMap = ActsExamples::IndexMultimap; - struct Config { /// The input tracks that should be used to create proto vertices. std::string inputTracks; @@ -55,7 +53,7 @@ class TruthVertexFinder final : public IAlgorithm { ReadDataHandle m_inputTracks{this, "InputTracks"}; ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMap"}; WriteDataHandle m_outputProtoVertices{ this, "OutputProtoVertices"}; diff --git a/Examples/Detectors/CMakeLists.txt b/Examples/Detectors/CMakeLists.txt index 7e7aed5daed..ee63c6a3eb5 100644 --- a/Examples/Detectors/CMakeLists.txt +++ b/Examples/Detectors/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(Common) add_subdirectory(ContextualDetector) add_subdirectory_if(DD4hepDetector ACTS_BUILD_EXAMPLES_DD4HEP) add_subdirectory(GenericDetector) @@ -7,3 +8,4 @@ add_subdirectory(TGeoDetector) add_subdirectory(ITkModuleSplitting) add_subdirectory(TelescopeDetector) add_subdirectory_if(MuonSpectrometerMockupDetector ACTS_BUILD_EXAMPLES_GEANT4) +add_subdirectory_if(GeoModelDetector ACTS_BUILD_PLUGIN_GEOMODEL) diff --git a/Examples/Detectors/Common/CMakeLists.txt b/Examples/Detectors/Common/CMakeLists.txt new file mode 100644 index 00000000000..1464b2274c8 --- /dev/null +++ b/Examples/Detectors/Common/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(ActsExamplesDetectorsCommon SHARED src/Detector.cpp) +target_include_directories( + ActsExamplesDetectorsCommon + PUBLIC $ +) +target_link_libraries( + ActsExamplesDetectorsCommon + PUBLIC ActsCore ActsExamplesFramework +) + +install( + TARGETS ActsExamplesDetectorsCommon + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/Examples/Detectors/Common/include/ActsExamples/DetectorCommons/Detector.hpp b/Examples/Detectors/Common/include/ActsExamples/DetectorCommons/Detector.hpp new file mode 100644 index 00000000000..40e026f1935 --- /dev/null +++ b/Examples/Detectors/Common/include/ActsExamples/DetectorCommons/Detector.hpp @@ -0,0 +1,69 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/GeometryContext.hpp" + +#include +#include + +namespace Acts { +class GeometryContext; +class TrackingGeometry; +class DetectorElementBase; +class Logger; +namespace Experimental { +class Detector; +} // namespace Experimental +} // namespace Acts + +namespace ActsExamples { +class IContextDecorator; +struct Geant4ConstructionOptions; +} // namespace ActsExamples + +class G4VUserDetectorConstruction; + +namespace ActsExamples { + +/// Base class for detector instances +class Detector { + public: + explicit Detector(std::unique_ptr logger); + virtual ~Detector(); + + virtual const Acts::GeometryContext& nominalGeometryContext() const; + + virtual std::shared_ptr trackingGeometry() + const; + virtual std::shared_ptr gen2Geometry() const; + virtual std::vector> contextDecorators() + const; + + /// Build the Geant4 detector construction + /// @note This throws an exception if Geant4 is not enabled + /// @param options The Geant4 construction options + /// @return The Geant4 detector construction + virtual std::unique_ptr + buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const; + + protected: + const Acts::Logger& logger() const; + + std::unique_ptr m_logger; + + Acts::GeometryContext m_nominalGeometryContext; + std::shared_ptr m_trackingGeometry; + std::shared_ptr m_gen2Geometry; + std::vector> m_detectorStore; + std::vector> m_contextDecorators; +}; + +} // namespace ActsExamples diff --git a/Examples/Detectors/Common/src/Detector.cpp b/Examples/Detectors/Common/src/Detector.cpp new file mode 100644 index 00000000000..ce1291a2485 --- /dev/null +++ b/Examples/Detectors/Common/src/Detector.cpp @@ -0,0 +1,58 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DetectorCommons/Detector.hpp" + +#include "Acts/Detector/Detector.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/Framework/IContextDecorator.hpp" + +namespace ActsExamples { + +Detector::Detector(std::unique_ptr logger) + : m_logger(std::move(logger)) {} + +Detector::~Detector() = default; + +std::vector> Detector::contextDecorators() + const { + return m_contextDecorators; +} + +std::unique_ptr +Detector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& /*options*/) const { + throw std::runtime_error("Geant4 detector construction is not available."); +} + +const Acts::GeometryContext& Detector::nominalGeometryContext() const { + return m_nominalGeometryContext; +} + +std::shared_ptr Detector::trackingGeometry() + const { + if (m_trackingGeometry == nullptr) { + throw std::runtime_error("Tracking geometry is not built"); + } + return m_trackingGeometry; +} + +std::shared_ptr Detector::gen2Geometry() const { + if (m_gen2Geometry == nullptr) { + throw std::runtime_error("Gen2 geometry is not built"); + } + return m_gen2Geometry; +} + +const Acts::Logger& Detector::logger() const { + return *m_logger; +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/ContextualDetector/CMakeLists.txt b/Examples/Detectors/ContextualDetector/CMakeLists.txt index 2cb61207c53..550a4cb450a 100644 --- a/Examples/Detectors/ContextualDetector/CMakeLists.txt +++ b/Examples/Detectors/ContextualDetector/CMakeLists.txt @@ -5,13 +5,19 @@ add_library( src/InternalAlignmentDecorator.cpp src/ExternalAlignmentDecorator.cpp ) + target_include_directories( ActsExamplesDetectorContextual PUBLIC $ ) + target_link_libraries( ActsExamplesDetectorContextual - PUBLIC ActsCore ActsExamplesFramework ActsExamplesDetectorGeneric + PUBLIC + ActsCore + ActsExamplesFramework + ActsExamplesDetectorsCommon + ActsExamplesDetectorGeneric ) install( diff --git a/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp b/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp index cfd12568d63..7a184f16fc1 100644 --- a/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp +++ b/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp @@ -10,38 +10,23 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/GenericDetector/GenericDetector.hpp" -#include -#include -#include -#include - -namespace Acts { -class TrackingGeometry; -class IMaterialDecorator; -} // namespace Acts - namespace ActsExamples { -class IContextDecorator; -class GenericDetectorElement; - class InternallyAlignedDetectorElement; class InternalAlignmentDecorator; -class AlignedDetector { +class AlignedDetector : public Detector { public: - using ContextDecorators = std::vector>; - using TrackingGeometryPtr = std::shared_ptr; - struct Config : public GenericDetector::Config { /// Seed for the decorator random numbers. - std::size_t seed = 1324354657; + unsigned int seed = 1324354657; /// Size of a valid IOV. - std::size_t iovSize = 100; + unsigned int iovSize = 100; /// Span until garbage collection is active. - std::size_t flushSize = 200; + unsigned int flushSize = 200; /// Run the garbage collection? bool doGarbageCollection = true; /// Sigma of the in-plane misalignment @@ -61,19 +46,10 @@ class AlignedDetector { Mode mode = Mode::Internal; }; - std::pair finalize( - const Config& cfg, - std::shared_ptr mdecorator); - - std::vector>>& - detectorStore() { - return m_detectorStore; - } + explicit AlignedDetector(const Config& cfg); private: - /// The Store of the detector elements (lifetime: job) - std::vector>> - m_detectorStore; + Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Detectors/ContextualDetector/src/AlignedDetector.cpp b/Examples/Detectors/ContextualDetector/src/AlignedDetector.cpp index 84e050c8470..3284723cebc 100644 --- a/Examples/Detectors/ContextualDetector/src/AlignedDetector.cpp +++ b/Examples/Detectors/ContextualDetector/src/AlignedDetector.cpp @@ -9,7 +9,6 @@ #include "ActsExamples/ContextualDetector/AlignedDetector.hpp" #include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/ContextualDetector/AlignmentDecorator.hpp" #include "ActsExamples/ContextualDetector/ExternalAlignmentDecorator.hpp" @@ -19,95 +18,88 @@ #include "ActsExamples/Framework/RandomNumbers.hpp" #include "ActsExamples/GenericDetector/BuildGenericDetector.hpp" +#include + namespace ActsExamples { -auto AlignedDetector::finalize( - const Config& cfg, - std::shared_ptr mdecorator) - -> std::pair { - ContextDecorators aContextDecorators; +AlignedDetector::AlignedDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("AlignedDetector", cfg.logLevel)), + m_cfg(cfg) { + if (m_cfg.mode == Config::Mode::External) { + InternallyAlignedDetectorElement::ContextType nominalContext; + m_nominalGeometryContext = Acts::GeometryContext(nominalContext); + } else { + InternallyAlignedDetectorElement::ContextType nominalContext; + nominalContext.nominal = true; + m_nominalGeometryContext = Acts::GeometryContext(nominalContext); + } // Let's create a random number service RandomNumbers::Config randomNumberConfig; - randomNumberConfig.seed = cfg.seed; + randomNumberConfig.seed = m_cfg.seed; auto randomNumberSvc = std::make_shared(randomNumberConfig); auto fillDecoratorConfig = [&](AlignmentDecorator::Config& config) { - config.iovSize = cfg.iovSize; - config.flushSize = cfg.flushSize; - config.doGarbageCollection = cfg.doGarbageCollection; + config.iovSize = m_cfg.iovSize; + config.flushSize = m_cfg.flushSize; + config.doGarbageCollection = m_cfg.doGarbageCollection; // The misalignments - config.gSigmaX = cfg.sigmaInPlane; - config.gSigmaY = cfg.sigmaInPlane; - config.gSigmaZ = cfg.sigmaOutPlane; - config.aSigmaX = cfg.sigmaOutRot; - config.aSigmaY = cfg.sigmaOutRot; - config.aSigmaZ = cfg.sigmaInRot; + config.gSigmaX = m_cfg.sigmaInPlane; + config.gSigmaY = m_cfg.sigmaInPlane; + config.gSigmaZ = m_cfg.sigmaOutPlane; + config.aSigmaX = m_cfg.sigmaOutRot; + config.aSigmaY = m_cfg.sigmaOutRot; + config.aSigmaZ = m_cfg.sigmaInRot; config.randomNumberSvc = randomNumberSvc; - config.firstIovNominal = cfg.firstIovNominal; + config.firstIovNominal = m_cfg.firstIovNominal; }; - TrackingGeometryPtr aTrackingGeometry; - if (cfg.mode == Config::Mode::External) { - ExternallyAlignedDetectorElement::ContextType nominalContext; - Acts::GeometryContext geometryContext(nominalContext); - + if (m_cfg.mode == Config::Mode::External) { ExternalAlignmentDecorator::Config agcsConfig; fillDecoratorConfig(agcsConfig); std::vector>> - detectorStore; + specificDetectorStore; - aTrackingGeometry = + m_trackingGeometry = Generic::buildDetector( - geometryContext, detectorStore, cfg.buildLevel, - std::move(mdecorator), cfg.buildProto, cfg.surfaceLogLevel, - cfg.layerLogLevel, cfg.volumeLogLevel); - - agcsConfig.trackingGeometry = aTrackingGeometry; + m_nominalGeometryContext, specificDetectorStore, m_cfg.buildLevel, + m_cfg.materialDecorator, m_cfg.buildProto, m_cfg.surfaceLogLevel, + m_cfg.layerLogLevel, m_cfg.volumeLogLevel); + agcsConfig.trackingGeometry = m_trackingGeometry; // need to upcast to store in this object as well - for (auto& lstore : detectorStore) { - auto& target = m_detectorStore.emplace_back(); + for (auto& lstore : specificDetectorStore) { for (auto& ldet : lstore) { - target.push_back(ldet); + m_detectorStore.push_back(ldet); } } - aContextDecorators.push_back(std::make_shared( + m_contextDecorators.push_back(std::make_shared( std::move(agcsConfig), - Acts::getDefaultLogger("AlignmentDecorator", cfg.decoratorLogLevel))); + Acts::getDefaultLogger("AlignmentDecorator", m_cfg.decoratorLogLevel))); } else { - InternallyAlignedDetectorElement::ContextType nominalContext; - nominalContext.nominal = true; - Acts::GeometryContext geometryContext(nominalContext); - InternalAlignmentDecorator::Config agcsConfig; fillDecoratorConfig(agcsConfig); - aTrackingGeometry = + m_trackingGeometry = Generic::buildDetector( - geometryContext, agcsConfig.detectorStore, cfg.buildLevel, - std::move(mdecorator), cfg.buildProto, cfg.surfaceLogLevel, - cfg.layerLogLevel, cfg.volumeLogLevel); + m_nominalGeometryContext, agcsConfig.detectorStore, + m_cfg.buildLevel, m_cfg.materialDecorator, m_cfg.buildProto, + m_cfg.surfaceLogLevel, m_cfg.layerLogLevel, m_cfg.volumeLogLevel); // need to upcast to store in this object as well for (auto& lstore : agcsConfig.detectorStore) { - auto& target = m_detectorStore.emplace_back(); for (auto& ldet : lstore) { - target.push_back(ldet); + m_detectorStore.push_back(ldet); } } - aContextDecorators.push_back(std::make_shared( + m_contextDecorators.push_back(std::make_shared( std::move(agcsConfig), - Acts::getDefaultLogger("AlignmentDecorator", cfg.decoratorLogLevel))); + Acts::getDefaultLogger("AlignmentDecorator", m_cfg.decoratorLogLevel))); } - - // return the pair of geometry and the alignment decorator(s) - return std::make_pair( - std::move(aTrackingGeometry), std::move(aContextDecorators)); } } // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/CMakeLists.txt b/Examples/Detectors/DD4hepDetector/CMakeLists.txt index 244b4f20a6d..d09e8e1345f 100644 --- a/Examples/Detectors/DD4hepDetector/CMakeLists.txt +++ b/Examples/Detectors/DD4hepDetector/CMakeLists.txt @@ -1,9 +1,4 @@ -add_library( - ActsExamplesDetectorDD4hep - SHARED - src/DD4hepDetector.cpp - src/DD4hepGeometryService.cpp -) +add_library(ActsExamplesDetectorDD4hep SHARED src/DD4hepDetector.cpp) target_include_directories( ActsExamplesDetectorDD4hep @@ -11,9 +6,37 @@ target_include_directories( ) target_link_libraries( ActsExamplesDetectorDD4hep - PUBLIC ActsCore ActsPluginDD4hep ActsExamplesFramework + PUBLIC + ActsCore + ActsPluginDD4hep + ActsExamplesFramework + ActsExamplesDetectorsCommon ) +if(ACTS_BUILD_EXAMPLES_GEANT4) + if(${DD4hep_VERSION} VERSION_LESS 1.11) + target_link_libraries( + ActsExamplesDetectorDD4hep + PUBLIC ActsExamplesGeant4 ${DD4hep_DDG4_LIBRARY} + ) + else() + target_link_libraries( + ActsExamplesDetectorDD4hep + PUBLIC ActsExamplesGeant4 DD4hep::DDG4 + ) + endif() + + target_sources( + ActsExamplesDetectorDD4hep + PUBLIC src/DDG4DetectorConstruction.cpp src/DD4hepDetectorGeant4.cpp + ) +else() + target_sources( + ActsExamplesDetectorDD4hep + PUBLIC src/DD4hepDetectorGeant4Stub.cpp + ) +endif() + if(${DD4hep_VERSION} VERSION_LESS 1.11) target_include_directories( ActsExamplesDetectorDD4hep diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp index 422af198ff5..acb17042e78 100644 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp @@ -8,84 +8,96 @@ #pragma once -#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp" -#include "Acts/Plugins/DD4hep/DD4hepDetectorStructure.hpp" -#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" - +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Material/IMaterialDecorator.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" + +#include #include -#include -#include +#include #include +class TGeoNode; + namespace dd4hep { class Detector; +class DetElement; } // namespace dd4hep -namespace Acts { -class TrackingGeometry; -class IMaterialDecorator; -class DD4hepFieldAdapter; -namespace Experimental { -class Detector; -} // namespace Experimental -} // namespace Acts - namespace ActsExamples { -class IContextDecorator; - -struct DD4hepDetector { - /// @brief The context decorators - using ContextDecorators = std::vector>; - - /// @brief The tracking geometry - using TrackingGeometryPtr = std::shared_ptr; - - /// @brief The detector geometry - using DetectorPtr = std::shared_ptr; - - /// @brief Default constructor - DD4hepDetector() = default; - /// @brief Constructor from geometry service - /// @param _geometryService the geometry service - explicit DD4hepDetector( - std::shared_ptr _geometryService); - /// @brief Default destructor - ~DD4hepDetector() = default; - - /// @brief The DD4hep geometry service - std::shared_ptr geometryService = nullptr; - - /// @brief Build the tracking geometry from the DD4hep geometry - /// - /// @param config is the configuration of the geometry service - /// @param mdecorator is the material decorator provided - /// - /// @return a pair of tracking geometry and context decorators - std::pair finalize( - DD4hepGeometryService::Config config, - std::shared_ptr mdecorator); - - /// @brief Build the detector from the DD4hep geometry - /// - /// @param gctx is the geometry context - /// @param options is the options struct for the building process - /// - /// @note the lifetime of the detector store has to exceed that of the - /// detector object as the converted surfaces point back to the - /// detector elements - /// - /// @return a tuple of detector, context decorators, and the element store - std::tuple - finalize( - const Acts::GeometryContext& gctx, - const Acts::Experimental::DD4hepDetectorStructure::Options& options = {}); - - void drop(); - - /// @brief Access to the DD4hep field - /// @return a shared pointer to the DD4hep field - std::shared_ptr field() const; +void sortFCChhDetElements(std::vector& det); + +/// @class DD4hepDetector +/// +/// @brief geometries from dd4hep input +/// +/// The DD4hepDetector creates the DD4hep, the TGeo and the ACTS +/// TrackingGeometry from DD4hep xml input. +class DD4hepDetector : public Detector { + public: + struct Config { + /// Log level for the geometry service. + Acts::Logging::Level logLevel = Acts::Logging::Level::INFO; + /// Log level for DD4hep itself + Acts::Logging::Level dd4hepLogLevel = Acts::Logging::Level::WARNING; + /// XML-file with the detector description + std::vector xmlFileNames; + /// The name of the service + std::string name = "default"; + /// Binningtype in phi + Acts::BinningType bTypePhi = Acts::equidistant; + /// Binningtype in r + Acts::BinningType bTypeR = Acts::arbitrary; + /// Binningtype in z + Acts::BinningType bTypeZ = Acts::equidistant; + /// The tolerance added to the geometrical extension in r + /// of the layers contained to build the volume envelope around + /// @note this parameter only needs to be set if the volumes containing + /// the + /// layers (e.g. barrel, endcap volumes) have no specific shape + /// (assemblies) + double envelopeR = 1 * Acts::UnitConstants::mm; + /// The tolerance added to the geometrical extension in z + /// of the layers contained to build the volume envelope around + /// @note this parameter only needs to be set if the volumes containing + /// the layers (e.g. barrel, endcap volumes) have no specific shape + /// (assemblies) + double envelopeZ = 1 * Acts::UnitConstants::mm; + double defaultLayerThickness = 1e-10; + std::function& detectors)> + sortDetectors = sortFCChhDetElements; + /// Material decorator + std::shared_ptr materialDecorator; + + /// Optional geometry identifier hook to be used during closure + std::shared_ptr geometryIdentifierHook = + std::make_shared(); + }; + + explicit DD4hepDetector(const Config& cfg); + + /// Interface method to access to the DD4hep geometry + dd4hep::Detector& dd4hepDetector(); + + /// Interface method to Access the TGeo geometry + /// @return The world TGeoNode (physical volume) + TGeoNode& tgeoGeometry(); + + std::unique_ptr buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const override; + + private: + Config m_cfg; + + /// Pointer to the interface to the DD4hep geometry + std::shared_ptr m_detector; + + std::unique_ptr buildDD4hepGeometry() const; }; } // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp deleted file mode 100644 index 1111f8de8e7..00000000000 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp +++ /dev/null @@ -1,137 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/GeometryIdentifier.hpp" -#include "ActsExamples/Framework/ProcessCode.hpp" -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -class TGeoNode; -namespace Acts { -class IMaterialDecorator; -class TrackingGeometry; -} // namespace Acts -namespace dd4hep { -class Detector; -} // namespace dd4hep - -namespace ActsExamples { - -void sortFCChhDetElements(std::vector& det); - -/// @class DD4hepGeometryService -/// -/// @brief service creating geometries from dd4hep input -/// -/// The DD4hepGeometryService creates the DD4hep, the TGeo and the ACTS -/// TrackingGeometry -/// from DD4hep xml input. The geometries are created only on demand. -class DD4hepGeometryService { - public: - struct Config { - /// Log level for the geometry service. - Acts::Logging::Level logLevel = Acts::Logging::Level::INFO; - /// Log level for DD4hep itself - Acts::Logging::Level dd4hepLogLevel = Acts::Logging::Level::WARNING; - /// XML-file with the detector description - std::vector xmlFileNames; - /// The name of the service - std::string name = "default"; - /// Binningtype in phi - Acts::BinningType bTypePhi = Acts::equidistant; - /// Binningtype in r - Acts::BinningType bTypeR = Acts::arbitrary; - /// Binningtype in z - Acts::BinningType bTypeZ = Acts::equidistant; - /// The tolerance added to the geometrical extension in r - /// of the layers contained to build the volume envelope around - /// @note this parameter only needs to be set if the volumes containing - /// the - /// layers (e.g. barrel, endcap volumes) have no specific shape - /// (assemblies) - double envelopeR = 1 * Acts::UnitConstants::mm; - /// The tolerance added to the geometrical extension in z - /// of the layers contained to build the volume envelope around - /// @note this parameter only needs to be set if the volumes containing - /// the layers (e.g. barrel, endcap volumes) have no specific shape - /// (assemblies) - double envelopeZ = 1 * Acts::UnitConstants::mm; - double defaultLayerThickness = 10e-10; - std::function& detectors)> - sortDetectors = sortFCChhDetElements; - /// Material decorator - std::shared_ptr matDecorator; - - /// Optional geometry identifier hook to be used during closure - std::shared_ptr geometryIdentifierHook = - std::make_shared(); - }; - - explicit DD4hepGeometryService(const Config& cfg); - DD4hepGeometryService(const DD4hepGeometryService&) = delete; - DD4hepGeometryService(DD4hepGeometryService&&) = delete; - ~DD4hepGeometryService(); - DD4hepGeometryService& operator=(const DD4hepGeometryService&) = delete; - DD4hepGeometryService& operator=(DD4hepGeometryService&&) = delete; - - /// Interface method to access to the DD4hep geometry - dd4hep::Detector& detector(); - - /// Interface method to access the DD4hep geometry - /// @return The world DD4hep DetElement - dd4hep::DetElement& geometry(); - - /// Interface method to Access the TGeo geometry - /// @return The world TGeoNode (physical volume) - TGeoNode& tgeoGeometry(); - - /// Interface method to access the ACTS TrackingGeometry - /// - /// @param gctx is the geometry context object - std::shared_ptr trackingGeometry( - const Acts::GeometryContext& gctx); - - void drop(); - - private: - /// Private method to initiate building of the DD4hep geometry - ProcessCode buildDD4hepGeometry(); - - /// Private method to initiate building of the ACTS tracking geometry - ProcessCode buildTrackingGeometry(const Acts::GeometryContext& gctx); - - /// The config class - Config m_cfg; - /// Pointer to the interface to the DD4hep geometry - dd4hep::Detector* m_detector = nullptr; - /// The world DD4hep DetElement - dd4hep::DetElement m_geometry; - /// The ACTS TrackingGeometry - std::shared_ptr m_trackingGeometry; - - const Acts::Logger& logger() const { return *m_logger; } - - std::unique_ptr m_logger; -}; - -} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp new file mode 100644 index 00000000000..aef260161c3 --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp @@ -0,0 +1,50 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" + +#include + +#include + +class G4VPhysicalVolume; + +namespace dd4hep { +class Detector; +} // namespace dd4hep + +namespace ActsExamples { +class DD4hepDetector; + +/// Construct the Geant4 detector from a DD4hep description. +class DDG4DetectorConstruction final : public G4VUserDetectorConstruction { + public: + explicit DDG4DetectorConstruction(std::shared_ptr detector, + const Geant4ConstructionOptions& options); + + /// Convert the stored DD4hep detector to a Geant4 description. + /// + /// Transfers ownership of the created object as all volumes (including world) + /// are deleted in ~G4PhysicalVolumeStore(). + /// + /// @note for facilitating configuration within the ACTS framework the world + /// volume is cached + G4VPhysicalVolume* Construct() final; + + private: + /// The DD4hep detector instance + std::shared_ptr m_detector; + /// Construction options + Geant4ConstructionOptions m_options; + /// The world volume + G4VPhysicalVolume* m_world = nullptr; +}; + +} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp index 4ecae308134..6ad70fa7d70 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp @@ -8,78 +8,136 @@ #include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Plugins/DD4hep/DD4hepFieldAdapter.hpp" -#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" +#include "Acts/Plugins/DD4hep/ConvertDD4hepDetector.hpp" +#include "Acts/Utilities/Logger.hpp" +#include #include #include -#include #include #include -#include -#include +#include +#include +#include +#include namespace ActsExamples { -DD4hepDetector::DD4hepDetector( - std::shared_ptr _geometryService) - : geometryService(std::move(_geometryService)) {} - -auto DD4hepDetector::finalize( - DD4hepGeometryService::Config config, - std::shared_ptr mdecorator) - -> std::pair { - Acts::GeometryContext dd4HepContext; - config.matDecorator = std::move(mdecorator); - geometryService = std::make_shared(config); - TrackingGeometryPtr dd4tGeometry = - geometryService->trackingGeometry(dd4HepContext); - if (!dd4tGeometry) { - throw std::runtime_error{ - "Did not receive tracking geometry from DD4hep geometry service"}; - } - ContextDecorators dd4ContextDecorators = {}; - // return the pair of geometry and empty decorators - return std::make_pair( - std::move(dd4tGeometry), std::move(dd4ContextDecorators)); -} - -auto DD4hepDetector::finalize( - const Acts::GeometryContext& gctx, - const Acts::Experimental::DD4hepDetectorStructure::Options& options) - -> std::tuple { - if (geometryService == nullptr) { - throw std::runtime_error{ - "No DD4hep geometry service configured, can not build " - "TrackingGeometry."}; +DD4hepDetector::DD4hepDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("DD4hepDetector", cfg.logLevel)), + m_cfg(cfg) { + if (m_cfg.xmlFileNames.empty()) { + throw std::invalid_argument("Missing DD4hep XML filenames"); } - auto world = geometryService->geometry(); - // Build the detector structure - Acts::Experimental::DD4hepDetectorStructure dd4hepStructure( - Acts::getDefaultLogger("DD4hepDetectorStructure", options.logLevel)); + m_nominalGeometryContext = Acts::GeometryContext(); - /// @return a detector and the detector store - auto [detector, detectorElements] = - dd4hepStructure.construct(gctx, world, options); + m_detector = buildDD4hepGeometry(); - // Prepare the return objects - ContextDecorators contextDecorators = {}; + auto logger = Acts::getDefaultLogger("DD4hepConversion", m_cfg.logLevel); + m_trackingGeometry = Acts::convertDD4hepDetector( + m_detector->world(), *logger, m_cfg.bTypePhi, m_cfg.bTypeR, m_cfg.bTypeZ, + m_cfg.envelopeR, m_cfg.envelopeZ, m_cfg.defaultLayerThickness, + m_cfg.sortDetectors, m_nominalGeometryContext, m_cfg.materialDecorator, + m_cfg.geometryIdentifierHook); +} - return {detector, contextDecorators, detectorElements}; +dd4hep::Detector& DD4hepDetector::dd4hepDetector() { + return *m_detector; } -void DD4hepDetector::drop() { - geometryService->drop(); +TGeoNode& DD4hepDetector::tgeoGeometry() { + return *m_detector->world().placement().ptr(); } -std::shared_ptr DD4hepDetector::field() const { - const auto& detector = geometryService->detector(); +std::unique_ptr DD4hepDetector::buildDD4hepGeometry() const { + const int old_gErrorIgnoreLevel = gErrorIgnoreLevel; + switch (m_cfg.dd4hepLogLevel) { + case Acts::Logging::Level::VERBOSE: + dd4hep::setPrintLevel(dd4hep::PrintLevel::VERBOSE); + break; + case Acts::Logging::Level::DEBUG: + dd4hep::setPrintLevel(dd4hep::PrintLevel::DEBUG); + break; + case Acts::Logging::Level::INFO: + dd4hep::setPrintLevel(dd4hep::PrintLevel::INFO); + break; + case Acts::Logging::Level::WARNING: + dd4hep::setPrintLevel(dd4hep::PrintLevel::WARNING); + gErrorIgnoreLevel = kWarning; + break; + case Acts::Logging::Level::ERROR: + dd4hep::setPrintLevel(dd4hep::PrintLevel::ERROR); + gErrorIgnoreLevel = kError; + break; + case Acts::Logging::Level::FATAL: + dd4hep::setPrintLevel(dd4hep::PrintLevel::FATAL); + gErrorIgnoreLevel = kFatal; + break; + case Acts::Logging::Level::MAX: + dd4hep::setPrintLevel(dd4hep::PrintLevel::ALWAYS); + break; + } + // completely silence std::cout as DD4HEP is using it for logging + if (m_cfg.dd4hepLogLevel >= Acts::Logging::Level::WARNING) { + std::cout.setstate(std::ios_base::failbit); + } + + std::unique_ptr detector = + dd4hep::Detector::make_unique(m_cfg.name); + for (const auto& file : m_cfg.xmlFileNames) { + detector->fromCompact(file); + } + detector->volumeManager(); + detector->apply("DD4hepVolumeManager", 0, nullptr); + + // restore the logging + gErrorIgnoreLevel = old_gErrorIgnoreLevel; + std::cout.clear(); - return std::make_shared(detector.field()); + return detector; } } // namespace ActsExamples + +void ActsExamples::sortFCChhDetElements(std::vector& det) { + std::vector tracker; + std::vector eCal; + std::vector hCal; + std::vector muon; + for (const auto& detElement : det) { + std::string detName = detElement.name(); + if (detName.find("Muon") != std::string::npos) { + muon.push_back(detElement); + } else if (detName.find("ECal") != std::string::npos) { + eCal.push_back(detElement); + } else if (detName.find("HCal") != std::string::npos) { + hCal.push_back(detElement); + } else { + tracker.push_back(detElement); + } + } + std::ranges::sort( + muon, [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + std::ranges::sort( + eCal, [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + std::ranges::sort( + hCal, [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + std::ranges::sort( + tracker, [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + det.clear(); + det = tracker; + + det.insert(det.end(), eCal.begin(), eCal.end()); + det.insert(det.end(), hCal.begin(), hCal.end()); + det.insert(det.end(), muon.begin(), muon.end()); +} diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4.cpp new file mode 100644 index 00000000000..ab2510fe8ae --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4.cpp @@ -0,0 +1,22 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" +#include "ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp" + +#include + +namespace ActsExamples { + +std::unique_ptr +DD4hepDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const { + return std::make_unique(m_detector, options); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4Stub.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4Stub.cpp new file mode 100644 index 00000000000..ea08bab2bfc --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4Stub.cpp @@ -0,0 +1,19 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" + +namespace ActsExamples { + +std::unique_ptr +DD4hepDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& /*options*/) const { + throw std::runtime_error("Geant4 is not enabled"); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp deleted file mode 100644 index c746dd84343..00000000000 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" - -#include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/Plugins/DD4hep/ConvertDD4hepDetector.hpp" -#include "Acts/Utilities/Logger.hpp" - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace ActsExamples { - -DD4hepGeometryService::DD4hepGeometryService(const Config& cfg) - : m_cfg(cfg), - m_logger{Acts::getDefaultLogger("DD4hepGeometryService", cfg.logLevel)} { - if (m_cfg.xmlFileNames.empty()) { - throw std::invalid_argument("Missing DD4hep XML filenames"); - } -} - -DD4hepGeometryService::~DD4hepGeometryService() { - drop(); -} - -ProcessCode DD4hepGeometryService::buildDD4hepGeometry() { - const int old_gErrorIgnoreLevel = gErrorIgnoreLevel; - switch (m_cfg.dd4hepLogLevel) { - case Acts::Logging::Level::VERBOSE: - dd4hep::setPrintLevel(dd4hep::PrintLevel::VERBOSE); - break; - case Acts::Logging::Level::DEBUG: - dd4hep::setPrintLevel(dd4hep::PrintLevel::DEBUG); - break; - case Acts::Logging::Level::INFO: - dd4hep::setPrintLevel(dd4hep::PrintLevel::INFO); - break; - case Acts::Logging::Level::WARNING: - dd4hep::setPrintLevel(dd4hep::PrintLevel::WARNING); - gErrorIgnoreLevel = kWarning; - break; - case Acts::Logging::Level::ERROR: - dd4hep::setPrintLevel(dd4hep::PrintLevel::ERROR); - gErrorIgnoreLevel = kError; - break; - case Acts::Logging::Level::FATAL: - dd4hep::setPrintLevel(dd4hep::PrintLevel::FATAL); - gErrorIgnoreLevel = kFatal; - break; - case Acts::Logging::Level::MAX: - dd4hep::setPrintLevel(dd4hep::PrintLevel::ALWAYS); - break; - } - // completely silence std::cout as DD4HEP is using it for logging - if (m_cfg.dd4hepLogLevel >= Acts::Logging::Level::WARNING) { - std::cout.setstate(std::ios_base::failbit); - } - - m_detector = &dd4hep::Detector::getInstance(); - for (auto& file : m_cfg.xmlFileNames) { - m_detector->fromCompact(file.c_str()); - } - m_detector->volumeManager(); - m_detector->apply("DD4hepVolumeManager", 0, nullptr); - m_geometry = m_detector->world(); - - // restore the logging - gErrorIgnoreLevel = old_gErrorIgnoreLevel; - std::cout.clear(); - - return ProcessCode::SUCCESS; -} - -dd4hep::Detector& DD4hepGeometryService::detector() { - if (m_detector == nullptr) { - buildDD4hepGeometry(); - } - return *m_detector; -} - -dd4hep::DetElement& DD4hepGeometryService::geometry() { - if (!m_geometry) { - buildDD4hepGeometry(); - } - return m_geometry; -} - -TGeoNode& DD4hepGeometryService::tgeoGeometry() { - if (!m_geometry) { - buildDD4hepGeometry(); - } - return *m_geometry.placement().ptr(); -} - -ProcessCode DD4hepGeometryService::buildTrackingGeometry( - const Acts::GeometryContext& gctx) { - // Set the tracking geometry - auto logger = Acts::getDefaultLogger("DD4hepConversion", m_cfg.logLevel); - m_trackingGeometry = Acts::convertDD4hepDetector( - geometry(), *logger, m_cfg.bTypePhi, m_cfg.bTypeR, m_cfg.bTypeZ, - m_cfg.envelopeR, m_cfg.envelopeZ, m_cfg.defaultLayerThickness, - m_cfg.sortDetectors, gctx, m_cfg.matDecorator, - m_cfg.geometryIdentifierHook); - return ProcessCode::SUCCESS; -} - -std::shared_ptr -DD4hepGeometryService::trackingGeometry(const Acts::GeometryContext& gctx) { - if (!m_trackingGeometry) { - buildTrackingGeometry(gctx); - } - return m_trackingGeometry; -} - -void DD4hepGeometryService::drop() { - if (m_detector == nullptr) { - return; - } - dd4hep::Detector::destroyInstance(m_cfg.name); - m_detector = nullptr; - m_geometry = dd4hep::DetElement(); - m_trackingGeometry = nullptr; -} - -void sortFCChhDetElements(std::vector& det) { - std::vector tracker; - std::vector eCal; - std::vector hCal; - std::vector muon; - - for (auto& detElement : det) { - std::string detName = detElement.name(); - if (detName.find("Muon") != std::string::npos) { - muon.push_back(detElement); - } else if (detName.find("ECal") != std::string::npos) { - eCal.push_back(detElement); - } else if (detName.find("HCal") != std::string::npos) { - hCal.push_back(detElement); - } else { - tracker.push_back(detElement); - } - } - - auto byId = [](const dd4hep::DetElement& a, - const dd4hep::DetElement& b) -> bool { - return a.id() < b.id(); - }; - sort(muon.begin(), muon.end(), byId); - sort(eCal.begin(), eCal.end(), byId); - sort(hCal.begin(), hCal.end(), byId); - sort(tracker.begin(), tracker.end(), byId); - - det.clear(); - det = tracker; - - det.insert(det.end(), eCal.begin(), eCal.end()); - det.insert(det.end(), hCal.begin(), hCal.end()); - det.insert(det.end(), muon.begin(), muon.end()); -} - -} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/src/DDG4DetectorConstruction.cpp b/Examples/Detectors/DD4hepDetector/src/DDG4DetectorConstruction.cpp new file mode 100644 index 00000000000..41700ae063e --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/src/DDG4DetectorConstruction.cpp @@ -0,0 +1,52 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp" + +#include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" +#include "ActsExamples/Geant4/RegionCreator.hpp" + +#include +#include +#include +#include +#include + +class G4VPhysicalVolume; + +namespace ActsExamples { + +DDG4DetectorConstruction::DDG4DetectorConstruction( + std::shared_ptr detector, + const Geant4ConstructionOptions& options) + : m_detector(std::move(detector)), m_options(options) {} + +// See DD4hep::Simulation::Geant4DetectorConstruction::Construct() +G4VPhysicalVolume* DDG4DetectorConstruction::Construct() { + if (m_world == nullptr) { + dd4hep::sim::Geant4Mapping g4map(*m_detector); + dd4hep::sim::Geant4Converter g4conv(*m_detector, + dd4hep::PrintLevel::VERBOSE); + dd4hep::sim::Geant4GeometryInfo* geoInfo = + g4conv.create(m_detector->world()).detach(); + g4map.attach(geoInfo); + // All volumes are deleted in ~G4PhysicalVolumeStore() + m_world = geoInfo->world(); + // Create Geant4 volume manager + g4map.volumeManager(); + + // Create regions + for (const auto& regionCreator : m_options.regionCreators) { + regionCreator->buildRegion(); + } + } + + return m_world; +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/Geant4Detector/CMakeLists.txt b/Examples/Detectors/Geant4Detector/CMakeLists.txt index 8a83657de5a..541271e65a5 100644 --- a/Examples/Detectors/Geant4Detector/CMakeLists.txt +++ b/Examples/Detectors/Geant4Detector/CMakeLists.txt @@ -1,11 +1,24 @@ -add_library(ActsExamplesDetectorGeant4 SHARED src/Geant4Detector.cpp) +add_library( + ActsExamplesDetectorGeant4 + SHARED + src/Geant4Detector.cpp + src/GdmlDetectorConstruction.cpp + src/GdmlDetector.cpp +) + target_include_directories( ActsExamplesDetectorGeant4 PUBLIC $ ) + target_link_libraries( ActsExamplesDetectorGeant4 - PUBLIC ActsCore ActsExamplesFramework ActsExamplesGeant4 ActsPluginGeant4 + PUBLIC + ActsCore + ActsExamplesFramework + ActsExamplesGeant4 + ActsPluginGeant4 + ActsExamplesDetectorsCommon ) install( diff --git a/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetector.hpp b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetector.hpp new file mode 100644 index 00000000000..df2c00778be --- /dev/null +++ b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetector.hpp @@ -0,0 +1,36 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" + +#include + +namespace ActsExamples { + +class GdmlDetector : public Detector { + public: + struct Config { + std::string path; + + /// Logging level of the child tools + Acts::Logging::Level logLevel = Acts::Logging::INFO; + }; + + explicit GdmlDetector(const Config& cfg); + + std::unique_ptr buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const override; + + private: + Config m_cfg; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/GdmlDetectorConstruction.hpp b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp similarity index 56% rename from Examples/Algorithms/Geant4/include/ActsExamples/Geant4/GdmlDetectorConstruction.hpp rename to Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp index 69156e94e06..c712023f19e 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/GdmlDetectorConstruction.hpp +++ b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp @@ -8,8 +8,7 @@ #pragma once -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" #include @@ -24,9 +23,8 @@ class GdmlDetectorConstruction final : public G4VUserDetectorConstruction { public: /// @param path is the path to the Gdml file /// @param regionCreators are the region creators - GdmlDetectorConstruction( - std::string path, - std::vector> regionCreators = {}); + GdmlDetectorConstruction(std::string path, + const Geant4ConstructionOptions& options); /// Read the file and parse it to construct the Geant4 description /// @@ -37,26 +35,10 @@ class GdmlDetectorConstruction final : public G4VUserDetectorConstruction { private: /// Path to the Gdml file std::string m_path; - /// Region creators - std::vector> m_regionCreators; + /// Construction options + Geant4ConstructionOptions m_options; /// Cached world volume G4VPhysicalVolume* m_world = nullptr; }; -class GdmlDetectorConstructionFactory final - : public Geant4::DetectorConstructionFactory { - public: - GdmlDetectorConstructionFactory( - std::string path, - std::vector> regionCreators = {}); - - std::unique_ptr factorize() const override; - - private: - /// Path to the Gdml file - std::string m_path; - /// Region creators - std::vector> m_regionCreators; -}; - } // namespace ActsExamples diff --git a/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/Geant4Detector.hpp b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/Geant4Detector.hpp index 32c7a7568ea..cad39584a36 100644 --- a/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/Geant4Detector.hpp +++ b/Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/Geant4Detector.hpp @@ -12,6 +12,7 @@ #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Plugins/Geant4/Geant4DetectorSurfaceFactory.hpp" #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include #include @@ -20,29 +21,9 @@ class G4VPhysicalVolume; -namespace Acts { -class TrackingGeometry; -class Geant4DetectorElement; -class Surface; - -namespace Experimental { -class Detector; -} -} // namespace Acts - namespace ActsExamples { -class IContextDecorator; - -struct Geant4Detector { - using DetectorElements = - std::vector>; - using DetectorPtr = std::shared_ptr; - using Surfaces = std::vector>; - - using ContextDecorators = std::vector>; - using TrackingGeometryPtr = std::shared_ptr; - +struct Geant4Detector : public Detector { /// Nested configuration struct struct Config { /// The detector/geometry name @@ -60,32 +41,20 @@ struct Geant4Detector { Acts::Logging::Level logLevel = Acts::Logging::INFO; }; - /// @brief Construct an Acts::Detector from a Geant4 world volume - /// @param cfg the configuration of the Geant4 detector - /// @param logger a logger instance - /// @return a tuple of an Acts::Detector object, a ContextDecorator & the created detector elements - std::tuple - constructDetector(const Config& cfg, const Acts::Logger& logger); - - /// @brief Construct a TrackingGeometry from a Geant4 world volume using the KDTreeTrackingGeometryBuilder builder - /// - /// @param cfg the configuration of the Geant4 detector - /// @param kdtCfg the configuration of the KDTreeTrackingGeometryBuilder - /// @param logger a logger instance - /// - /// @return a tuple of an Acts::TrackingGeometry object, a ContextDecorator & the created detector elements - std::tuple - constructTrackingGeometry(const Config& cfg, const Acts::Logger& logger); - - private: /// @brief Convert Geant4VPhysicalVolume objects into Acts components /// /// @param cfg the configuration of the Geant4 detector /// @param logger a logger instance /// /// @return a tuple of surfaces and detector elements - std::tuple convertGeant4Volumes( - const Config& cfg, const Acts::Logger& logger) const; + static std::tuple>, + std::vector>> + buildGeant4Volumes(const Config& cfg, const Acts::Logger& logger); + + explicit Geant4Detector(const Config& cfg); + + private: + Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Detectors/Geant4Detector/src/GdmlDetector.cpp b/Examples/Detectors/Geant4Detector/src/GdmlDetector.cpp new file mode 100644 index 00000000000..75f0a6ba036 --- /dev/null +++ b/Examples/Detectors/Geant4Detector/src/GdmlDetector.cpp @@ -0,0 +1,27 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Geant4Detector/GdmlDetector.hpp" + +#include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp" + +#include + +namespace ActsExamples { + +GdmlDetector::GdmlDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("GdmlDetector", cfg.logLevel)), + m_cfg(cfg) {} + +std::unique_ptr +GdmlDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const { + return std::make_unique(m_cfg.path, options); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/Geant4Detector/src/GdmlDetectorConstruction.cpp b/Examples/Detectors/Geant4Detector/src/GdmlDetectorConstruction.cpp new file mode 100644 index 00000000000..b617cf687b6 --- /dev/null +++ b/Examples/Detectors/Geant4Detector/src/GdmlDetectorConstruction.cpp @@ -0,0 +1,41 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp" + +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" +#include "ActsExamples/Geant4/RegionCreator.hpp" + +#include + +#include + +namespace ActsExamples { + +GdmlDetectorConstruction::GdmlDetectorConstruction( + std::string path, const Geant4ConstructionOptions& options) + : G4VUserDetectorConstruction(), + m_path(std::move(path)), + m_options(options) {} + +G4VPhysicalVolume* GdmlDetectorConstruction::Construct() { + if (m_world == nullptr) { + G4GDMLParser parser; + // TODO how to handle errors + parser.Read(m_path); + m_world = parser.GetWorldVolume(); + + // Create regions + for (const auto& regionCreator : m_options.regionCreators) { + regionCreator->buildRegion(); + } + } + return m_world; +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/Geant4Detector/src/Geant4Detector.cpp b/Examples/Detectors/Geant4Detector/src/Geant4Detector.cpp index 67ab547db26..15642e9f912 100644 --- a/Examples/Detectors/Geant4Detector/src/Geant4Detector.cpp +++ b/Examples/Detectors/Geant4Detector/src/Geant4Detector.cpp @@ -9,14 +9,16 @@ #include "ActsExamples/Geant4Detector/Geant4Detector.hpp" #include "Acts/Geometry/CylinderVolumeHelper.hpp" -#include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/KDTreeTrackingGeometryBuilder.hpp" #include "Acts/Geometry/LayerArrayCreator.hpp" #include "Acts/Geometry/LayerCreator.hpp" #include "Acts/Geometry/SurfaceArrayCreator.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Geometry/TrackingVolumeArrayCreator.hpp" +#include "Acts/Plugins/Geant4/Geant4DetectorElement.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include #include #include @@ -25,88 +27,67 @@ namespace ActsExamples { -auto Geant4Detector::constructDetector(const Geant4Detector::Config& cfg, - const Acts::Logger& logger) - -> std::tuple { - if (cfg.g4World == nullptr) { - throw std::invalid_argument( - "Geant4Detector: no world Geant4 volume provided"); - } - - ACTS_INFO("Building an Acts::Detector called '" - << cfg.name << "' from the Geant4PhysVolume '" - << cfg.g4World->GetName() << "'"); - - DetectorPtr detector = nullptr; - ContextDecorators decorators = {}; - - auto [surfaces, elements] = convertGeant4Volumes(cfg, logger); - - return std::tie(detector, decorators, elements); -} - -auto Geant4Detector::constructTrackingGeometry( - const Geant4Detector::Config& cfg, const Acts::Logger& logger) - -> std::tuple { - if (cfg.g4World == nullptr) { +Geant4Detector::Geant4Detector(const Config& cfg) + : Detector(Acts::getDefaultLogger("Geant4Detector", cfg.logLevel)), + m_cfg(cfg) { + if (m_cfg.g4World == nullptr) { throw std::invalid_argument( "Geant4Detector: no world Geant4 volume provided"); } ACTS_INFO("Building an Acts::TrackingGeometry called '" - << cfg.name << "' from the Geant4PhysVolume '" - << cfg.g4World->GetName() << "'"); + << m_cfg.name << "' from the Geant4PhysVolume '" + << m_cfg.g4World->GetName() << "'"); - ContextDecorators decorators = {}; + m_nominalGeometryContext = Acts::GeometryContext(); - auto [surfaces, elements] = convertGeant4Volumes(cfg, logger); + auto [surfaces, elements] = buildGeant4Volumes(cfg, logger()); // Surface array creator auto surfaceArrayCreator = std::make_shared( - Acts::SurfaceArrayCreator::Config(), logger.clone("SurfaceArrayCreator")); + Acts::SurfaceArrayCreator::Config(), + logger().clone("SurfaceArrayCreator")); // Layer Creator Acts::LayerCreator::Config lcConfig; lcConfig.surfaceArrayCreator = surfaceArrayCreator; auto layerCreator = std::make_shared( - lcConfig, logger.clone("LayerCreator")); + lcConfig, logger().clone("LayerCreator")); // Layer array creator Acts::LayerArrayCreator::Config lacConfig; auto layerArrayCreator = std::make_shared( - lacConfig, logger.clone("LayerArrayCreator")); + lacConfig, logger().clone("LayerArrayCreator")); // Tracking volume array creator Acts::TrackingVolumeArrayCreator::Config tvacConfig; auto tVolumeArrayCreator = std::make_shared( - tvacConfig, logger.clone("TrackingVolumeArrayCreator")); + tvacConfig, logger().clone("TrackingVolumeArrayCreator")); // configure the cylinder volume helper Acts::CylinderVolumeHelper::Config cvhConfig; cvhConfig.layerArrayCreator = layerArrayCreator; cvhConfig.trackingVolumeArrayCreator = tVolumeArrayCreator; auto cylinderVolumeHelper = std::make_shared( - cvhConfig, logger.clone("CylinderVolumeHelper")); + cvhConfig, logger().clone("CylinderVolumeHelper")); // Configure the tracking geometry builder, copy the surfaces in Acts::KDTreeTrackingGeometryBuilder::Config kdtCfg; kdtCfg.surfaces = surfaces; kdtCfg.layerCreator = layerCreator; kdtCfg.trackingVolumeHelper = cylinderVolumeHelper; - kdtCfg.protoDetector = cfg.protoDetector; - kdtCfg.geometryIdentifierHook = cfg.geometryIdentifierHook; + kdtCfg.protoDetector = m_cfg.protoDetector; + kdtCfg.geometryIdentifierHook = m_cfg.geometryIdentifierHook; // The KDT tracking geometry builder auto kdtBuilder = Acts::KDTreeTrackingGeometryBuilder( - kdtCfg, logger.clone("KDTreeTrackingGeometryBuilder")); + kdtCfg, logger().clone("KDTreeTrackingGeometryBuilder")); - Acts::GeometryContext tContext; - TrackingGeometryPtr trackingGeometry = kdtBuilder.trackingGeometry(tContext); - - return std::tie(trackingGeometry, decorators, elements); + m_trackingGeometry = kdtBuilder.trackingGeometry(m_nominalGeometryContext); } -auto Geant4Detector::convertGeant4Volumes(const Geant4Detector::Config& cfg, - const Acts::Logger& logger) const - -> std::tuple { +std::tuple>, + std::vector>> +Geant4Detector::buildGeant4Volumes(const Config& cfg, + const Acts::Logger& logger) { // Generate the surface cache Acts::Geant4DetectorSurfaceFactory::Cache g4SurfaceCache; G4Transform3D g4ToWorld; @@ -123,8 +104,8 @@ auto Geant4Detector::convertGeant4Volumes(const Geant4Detector::Config& cfg, ACTS_INFO("Found " << g4SurfaceCache.convertedMaterials << " converted Geant4 Material slabs."); - Surfaces surfaces = {}; - DetectorElements elements = {}; + std::vector> surfaces; + std::vector> elements; // Reserve the right amount of surfaces surfaces.reserve(g4SurfaceCache.sensitiveSurfaces.size() + @@ -133,14 +114,14 @@ auto Geant4Detector::convertGeant4Volumes(const Geant4Detector::Config& cfg, // Add the sensitive surfaces for (const auto& [e, s] : g4SurfaceCache.sensitiveSurfaces) { - elements.push_back(e); surfaces.push_back(s); + elements.push_back(e); } // Add the passive surfaces surfaces.insert(surfaces.end(), g4SurfaceCache.passiveSurfaces.begin(), g4SurfaceCache.passiveSurfaces.end()); - return std::tie(surfaces, elements); + return {std::move(surfaces), std::move(elements)}; } } // namespace ActsExamples diff --git a/Examples/Detectors/GenericDetector/CMakeLists.txt b/Examples/Detectors/GenericDetector/CMakeLists.txt index 242e710df42..7260a22b128 100644 --- a/Examples/Detectors/GenericDetector/CMakeLists.txt +++ b/Examples/Detectors/GenericDetector/CMakeLists.txt @@ -5,12 +5,16 @@ add_library( src/GenericDetector.cpp src/GenericDetectorElement.cpp ) + target_include_directories( ActsExamplesDetectorGeneric PUBLIC $ ) -target_link_libraries(ActsExamplesDetectorGeneric PUBLIC ActsCore) -target_link_libraries(ActsExamplesDetectorGeneric PUBLIC ActsExamplesFramework) + +target_link_libraries( + ActsExamplesDetectorGeneric + PUBLIC ActsCore ActsExamplesFramework ActsExamplesDetectorsCommon +) install( TARGETS ActsExamplesDetectorGeneric diff --git a/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetector.hpp b/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetector.hpp index 21a1f85bec6..5b8517baad1 100644 --- a/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetector.hpp +++ b/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetector.hpp @@ -9,44 +9,35 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include #include -#include -#include namespace Acts { -class TrackingGeometry; class IMaterialDecorator; -} // namespace Acts - -namespace ActsExamples { -class IContextDecorator; -} // namespace ActsExamples +} namespace ActsExamples { class GenericDetectorElement; -struct GenericDetector { - using ContextDecorators = std::vector>; - using TrackingGeometryPtr = std::shared_ptr; - +class GenericDetector : public Detector { + public: struct Config { - std::size_t buildLevel{3}; - Acts::Logging::Level surfaceLogLevel{Acts::Logging::INFO}; - Acts::Logging::Level layerLogLevel{Acts::Logging::INFO}; - Acts::Logging::Level volumeLogLevel{Acts::Logging::INFO}; - bool buildProto{false}; + std::size_t buildLevel = 3; + Acts::Logging::Level logLevel = Acts::Logging::INFO; + Acts::Logging::Level surfaceLogLevel = Acts::Logging::INFO; + Acts::Logging::Level layerLogLevel = Acts::Logging::INFO; + Acts::Logging::Level volumeLogLevel = Acts::Logging::INFO; + bool buildProto = false; + std::shared_ptr materialDecorator; }; - /// The Store of the detector elements (lifetime: job) - std::vector>> - detectorStore; + explicit GenericDetector(const Config& cfg); - std::pair finalize( - const Config& cfg, - std::shared_ptr mdecorator); + private: + Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Detectors/GenericDetector/src/GenericDetector.cpp b/Examples/Detectors/GenericDetector/src/GenericDetector.cpp index bf033fc1e3e..f996d225e79 100644 --- a/Examples/Detectors/GenericDetector/src/GenericDetector.cpp +++ b/Examples/Detectors/GenericDetector/src/GenericDetector.cpp @@ -8,27 +8,28 @@ #include "ActsExamples/GenericDetector/GenericDetector.hpp" -#include "Acts/Geometry/TrackingGeometry.hpp" #include "ActsExamples/GenericDetector/BuildGenericDetector.hpp" #include "ActsExamples/GenericDetector/GenericDetectorElement.hpp" namespace ActsExamples { -auto GenericDetector::finalize( - const Config& cfg, - std::shared_ptr mdecorator) - -> std::pair { - GenericDetectorElement::ContextType nominalContext; - /// Return the generic detector - TrackingGeometryPtr gGeometry = - Generic::buildDetector( - nominalContext, detectorStore, cfg.buildLevel, std::move(mdecorator), - cfg.buildProto, cfg.surfaceLogLevel, cfg.layerLogLevel, - cfg.volumeLogLevel); - ContextDecorators gContextDecorators = {}; - // return the pair of geometry and empty decorators - return std::make_pair( - std::move(gGeometry), std::move(gContextDecorators)); +GenericDetector::GenericDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("GenericDetector", cfg.logLevel)), + m_cfg(cfg) { + m_nominalGeometryContext = Acts::GeometryContext(); + + std::vector>> + specificDetectorStore; + m_trackingGeometry = Generic::buildDetector( + m_nominalGeometryContext, specificDetectorStore, m_cfg.buildLevel, + m_cfg.materialDecorator, m_cfg.buildProto, m_cfg.surfaceLogLevel, + m_cfg.layerLogLevel, m_cfg.volumeLogLevel); + + for (const auto& something : specificDetectorStore) { + for (const auto& element : something) { + m_detectorStore.push_back(element); + } + } } } // namespace ActsExamples diff --git a/Examples/Detectors/GeoModelDetector/CMakeLists.txt b/Examples/Detectors/GeoModelDetector/CMakeLists.txt new file mode 100644 index 00000000000..af7c6d7e494 --- /dev/null +++ b/Examples/Detectors/GeoModelDetector/CMakeLists.txt @@ -0,0 +1,41 @@ +add_library(ActsExamplesDetectorGeoModel SHARED src/GeoModelDetector.cpp) + +target_include_directories( + ActsExamplesDetectorGeoModel + PUBLIC $ +) + +target_link_libraries( + ActsExamplesDetectorGeoModel + PUBLIC + ActsCore + ActsExamplesFramework + ActsPluginGeoModel + ActsExamplesDetectorsCommon +) + +if(ACTS_BUILD_EXAMPLES_GEANT4) + find_library(GeoModel2G4_LIBRARY GeoModel2G4 REQUIRED) + + target_link_libraries( + ActsExamplesDetectorGeoModel + PUBLIC ActsExamplesGeant4 ${GeoModel2G4_LIBRARY} + ) + + target_sources( + ActsExamplesDetectorGeoModel + PUBLIC + src/GeoModelGeant4DetectorConstruction.cpp + src/GeoModelDetectorGeant4.cpp + ) +else() + target_sources( + ActsExamplesDetectorDD4hep + PUBLIC src/GeoModelDetectorGeant4Stub.cpp + ) +endif() + +install( + TARGETS ActsExamplesDetectorGeoModel + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelDetector.hpp b/Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelDetector.hpp new file mode 100644 index 00000000000..6d687dcc625 --- /dev/null +++ b/Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelDetector.hpp @@ -0,0 +1,37 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Plugins/GeoModel/GeoModelTree.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" + +#include + +namespace ActsExamples { + +struct GeoModelDetector : public Detector { + struct Config { + std::string path; + + /// Logging level of the child tools + Acts::Logging::Level logLevel = Acts::Logging::INFO; + }; + + explicit GeoModelDetector(const Config& cfg); + + std::unique_ptr buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const override; + + private: + Config m_cfg; + Acts::GeoModelTree m_geoModel; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp b/Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp similarity index 52% rename from Examples/Algorithms/Geant4/include/ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp rename to Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp index d3070bbc590..fd8390d566c 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp +++ b/Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp @@ -9,8 +9,7 @@ #pragma once #include "Acts/Plugins/GeoModel/GeoModelTree.hpp" -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" #include @@ -19,13 +18,13 @@ class G4VPhysicalVolume; namespace ActsExamples { /// Construct the Geant4 detector from a GeoModel world volume -class GeoModelDetectorConstruction final : public G4VUserDetectorConstruction { +class GeoModelGeant4DetectorConstruction final + : public G4VUserDetectorConstruction { public: /// @param geoModelTree is the GeoModel tree containing the world volume /// @param regionCreators are the region creators - GeoModelDetectorConstruction( - const Acts::GeoModelTree& geoModelTree, - std::vector> regionCreators = {}); + GeoModelGeant4DetectorConstruction(const Acts::GeoModelTree& geoModelTree, + const Geant4ConstructionOptions& options); /// Read the file and parse it to construct the Geant4 description /// @@ -36,26 +35,10 @@ class GeoModelDetectorConstruction final : public G4VUserDetectorConstruction { private: /// The GeoModel tree Acts::GeoModelTree m_geoModelTree; - /// Region creators - std::vector> m_regionCreators; + /// Construction options + Geant4ConstructionOptions m_options; /// The world volume G4VPhysicalVolume* m_g4World = nullptr; }; -class GeoModelDetectorConstructionFactory final - : public Geant4::DetectorConstructionFactory { - public: - GeoModelDetectorConstructionFactory( - const Acts::GeoModelTree& geoModelTree, - std::vector> regionCreators = {}); - - std::unique_ptr factorize() const override; - - private: - /// The GeoModel tree - Acts::GeoModelTree m_geoModelTree; - /// Region creators - std::vector> m_regionCreators; -}; - } // namespace ActsExamples diff --git a/Examples/Detectors/GeoModelDetector/src/GeoModelDetector.cpp b/Examples/Detectors/GeoModelDetector/src/GeoModelDetector.cpp new file mode 100644 index 00000000000..12e151385ed --- /dev/null +++ b/Examples/Detectors/GeoModelDetector/src/GeoModelDetector.cpp @@ -0,0 +1,24 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/GeoModelDetector/GeoModelDetector.hpp" + +#include "Acts/Plugins/GeoModel/GeoModelReader.hpp" +#include "Acts/Plugins/GeoModel/GeoModelTree.hpp" + +#include + +namespace ActsExamples { + +GeoModelDetector::GeoModelDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("GeoModelDetector", cfg.logLevel)), + m_cfg(cfg) { + m_geoModel = Acts::GeoModelReader::readFromDb(m_cfg.path); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4.cpp b/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4.cpp new file mode 100644 index 00000000000..1035ea9bcca --- /dev/null +++ b/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4.cpp @@ -0,0 +1,21 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/GeoModelDetector/GeoModelDetector.hpp" +#include "ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp" + +namespace ActsExamples { + +std::unique_ptr +GeoModelDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const { + return std::make_unique(m_geoModel, + options); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4Stub.cpp b/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4Stub.cpp new file mode 100644 index 00000000000..2d19ee1770a --- /dev/null +++ b/Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4Stub.cpp @@ -0,0 +1,19 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/GeoModelDetector/GeoModelDetector.hpp" + +namespace ActsExamples { + +std::unique_ptr +GeoModelDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& /*options*/) const { + throw std::runtime_error("Geant4 is not enabled"); +} + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/src/GeoModelDetectorConstruction.cpp b/Examples/Detectors/GeoModelDetector/src/GeoModelGeant4DetectorConstruction.cpp similarity index 53% rename from Examples/Algorithms/Geant4/src/GeoModelDetectorConstruction.cpp rename to Examples/Detectors/GeoModelDetector/src/GeoModelGeant4DetectorConstruction.cpp index 8d33622714b..d58151ba921 100644 --- a/Examples/Algorithms/Geant4/src/GeoModelDetectorConstruction.cpp +++ b/Examples/Detectors/GeoModelDetector/src/GeoModelGeant4DetectorConstruction.cpp @@ -6,9 +6,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp" +#include "ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp" -#include +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" +#include "ActsExamples/Geant4/RegionCreator.hpp" #include #include @@ -17,22 +18,22 @@ #include #include -using namespace ActsExamples; +namespace ActsExamples { -GeoModelDetectorConstruction::GeoModelDetectorConstruction( +GeoModelGeant4DetectorConstruction::GeoModelGeant4DetectorConstruction( const Acts::GeoModelTree& geoModelTree, - std::vector> regionCreators) + const Geant4ConstructionOptions& options) : G4VUserDetectorConstruction(), m_geoModelTree(geoModelTree), - m_regionCreators(std::move(regionCreators)) { + m_options(options) { if (geoModelTree.worldVolume == nullptr) { throw std::invalid_argument( - "GeoModelDetectorConstruction: " + "GeoModelGeant4DetectorConstruction: " "GeoModel world volume is nullptr"); } } -G4VPhysicalVolume* GeoModelDetectorConstruction::Construct() { +G4VPhysicalVolume* GeoModelGeant4DetectorConstruction::Construct() { if (m_g4World == nullptr) { ExtParameterisedVolumeBuilder builder(m_geoModelTree.worldVolumeName); G4LogicalVolume* g4WorldLog = builder.Build(m_geoModelTree.worldVolume); @@ -41,21 +42,11 @@ G4VPhysicalVolume* GeoModelDetectorConstruction::Construct() { m_geoModelTree.worldVolumeName, nullptr, false, 0); // Create regions - for (const auto& regionCreator : m_regionCreators) { - regionCreator->construct(); + for (const auto& regionCreator : m_options.regionCreators) { + regionCreator->buildRegion(); } } return m_g4World; } -GeoModelDetectorConstructionFactory::GeoModelDetectorConstructionFactory( - const Acts::GeoModelTree& geoModelTree, - std::vector> regionCreators) - : m_geoModelTree(geoModelTree), - m_regionCreators(std::move(regionCreators)) {} - -std::unique_ptr -GeoModelDetectorConstructionFactory::factorize() const { - return std::make_unique(m_geoModelTree, - m_regionCreators); -} +} // namespace ActsExamples diff --git a/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp b/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp index 585aef06756..71cd8c83907 100644 --- a/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp +++ b/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp @@ -28,7 +28,7 @@ #include "Acts/Visualization/GeometryView3D.hpp" #include "Acts/Visualization/ObjVisualization3D.hpp" #include "Acts/Visualization/ViewConfig.hpp" -#include "ActsExamples/Geant4/GdmlDetectorConstruction.hpp" +#include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp" #include "ActsExamples/Geant4Detector/Geant4Detector.hpp" #include @@ -47,7 +47,7 @@ namespace ActsExamples { MockupSectorBuilder::MockupSectorBuilder( const MockupSectorBuilder::Config& config) { mCfg = config; - GdmlDetectorConstruction geo_gdml(mCfg.gdmlPath); + GdmlDetectorConstruction geo_gdml(mCfg.gdmlPath, {}); g4World = geo_gdml.Construct(); } @@ -78,13 +78,14 @@ MockupSectorBuilder::buildChamber( g4SurfaceOptions.passiveSurfaceSelector = g4Passive; g4WorldConfig.g4SurfaceOptions = g4SurfaceOptions; - auto g4detector = Geant4Detector(); - - auto [detector, surfaces, detectorElements] = - g4detector.constructDetector(g4WorldConfig, Acts::getDummyLogger()); + auto g4Detector = Geant4Detector(g4WorldConfig); + // Trigger the build of the detector + auto [surface, elements] = Geant4Detector::buildGeant4Volumes( + g4WorldConfig, + *Acts::getDefaultLogger("MockupSectorBuilder", Acts::Logging::INFO)); // The vector that holds the converted sensitive surfaces of the chamber - std::vector> strawSurfaces = {}; + std::vector> strawSurfaces; std::array, 3> min_max; std::fill(min_max.begin(), min_max.end(), @@ -92,7 +93,7 @@ MockupSectorBuilder::buildChamber( -std::numeric_limits::max())); // Convert the physical volumes of the detector elements to straw surfaces - for (auto& detectorElement : detectorElements) { + for (const auto& detectorElement : elements) { auto context = Acts::GeometryContext(); auto g4conv = Acts::Geant4PhysicalVolumeConverter(); diff --git a/Examples/Detectors/TGeoDetector/CMakeLists.txt b/Examples/Detectors/TGeoDetector/CMakeLists.txt index 3df36c62f15..11fac0c179c 100644 --- a/Examples/Detectors/TGeoDetector/CMakeLists.txt +++ b/Examples/Detectors/TGeoDetector/CMakeLists.txt @@ -9,6 +9,7 @@ target_include_directories( ActsExamplesDetectorTGeo PUBLIC $ ) + target_link_libraries( ActsExamplesDetectorTGeo PUBLIC @@ -17,6 +18,7 @@ target_link_libraries( ActsPluginJson ActsExamplesFramework ActsExamplesDetectorGeneric + ActsExamplesDetectorsCommon ActsExamplesITkModuleSplitting ) diff --git a/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp b/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp index c1b006f6593..893367aa890 100644 --- a/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp +++ b/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp @@ -18,6 +18,8 @@ #include #include +#include + // Namespace of the module splitters namespace Acts { @@ -53,9 +55,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Acts::BinningType, } // namespace Acts -namespace ActsExamples { - -namespace Options { +namespace ActsExamples::Options { /// Read config for options interval void from_json(const nlohmann::json& j, Interval& interval) { @@ -70,7 +70,9 @@ void to_json(nlohmann::json& j, const Interval& interval) { {"upper", interval.upper.value_or(0)}}; } -} // namespace Options +} // namespace ActsExamples::Options + +namespace ActsExamples { void from_json(const nlohmann::json& j, TGeoITkModuleSplitter::Config& msc) { msc.barrelMap = diff --git a/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/TGeoDetector.hpp b/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/TGeoDetector.hpp index 41134fdfa54..3cad24ecdd8 100644 --- a/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/TGeoDetector.hpp +++ b/Examples/Detectors/TGeoDetector/include/ActsExamples/TGeoDetector/TGeoDetector.hpp @@ -9,9 +9,11 @@ #pragma once #include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Material/IMaterialDecorator.hpp" #include "Acts/Plugins/TGeo/TGeoLayerBuilder.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/Utilities/Options.hpp" #include @@ -22,27 +24,12 @@ #include #include -namespace Acts { -class TGeoDetectorElement; -class TrackingGeometry; -class IMaterialDecorator; -} // namespace Acts - namespace ActsExamples { -class IContextDecorator; - -struct TGeoDetector { - using DetectorElementPtr = std::shared_ptr; - using DetectorStore = std::vector; - - using ContextDecorators = std::vector>; - using TrackingGeometryPtr = std::shared_ptr; - - /// The Store of the detector elements (lifetime: job) - DetectorStore detectorStore; - +class TGeoDetector : public Detector { + public: struct Config { + Acts::Logging::Level logLevel = Acts::Logging::WARNING; Acts::Logging::Level surfaceLogLevel = Acts::Logging::WARNING; Acts::Logging::Level layerLogLevel = Acts::Logging::WARNING; Acts::Logging::Level volumeLogLevel = Acts::Logging::WARNING; @@ -66,6 +53,8 @@ struct TGeoDetector { std::shared_ptr geometryIdentifierHook = std::make_shared(); + std::shared_ptr materialDecorator; + enum SubVolume : std::size_t { Negative = 0, Central, Positive }; template @@ -147,9 +136,10 @@ struct TGeoDetector { static void readTGeoLayerBuilderConfigsFile(const std::string& path, Config& config); - std::pair finalize( - const Config& cfg, - std::shared_ptr mdecorator); + explicit TGeoDetector(const Config& cfg); + + private: + Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Detectors/TGeoDetector/src/TGeoDetector.cpp b/Examples/Detectors/TGeoDetector/src/TGeoDetector.cpp index 0ece428a438..884cfc8345d 100644 --- a/Examples/Detectors/TGeoDetector/src/TGeoDetector.cpp +++ b/Examples/Detectors/TGeoDetector/src/TGeoDetector.cpp @@ -10,6 +10,7 @@ #include "Acts/Geometry/CylinderVolumeBuilder.hpp" #include "Acts/Geometry/CylinderVolumeHelper.hpp" +#include "Acts/Geometry/DetectorElementBase.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/ITrackingVolumeBuilder.hpp" #include "Acts/Geometry/LayerArrayCreator.hpp" @@ -26,7 +27,6 @@ #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/TGeoDetector/JsonTGeoDetectorConfig.hpp" #include "ActsExamples/TGeoDetector/TGeoITkModuleSplitter.hpp" -#include "ActsExamples/Utilities/Options.hpp" #include #include @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,6 @@ #include "TGeoManager.h" namespace ActsExamples { -using namespace Options; namespace { @@ -159,7 +159,7 @@ std::vector makeLayerBuilderConfigs( /// @param vm is the variable map from the options std::shared_ptr buildTGeoDetector( const TGeoDetector::Config& config, const Acts::GeometryContext& context, - std::vector>& + std::vector>& detElementStore, std::shared_ptr mdecorator, const Acts::Logger& logger) { @@ -364,19 +364,14 @@ void TGeoDetector::readTGeoLayerBuilderConfigsFile(const std::string& path, } } -auto TGeoDetector::finalize( - const Config& cfg, - std::shared_ptr mdecorator) - -> std::pair { - Acts::GeometryContext tGeoContext; - auto logger = Acts::getDefaultLogger("TGeoDetector", Acts::Logging::INFO); - TrackingGeometryPtr tgeoTrackingGeometry = buildTGeoDetector( - cfg, tGeoContext, detectorStore, std::move(mdecorator), *logger); - - ContextDecorators tgeoContextDecorators = {}; - // Return the pair of geometry and empty decorators - return std::make_pair( - std::move(tgeoTrackingGeometry), std::move(tgeoContextDecorators)); +TGeoDetector::TGeoDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("TGeoDetector", cfg.logLevel)), + m_cfg(cfg) { + m_nominalGeometryContext = Acts::GeometryContext(); + + m_trackingGeometry = + buildTGeoDetector(m_cfg, m_nominalGeometryContext, m_detectorStore, + m_cfg.materialDecorator, logger()); } void TGeoDetector::Config::readJson(const std::string& jsonFile) { diff --git a/Examples/Detectors/TelescopeDetector/CMakeLists.txt b/Examples/Detectors/TelescopeDetector/CMakeLists.txt index 9b41a082c42..793465cd6ca 100644 --- a/Examples/Detectors/TelescopeDetector/CMakeLists.txt +++ b/Examples/Detectors/TelescopeDetector/CMakeLists.txt @@ -5,15 +5,36 @@ add_library( src/TelescopeDetectorElement.cpp src/BuildTelescopeDetector.cpp ) + target_include_directories( ActsExamplesDetectorTelescope PUBLIC $ ) + target_link_libraries( ActsExamplesDetectorTelescope - PUBLIC ActsCore ActsExamplesFramework + PUBLIC ActsCore ActsExamplesFramework ActsExamplesDetectorsCommon ) +if(ACTS_BUILD_EXAMPLES_GEANT4) + target_link_libraries( + ActsExamplesDetectorTelescope + PUBLIC ActsExamplesGeant4 + ) + + target_sources( + ActsExamplesDetectorTelescope + PUBLIC + src/TelescopeDetectorGeant4.cpp + src/TelescopeG4DetectorConstruction.cpp + ) +else() + target_sources( + ActsExamplesDetectorTelescope + PUBLIC src/TelescopeDetectorGeant4Stub.cpp + ) +endif() + install( TARGETS ActsExamplesDetectorTelescope LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp index 76b81984fb0..17145d4d301 100644 --- a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp +++ b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp @@ -9,26 +9,21 @@ #pragma once #include "Acts/Definitions/Units.hpp" -#include "Acts/Geometry/DetectorElementBase.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include #include -#include #include namespace Acts { -class TrackingGeometry; class IMaterialDecorator; } // namespace Acts namespace ActsExamples { -class IContextDecorator; - -struct TelescopeDetector { - using ContextDecorators = std::vector>; - using TrackingGeometryPtr = std::shared_ptr; - +class TelescopeDetector : public Detector { + public: struct Config { std::vector positions{{0, 30, 60, 120, 150, 180}}; std::vector stereos{{0, 0, 0, 0, 0, 0}}; @@ -37,15 +32,17 @@ struct TelescopeDetector { double thickness{80 * Acts::UnitConstants::um}; int surfaceType{0}; int binValue{2}; + std::shared_ptr materialDecorator; + Acts::Logging::Level logLevel{Acts::Logging::WARNING}; }; - Config config; - /// The store of the detector elements (lifetime: job) - std::vector> detectorStore; + explicit TelescopeDetector(const Config& cfg); + + std::unique_ptr buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const override; - std::pair finalize( - const Config& cfg, - const std::shared_ptr& mdecorator); + private: + Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp new file mode 100644 index 00000000000..62ee1cc0c28 --- /dev/null +++ b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp @@ -0,0 +1,38 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" +#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" + +#include "G4VUserDetectorConstruction.hh" + +class G4VPhysicalVolume; +class G4LogicalVolume; + +namespace ActsExamples { + +class TelescopeG4DetectorConstruction final + : public G4VUserDetectorConstruction { + public: + TelescopeG4DetectorConstruction(const TelescopeDetector::Config& cfg, + const Geant4ConstructionOptions& options); + + G4VPhysicalVolume* Construct() final; + + private: + /// The configuration of the telescope detector + TelescopeDetector::Config m_cfg; + /// The Geant4 construction options + Geant4ConstructionOptions m_options; + /// The world volume + G4VPhysicalVolume* m_world{}; +}; + +} // namespace ActsExamples diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp index bbf1aff4106..414658204b8 100644 --- a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp @@ -9,60 +9,42 @@ #include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" #include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/Utilities/BinningType.hpp" #include "ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp" -#include "ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp" -#include #include namespace ActsExamples { -auto TelescopeDetector::finalize( - const Config& cfg, const std::shared_ptr& - /*mdecorator*/) -> std::pair { - TelescopeDetectorElement::ContextType nominalContext; - - if (cfg.surfaceType > 1) { +TelescopeDetector::TelescopeDetector(const Config& cfg) + : Detector(Acts::getDefaultLogger("TelescopeDetector", cfg.logLevel)), + m_cfg(cfg) { + if (m_cfg.surfaceType > 1) { throw std::invalid_argument( "The surface type could either be 0 for plane surface or 1 for disc " "surface."); } - if (cfg.binValue > 2) { + if (m_cfg.binValue > 2) { throw std::invalid_argument("The axis value could only be 0, 1, or 2."); } // Check if the bounds values are valid - if (cfg.surfaceType == 1 && cfg.bounds[0] >= cfg.bounds[1]) { + if (m_cfg.surfaceType == 1 && m_cfg.bounds[0] >= m_cfg.bounds[1]) { throw std::invalid_argument( "The minR should be smaller than the maxR for disc surface bounds."); } - if (cfg.positions.size() != cfg.stereos.size()) { + if (m_cfg.positions.size() != m_cfg.stereos.size()) { throw std::invalid_argument( "The number of provided positions must match the number of " "provided stereo angles."); } - config = cfg; - - // Sort the provided distances - std::vector positions = cfg.positions; - std::vector stereos = cfg.stereos; - std::ranges::sort(positions); - - Acts::GeometryContext geometryContext(nominalContext); + m_nominalGeometryContext = Acts::GeometryContext(); - // Return the telescope detector - TrackingGeometryPtr gGeometry = - buildTelescopeDetector(geometryContext, detectorStore, positions, stereos, - cfg.offsets, cfg.bounds, cfg.thickness, - static_cast(cfg.surfaceType), - static_cast(cfg.binValue)); - ContextDecorators gContextDecorators = {}; - // return the pair of geometry and empty decorators - return std::make_pair( - std::move(gGeometry), std::move(gContextDecorators)); + m_trackingGeometry = buildTelescopeDetector( + m_nominalGeometryContext, m_detectorStore, m_cfg.positions, m_cfg.stereos, + m_cfg.offsets, m_cfg.bounds, m_cfg.thickness, + static_cast(m_cfg.surfaceType), + static_cast(m_cfg.binValue)); } } // namespace ActsExamples diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4.cpp new file mode 100644 index 00000000000..f385a0a98ef --- /dev/null +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4.cpp @@ -0,0 +1,20 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" +#include "ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp" + +namespace ActsExamples { + +std::unique_ptr +TelescopeDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& options) const { + return std::make_unique(m_cfg, options); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4Stub.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4Stub.cpp new file mode 100644 index 00000000000..93bc305c292 --- /dev/null +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4Stub.cpp @@ -0,0 +1,19 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" + +namespace ActsExamples { + +std::unique_ptr +TelescopeDetector::buildGeant4DetectorConstruction( + const Geant4ConstructionOptions& /*options*/) const { + throw std::runtime_error("Geant4 is not enabled"); +} + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/src/TelescopeG4DetectorConstruction.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeG4DetectorConstruction.cpp similarity index 87% rename from Examples/Algorithms/Geant4/src/TelescopeG4DetectorConstruction.cpp rename to Examples/Detectors/TelescopeDetector/src/TelescopeG4DetectorConstruction.cpp index e8412a18c93..2566c069f61 100644 --- a/Examples/Algorithms/Geant4/src/TelescopeG4DetectorConstruction.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeG4DetectorConstruction.cpp @@ -8,16 +8,10 @@ #include "ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp" -#include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/ThrowAssert.hpp" +#include "ActsExamples/Geant4/RegionCreator.hpp" #include "ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp" - -#include -#include -#include -#include -#include -#include +#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" #include "G4Box.hh" #include "G4LogicalVolume.hh" @@ -30,8 +24,8 @@ namespace ActsExamples { TelescopeG4DetectorConstruction::TelescopeG4DetectorConstruction( const TelescopeDetector::Config& cfg, - std::vector> regionCreators) - : m_cfg(cfg), m_regionCreators(std::move(regionCreators)) { + const Geant4ConstructionOptions& options) + : m_cfg(cfg), m_options(options) { throw_assert(cfg.surfaceType == static_cast(TelescopeSurfaceType::Plane), "only plan is supported right now"); } @@ -160,22 +154,11 @@ G4VPhysicalVolume* TelescopeG4DetectorConstruction::Construct() { } // Create regions - for (const auto& regionCreator : m_regionCreators) { - regionCreator->construct(); + for (const auto& regionCreator : m_options.regionCreators) { + regionCreator->buildRegion(); } return m_world; } -TelescopeG4DetectorConstructionFactory::TelescopeG4DetectorConstructionFactory( - const TelescopeDetector::Config& cfg, - std::vector> regionCreators) - : m_cfg(cfg), m_regionCreators(std::move(regionCreators)) {} - -std::unique_ptr -TelescopeG4DetectorConstructionFactory::factorize() const { - return std::make_unique(m_cfg, - m_regionCreators); -} - } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/Index.hpp b/Examples/Framework/include/ActsExamples/EventData/Index.hpp index 46e431ea76a..5750737b05f 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Index.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Index.hpp @@ -30,18 +30,21 @@ using Index = std::uint32_t; template using IndexMultimap = boost::container::flat_multimap; -/// Invert the multimap, i.e. from a -> {b...} to b -> {a...}. +/// Store the inverse of an index multimap, i.e. from a -> {b...} to b -> +/// {a...}. /// /// @note This assumes that the value in the initial multimap is itself a /// sortable index-like object, as would be the case when mapping e.g. /// hit ids to particle ids/ barcodes. template -inline boost::container::flat_multimap invertIndexMultimap( - const IndexMultimap& multimap) { - using InverseMultimap = boost::container::flat_multimap; +using InverseMultimap = boost::container::flat_multimap; +/// Invert the multimap, i.e. from a -> {b...} to b -> {a...} +template +inline InverseMultimap invertIndexMultimap( + const IndexMultimap& multimap) { // switch key-value without enforcing the new ordering (linear copy) - typename InverseMultimap::sequence_type unordered; + typename InverseMultimap::sequence_type unordered; unordered.reserve(multimap.size()); for (auto&& [index, value] : multimap) { // value is now the key and the index is now the value @@ -49,7 +52,7 @@ inline boost::container::flat_multimap invertIndexMultimap( } // adopting the unordered sequence will reestablish the correct order - InverseMultimap inverse; + InverseMultimap inverse; #if BOOST_VERSION < 107800 for (const auto& i : unordered) { inverse.insert(i); diff --git a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp index 251d7bc293a..86a8be27aba 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp @@ -17,6 +17,7 @@ #include "ActsExamples/EventData/GeometryContainers.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/MeasurementConcept.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" #include #include @@ -531,4 +532,10 @@ static_assert( std::random_access_iterator && std::random_access_iterator); +using MeasurementSimHitsMap = IndexMultimap; +using MeasurementParticlesMap = IndexMultimap; + +using SimHitMeasurementsMap = InverseMultimap; +using ParticleMeasurementsMap = InverseMultimap; + } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/SimHit.hpp b/Examples/Framework/include/ActsExamples/EventData/SimHit.hpp index be10ce2639c..8c7854082fd 100644 --- a/Examples/Framework/include/ActsExamples/EventData/SimHit.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/SimHit.hpp @@ -9,8 +9,6 @@ #pragma once #include "ActsExamples/EventData/GeometryContainers.hpp" -#include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/SimParticle.hpp" #include "ActsFatras/EventData/Hit.hpp" namespace ActsExamples { @@ -19,8 +17,4 @@ using SimHit = ::ActsFatras::Hit; /// Store hits ordered by geometry identifier. using SimHitContainer = GeometryIdMultiset; -using HitParticlesMap = IndexMultimap; - -using HitSimHitsMap = IndexMultimap; - } // namespace ActsExamples diff --git a/Examples/Framework/src/EventData/ScalingCalibrator.cpp b/Examples/Framework/src/EventData/ScalingCalibrator.cpp index 50e693e8b15..068f6221a25 100644 --- a/Examples/Framework/src/EventData/ScalingCalibrator.cpp +++ b/Examples/Framework/src/EventData/ScalingCalibrator.cpp @@ -52,22 +52,22 @@ std::pair parseMapKey( std::regex reg("^map_([0-9]+)-([0-9]+)-([0-9]+)_([xy]_.*)$"); std::smatch matches; - if (std::regex_search(mapkey, matches, reg) && matches.size() == 5) { - std::size_t vol = std::stoull(matches[1].str()); - std::size_t lyr = std::stoull(matches[2].str()); - std::size_t mod = std::stoull(matches[3].str()); + if (!std::regex_search(mapkey, matches, reg) || matches.size() != 5) { + throw std::runtime_error("Invalid map key: " + mapkey); + } - Acts::GeometryIdentifier geoId; - geoId.setVolume(vol); - geoId.setLayer(lyr); - geoId.setSensitive(mod); + std::size_t vol = std::stoull(matches[1].str()); + std::size_t lyr = std::stoull(matches[2].str()); + std::size_t mod = std::stoull(matches[3].str()); - std::string var(matches[4].str()); + Acts::GeometryIdentifier geoId; + geoId.setVolume(vol); + geoId.setLayer(lyr); + geoId.setSensitive(mod); - return std::make_pair(geoId, var); - } else { - throw std::runtime_error("Invalid map key: " + mapkey); - } + std::string var(matches[4].str()); + + return {geoId, var}; } std::map diff --git a/Examples/Framework/src/Utilities/Paths.cpp b/Examples/Framework/src/Utilities/Paths.cpp index 02bf5889c68..4de064d6eb0 100644 --- a/Examples/Framework/src/Utilities/Paths.cpp +++ b/Examples/Framework/src/Utilities/Paths.cpp @@ -110,7 +110,7 @@ std::pair ActsExamples::determineEventFilesRange( // should only occur if no files matched and the initial values persisted. if (eventMax < eventMin) { - return std::make_pair(0u, 0u); + return {0u, 0u}; } - return std::make_pair(eventMin, eventMax + 1); + return {eventMin, eventMax + 1}; } diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSeedWriter.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSeedWriter.hpp index 670183f7c29..d893c5aae42 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSeedWriter.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSeedWriter.hpp @@ -8,6 +8,7 @@ #pragma once +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" @@ -76,9 +77,9 @@ class CsvSeedWriter : public WriterT { ReadDataHandle m_inputParticles{this, "InputParticles"}; ReadDataHandle m_inputSimSeeds{this, "InputSimSeeds"}; ReadDataHandle m_inputSimHits{this, "InputSimHits"}; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMap"}; - ReadDataHandle m_inputMeasurementSimHitsMap{ + ReadDataHandle m_inputMeasurementSimHitsMap{ this, "InputMeasurementSimHitsMap"}; /// @brief Struct for brief seed summary info @@ -95,7 +96,7 @@ class CsvSeedWriter : public WriterT { float truthDistance = -1; std::string seedType = "unknown"; ProtoTrack measurementsID; - }; // trackInfo struct + }; }; } // namespace ActsExamples diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacepointWriter.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacePointWriter.hpp similarity index 93% rename from Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacepointWriter.hpp rename to Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacePointWriter.hpp index 1e24eb4bc97..ea7315d4927 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacepointWriter.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvSpacePointWriter.hpp @@ -34,7 +34,7 @@ namespace ActsExamples { /// ... /// /// Intrinsically thread-safe as one file per event. -class CsvSpacepointWriter final : public WriterT { +class CsvSpacePointWriter final : public WriterT { public: struct Config { /// Which measurement collection to write. @@ -48,10 +48,10 @@ class CsvSpacepointWriter final : public WriterT { /// Constructor with /// @param config configuration struct /// @param level logging level - CsvSpacepointWriter(const Config& config, Acts::Logging::Level level); + CsvSpacePointWriter(const Config& config, Acts::Logging::Level level); /// Virtual destructor - ~CsvSpacepointWriter() override; + ~CsvSpacePointWriter() override; /// End-of-run hook ProcessCode finalize() override; diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvTrackWriter.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvTrackWriter.hpp index 06a449e2615..d3bbfe989f7 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvTrackWriter.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvTrackWriter.hpp @@ -11,7 +11,7 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/WriterT.hpp" @@ -22,12 +22,6 @@ #include #include -namespace ActsExamples { -struct AlgorithmContext; -} // namespace ActsExamples - -using namespace Acts::UnitLiterals; - namespace ActsExamples { /// @class CsvTrackWriter @@ -46,16 +40,24 @@ namespace ActsExamples { class CsvTrackWriter : public WriterT { public: struct Config { - std::string inputTracks; ///< Input track collection - std::string outputDir; ///< where to place output files - std::string fileName = "CKFtracks.csv"; ///< name of the output files - std::string - inputMeasurementParticlesMap; ///< Input hit-particles map collection - std::size_t outputPrecision = 6; ///< floating point precision - std::size_t nMeasurementsMin = 7; ///< Min number of measurements - bool onlyTruthMatched = false; ///< Only write truth matched tracks - double truthMatchProbMin = 0.5; ///< Probability threshold for fake tracks - double ptMin = 1_GeV; ///< Min pt of tracks + /// Input track collection + std::string inputTracks; + /// where to place output files + std::string outputDir; + /// name of the output files + std::string fileName = "CKFtracks.csv"; + /// Input hit-particles map collection + std::string inputMeasurementParticlesMap; + /// floating point precision + std::size_t outputPrecision = 6; + /// Min number of measurements + std::size_t nMeasurementsMin = 7; + /// Only write truth matched tracks + bool onlyTruthMatched = false; + /// Probability threshold for fake tracks + double truthMatchProbMin = 0.5; + /// Min pt of tracks + double ptMin = 1 * Acts::UnitConstants::GeV; }; /// constructor @@ -75,9 +77,10 @@ class CsvTrackWriter : public WriterT { const ConstTrackContainer& tracks) override; private: - Config m_cfg; //!< Nested configuration struct + /// Nested configuration struct + Config m_cfg; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMap"}; /// @brief Struct for brief trajectory summary info @@ -91,7 +94,7 @@ class CsvTrackWriter : public WriterT { double truthMatchProb = 0; std::optional fittedParameters; std::vector measurementsID; - }; // TrackInfo struct + }; }; } // namespace ActsExamples diff --git a/Examples/Io/Csv/src/CsvSpacePointWriter.cpp b/Examples/Io/Csv/src/CsvSpacePointWriter.cpp index 9eb440f7692..4e71020a390 100644 --- a/Examples/Io/Csv/src/CsvSpacePointWriter.cpp +++ b/Examples/Io/Csv/src/CsvSpacePointWriter.cpp @@ -6,7 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "ActsExamples/Io/Csv/CsvSpacepointWriter.hpp" +#include "ActsExamples/Io/Csv/CsvSpacePointWriter.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" @@ -24,20 +24,20 @@ #include "CsvOutputData.hpp" -ActsExamples::CsvSpacepointWriter::CsvSpacepointWriter( - const ActsExamples::CsvSpacepointWriter::Config& config, +ActsExamples::CsvSpacePointWriter::CsvSpacePointWriter( + const ActsExamples::CsvSpacePointWriter::Config& config, Acts::Logging::Level level) - : WriterT(config.inputSpacepoints, "CsvSpacepointWriter", level), + : WriterT(config.inputSpacepoints, "CsvSpacePointWriter", level), m_cfg(config) {} -ActsExamples::CsvSpacepointWriter::~CsvSpacepointWriter() = default; +ActsExamples::CsvSpacePointWriter::~CsvSpacePointWriter() = default; -ActsExamples::ProcessCode ActsExamples::CsvSpacepointWriter::finalize() { +ActsExamples::ProcessCode ActsExamples::CsvSpacePointWriter::finalize() { // Write the tree return ProcessCode::SUCCESS; } -ActsExamples::ProcessCode ActsExamples::CsvSpacepointWriter::writeT( +ActsExamples::ProcessCode ActsExamples::CsvSpacePointWriter::writeT( const AlgorithmContext& ctx, const SimSpacePointContainer& spacepoints) { // Open per-event file for all components std::string pathSP = diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepReader.hpp index 07c4e771857..60c6a7fb3cf 100644 --- a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepReader.hpp +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepReader.hpp @@ -25,7 +25,7 @@ namespace ActsExamples { -struct DD4hepDetector; +class DD4hepDetector; /// Read particles from EDM4hep. /// diff --git a/Examples/Io/EDM4hep/src/EDM4hepReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepReader.cpp index 0b82a20181b..0be54783e8e 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepReader.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepReader.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -323,8 +324,8 @@ ProcessCode EDM4hepReader::read(const AlgorithmContext& ctx) { [&](std::uint64_t cellId) { ACTS_VERBOSE("CellID: " << cellId); - const auto& vm = m_cfg.dd4hepDetector->geometryService->detector() - .volumeManager(); + const auto& vm = + m_cfg.dd4hepDetector->dd4hepDetector().volumeManager(); const auto detElement = vm.lookupDetElement(cellId); diff --git a/Examples/Io/Json/src/JsonDigitizationConfig.cpp b/Examples/Io/Json/src/JsonDigitizationConfig.cpp index 8e3b3abe2cc..63c92965212 100644 --- a/Examples/Io/Json/src/JsonDigitizationConfig.cpp +++ b/Examples/Io/Json/src/JsonDigitizationConfig.cpp @@ -64,7 +64,7 @@ void to_json(nlohmann::json& j, const ActsFatras::SingleParameterSmearFunction< // Digital auto digital = f.target(); if (digital != nullptr) { - j["type"] = "Digitial"; + j["type"] = "Digital"; j["bindata"] = nlohmann::json(digital->binningData); return; } @@ -98,7 +98,7 @@ void from_json( Acts::BinningData bd; from_json(j["bindata"], bd); f = Digitization::Uniform(bd); - } else if (sType == "Digitial") { + } else if (sType == "Digital") { Acts::BinningData bd; from_json(j["bindata"], bd); f = Digitization::Digital(bd); diff --git a/Examples/Io/Obj/include/ActsExamples/Io/Obj/ObjSimHitWriter.hpp b/Examples/Io/Obj/include/ActsExamples/Io/Obj/ObjSimHitWriter.hpp index 8ccd92645b9..b510bd3c035 100644 --- a/Examples/Io/Obj/include/ActsExamples/Io/Obj/ObjSimHitWriter.hpp +++ b/Examples/Io/Obj/include/ActsExamples/Io/Obj/ObjSimHitWriter.hpp @@ -32,6 +32,9 @@ struct AlgorithmContext; /// event000000002-.obj /// event000000002-_trajectory.obj /// +/// +/// The trajectory can be smoothed using a spline interpolation, where +/// nInterpolatedPoints points are added between each hit. class ObjSimHitWriter : public WriterT { public: struct Config { @@ -49,8 +52,11 @@ class ObjSimHitWriter : public WriterT { double momentumThreshold = 0.05 * Acts::UnitConstants::GeV; /// Momentum threshold for trajectories double momentumThresholdTraj = 0.05 * Acts::UnitConstants::GeV; - /// Number of points to interpolate between hits - std::size_t nInterpolatedPoints = 10; + /// Number of points to interpolated between hits to smooth the + /// trajectory view in the obj file. + std::size_t nInterpolatedPoints = 4; + /// Keep the original hits in the trajectory file + bool keepOriginalHits = false; }; /// Construct the particle writer. diff --git a/Examples/Io/Obj/src/ObjSimHitWriter.cpp b/Examples/Io/Obj/src/ObjSimHitWriter.cpp index 564ff9c5409..e5552b627e2 100644 --- a/Examples/Io/Obj/src/ObjSimHitWriter.cpp +++ b/Examples/Io/Obj/src/ObjSimHitWriter.cpp @@ -11,6 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Visualization/Interpolation3D.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/Paths.hpp" @@ -22,47 +23,6 @@ #include #include -#include - -namespace { - -/// @brief Helper function to interpolate points -/// -/// @tparam input_vector_type -/// @param inputs input vector points -/// @param nPoints number of interpolation points -/// -/// @return std::vector interpolated points -template -std::vector interpolatedPoints( - const std::vector& inputs, std::size_t nPoints) { - std::vector output; - - if (nPoints < 2) { - // No interpolation done return simply the output vector - for (const auto& input : inputs) { - output.push_back(input.template head<3>()); - } - - } else { - Eigen::MatrixXd points(3, inputs.size()); - for (std::size_t i = 0; i < inputs.size(); ++i) { - points.col(i) = inputs[i].template head<3>().transpose(); - } - Eigen::Spline spline3D = - Eigen::SplineFitting>::Interpolate(points, 2); - - double step = 1. / (nPoints - 1); - for (std::size_t i = 0; i < nPoints; ++i) { - double t = i * step; - output.push_back(spline3D(t)); - } - } - return output; -} - -} // namespace - ActsExamples::ObjSimHitWriter::ObjSimHitWriter( const ActsExamples::ObjSimHitWriter::Config& config, Acts::Logging::Level level) @@ -152,15 +112,17 @@ ActsExamples::ProcessCode ActsExamples::ObjSimHitWriter::writeT( } osHits << '\n'; - // Interpolate the points - std::vector trajectory; - if (pHits.size() < 3) { - for (const auto& hit : pHits) { - trajectory.push_back(hit.template head<3>()); - } + // Interpolate the points, a minimum number of 3 hits is necessary for + // that + std::vector trajectory; + if (pHits.size() < 3 || m_cfg.nInterpolatedPoints == 0) { + trajectory = pHits; } else { - trajectory = - interpolatedPoints(pHits, pHits.size() * m_cfg.nInterpolatedPoints); + // The total number of points is the number of hits times the number of + // interpolated points plus the number of hits + trajectory = Acts::Interpolation3D::spline( + pHits, pHits.size() * (m_cfg.nInterpolatedPoints + 1) - 1, + m_cfg.keepOriginalHits); } osTrajectory << "o particle_trajectory_" << pId << std::endl; diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialDecorator.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialDecorator.hpp index f5bab09fb70..0cc10482c9e 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialDecorator.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialDecorator.hpp @@ -128,7 +128,7 @@ class RootMaterialDecorator : public Acts::IMaterialDecorator { /// Return the maps const Acts::DetectorMaterialMaps materialMaps() const { - return std::make_pair(m_surfaceMaterialMap, m_volumeMaterialMap); + return {m_surfaceMaterialMap, m_volumeMaterialMap}; } /// Get readonly access to the config parameters diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackParameterWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackParameterWriter.hpp index 9f5b2fa2c42..22540f90399 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackParameterWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackParameterWriter.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" @@ -25,16 +25,14 @@ class TFile; class TTree; namespace ActsExamples { -struct AlgorithmContext; - -using TrackParameterWriter = WriterT; /// Write out the track parameters from both simulation and those estimated from /// reconstructed seeds into a TTree /// /// Each entry in the TTree corresponds to one seed for optimum writing /// speed. The event number is part of the written data. -class RootTrackParameterWriter final : public TrackParameterWriter { +class RootTrackParameterWriter final + : public WriterT { public: struct Config { /// Input estimated track parameters collection. @@ -87,9 +85,9 @@ class RootTrackParameterWriter final : public TrackParameterWriter { "InputProtoTracks"}; ReadDataHandle m_inputParticles{this, "InputParticles"}; ReadDataHandle m_inputSimHits{this, "InputSimHits"}; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMap"}; - ReadDataHandle m_inputMeasurementSimHitsMap{ + ReadDataHandle m_inputMeasurementSimHitsMap{ this, "InputMeasurementSimHitsMap"}; /// Mutex used to protect multi-threaded writes diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackStatesWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackStatesWriter.hpp index bacb794113e..73da1c56e48 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackStatesWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackStatesWriter.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/Track.hpp" @@ -26,12 +26,8 @@ class TFile; class TTree; -namespace ActsFatras { -class Barcode; -} // namespace ActsFatras namespace ActsExamples { -struct AlgorithmContext; /// @class RootTrackStatesWriter /// @@ -108,7 +104,7 @@ class RootTrackStatesWriter final : public WriterT { ReadDataHandle m_inputTrackParticleMatching{ this, "InputTrackParticleMatching"}; ReadDataHandle m_inputSimHits{this, "InputSimHits"}; - ReadDataHandle m_inputMeasurementSimHitsMap{ + ReadDataHandle m_inputMeasurementSimHitsMap{ this, "InputMeasurementSimHitsMap"}; /// Mutex used to protect multi-threaded writes diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp index 49006631b92..b85ab90a0ca 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/SeedingPerformanceWriter.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/SimSeed.hpp" #include "ActsExamples/Framework/DataHandle.hpp" @@ -24,12 +24,8 @@ class TFile; class TTree; -namespace ActsFatras { -class Barcode; -} // namespace ActsFatras namespace ActsExamples { -struct AlgorithmContext; class SeedingPerformanceWriter final : public WriterT { public: @@ -84,7 +80,7 @@ class SeedingPerformanceWriter final : public WriterT { std::size_t m_nTotalDuplicatedParticles = 0; ReadDataHandle m_inputParticles{this, "InputParticles"}; - ReadDataHandle m_inputMeasurementParticlesMap{ + ReadDataHandle m_inputMeasurementParticlesMap{ this, "InputMeasurementParticlesMaps"}; }; diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderNTupleWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderNTupleWriter.hpp index 799471be784..fc090773b98 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderNTupleWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/TrackFinderNTupleWriter.hpp @@ -18,7 +18,6 @@ #include namespace ActsExamples { -struct AlgorithmContext; /// Write track finder performance measures. /// @@ -31,8 +30,8 @@ class TrackFinderNTupleWriter final : public WriterT { std::string inputTracks; /// Input particles collection. std::string inputParticles; - /// Input hit-particles map collection. - std::string inputMeasurementParticlesMap; + /// Input particle-measurements map collection. + std::string inputParticleMeasurementsMap; /// Input proto track-particle matching. std::string inputTrackParticleMatching; /// Output filename. diff --git a/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp b/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp index 995f7bdf57d..265fde166b9 100644 --- a/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp +++ b/Examples/Io/Root/src/RootNuclearInteractionParametersWriter.cpp @@ -121,7 +121,7 @@ buildNotNormalisedMap(TH1F const* hist) { if (integral == 0.) { histoBorders.clear(); temp_HistoContents.clear(); - return std::make_tuple(histoBorders, temp_HistoContents, integral); + return {histoBorders, temp_HistoContents, integral}; } // Set the bin borders @@ -130,7 +130,7 @@ buildNotNormalisedMap(TH1F const* hist) { } histoBorders[nBins] = hist->GetXaxis()->GetXmax(); - return std::make_tuple(histoBorders, temp_HistoContents, integral); + return {histoBorders, temp_HistoContents, integral}; } /// @brief This function combines neighbouring bins with the same value @@ -169,7 +169,7 @@ std::pair, std::vector> buildMap( // Fast exit if the histogram is empty if (histoContents.empty()) { - return std::make_pair(std::get<0>(map), std::vector()); + return {std::get<0>(map), std::vector()}; } // Set the bin content @@ -183,7 +183,7 @@ std::pair, std::vector> buildMap( auto histoBorders = std::get<0>(map); reduceMap(histoBorders, normalisedHistoContents); - return std::make_pair(histoBorders, normalisedHistoContents); + return {histoBorders, normalisedHistoContents}; } /// @brief This method transforms a probability distribution into components @@ -208,7 +208,7 @@ std::pair, std::vector> buildMap( // Fast exit if the histogram is empty if (histoContents.empty()) { - return std::make_pair(std::get<0>(map), std::vector()); + return {std::get<0>(map), std::vector()}; } // Set the bin content @@ -223,7 +223,7 @@ std::pair, std::vector> buildMap( std::vector histoBorders = std::get<0>(map); reduceMap(histoBorders, normalisedHistoContents); - return std::make_pair(histoBorders, normalisedHistoContents); + return {histoBorders, normalisedHistoContents}; } /// @brief This method builds decomposed cumulative probability distributions diff --git a/Examples/Io/Root/src/RootTrackParameterWriter.cpp b/Examples/Io/Root/src/RootTrackParameterWriter.cpp index a4e74a44e2c..520a875f1a8 100644 --- a/Examples/Io/Root/src/RootTrackParameterWriter.cpp +++ b/Examples/Io/Root/src/RootTrackParameterWriter.cpp @@ -18,16 +18,13 @@ #include "ActsExamples/Validation/TrackClassification.hpp" #include "ActsFatras/EventData/Barcode.hpp" #include "ActsFatras/EventData/Hit.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include #include #include #include -#include #include #include -#include #include #include @@ -43,8 +40,8 @@ namespace ActsExamples { RootTrackParameterWriter::RootTrackParameterWriter( const RootTrackParameterWriter::Config& config, Acts::Logging::Level level) - : TrackParameterWriter(config.inputTrackParameters, - "RootTrackParameterWriter", level), + : WriterT(config.inputTrackParameters, + "RootTrackParameterWriter", level), m_cfg(config) { if (m_cfg.inputProtoTracks.empty()) { throw std::invalid_argument("Missing proto tracks input collection"); diff --git a/Examples/Io/Root/src/RootTrackStatesWriter.cpp b/Examples/Io/Root/src/RootTrackStatesWriter.cpp index 37d7cdf2fc9..ee199e48ff3 100644 --- a/Examples/Io/Root/src/RootTrackStatesWriter.cpp +++ b/Examples/Io/Root/src/RootTrackStatesWriter.cpp @@ -471,13 +471,13 @@ ProcessCode RootTrackStatesWriter::writeT(const AlgorithmContext& ctx, auto getTrackParams = [&](unsigned int ipar) -> std::optional> { if (ipar == ePredicted && state.hasPredicted()) { - return std::make_pair(state.predicted(), state.predictedCovariance()); + return std::pair(state.predicted(), state.predictedCovariance()); } if (ipar == eFiltered && state.hasFiltered()) { - return std::make_pair(state.filtered(), state.filteredCovariance()); + return std::pair(state.filtered(), state.filteredCovariance()); } if (ipar == eSmoothed && state.hasSmoothed()) { - return std::make_pair(state.smoothed(), state.smoothedCovariance()); + return std::pair(state.smoothed(), state.smoothedCovariance()); } if (ipar == eUnbiased && state.hasSmoothed() && state.hasProjector() && state.hasCalibrated()) { diff --git a/Examples/Io/Root/src/TrackFinderNTupleWriter.cpp b/Examples/Io/Root/src/TrackFinderNTupleWriter.cpp index 3ddfc2b6f84..4b9aa94b4d0 100644 --- a/Examples/Io/Root/src/TrackFinderNTupleWriter.cpp +++ b/Examples/Io/Root/src/TrackFinderNTupleWriter.cpp @@ -9,8 +9,7 @@ #include "ActsExamples/Io/Root/TrackFinderNTupleWriter.hpp" #include "Acts/Definitions/Units.hpp" -#include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/TruthMatching.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" @@ -18,7 +17,6 @@ #include "ActsExamples/Utilities/Range.hpp" #include "ActsExamples/Validation/TrackClassification.hpp" #include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Particle.hpp" #include #include @@ -32,11 +30,13 @@ #include #include -struct ActsExamples::TrackFinderNTupleWriter::Impl { +namespace ActsExamples { + +struct TrackFinderNTupleWriter::Impl { Config cfg; ReadDataHandle inputParticles; - ReadDataHandle inputMeasurementParticlesMap; + ReadDataHandle inputParticleMeasurementsMap; ReadDataHandle inputTrackParticleMatching; TFile* file = nullptr; @@ -78,7 +78,7 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { // particle charge in e float prtQ = 0; // particle reconstruction - UShort_t prtNumHits = 0; // number of hits for this particle + UShort_t prtNumMeasurements = 0; // number of hits for this particle UShort_t prtNumTracks = 0; // number of tracks this particle was reconstructed in UShort_t prtNumTracksMajority = @@ -89,7 +89,7 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { Impl(TrackFinderNTupleWriter* parent, Config&& c, const Acts::Logger& l) : cfg(std::move(c)), inputParticles{parent, "InputParticles"}, - inputMeasurementParticlesMap{parent, "InputMeasurementParticlesMap"}, + inputParticleMeasurementsMap{parent, "InputParticleMeasurementsMap"}, inputTrackParticleMatching{parent, "InputTrackParticleMatching"}, _logger(l) { if (cfg.inputTracks.empty()) { @@ -98,8 +98,9 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { if (cfg.inputParticles.empty()) { throw std::invalid_argument("Missing particles input collection"); } - if (cfg.inputMeasurementParticlesMap.empty()) { - throw std::invalid_argument("Missing hit-particles map input collection"); + if (cfg.inputParticleMeasurementsMap.empty()) { + throw std::invalid_argument( + "Missing particle-measurements map input collection"); } if (cfg.inputTrackParticleMatching.empty()) { throw std::invalid_argument( @@ -110,7 +111,7 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { } inputParticles.initialize(cfg.inputParticles); - inputMeasurementParticlesMap.initialize(cfg.inputMeasurementParticlesMap); + inputParticleMeasurementsMap.initialize(cfg.inputParticleMeasurementsMap); inputTrackParticleMatching.initialize(cfg.inputTrackParticleMatching); // the output file can not be given externally since TFile accesses to the @@ -146,7 +147,7 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { prtTree->Branch("pz", &prtPz); prtTree->Branch("m", &prtM); prtTree->Branch("q", &prtQ); - prtTree->Branch("nhits", &prtNumHits); + prtTree->Branch("nhits", &prtNumMeasurements); prtTree->Branch("ntracks", &prtNumTracks); prtTree->Branch("ntracks_majority", &prtNumTracksMajority); } @@ -155,10 +156,8 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { void write(std::uint64_t eventId, const ConstTrackContainer& tracks, const SimParticleContainer& particles, - const HitParticlesMap& hitParticlesMap, + const ParticleMeasurementsMap& particleMeasurementsMap, const TrackParticleMatching& trackParticleMatching) { - const auto& particleHitsMap = invertIndexMultimap(hitParticlesMap); - // How often a particle was reconstructed. std::unordered_map reconCount; reconCount.reserve(particles.size()); @@ -209,8 +208,8 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { for (const auto& phc : particleMatch.contributingParticles) { trkParticleId.push_back(phc.particleId.value()); // count total number of hits for this particle - auto trueParticleHits = - makeRange(particleHitsMap.equal_range(phc.particleId.value())); + auto trueParticleHits = makeRange( + particleMeasurementsMap.equal_range(phc.particleId.value())); trkParticleNumHitsTotal.push_back(trueParticleHits.size()); trkParticleNumHitsOnTrack.push_back(phc.hitCount); } @@ -223,9 +222,9 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { { std::lock_guard guardPrt(trkMutex); for (const auto& particle : particles) { - // find all hits for this particle - auto hits = - makeRange(particleHitsMap.equal_range(particle.particleId())); + // find all measurements for this particle + auto measurements = makeRange( + particleMeasurementsMap.equal_range(particle.particleId())); // identification prtEventId = eventId; @@ -243,7 +242,7 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { prtM = particle.mass() / Acts::UnitConstants::GeV; prtQ = particle.charge() / Acts::UnitConstants::e; // reconstruction - prtNumHits = hits.size(); + prtNumMeasurements = measurements.size(); auto nt = reconCount.find(particle.particleId()); prtNumTracks = (nt != reconCount.end()) ? nt->second : 0u; auto nm = majorityCount.find(particle.particleId()); @@ -265,31 +264,31 @@ struct ActsExamples::TrackFinderNTupleWriter::Impl { } }; -ActsExamples::TrackFinderNTupleWriter::TrackFinderNTupleWriter( - ActsExamples::TrackFinderNTupleWriter::Config config, - Acts::Logging::Level level) +TrackFinderNTupleWriter::TrackFinderNTupleWriter( + TrackFinderNTupleWriter::Config config, Acts::Logging::Level level) : WriterT(config.inputTracks, "TrackFinderNTupleWriter", level), m_impl(std::make_unique(this, std::move(config), logger())) {} -ActsExamples::TrackFinderNTupleWriter::~TrackFinderNTupleWriter() = default; +TrackFinderNTupleWriter::~TrackFinderNTupleWriter() = default; -ActsExamples::ProcessCode ActsExamples::TrackFinderNTupleWriter::writeT( - const ActsExamples::AlgorithmContext& ctx, - const ActsExamples::ConstTrackContainer& tracks) { +ProcessCode TrackFinderNTupleWriter::writeT(const AlgorithmContext& ctx, + const ConstTrackContainer& tracks) { const auto& particles = m_impl->inputParticles(ctx); - const auto& hitParticlesMap = m_impl->inputMeasurementParticlesMap(ctx); + const auto& particleMeasurementsMap = + m_impl->inputParticleMeasurementsMap(ctx); const auto& trackParticleMatching = m_impl->inputTrackParticleMatching(ctx); - m_impl->write(ctx.eventNumber, tracks, particles, hitParticlesMap, + m_impl->write(ctx.eventNumber, tracks, particles, particleMeasurementsMap, trackParticleMatching); return ProcessCode::SUCCESS; } -ActsExamples::ProcessCode ActsExamples::TrackFinderNTupleWriter::finalize() { +ProcessCode TrackFinderNTupleWriter::finalize() { m_impl->close(); return ProcessCode::SUCCESS; } -const ActsExamples::TrackFinderNTupleWriter::Config& -ActsExamples::TrackFinderNTupleWriter::config() const { +const TrackFinderNTupleWriter::Config& TrackFinderNTupleWriter::config() const { return m_impl->cfg; } + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp b/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp index 244c30ca039..ee89b828590 100644 --- a/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp +++ b/Examples/Io/Root/src/detail/NuclearInteractionParametrisation.cpp @@ -87,7 +87,7 @@ std::pair calculateMeanAndCovariance( } covariance /= events.size(); - return std::make_pair(mean, covariance); + return {mean, covariance}; } EigenspaceComponents calculateEigenspace(const Vector& mean, @@ -99,7 +99,7 @@ EigenspaceComponents calculateEigenspace(const Vector& mean, // Transform the mean vector into eigenspace Vector meanEigenspace = eigenvectors * mean; - return std::make_tuple(eigenvalues, eigenvectors, meanEigenspace); + return {eigenvalues, eigenvectors, meanEigenspace}; } Parametrisation buildMomentumParameters(const EventCollection& events, @@ -122,7 +122,7 @@ Parametrisation buildMomentumParameters(const EventCollection& events, EigenspaceComponents eigenspaceElements = calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second); // Calculate the cumulative distributions - return std::make_pair(eigenspaceElements, histos); + return {eigenspaceElements, histos}; } EventProperties prepareMomenta(const EventCollection& events, @@ -255,7 +255,7 @@ Parametrisation buildInvariantMassParameters(const EventCollection& events, EigenspaceComponents eigenspaceElements = calculateEigenspace(meanAndCovariance.first, meanAndCovariance.second); // Calculate the cumulative distributions - return std::make_pair(eigenspaceElements, histos); + return {eigenspaceElements, histos}; } std::unordered_map> @@ -341,7 +341,7 @@ cumulativeMultiplicityProbability(const EventCollection& events, } } - return std::make_pair(softHisto, hardHisto); + return {softHisto, hardHisto}; } TVectorF softProbability(const EventCollection& events) { diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index 0eb4b1ff4ec..acca27e91f4 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -16,6 +16,7 @@ pybind11_add_module(ActsPythonBindings src/Material.cpp src/Geometry.cpp src/GeometryBuildingGen1.cpp + src/Blueprint.cpp src/ExampleAlgorithms.cpp src/MagneticField.cpp src/Output.cpp @@ -91,12 +92,22 @@ else() endif() if(ACTS_BUILD_PLUGIN_GEOMODEL) - target_link_libraries(ActsPythonBindings PUBLIC ActsPluginGeoModel) + target_link_libraries( + ActsPythonBindings + PUBLIC ActsPluginGeoModel ActsExamplesDetectorGeoModel + ) target_sources(ActsPythonBindings PRIVATE src/GeoModel.cpp) else() target_sources(ActsPythonBindings PRIVATE src/GeoModelStub.cpp) endif() +if(ACTS_BUILD_PLUGIN_TGEO) + target_link_libraries(ActsPythonBindings PUBLIC ActsPluginTGeo) + target_sources(ActsPythonBindings PRIVATE src/TGeo.cpp) +else() + target_sources(ActsPythonBindings PRIVATE src/TGeoStub.cpp) +endif() + if(ACTS_BUILD_PLUGIN_TRACCC) target_link_libraries(ActsPythonBindings PUBLIC ActsPluginDetray) target_sources(ActsPythonBindings PRIVATE src/Detray.cpp) @@ -170,56 +181,6 @@ if(ACTS_BUILD_EXAMPLES_GEANT4) ) list(APPEND py_files examples/geant4/__init__.py) - if(ACTS_BUILD_PLUGIN_DD4HEP AND ACTS_BUILD_EXAMPLES_DD4HEP) - pybind11_add_module(ActsPythonBindingsDDG4 src/Geant4DD4hepComponent.cpp) - target_link_libraries( - ActsPythonBindingsDDG4 - PUBLIC - ActsPythonUtilities - ActsExamplesGeant4 - ActsExamplesDetectorDD4hep - ) - add_dependencies(ActsPythonBindings ActsPythonBindingsDDG4) - - install( - TARGETS ActsPythonBindingsDDG4 DESTINATION ${_python_install_dir} - ) - set_target_properties( - ActsPythonBindingsDDG4 - PROPERTIES INSTALL_RPATH "\$ORIGIN/../../${CMAKE_INSTALL_LIBDIR}" - ) - set_target_properties( - ActsPythonBindingsDDG4 - PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${_python_dir}/acts - ) - list(APPEND py_files examples/geant4/dd4hep.py) - endif() - - if(ACTS_BUILD_PLUGIN_GEOMODEL) - pybind11_add_module(ActsPythonBindingsGeoModelG4 src/Geant4GeoModelComponent.cpp) - target_link_libraries( - ActsPythonBindingsGeoModelG4 - PUBLIC ActsPythonUtilities ActsExamplesGeant4 - ) - add_dependencies(ActsPythonBindings ActsPythonBindingsGeoModelG4) - install( - TARGETS - ActsPythonBindingsGeoModelG4 - DESTINATION - ${_python_install_dir} - ) - set_target_properties( - ActsPythonBindingsGeoModelG4 - PROPERTIES INSTALL_RPATH "\$ORIGIN/../../${CMAKE_INSTALL_LIBDIR}" - ) - set_target_properties( - ActsPythonBindingsGeoModelG4 - PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${_python_dir}/acts - ) - - list(APPEND py_files examples/geant4/geomodel.py) - endif() - if(ACTS_BUILD_EXAMPLES_HEPMC3) target_link_libraries( ActsPythonBindingsGeant4 diff --git a/Examples/Python/python/acts/_adapter.py b/Examples/Python/python/acts/_adapter.py index 493fb53b508..ad1962fd079 100644 --- a/Examples/Python/python/acts/_adapter.py +++ b/Examples/Python/python/acts/_adapter.py @@ -2,9 +2,6 @@ import functools from typing import Optional, Callable, Dict, Any from pathlib import Path -from collections import namedtuple - -import acts def _make_config_adapter(fn): @@ -93,53 +90,6 @@ def _patch_config(m): if name == "Config": _patchKwargsConstructor(cls) - if name.endswith("Detector"): - continue - if hasattr(cls, "Config"): cls.__init__ = _make_config_adapter(cls.__init__) _patchKwargsConstructor(cls.Config) - - -def _detector_create(cls, config_class=None): - def create(*args, mdecorator=None, **kwargs): - if mdecorator is not None: - if not isinstance(mdecorator, inspect.unwrap(acts.IMaterialDecorator)): - raise TypeError("Material decorator is not valid") - if config_class is None: - cfg = cls.Config() - else: - cfg = config_class() - _kwargs = {} - for k, v in kwargs.items(): - try: - setattr(cfg, k, v) - except AttributeError: - _kwargs[k] = v - det = cls() - tg, deco = det.finalize(cfg, mdecorator, *args, **_kwargs) - Detector = namedtuple( - "Detector", ["detector", "trackingGeometry", "decorators"] - ) - - class DetectorContextManager(Detector): - def __new__(cls, detector, trackingGeometry, decorators): - return super(DetectorContextManager, cls).__new__( - cls, detector, trackingGeometry, decorators - ) - - def __enter__(self): - return self - - def __exit__(self, *args): - pass - - return DetectorContextManager(det, tg, deco) - - return create - - -def _patch_detectors(m): - for name, cls in inspect.getmembers(m, inspect.isclass): - if name.endswith("Detector"): - cls.create = _detector_create(cls) diff --git a/Examples/Python/python/acts/examples/__init__.py b/Examples/Python/python/acts/examples/__init__.py index 162e0272d77..5c1a903ad50 100644 --- a/Examples/Python/python/acts/examples/__init__.py +++ b/Examples/Python/python/acts/examples/__init__.py @@ -7,7 +7,7 @@ from acts.ActsPythonBindings._examples import * from acts import ActsPythonBindings import acts -from acts._adapter import _patch_config, _patch_detectors, _patchKwargsConstructor +from acts._adapter import _patch_config, _patchKwargsConstructor _propagators = [] _concrete_propagators = [] @@ -34,8 +34,6 @@ def ConcretePropagator(propagator): _patch_config(ActsPythonBindings._examples) -_patch_detectors(ActsPythonBindings._examples) - # Manually patch ExaTrkX constructors # Need to do it this way, since they are not always present for module in [ diff --git a/Examples/Python/python/acts/examples/dd4hep.py b/Examples/Python/python/acts/examples/dd4hep.py index cd23533dfda..811c97f5657 100644 --- a/Examples/Python/python/acts/examples/dd4hep.py +++ b/Examples/Python/python/acts/examples/dd4hep.py @@ -12,14 +12,9 @@ print("Error encountered importing DD4hep. Likely you need to set LD_LIBRARY_PATH.") sys.exit(1) -from acts._adapter import _patch_config, _detector_create, _patch_detectors +from acts._adapter import _patch_config from acts import ActsPythonBindingsDD4hep _patch_config(ActsPythonBindingsDD4hep) -_patch_detectors(ActsPythonBindingsDD4hep) -ActsPythonBindingsDD4hep.DD4hepDetector.create = _detector_create( - ActsPythonBindingsDD4hep.DD4hepDetector, - ActsPythonBindingsDD4hep.DD4hepGeometryService.Config, -) from acts.ActsPythonBindingsDD4hep import * diff --git a/Examples/Python/python/acts/examples/geant4/dd4hep.py b/Examples/Python/python/acts/examples/geant4/dd4hep.py deleted file mode 100644 index 2d16253e403..00000000000 --- a/Examples/Python/python/acts/examples/geant4/dd4hep.py +++ /dev/null @@ -1,20 +0,0 @@ -import subprocess -import sys - - -# Cannot conveniently catch linker errors, so we launch a suprocess to -# try importing and see if it works in order to provide a useful error message -try: - subprocess.check_call( - [sys.executable, "-c", "from acts import ActsPythonBindingsDDG4"] - ) -except subprocess.CalledProcessError as e: - print("Error encountered importing DD4hep. Likely you need to set LD_LIBRARY_PATH.") - sys.exit(1) - -from acts._adapter import _patch_config -from acts import ActsPythonBindingsDDG4 - -_patch_config(ActsPythonBindingsDDG4) - -from acts.ActsPythonBindingsDDG4 import * diff --git a/Examples/Python/python/acts/examples/geant4/geomodel.py b/Examples/Python/python/acts/examples/geant4/geomodel.py deleted file mode 100644 index bbc5882eaf3..00000000000 --- a/Examples/Python/python/acts/examples/geant4/geomodel.py +++ /dev/null @@ -1,6 +0,0 @@ -from acts._adapter import _patch_config -from acts import ActsPythonBindingsGeoModelG4 - -_patch_config(ActsPythonBindingsGeoModelG4) - -from acts.ActsPythonBindingsGeoModelG4 import * diff --git a/Examples/Python/python/acts/examples/itk.py b/Examples/Python/python/acts/examples/itk.py index b8e2eba8796..0141e367ee9 100644 --- a/Examples/Python/python/acts/examples/itk.py +++ b/Examples/Python/python/acts/examples/itk.py @@ -53,7 +53,7 @@ def buildITkGeometry( if jsonconfig: jsonFile = geo_dir / "itk-hgtd/tgeo-atlas-itk-hgtd.json" logger.info("Create geometry from %s", jsonFile.absolute()) - return TGeoDetector.create( + return TGeoDetector( jsonFile=str(jsonFile), fileName=str(tgeo_fileName), surfaceLogLevel=customLogLevel(), @@ -72,7 +72,7 @@ def buildITkGeometry( # in the root file (it changed ATLAS-P2-23 -> ATLAS-P2-RUN4-01-00-00). # `TGeoParser` searches the tree below `subVolumeName` for all elements that match any of the # list of `sensitiveNames` wildcards and also fall inside the `rRange`/`zRange` selections. - # If no `TGeoDetectorElements`` are found for an ACTS `Volume()`, then `TGeoDetector.create()` + # If no `TGeoDetectorElements`` are found for an ACTS `Volume()`, then `TGeoDetector()` # raises an exception along the lines of: # 1. Missing tracking geometry - or # 2. Incorrect binning configuration found: Number of configurations does not match number of protolayers @@ -83,7 +83,7 @@ def buildITkGeometry( # * browsing `TGeoManager` with ROOT's `TBrowser` (easy to navigate, but have to scan through long lists by eye). # If the detector has moved significantly, it may be necessary to change the `rRange`/`zRange`. # This specification should be kept in sync with `itk-hgtd/tgeo-atlas-itk-hgtd.json`. - return TGeoDetector.create( + return TGeoDetector( fileName=str(tgeo_fileName), mdecorator=matDeco, buildBeamPipe=True, diff --git a/Examples/Python/python/acts/examples/odd.py b/Examples/Python/python/acts/examples/odd.py index 5e187948ee2..e0edef15776 100644 --- a/Examples/Python/python/acts/examples/odd.py +++ b/Examples/Python/python/acts/examples/odd.py @@ -1,7 +1,6 @@ import os import sys import math -from collections import namedtuple from pathlib import Path from typing import Optional import acts @@ -87,36 +86,19 @@ def geoid_hook(geoid, surface): return geoid - dd4hepConfig = acts.examples.dd4hep.DD4hepGeometryService.Config( - xmlFileNames=[str(odd_xml)], - logLevel=customLogLevel(), - dd4hepLogLevel=customLogLevel(minLevel=acts.logging.WARNING), - geometryIdentifierHook=acts.GeometryIdentifierHook(geoid_hook), - ) - detector = acts.examples.dd4hep.DD4hepDetector() - if mdecorator is None: mdecorator = acts.examples.RootMaterialDecorator( fileName=str(odd_dir / "data/odd-material-maps.root"), level=customLogLevel(minLevel=acts.logging.WARNING), ) - trackingGeometry, decorators = detector.finalize(dd4hepConfig, mdecorator) - - OpenDataDetector = namedtuple( - "OpenDataDetector", ["detector", "trackingGeometry", "decorators"] + dd4hepConfig = acts.examples.dd4hep.DD4hepDetector.Config( + xmlFileNames=[str(odd_xml)], + name="OpenDataDetector", + logLevel=customLogLevel(), + dd4hepLogLevel=customLogLevel(minLevel=acts.logging.WARNING), + geometryIdentifierHook=acts.GeometryIdentifierHook(geoid_hook), + materialDecorator=mdecorator, ) - - class OpenDataDetectorContextManager(OpenDataDetector): - def __new__(cls, detector, trackingGeometry, decorators): - return super(OpenDataDetectorContextManager, cls).__new__( - cls, detector, trackingGeometry, decorators - ) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.detector.drop() - - return OpenDataDetectorContextManager(detector, trackingGeometry, decorators) + detector = acts.examples.dd4hep.DD4hepDetector(dd4hepConfig) + return detector diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index a365572cabc..0e30961fe39 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -234,12 +234,6 @@ def trackSelectorDefaultKWArgs(c): defaults=[None] * 3, ) -AmbiguityResolutionMLDBScanConfig = namedtuple( - "AmbiguityResolutionMLDBScanConfig", - ["nMeasurementsMin", "epsilonDBScan", "minPointsDBScan"], - defaults=[None] * 3, -) - SeedFilterMLDBScanConfig = namedtuple( "SeedFilterMLDBScanConfig", ["epsilonDBScan", "minPointsDBScan", "minSeedScore"], @@ -580,7 +574,7 @@ def addTruthSmearedSeeding( truthTrkFndAlg = acts.examples.TruthTrackFinder( level=logLevel, inputParticles=selectedParticles, - inputMeasurementParticlesMap="measurement_particles_map", + inputParticleMeasurementsMap="particle_measurements_map", outputProtoTracks="truth_particle_tracks", ) s.addAlgorithm(truthTrkFndAlg) @@ -601,7 +595,7 @@ def addTruthEstimatedSeeding( truthSeeding = acts.examples.TruthSeedingAlgorithm( level=logLevel, inputParticles=inputParticles, - inputMeasurementParticlesMap="measurement_particles_map", + inputParticleMeasurementsMap="particle_measurements_map", inputSpacePoints=[spacePoints], outputParticles="truth_seeded_particles", outputProtoTracks="truth_particle_tracks", @@ -1294,7 +1288,6 @@ def addKalmanTracks( s: acts.examples.Sequencer, trackingGeometry: acts.TrackingGeometry, field: acts.MagneticFieldProvider, - directNavigation: bool = False, reverseFilteringMomThreshold: float = 0 * u.GeV, inputProtoTracks: str = "truth_particle_tracks", multipleScattering: bool = True, @@ -1305,17 +1298,6 @@ def addKalmanTracks( ) -> None: customLogLevel = acts.examples.defaultLogging(s, logLevel) - if directNavigation: - srfSortAlg = acts.examples.SurfaceSortingAlgorithm( - level=customLogLevel(), - inputProtoTracks=inputProtoTracks, - inputSimHits="simhits", - inputMeasurementSimHitsMap="measurement_simhits_map", - outputProtoTracks="sorted_truth_particle_tracks", - ) - s.addAlgorithm(srfSortAlg) - inputProtoTracks = srfSortAlg.config.outputProtoTracks - kalmanOptions = { "multipleScattering": multipleScattering, "energyLoss": energyLoss, @@ -1792,13 +1774,12 @@ def addExaTrkX( if backend == ExaTrkXBackend.Torch: metricLearningConfig["modelPath"] = str(modelDir / "embed.pt") - metricLearningConfig["numFeatures"] = 3 + metricLearningConfig["selectedFeatures"] = [0, 1, 2] filterConfig["modelPath"] = str(modelDir / "filter.pt") - filterConfig["nChunks"] = 10 - filterConfig["numFeatures"] = 3 + filterConfig["selectedFeatures"] = [0, 1, 2] gnnConfig["modelPath"] = str(modelDir / "gnn.pt") gnnConfig["undirected"] = True - gnnConfig["numFeatures"] = 3 + gnnConfig["selectedFeatures"] = [0, 1, 2] graphConstructor = acts.examples.TorchMetricLearning(**metricLearningConfig) edgeClassifiers = [ @@ -1830,11 +1811,18 @@ def addExaTrkX( s.addAlgorithm(findingAlg) s.addWhiteboardAlias("prototracks", findingAlg.config.outputProtoTracks) - # TODO convert prototracks to tracks + s.addAlgorithm( + acts.examples.PrototracksToTracks( + level=customLogLevel(), + inputProtoTracks="prototracks", + inputMeasurements="measurements", + outputTracks="tracks", + ) + ) matchAlg = acts.examples.TrackTruthMatcher( level=customLogLevel(), - inputProtoTracks=findingAlg.config.outputProtoTracks, + inputTracks="tracks", inputParticles="particles", inputMeasurementParticlesMap="measurement_particles_map", outputTrackParticleMatching="exatrkx_track_particle_matching", @@ -1849,15 +1837,13 @@ def addExaTrkX( "particle_track_matching", matchAlg.config.outputParticleTrackMatching ) - # Write truth track finding / seeding performance if outputDirRoot is not None: s.addWriter( acts.examples.TrackFinderNTupleWriter( level=customLogLevel(), - inputProtoTracks=findingAlg.config.outputProtoTracks, - # the original selected particles after digitization - inputParticles="particles_initial", - inputMeasurementParticlesMap="measurement_particles_map", + inputTracks="tracks", + inputParticles="particles", + inputParticleMeasurementsMap="particle_measurements_map", inputTrackParticleMatching=matchAlg.config.outputTrackParticleMatching, filePath=str(Path(outputDirRoot) / "performance_track_finding.root"), ) @@ -1972,14 +1958,31 @@ def addScoreBasedAmbiguityResolution( s.addAlgorithm(algScoreBased) s.addWhiteboardAlias("tracks", algScoreBased.config.outputTracks) + matchAlg = acts.examples.TrackTruthMatcher( + level=customLogLevel(), + inputTracks=algScoreBased.config.outputTracks, + inputParticles="particles", + inputMeasurementParticlesMap="measurement_particles_map", + outputTrackParticleMatching="ambi_scorebased_track_particle_matching", + outputParticleTrackMatching="ambi_scorebased_particle_track_matching", + doubleMatching=True, + ) + s.addAlgorithm(matchAlg) + s.addWhiteboardAlias( + "track_particle_matching", matchAlg.config.outputTrackParticleMatching + ) + s.addWhiteboardAlias( + "particle_track_matching", matchAlg.config.outputParticleTrackMatching + ) + addTrackWriters( s, name="ambi_scorebased", tracks=algScoreBased.config.outputTracks, outputDirCsv=outputDirCsv, outputDirRoot=outputDirRoot, - writeSummary=writeTrackStates, - writeStates=writeTrackSummary, + writeSummary=writeTrackSummary, + writeStates=writeTrackStates, writeFitterPerformance=writePerformance, writeFinderPerformance=writePerformance, writeCovMat=writeCovMat, @@ -1995,6 +1998,7 @@ def addScoreBasedAmbiguityResolution( def addAmbiguityResolutionML( s, config: AmbiguityResolutionMLConfig = AmbiguityResolutionMLConfig(), + tracks: str = "tracks", onnxModelFile: Optional[Union[Path, str]] = None, outputDirCsv: Optional[Union[Path, str]] = None, outputDirRoot: Optional[Union[Path, str]] = None, @@ -2008,10 +2012,9 @@ def addAmbiguityResolutionML( from acts.examples import GreedyAmbiguityResolutionAlgorithm customLogLevel = acts.examples.defaultLogging(s, logLevel) - algML = AmbiguityResolutionMLAlgorithm( level=customLogLevel(), - inputTracks="tracks", + inputTracks=tracks, inputDuplicateNN=onnxModelFile, outputTracks="ambiTracksML", **acts.examples.defaultKWArgs( @@ -2033,63 +2036,33 @@ def addAmbiguityResolutionML( s.addAlgorithm(algML) s.addAlgorithm(algGreedy) - addTrackWriters( - s, - name="ambiML", - tracks=algGreedy.config.outputTracks, - outputDirCsv=outputDirCsv, - outputDirRoot=outputDirRoot, - writeSummary=writeTrackStates, - writeStates=writeTrackSummary, - writeFitterPerformance=writePerformance, - writeFinderPerformance=writePerformance, - writeCovMat=writeCovMat, - logLevel=logLevel, - ) - - return s - + s.addWhiteboardAlias("tracks", algGreedy.config.outputTracks) -@acts.examples.NamedTypeArgs( - config=AmbiguityResolutionMLDBScanConfig, -) -def addAmbiguityResolutionMLDBScan( - s, - config: AmbiguityResolutionMLDBScanConfig = AmbiguityResolutionMLDBScanConfig(), - onnxModelFile: Optional[Union[Path, str]] = None, - outputDirCsv: Optional[Union[Path, str]] = None, - outputDirRoot: Optional[Union[Path, str]] = None, - writeTrackSummary: bool = True, - writeTrackStates: bool = False, - writePerformance: bool = True, - writeCovMat=False, - logLevel: Optional[acts.logging.Level] = None, -) -> None: - from acts.examples import AmbiguityResolutionMLDBScanAlgorithm - - customLogLevel = acts.examples.defaultLogging(s, logLevel) - - alg = AmbiguityResolutionMLDBScanAlgorithm( + matchAlg = acts.examples.TrackTruthMatcher( level=customLogLevel(), - inputTracks="tracks", - inputDuplicateNN=onnxModelFile, - outputTracks="ambiTracksMLDBScan", - **acts.examples.defaultKWArgs( - nMeasurementsMin=config.nMeasurementsMin, - epsilonDBScan=config.epsilonDBScan, - minPointsDBScan=config.minPointsDBScan, - ), + inputTracks=algGreedy.config.outputTracks, + inputParticles="particles", + inputMeasurementParticlesMap="measurement_particles_map", + outputTrackParticleMatching="ambiML_track_particle_matching", + outputParticleTrackMatching="ambiML_particle_track_matching", + doubleMatching=True, + ) + s.addAlgorithm(matchAlg) + s.addWhiteboardAlias( + "track_particle_matching", matchAlg.config.outputTrackParticleMatching + ) + s.addWhiteboardAlias( + "particle_track_matching", matchAlg.config.outputParticleTrackMatching ) - s.addAlgorithm(alg) addTrackWriters( s, - name="ambiMLDBScan", - trajectories=alg.config.outputTracks, - outputDirRoot=outputDirRoot, + name="ambiML", + tracks=algGreedy.config.outputTracks, outputDirCsv=outputDirCsv, - writeSummary=writeTrackStates, - writeStates=writeTrackSummary, + outputDirRoot=outputDirRoot, + writeSummary=writeTrackSummary, + writeStates=writeTrackStates, writeFitterPerformance=writePerformance, writeFinderPerformance=writePerformance, writeCovMat=writeCovMat, diff --git a/Examples/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index d076a79b30b..a82c16e4198 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -41,12 +41,13 @@ "absEta", # (min,max) "pt", # (min,max) "m", # (min,max) + "hits", # (min,max) "measurements", # (min,max) "removeCharged", # bool "removeNeutral", # bool "removeSecondaries", # bool ], - defaults=[(None, None)] * 9 + [None] * 3, + defaults=[(None, None)] * 10 + [None] * 3, ) @@ -393,6 +394,8 @@ def addParticleSelection( ptMax=config.pt[1], mMin=config.m[0], mMax=config.m[1], + hitsMin=config.hits[0], + hitsMax=config.hits[1], measurementsMin=config.measurements[0], measurementsMax=config.measurements[1], removeCharged=config.removeCharged, @@ -582,40 +585,6 @@ def addSimWriters( ) -def getG4DetectorConstructionFactory( - detector: Any, - regionList: List[Any] = [], -) -> Any: - try: - from acts.examples import TelescopeDetector - from acts.examples.geant4 import TelescopeG4DetectorConstructionFactory - - if type(detector) is TelescopeDetector: - return TelescopeG4DetectorConstructionFactory(detector, regionList) - except Exception as e: - print(e) - - try: - from acts.examples.dd4hep import DD4hepDetector - from acts.examples.geant4.dd4hep import DDG4DetectorConstructionFactory - - if type(detector) is DD4hepDetector: - return DDG4DetectorConstructionFactory(detector, regionList) - except Exception as e: - print(e) - - try: - from acts import geomodel as gm - from acts.examples.geant4.geomodel import GeoModelDetectorConstructionFactory - - if type(detector) is gm.GeoModelTree: - return GeoModelDetectorConstructionFactory(detector, regionList) - except Exception as e: - print(e) - - raise AttributeError(f"cannot find a suitable detector construction for {detector}") - - # holds the Geant4Handle for potential reuse __geant4Handle = None @@ -626,7 +595,6 @@ def addGeant4( trackingGeometry: Union[acts.TrackingGeometry, acts.Detector], field: acts.MagneticFieldProvider, rnd: acts.examples.RandomNumbers, - g4DetectorConstructionFactory: Optional[Any] = None, volumeMappings: List[str] = [], materialMappings: List[str] = ["Silicon"], inputParticles: str = "particles_input", @@ -694,13 +662,6 @@ def addGeant4( s.addWhiteboardAlias("particles_selected", particlesPreSelected) - if g4DetectorConstructionFactory is None: - if detector is None: - raise AttributeError("detector not given") - g4DetectorConstructionFactory = getG4DetectorConstructionFactory( - detector, regionList - ) - global __geant4Handle smmConfig = SensitiveSurfaceMapper.Config() @@ -714,7 +675,7 @@ def addGeant4( alg = Geant4Simulation( level=customLogLevel(), geant4Handle=__geant4Handle, - detectorConstructionFactory=g4DetectorConstructionFactory, + detector=detector, randomNumbers=rnd, inputParticles=particlesPreSelected, outputParticles=outputParticles, @@ -811,6 +772,8 @@ def addDigitization( outputMeasurements="measurements", outputMeasurementParticlesMap="measurement_particles_map", outputMeasurementSimHitsMap="measurement_simhits_map", + outputParticleMeasurementsMap="particle_measurements_map", + outputSimHitMeasurementsMap="simhit_measurements_map", **acts.examples.defaultKWArgs( doMerge=doMerge, ), diff --git a/Examples/Python/src/Blueprint.cpp b/Examples/Python/src/Blueprint.cpp new file mode 100644 index 00000000000..f5ce4ddc3d0 --- /dev/null +++ b/Examples/Python/src/Blueprint.cpp @@ -0,0 +1,432 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/Blueprint.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/CylinderContainerBlueprintNode.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" +#include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp" +#include "Acts/Geometry/StaticBlueprintNode.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Plugins/Python/Utilities.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +namespace Acts::Python { +namespace { +using std::uniform_real_distribution; + +// This is temporary! +void pseudoNavigation(const TrackingGeometry& trackingGeometry, + const GeometryContext& gctx, std::filesystem::path& path, + std::size_t runs, std::size_t substepsPerCm, + std::pair etaRange, + Logging::Level logLevel) { + using namespace Acts::UnitLiterals; + + ACTS_LOCAL_LOGGER(getDefaultLogger("pseudoNavigation", logLevel)); + + std::ofstream csv{path}; + csv << "x,y,z,volume,boundary,sensitive,material" << std::endl; + + std::mt19937 rnd{42}; + + std::uniform_real_distribution<> dist{-1, 1}; + std::uniform_real_distribution<> subStepDist{0.01, 0.99}; + + double thetaMin = 2 * std::atan(std::exp(-etaRange.first)); + double thetaMax = 2 * std::atan(std::exp(-etaRange.second)); + std::uniform_real_distribution<> thetaDist{thetaMin, thetaMax}; + + using namespace Acts::UnitLiterals; + + for (std::size_t run = 0; run < runs; run++) { + Vector3 position = Vector3::Zero(); + + double theta = thetaDist(rnd); + double phi = 2 * std::numbers::pi * dist(rnd); + + Vector3 direction; + direction[0] = std::sin(theta) * std::cos(phi); + direction[1] = std::sin(theta) * std::sin(phi); + direction[2] = std::cos(theta); + + ACTS_VERBOSE("start navigation " << run); + ACTS_VERBOSE("pos: " << position.transpose()); + ACTS_VERBOSE("dir: " << direction.transpose()); + ACTS_VERBOSE(direction.norm()); + + std::mt19937 rng{static_cast(run)}; + + const auto* volume = trackingGeometry.lowestTrackingVolume(gctx, position); + assert(volume != nullptr); + ACTS_VERBOSE(volume->volumeName()); + + NavigationStream main; + const TrackingVolume* currentVolume = volume; + + csv << run << "," << position[0] << "," << position[1] << "," + << position[2]; + csv << "," << volume->geometryId().volume(); + csv << "," << volume->geometryId().boundary(); + csv << "," << volume->geometryId().sensitive(); + csv << "," << 0; + csv << std::endl; + + ACTS_VERBOSE("start pseudo navigation"); + + auto writeIntersection = [&](const Vector3& pos, const Surface& surface) { + csv << run << "," << pos[0] << "," << pos[1] << "," << pos[2]; + csv << "," << surface.geometryId().volume(); + csv << "," << surface.geometryId().boundary(); + csv << "," << surface.geometryId().sensitive(); + csv << "," << (surface.surfaceMaterial() != nullptr ? 1 : 0); + csv << std::endl; + }; + + for (std::size_t i = 0; i < 100; i++) { + assert(currentVolume != nullptr); + main = NavigationStream{}; + + AppendOnlyNavigationStream navStream{main}; + currentVolume->initializeNavigationCandidates( + {.position = position, .direction = direction}, navStream, logger()); + + ACTS_VERBOSE(main.candidates().size() << " candidates"); + + for (const auto& candidate : main.candidates()) { + ACTS_VERBOSE(" -> " << candidate.surface().geometryId()); + ACTS_VERBOSE(" " << candidate.surface().toStream(gctx)); + } + + ACTS_VERBOSE("initializing candidates"); + main.initialize(gctx, {position, direction}, BoundaryTolerance::None()); + + ACTS_VERBOSE(main.candidates().size() << " candidates remaining"); + + for (const auto& candidate : main.candidates()) { + ACTS_VERBOSE(" -> " << candidate.surface().geometryId()); + ACTS_VERBOSE(" " << candidate.surface().toStream(gctx)); + } + + if (main.currentCandidate().surface().isOnSurface(gctx, position, + direction)) { + ACTS_VERBOSE( + "Already on surface at initialization, skipping candidate"); + + writeIntersection(position, main.currentCandidate().surface()); + + if (!main.switchToNextCandidate()) { + ACTS_WARNING("candidates exhausted unexpectedly"); + break; + } + } + + bool terminated = false; + while (main.remainingCandidates() > 0) { + const auto& candidate = main.currentCandidate(); + + ACTS_VERBOSE(candidate.portal); + ACTS_VERBOSE(candidate.intersection.position().transpose()); + + ACTS_VERBOSE("moving to position: " << position.transpose() << " (r=" + << VectorHelpers::perp(position) + << ")"); + + Vector3 delta = candidate.intersection.position() - position; + + std::size_t substeps = + std::max(1l, std::lround(delta.norm() / 10_cm * substepsPerCm)); + + for (std::size_t j = 0; j < substeps; j++) { + Vector3 subpos = position + subStepDist(rng) * delta; + csv << run << "," << subpos[0] << "," << subpos[1] << "," + << subpos[2]; + csv << "," << currentVolume->geometryId().volume(); + csv << ",0,0,0"; // zero boundary and sensitive ids + csv << std::endl; + } + + position = candidate.intersection.position(); + ACTS_VERBOSE(" -> " + << position.transpose() + << " (r=" << VectorHelpers::perp(position) << ")"); + + writeIntersection(position, candidate.surface()); + + if (candidate.portal != nullptr) { + ACTS_VERBOSE( + "On portal: " << candidate.portal->surface().toStream(gctx)); + currentVolume = + candidate.portal->resolveVolume(gctx, position, direction) + .value(); + + if (currentVolume == nullptr) { + ACTS_VERBOSE("switched to nullptr -> we're done"); + terminated = true; + } + break; + + } else { + ACTS_VERBOSE("Not on portal"); + } + + main.switchToNextCandidate(); + } + + if (terminated) { + ACTS_VERBOSE("Terminate pseudo navigation"); + break; + } + + ACTS_VERBOSE("switched to " << currentVolume->volumeName()); + + ACTS_VERBOSE("-----"); + } + } +} + +} // namespace + +void addBlueprint(Context& ctx) { + auto m = ctx.get("main"); + + auto blueprintNode = + py::class_>( + m, "BlueprintNode"); + + auto rootNode = + py::class_>( + m, "Blueprint"); + + rootNode + .def(py::init()) + // Return value needs to be shared pointer because python otherwise + // can't manage the lifetime + .def( + "construct", + [](Blueprint& self, const BlueprintOptions& options, + const GeometryContext& gctx, + Logging::Level level) -> std::shared_ptr { + return self.construct(options, gctx, + *getDefaultLogger("Blueprint", level)); + }, + py::arg("options"), py::arg("gctx"), + py::arg("level") = Logging::INFO); + + { + auto c = py::class_(rootNode, "Config").def(py::init()); + ACTS_PYTHON_STRUCT_BEGIN(c, Blueprint::Config); + ACTS_PYTHON_MEMBER(envelope); + ACTS_PYTHON_MEMBER(geometryIdentifierHook); + ACTS_PYTHON_STRUCT_END(); + } + + auto addContextManagerProtocol = [](class_& cls) { + using type = typename class_::type; + cls.def("__enter__", [](type& self) -> type& { return self; }) + .def("__exit__", [](type& /*self*/, const py::object& /*exc_type*/, + const py::object& /*exc_value*/, + const py::object& /*traceback*/) { + // No action needed on exit + }); + }; + + auto addNodeMethods = [&blueprintNode](const std::string& name, + auto&& callable, auto&&... args) { + blueprintNode.def(name.c_str(), callable, args...) + .def(("add" + name).c_str(), callable, args...); + }; + + blueprintNode + .def("__str__", + [](const BlueprintNode& self) { + std::stringstream ss; + ss << self; + return ss.str(); + }) + .def("addChild", &BlueprintNode::addChild) + .def_property_readonly("children", + py::overload_cast<>(&BlueprintNode::children)) + .def("clearChildren", &BlueprintNode::clearChildren) + .def_property_readonly("name", &BlueprintNode::name) + .def_property_readonly("depth", &BlueprintNode::depth) + .def("graphviz", [](BlueprintNode& self, const py::object& fh) { + std::stringstream ss; + self.graphviz(ss); + fh.attr("write")(ss.str()); + }); + + py::class_(m, "BlueprintOptions") + .def(py::init<>()) + .def_readwrite("defaultNavigationPolicyFactory", + &BlueprintOptions::defaultNavigationPolicyFactory); + + py::class_(blueprintNode, + "MutableChildRange") + .def( + "__iter__", + [](BlueprintNode::MutableChildRange& self) { + return py::make_iterator(self.begin(), self.end()); + }, + py::keep_alive<0, 1>()) + .def( + "__getitem__", + [](BlueprintNode::MutableChildRange& self, + int i) -> Acts::BlueprintNode& { + if (i < 0) { + i += self.size(); + } + return self.at(i); + }, + py::return_value_policy::reference_internal) + .def("__len__", [](const BlueprintNode::MutableChildRange& self) { + return self.size(); + }); + + auto staticNode = + py::class_>( + m, "StaticBlueprintNode") + .def(py::init([](const Transform3& transform, + const std::shared_ptr& bounds, + const std::string& name) { + return std::make_shared( + std::make_unique(transform, bounds, + name)); + }), + py::arg("transform"), py::arg("bounds"), + py::arg("name") = "undefined") + .def_property("navigationPolicyFactory", + &Acts::StaticBlueprintNode::navigationPolicyFactory, + &Acts::StaticBlueprintNode::setNavigationPolicyFactory); + + addContextManagerProtocol(staticNode); + + addNodeMethods( + "StaticVolume", + [](BlueprintNode& self, const Transform3& transform, + const std::shared_ptr& bounds, const std::string& name) { + auto node = std::make_shared( + std::make_unique(transform, bounds, name)); + self.addChild(node); + return node; + }, + py::arg("transform"), py::arg("bounds"), py::arg("name") = "undefined"); + + auto cylNode = + py::class_>( + m, "CylinderContainerBlueprintNode") + .def(py::init(), + py::arg("name"), py::arg("direction"), + py::arg("attachmentStrategy") = + CylinderVolumeStack::AttachmentStrategy::Gap, + py::arg("resizeStrategy") = + CylinderVolumeStack::ResizeStrategy::Gap) + .def_property( + "attachmentStrategy", + &Acts::CylinderContainerBlueprintNode::attachmentStrategy, + &Acts::CylinderContainerBlueprintNode::setAttachmentStrategy) + .def_property( + "resizeStrategy", + &Acts::CylinderContainerBlueprintNode::resizeStrategy, + &Acts::CylinderContainerBlueprintNode::setResizeStrategy) + .def_property("direction", + &Acts::CylinderContainerBlueprintNode::direction, + &Acts::CylinderContainerBlueprintNode::setDirection); + + addContextManagerProtocol(cylNode); + + addNodeMethods( + "CylinderContainer", + [](BlueprintNode& self, const std::string& name, BinningValue direction) { + auto cylinder = + std::make_shared(name, direction); + self.addChild(cylinder); + return cylinder; + }, + py::arg("name"), py::arg("direction")); + + auto matNode = + py::class_>( + m, "MaterialDesignatorBlueprintNode") + .def(py::init(), "name"_a) + .def_property("binning", &MaterialDesignatorBlueprintNode::binning, + &MaterialDesignatorBlueprintNode::setBinning); + + addContextManagerProtocol(matNode); + + addNodeMethods( + "Material", + [](BlueprintNode& self, const std::string& name) { + auto child = std::make_shared(name); + self.addChild(child); + return child; + }, + "name"_a); + + auto layerNode = + py::class_>( + m, "LayerBlueprintNode") + .def(py::init(), py::arg("name")) + .def_property_readonly("name", &Acts::LayerBlueprintNode::name) + .def_property("surfaces", &Acts::LayerBlueprintNode::surfaces, + &Acts::LayerBlueprintNode::setSurfaces) + .def_property("transform", &Acts::LayerBlueprintNode::transform, + &Acts::LayerBlueprintNode::setTransform) + .def_property("envelope", &Acts::LayerBlueprintNode::envelope, + &Acts::LayerBlueprintNode::setEnvelope) + .def_property("layerType", &Acts::LayerBlueprintNode::layerType, + &Acts::LayerBlueprintNode::setLayerType) + .def_property("navigationPolicyFactory", + &Acts::LayerBlueprintNode::navigationPolicyFactory, + &Acts::LayerBlueprintNode::setNavigationPolicyFactory); + + py::enum_(layerNode, "LayerType") + .value("Cylinder", Acts::LayerBlueprintNode::LayerType::Cylinder) + .value("Disc", Acts::LayerBlueprintNode::LayerType::Disc) + .value("Plane", Acts::LayerBlueprintNode::LayerType::Plane); + + addContextManagerProtocol(layerNode); + + addNodeMethods( + "Layer", + [](BlueprintNode& self, const std::string& name) { + auto child = std::make_shared(name); + self.addChild(child); + return child; + }, + py::arg("name")); + + // TEMPORARY + m.def("pseudoNavigation", &pseudoNavigation, "trackingGeometry"_a, "gctx"_a, + "path"_a, "runs"_a, "substepsPerCm"_a = 2, + "etaRange"_a = std::pair{-4.5, 4.5}, "logLevel"_a = Logging::INFO); +} + +} // namespace Acts::Python diff --git a/Examples/Python/src/Covfie.cpp b/Examples/Python/src/Covfie.cpp index 5a630400d88..27ceb17373f 100644 --- a/Examples/Python/src/Covfie.cpp +++ b/Examples/Python/src/Covfie.cpp @@ -9,6 +9,8 @@ #include "Acts/Plugins/Covfie/FieldConversion.hpp" #include "Acts/Plugins/Python/Utilities.hpp" +#include + #include #include diff --git a/Examples/Python/src/DD4hepComponent.cpp b/Examples/Python/src/DD4hepComponent.cpp index e38a33cf82c..1efd050fef5 100644 --- a/Examples/Python/src/DD4hepComponent.cpp +++ b/Examples/Python/src/DD4hepComponent.cpp @@ -14,12 +14,11 @@ #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" -#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" -#include "ActsExamples/Framework/IContextDecorator.hpp" #include #include +#include #include #include @@ -29,15 +28,19 @@ using namespace Acts::Python; PYBIND11_MODULE(ActsPythonBindingsDD4hep, m) { { - using Config = DD4hepGeometryService::Config; - auto s = py::class_>( - m, "DD4hepGeometryService") - .def(py::init()) - .def("drop", &DD4hepGeometryService::drop); - - auto c = py::class_(s, "Config").def(py::init<>()); - ACTS_PYTHON_STRUCT_BEGIN(c, Config); + py::class_>( + m, "DD4hepDetectorElement"); + } + + { + auto f = + py::class_>( + m, "DD4hepDetector") + .def(py::init()); + + auto c = py::class_(f, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, DD4hepDetector::Config); ACTS_PYTHON_MEMBER(logLevel); ACTS_PYTHON_MEMBER(dd4hepLogLevel); ACTS_PYTHON_MEMBER(xmlFileNames); @@ -48,6 +51,7 @@ PYBIND11_MODULE(ActsPythonBindingsDD4hep, m) { ACTS_PYTHON_MEMBER(envelopeR); ACTS_PYTHON_MEMBER(envelopeZ); ACTS_PYTHON_MEMBER(defaultLayerThickness); + ACTS_PYTHON_MEMBER(materialDecorator); ACTS_PYTHON_MEMBER(geometryIdentifierHook); ACTS_PYTHON_STRUCT_END(); @@ -60,12 +64,6 @@ PYBIND11_MODULE(ActsPythonBindingsDD4hep, m) { "DD4hepFieldAdapter"); } - { - py::class_>( - m, "DD4hepDetectorElement"); - } - { m.def("createDD4hepIdGeoIdMap", [](const Acts::TrackingGeometry& tGeometry) @@ -146,22 +144,4 @@ PYBIND11_MODULE(ActsPythonBindingsDD4hep, m) { options.geoIdGenerator = chainedGeoIdGenerator; }); } - - { - py::class_>( - m, "DD4hepDetector") - .def(py::init<>()) - .def(py::init>()) - .def("finalize", - py::overload_cast>( - &DD4hepDetector::finalize)) - .def("finalize", - py::overload_cast< - const Acts::GeometryContext&, - const Acts::Experimental::DD4hepDetectorStructure::Options&>( - &DD4hepDetector::finalize)) - .def("drop", &DD4hepDetector::drop) - .def_property_readonly("field", &DD4hepDetector::field); - } } diff --git a/Examples/Python/src/Detector.cpp b/Examples/Python/src/Detector.cpp index 94770ce7283..59a7cd13d06 100644 --- a/Examples/Python/src/Detector.cpp +++ b/Examples/Python/src/Detector.cpp @@ -6,11 +6,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +#include "Acts/Detector/Detector.hpp" + +#include "Acts/Geometry/DetectorElementBase.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Material/IMaterialDecorator.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/BinningType.hpp" #include "ActsExamples/ContextualDetector/AlignedDetector.hpp" +#include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/Framework/IContextDecorator.hpp" #include "ActsExamples/GenericDetector/GenericDetector.hpp" #include "ActsExamples/TGeoDetector/TGeoDetector.hpp" @@ -33,6 +37,7 @@ namespace Acts::Python { void addDetector(Context& ctx) { auto [m, mex] = ctx.get("main", "examples"); + { py::class_>( mex, "IContextDecorator") @@ -41,68 +46,69 @@ void addDetector(Context& ctx) { } { - using Detector = GenericDetector; - using Config = Detector::Config; - - auto gd = - py::class_>(mex, "GenericDetector") - .def(py::init<>()) - .def("finalize", - py::overload_cast< - const Config&, - std::shared_ptr>( - &Detector::finalize)); - - py::class_(gd, "Config") - .def(py::init<>()) - .def_readwrite("buildLevel", &Config::buildLevel) - .def_readwrite("surfaceLogLevel", &Config::surfaceLogLevel) - .def_readwrite("layerLogLevel", &Config::layerLogLevel) - .def_readwrite("volumeLogLevel", &Config::volumeLogLevel) - .def_readwrite("buildProto", &Config::buildProto); + py::class_>(mex, "DetectorBase") + .def("nominalGeometryContext", &Detector::nominalGeometryContext) + .def("trackingGeometry", &Detector::trackingGeometry) + .def("gen2Geometry", &Detector::gen2Geometry) + .def("contextDecorators", &Detector::contextDecorators) + .def("__enter__", + [](const std::shared_ptr& self) { return self; }) + .def("__exit__", + [](std::shared_ptr& self, + const std::optional&, + const std::optional&, + const std::optional&) { self.reset(); }); } { - using Detector = TelescopeDetector; - using Config = Detector::Config; - - auto td = - py::class_>(mex, - "TelescopeDetector") - .def(py::init<>()) - .def("finalize", - py::overload_cast< - const Config&, - const std::shared_ptr&>( - &Detector::finalize)); - - py::class_(td, "Config") - .def(py::init<>()) - .def_readwrite("positions", &Config::positions) - .def_readwrite("stereos", &Config::stereos) - .def_readwrite("offsets", &Config::offsets) - .def_readwrite("bounds", &Config::bounds) - .def_readwrite("thickness", &Config::thickness) - .def_readwrite("surfaceType", &Config::surfaceType) - .def_readwrite("binValue", &Config::binValue); + auto d = + py::class_>( + mex, "GenericDetector") + .def(py::init()); + + auto c = py::class_(d, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, GenericDetector::Config); + ACTS_PYTHON_MEMBER(buildLevel); + ACTS_PYTHON_MEMBER(logLevel); + ACTS_PYTHON_MEMBER(surfaceLogLevel); + ACTS_PYTHON_MEMBER(layerLogLevel); + ACTS_PYTHON_MEMBER(volumeLogLevel); + ACTS_PYTHON_MEMBER(buildProto); + ACTS_PYTHON_MEMBER(materialDecorator); + ACTS_PYTHON_STRUCT_END(); } { - using Detector = AlignedDetector; - using Config = Detector::Config; + auto d = + py::class_>(mex, "TelescopeDetector") + .def(py::init()); + + auto c = + py::class_(d, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, TelescopeDetector::Config); + ACTS_PYTHON_MEMBER(positions); + ACTS_PYTHON_MEMBER(stereos); + ACTS_PYTHON_MEMBER(offsets); + ACTS_PYTHON_MEMBER(bounds); + ACTS_PYTHON_MEMBER(thickness); + ACTS_PYTHON_MEMBER(surfaceType); + ACTS_PYTHON_MEMBER(binValue); + ACTS_PYTHON_MEMBER(materialDecorator); + ACTS_PYTHON_MEMBER(logLevel); + ACTS_PYTHON_STRUCT_END(); + } + { auto d = - py::class_>(mex, "AlignedDetector") - .def(py::init<>()) - .def("finalize", - py::overload_cast< - const Config&, - std::shared_ptr>( - &Detector::finalize)); - - auto c = py::class_(d, "Config") + py::class_>( + mex, "AlignedDetector") + .def(py::init()); + + auto c = py::class_( + d, "Config") .def(py::init<>()); - ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_STRUCT_BEGIN(c, AlignedDetector::Config); ACTS_PYTHON_MEMBER(seed); ACTS_PYTHON_MEMBER(iovSize); ACTS_PYTHON_MEMBER(flushSize); @@ -116,23 +122,15 @@ void addDetector(Context& ctx) { ACTS_PYTHON_MEMBER(mode); ACTS_PYTHON_STRUCT_END(); - py::enum_(c, "Mode") - .value("Internal", Config::Mode::Internal) - .value("External", Config::Mode::External); + py::enum_(c, "Mode") + .value("Internal", AlignedDetector::Config::Mode::Internal) + .value("External", AlignedDetector::Config::Mode::External); } { - using Detector = TGeoDetector; - using Config = Detector::Config; - - auto d = - py::class_>(mex, "TGeoDetector") - .def(py::init<>()) - .def("finalize", - py::overload_cast< - const Config&, - std::shared_ptr>( - &Detector::finalize)); + auto d = py::class_>( + mex, "TGeoDetector") + .def(py::init()); py::class_(mex, "Interval") .def(py::init<>()) @@ -140,23 +138,25 @@ void addDetector(Context& ctx) { .def_readwrite("lower", &Options::Interval::lower) .def_readwrite("upper", &Options::Interval::upper); - auto c = py::class_(d, "Config").def(py::init<>()); + auto c = py::class_(d, "Config").def(py::init<>()); - c.def_property( - "jsonFile", nullptr, - [](Config& cfg, const std::string& file) { cfg.readJson(file); }); + c.def_property("jsonFile", nullptr, + [](TGeoDetector::Config& cfg, const std::string& file) { + cfg.readJson(file); + }); - py::enum_(c, "SubVolume") - .value("Negative", Config::SubVolume::Negative) - .value("Central", Config::SubVolume::Central) - .value("Positive", Config::SubVolume::Positive); + py::enum_(c, "SubVolume") + .value("Negative", TGeoDetector::Config::SubVolume::Negative) + .value("Central", TGeoDetector::Config::SubVolume::Central) + .value("Positive", TGeoDetector::Config::SubVolume::Positive); py::enum_(c, "BinningType") .value("equidistant", Acts::BinningType::equidistant) .value("arbitrary", Acts::BinningType::arbitrary); - auto volume = py::class_(c, "Volume").def(py::init<>()); - ACTS_PYTHON_STRUCT_BEGIN(volume, Config::Volume); + auto volume = + py::class_(c, "Volume").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(volume, TGeoDetector::Config::Volume); ACTS_PYTHON_MEMBER(name); ACTS_PYTHON_MEMBER(binToleranceR); ACTS_PYTHON_MEMBER(binTolerancePhi); @@ -185,15 +185,18 @@ void addDetector(Context& ctx) { auto regTriplet = [&c](const std::string& name, auto v) { using type = decltype(v); - py::class_>(c, name.c_str()) + py::class_>(c, name.c_str()) .def(py::init<>()) .def(py::init()) .def(py::init()) - .def_readwrite("negative", &Config::LayerTriplet::negative) - .def_readwrite("central", &Config::LayerTriplet::central) - .def_readwrite("positive", &Config::LayerTriplet::positive) - .def("at", py::overload_cast( - &Config::LayerTriplet::at)); + .def_readwrite("negative", + &TGeoDetector::Config::LayerTriplet::negative) + .def_readwrite("central", + &TGeoDetector::Config::LayerTriplet::central) + .def_readwrite("positive", + &TGeoDetector::Config::LayerTriplet::positive) + .def("at", py::overload_cast( + &TGeoDetector::Config::LayerTriplet::at)); }; regTriplet("LayerTripletBool", true); @@ -204,7 +207,7 @@ void addDetector(Context& ctx) { regTriplet("LayerTripletVectorBinning", std::vector>{}); - ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_STRUCT_BEGIN(c, TGeoDetector::Config); ACTS_PYTHON_MEMBER(surfaceLogLevel); ACTS_PYTHON_MEMBER(layerLogLevel); ACTS_PYTHON_MEMBER(volumeLogLevel); diff --git a/Examples/Python/src/Digitization.cpp b/Examples/Python/src/Digitization.cpp index 5bcce4b74ca..cfafb7bf627 100644 --- a/Examples/Python/src/Digitization.cpp +++ b/Examples/Python/src/Digitization.cpp @@ -55,6 +55,8 @@ void addDigitization(Context& ctx) { ACTS_PYTHON_MEMBER(outputClusters); ACTS_PYTHON_MEMBER(outputMeasurementParticlesMap); ACTS_PYTHON_MEMBER(outputMeasurementSimHitsMap); + ACTS_PYTHON_MEMBER(outputParticleMeasurementsMap); + ACTS_PYTHON_MEMBER(outputSimHitMeasurementsMap); ACTS_PYTHON_MEMBER(surfaceByIdentifier); ACTS_PYTHON_MEMBER(randomNumbers); ACTS_PYTHON_MEMBER(doOutputCells); diff --git a/Examples/Python/src/Geant4Component.cpp b/Examples/Python/src/Geant4Component.cpp index d1f59e3973f..96129e6cc2f 100644 --- a/Examples/Python/src/Geant4Component.cpp +++ b/Examples/Python/src/Geant4Component.cpp @@ -16,17 +16,15 @@ #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Surfaces/SurfaceVisitorConcept.hpp" #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/Framework/IContextDecorator.hpp" -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/GdmlDetectorConstruction.hpp" +#include "ActsExamples/Geant4/Geant4ConstructionOptions.hpp" #include "ActsExamples/Geant4/Geant4Manager.hpp" #include "ActsExamples/Geant4/Geant4Simulation.hpp" #include "ActsExamples/Geant4/RegionCreator.hpp" #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp" +#include "ActsExamples/Geant4Detector/GdmlDetector.hpp" +#include "ActsExamples/Geant4Detector/GdmlDetectorConstruction.hpp" #include "ActsExamples/Geant4Detector/Geant4Detector.hpp" #include "ActsExamples/MuonSpectrometerMockupDetector/MockupSectorBuilder.hpp" -#include "ActsExamples/TelescopeDetector/TelescopeDetector.hpp" -#include "ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp" #include #include @@ -86,10 +84,6 @@ struct ExperimentalSensitiveCandidates }; PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { - py::class_>( - mod, "DetectorConstructionFactory"); - py::class_>( mod, "Geant4Manager") .def_static("instance", &Geant4Manager::instance, @@ -99,6 +93,15 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { py::class_>(mod, "Geant4Handle") .def("tweakLogging", &Geant4Handle::tweakLogging); + { + py::class_>( + mod, "Geant4ConstructionOptions") + .def(py::init<>()) + .def_readwrite("regionCreators", + &Geant4ConstructionOptions::regionCreators); + } + { using Algorithm = Geant4SimulationBase; using Config = Algorithm::Config; @@ -112,7 +115,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { ACTS_PYTHON_STRUCT_BEGIN(c1, Config); ACTS_PYTHON_MEMBER(inputParticles); ACTS_PYTHON_MEMBER(randomNumbers); - ACTS_PYTHON_MEMBER(detectorConstructionFactory); + ACTS_PYTHON_MEMBER(detector); ACTS_PYTHON_MEMBER(geant4Handle); ACTS_PYTHON_STRUCT_END(); } @@ -140,7 +143,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { sm.def("create", [](const Config& cfg, Acts::Logging::Level level, - const std::shared_ptr tGeometry) { + const std::shared_ptr& tGeometry) { // Set a new surface finder Config ccfg = cfg; auto candidateSurfaces = @@ -169,10 +172,11 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { sm.def( "remapSensitiveNames", [](Geant4::SensitiveSurfaceMapper& self, State& state, - GeometryContext& gctx, Geant4::DetectorConstructionFactory& factory, - Transform3& transform) { + GeometryContext& gctx, Detector& detector, Transform3& transform) { return self.remapSensitiveNames( - state, gctx, factory.factorize()->Construct(), transform); + state, gctx, + detector.buildGeant4DetectorConstruction({})->Construct(), + transform); }, "state"_a, "gctx"_a, "g4physicalVolume"_a, "motherTransform"_a); sm.def("checkMapping", &Geant4::SensitiveSurfaceMapper::checkMapping, @@ -230,30 +234,6 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { ACTS_PYTHON_STRUCT_END(); } - { - py::class_>( - mod, "GdmlDetectorConstructionFactory") - .def(py::init>>(), - py::arg("path"), - py::arg("regionCreators") = - std::vector>()); - } - - { - py::class_>( - mod, "TelescopeG4DetectorConstructionFactory") - .def(py::init>>(), - py::arg("cfg"), - py::arg("regionCreators") = - std::vector>()); - } - { using ISelector = Acts::IGeant4PhysicalVolumeSelector; auto is = py::class_>( @@ -277,33 +257,13 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { } { - py::class_>( - mod, "Geant4DetectorElement"); - - using Detector = Geant4Detector; - using Config = Detector::Config; - - auto g = - py::class_>(mod, "Geant4Detector") - .def(py::init<>()) - .def( - "constructDetector", - [](Detector& self, const Config& cfg, Logging::Level logLevel) { - auto logger = getDefaultLogger("Geant4Detector", logLevel); - return self.constructDetector(cfg, *logger); - }, - py::arg("cfg"), py::arg("logLevel") = Logging::INFO) - .def( - "constructTrackingGeometry", - [](Detector& self, const Config& cfg, Logging::Level logLevel) { - auto logger = getDefaultLogger("Geant4Detector", logLevel); - return self.constructTrackingGeometry(cfg, *logger); - }, - py::arg("cfg"), py::arg("logLevel") = Logging::INFO); - - auto c = py::class_(g, "Config").def(py::init<>()); - ACTS_PYTHON_STRUCT_BEGIN(c, Config); + auto f = + py::class_>( + mod, "Geant4Detector") + .def(py::init()); + + auto c = py::class_(f, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Geant4Detector::Config); ACTS_PYTHON_MEMBER(name); ACTS_PYTHON_MEMBER(g4World); ACTS_PYTHON_MEMBER(g4SurfaceOptions); @@ -313,6 +273,18 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { ACTS_PYTHON_STRUCT_END(); } + { + auto f = py::class_>( + mod, "GdmlDetector") + .def(py::init()); + + auto c = py::class_(f, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, GdmlDetector::Config); + ACTS_PYTHON_MEMBER(path); + ACTS_PYTHON_MEMBER(logLevel); + ACTS_PYTHON_STRUCT_END(); + } + { /// Helper function to test if the automatic geometry conversion works /// @@ -326,7 +298,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { passiveMatches, bool convertMaterial) { // Initiate the detector construction & retrieve world - ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName); + ActsExamples::GdmlDetectorConstruction gdmlContruction(gdmlFileName, {}); const auto* world = gdmlContruction.Construct(); // Create the selectors @@ -406,10 +378,8 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { { using Tool = Geant4::RegionCreator; using Config = Tool::Config; - auto tool = py::class_>(mod, "RegionCreator") - .def(py::init(), - py::arg("config"), py::arg("name"), - py::arg("logLevel") = Logging::INFO) + auto tool = py::class_(mod, "RegionCreator") + .def(py::init(), py::arg("config")) .def_property_readonly("config", &Tool::config); auto c = py::class_(tool, "Config").def(py::init<>()); diff --git a/Examples/Python/src/Geant4DD4hepComponent.cpp b/Examples/Python/src/Geant4DD4hepComponent.cpp deleted file mode 100644 index 409bca273d1..00000000000 --- a/Examples/Python/src/Geant4DD4hepComponent.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/DD4hepDetector/DD4hepDetector.hpp" -#include "ActsExamples/DDG4/DDG4DetectorConstruction.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" - -#include -#include -#include - -namespace py = pybind11; - -using namespace ActsExamples; -using namespace Acts; - -PYBIND11_MODULE(ActsPythonBindingsDDG4, m) { - py::module_::import("acts.ActsPythonBindingsGeant4"); - - py::class_>( - m, "DDG4DetectorConstructionFactory") - .def(py::init, - std::vector>>(), - py::arg("detector"), - py::arg("regionCreators") = - std::vector>()); -} diff --git a/Examples/Python/src/Geant4GeoModelComponent.cpp b/Examples/Python/src/Geant4GeoModelComponent.cpp deleted file mode 100644 index d9c372984db..00000000000 --- a/Examples/Python/src/Geant4GeoModelComponent.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" -#include "ActsExamples/Geant4/RegionCreator.hpp" -#include "ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp" - -#include -#include -#include - -class GeoVPhysVol; - -namespace py = pybind11; - -using namespace ActsExamples; -using namespace Acts; - -PYBIND11_MODULE(ActsPythonBindingsGeoModelG4, m) { - py::module_::import("acts.ActsPythonBindingsGeant4"); - - py::class_>( - m, "GeoModelDetectorConstructionFactory") - .def(py::init>>(), - py::arg("geoModelTree"), - py::arg("regionCreators") = - std::vector>()); -} diff --git a/Examples/Python/src/Geant4HepMC3.cpp b/Examples/Python/src/Geant4HepMC3.cpp index 913a23da60f..596cf744660 100644 --- a/Examples/Python/src/Geant4HepMC3.cpp +++ b/Examples/Python/src/Geant4HepMC3.cpp @@ -7,11 +7,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. #include "Acts/Plugins/Python/Utilities.hpp" -#include "ActsExamples/Geant4/DetectorConstructionFactory.hpp" #include "ActsExamples/Geant4HepMC/EventRecording.hpp" -#include - #include #include @@ -29,8 +26,7 @@ void addGeant4HepMC3(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( EventRecording, h3, "EventRecording", inputParticles, outputHepMcTracks, - detectorConstructionFactory, seed1, seed2, processesCombine, - processSelect, processesReject); + detector, seed1, seed2, processesCombine, processSelect, processesReject); } } // namespace Acts::Python diff --git a/Examples/Python/src/GeoModel.cpp b/Examples/Python/src/GeoModel.cpp index 58609a573d5..899781a1f2c 100644 --- a/Examples/Python/src/GeoModel.cpp +++ b/Examples/Python/src/GeoModel.cpp @@ -27,6 +27,7 @@ #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" #include "Acts/Surfaces/RectangleBounds.hpp" +#include "ActsExamples/GeoModelDetector/GeoModelDetector.hpp" #include "ActsExamples/ITkModuleSplitting/ITkModuleSplitting.hpp" #include @@ -40,6 +41,7 @@ namespace py = pybind11; using namespace pybind11::literals; namespace Acts::Python { + void addGeoModel(Context& ctx) { auto m = ctx.get("main"); @@ -59,6 +61,21 @@ void addGeoModel(Context& ctx) { return self.surface().getSharedPtr(); }); + { + auto f = + py::class_>( + gm, "GeoModelDetector") + .def(py::init()); + + auto c = py::class_(f, "Config") + .def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, ActsExamples::GeoModelDetector::Config); + ACTS_PYTHON_MEMBER(path); + ACTS_PYTHON_MEMBER(logLevel); + ACTS_PYTHON_STRUCT_END(); + } + // Shape converters { py::class_(m, "ProtoLayer") + .def(py::init>&, + const Transform3&>(), + "gctx"_a, "surfaces"_a, "transform"_a = Transform3::Identity()) + .def("min", &ProtoLayer::min, "bval"_a, "addenv"_a = true) + .def("max", &ProtoLayer::max, "bval"_a, "addenv"_a = true) + .def_property_readonly("surfaces", &ProtoLayer::surfaces); } } // namespace Acts::Python diff --git a/Examples/Python/src/GeometryBuildingGen1.cpp b/Examples/Python/src/GeometryBuildingGen1.cpp index 2886c41e1d5..0baea2a7746 100644 --- a/Examples/Python/src/GeometryBuildingGen1.cpp +++ b/Examples/Python/src/GeometryBuildingGen1.cpp @@ -6,7 +6,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/CylinderVolumeHelper.hpp" #include "Acts/Geometry/Layer.hpp" #include "Acts/Geometry/LayerArrayCreator.hpp" @@ -17,9 +16,7 @@ #include "Acts/Geometry/VolumeBounds.hpp" #include "Acts/Plugins/Python/Utilities.hpp" -#include #include -#include #include #include @@ -44,14 +41,15 @@ void addGeometryBuildingGen1(Context &ctx) { [](const Acts::LayerCreator &self, const GeometryContext &gctx, SurfacePtrVector surfaces, std::size_t binsPhi, std::size_t binsZ) { - return self.cylinderLayer(gctx, surfaces, binsPhi, binsZ); + return self.cylinderLayer(gctx, std::move(surfaces), binsPhi, + binsZ); }) - .def("discLayer", - [](const Acts::LayerCreator &self, const GeometryContext &gctx, - SurfacePtrVector surfaces, std::size_t binsR, - std::size_t binsPhi) { - return self.discLayer(gctx, surfaces, binsR, binsPhi); - }); + .def("discLayer", [](const Acts::LayerCreator &self, + const GeometryContext &gctx, + SurfacePtrVector surfaces, std::size_t binsR, + std::size_t binsPhi) { + return self.discLayer(gctx, std::move(surfaces), binsR, binsPhi); + }); auto config = py::class_(creator, "Config").def(py::init<>()); @@ -115,8 +113,9 @@ void addGeometryBuildingGen1(Context &ctx) { GeometryContext gctx, const LayerVector &layers, std::shared_ptr volumeBounds, const Transform3 &trafo, const std::string &name) { - return self.createTrackingVolume( - gctx, layers, {}, volumeBounds, {}, trafo, name); + return self.createTrackingVolume(gctx, layers, {}, + std::move(volumeBounds), {}, + trafo, name); }) .def("createContainerTrackingVolume", &Acts::CylinderVolumeHelper::createContainerTrackingVolume); diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index a74d277f7a5..9f494dddee6 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -7,25 +7,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. #include "Acts/ActsVersion.hpp" -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/GeometryIdentifier.hpp" -#include "Acts/MagneticField/MagneticFieldContext.hpp" -#include "Acts/Plugins/FpeMonitoring/FpeMonitor.hpp" #include "Acts/Plugins/Python/Utilities.hpp" -#include "Acts/Utilities/CalibrationContext.hpp" -#include "Acts/Utilities/Logger.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include + #include #include -#include #include #include @@ -75,6 +60,7 @@ void addUtilities(Context& ctx); void addDigitization(Context& ctx); void addPythia8(Context& ctx); void addGeoModel(Context& ctx); +void addTGeo(Context& ctx); void addJson(Context& ctx); void addDetray(Context& ctx); void addHepMC3(Context& ctx); @@ -146,6 +132,7 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addPythia8(ctx); addJson(ctx); addGeoModel(ctx); + addTGeo(ctx); addDetray(ctx); addHepMC3(ctx); addExaTrkXTrackFinding(ctx); diff --git a/Examples/Python/src/Onnx.cpp b/Examples/Python/src/Onnx.cpp index c22e595bb43..8bd75a3047a 100644 --- a/Examples/Python/src/Onnx.cpp +++ b/Examples/Python/src/Onnx.cpp @@ -8,7 +8,6 @@ #include "Acts/Plugins/Python/Utilities.hpp" #include "ActsExamples/TrackFindingML/AmbiguityResolutionMLAlgorithm.hpp" -#include "ActsExamples/TrackFindingML/AmbiguityResolutionMLDBScanAlgorithm.hpp" #include "ActsExamples/TrackFindingML/SeedFilterMLAlgorithm.hpp" #include @@ -31,11 +30,6 @@ void addOnnx(Context& ctx) { inputTracks, inputDuplicateNN, outputTracks, nMeasurementsMin); - ACTS_PYTHON_DECLARE_ALGORITHM( - ActsExamples::AmbiguityResolutionMLDBScanAlgorithm, onnx, - "AmbiguityResolutionMLDBScanAlgorithm", inputTracks, inputDuplicateNN, - outputTracks, nMeasurementsMin, epsilonDBScan, minPointsDBScan); - ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::SeedFilterMLAlgorithm, onnx, "SeedFilterMLAlgorithm", inputTrackParameters, inputSimSeeds, inputSeedFilterNN, diff --git a/Examples/Python/src/Output.cpp b/Examples/Python/src/Output.cpp index 251ca2b6357..838649816f6 100644 --- a/Examples/Python/src/Output.cpp +++ b/Examples/Python/src/Output.cpp @@ -6,13 +6,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Visualization/IVisualization3D.hpp" #include "Acts/Visualization/ViewConfig.hpp" -#include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" #include "ActsExamples/Io/Csv/CsvBFieldWriter.hpp" #include "ActsExamples/Io/Csv/CsvExaTrkXGraphWriter.hpp" #include "ActsExamples/Io/Csv/CsvMeasurementWriter.hpp" @@ -20,8 +17,8 @@ #include "ActsExamples/Io/Csv/CsvProtoTrackWriter.hpp" #include "ActsExamples/Io/Csv/CsvSeedWriter.hpp" #include "ActsExamples/Io/Csv/CsvSimHitWriter.hpp" +#include "ActsExamples/Io/Csv/CsvSpacePointWriter.hpp" #include "ActsExamples/Io/Csv/CsvSpacePointsBucketWriter.hpp" -#include "ActsExamples/Io/Csv/CsvSpacepointWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackParameterWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackWriter.hpp" #include "ActsExamples/Io/Csv/CsvTrackingGeometryWriter.hpp" @@ -114,7 +111,9 @@ void addOutput(Context& ctx) { ACTS_PYTHON_DECLARE_WRITER(ActsExamples::ObjSimHitWriter, mex, "ObjSimHitWriter", inputSimHits, outputDir, - outputStem, outputPrecision, drawConnections); + outputStem, outputPrecision, drawConnections, + momentumThreshold, momentumThresholdTraj, + nInterpolatedPoints, keepOriginalHits); { auto c = py::class_(m, "ViewConfig").def(py::init<>()); @@ -206,7 +205,7 @@ void addOutput(Context& ctx) { ACTS_PYTHON_DECLARE_WRITER(ActsExamples::TrackFinderNTupleWriter, mex, "TrackFinderNTupleWriter", inputTracks, - inputParticles, inputMeasurementParticlesMap, + inputParticles, inputParticleMeasurementsMap, inputTrackParticleMatching, filePath, fileMode, treeNameTracks, treeNameParticles); @@ -376,8 +375,8 @@ void addOutput(Context& ctx) { "CsvSimHitWriter", inputSimHits, outputDir, outputStem, outputPrecision); - ACTS_PYTHON_DECLARE_WRITER(ActsExamples::CsvSpacepointWriter, mex, - "CsvSpacepointWriter", inputSpacepoints, outputDir, + ACTS_PYTHON_DECLARE_WRITER(ActsExamples::CsvSpacePointWriter, mex, + "CsvSpacePointWriter", inputSpacepoints, outputDir, outputPrecision); ACTS_PYTHON_DECLARE_WRITER(ActsExamples::CsvSpacePointsBucketWriter, mex, diff --git a/Examples/Python/src/TGeo.cpp b/Examples/Python/src/TGeo.cpp new file mode 100644 index 00000000000..c67c2f3d653 --- /dev/null +++ b/Examples/Python/src/TGeo.cpp @@ -0,0 +1,87 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Python/Utilities.hpp" +#include "Acts/Plugins/TGeo/TGeoDetectorElement.hpp" +#include "Acts/Plugins/TGeo/TGeoLayerBuilder.hpp" +#include "Acts/Plugins/TGeo/TGeoParser.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +namespace Acts::Python { +void addTGeo(Context& ctx) { + auto [m, mex] = ctx.get("main", "examples"); + + auto tgeo = mex.def_submodule("tgeo"); + + { + py::class_>( + tgeo, "TGeoDetectorElement") + .def("surface", [](const Acts::TGeoDetectorElement& self) { + return self.surface().getSharedPtr(); + }); + } + + { + /// Helper function to test if the automatic geometry conversion works + /// + /// @param rootFileName is the name of the GDML file + /// @param sensitiveMatches is a list of strings to match sensitive volumes + /// @param localAxes is the TGeo->ACTS axis conversion convention + /// @param scaleConversion is a unit scalor conversion factor + tgeo.def("_convertToElements", + [](const std::string& rootFileName, + const std::vector& sensitiveMatches, + const std::string& localAxes, double scaleConversion) { + // Return vector + std::vector> + tgElements; + // TGeo import + TGeoManager::Import(rootFileName.c_str()); + if (gGeoManager != nullptr) { + auto tVolume = gGeoManager->GetTopVolume(); + if (tVolume != nullptr) { + TGeoHMatrix gmatrix = TGeoIdentity(tVolume->GetName()); + + TGeoParser::Options tgpOptions; + tgpOptions.volumeNames = {tVolume->GetName()}; + tgpOptions.targetNames = sensitiveMatches; + tgpOptions.unit = scaleConversion; + TGeoParser::State tgpState; + tgpState.volume = tVolume; + tgpState.onBranch = true; + + TGeoParser::select(tgpState, tgpOptions, gmatrix); + tgElements.reserve(tgpState.selectedNodes.size()); + + for (const auto& snode : tgpState.selectedNodes) { + auto identifier = Acts::TGeoDetectorElement::Identifier(); + auto tgElement = TGeoLayerBuilder::defaultElementFactory( + identifier, *snode.node, *snode.transform, localAxes, + scaleConversion, nullptr); + tgElements.emplace_back(tgElement); + } + } + } + // Return the elements + return tgElements; + }); + } +} + +} // namespace Acts::Python diff --git a/Examples/Python/src/TGeoStub.cpp b/Examples/Python/src/TGeoStub.cpp new file mode 100644 index 00000000000..09a800e693f --- /dev/null +++ b/Examples/Python/src/TGeoStub.cpp @@ -0,0 +1,15 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace Acts::Python { +struct Context; +} // namespace Acts::Python + +namespace Acts::Python { +void addTGeo(Context& /*ctx*/) {} +} // namespace Acts::Python diff --git a/Examples/Python/src/TrackFitting.cpp b/Examples/Python/src/TrackFitting.cpp index c8dc72f9eef..6d1b9818e51 100644 --- a/Examples/Python/src/TrackFitting.cpp +++ b/Examples/Python/src/TrackFitting.cpp @@ -6,35 +6,23 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Definitions/Algebra.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/TrackFitting/BetheHeitlerApprox.hpp" #include "Acts/TrackFitting/GsfOptions.hpp" #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ScalingCalibrator.hpp" #include "ActsExamples/TrackFitting/RefittingAlgorithm.hpp" -#include "ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp" #include "ActsExamples/TrackFitting/TrackFitterFunction.hpp" #include "ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp" #include #include -#include #include #include -namespace Acts { -class MagneticFieldProvider; -class TrackingGeometry; -} // namespace Acts -namespace ActsExamples { -class IAlgorithm; -} // namespace ActsExamples - namespace py = pybind11; using namespace ActsExamples; @@ -46,11 +34,6 @@ namespace Acts::Python { void addTrackFitting(Context& ctx) { auto mex = ctx.get("examples"); - ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::SurfaceSortingAlgorithm, mex, - "SurfaceSortingAlgorithm", inputProtoTracks, - inputSimHits, inputMeasurementSimHitsMap, - outputProtoTracks); - ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::TrackFittingAlgorithm, mex, "TrackFittingAlgorithm", inputMeasurements, inputProtoTracks, inputInitialTrackParameters, diff --git a/Examples/Python/src/TruthTracking.cpp b/Examples/Python/src/TruthTracking.cpp index 319ee24e2ee..4d3569f4656 100644 --- a/Examples/Python/src/TruthTracking.cpp +++ b/Examples/Python/src/TruthTracking.cpp @@ -40,7 +40,7 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::TruthTrackFinder, mex, "TruthTrackFinder", inputParticles, - inputMeasurementParticlesMap, outputProtoTracks); + inputParticleMeasurementsMap, outputProtoTracks); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::ParticleTrackParamExtractor, mex, "ParticleTrackParamExtractor", inputParticles, @@ -84,6 +84,8 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_MEMBER(mMax); ACTS_PYTHON_MEMBER(ptMin); ACTS_PYTHON_MEMBER(ptMax); + ACTS_PYTHON_MEMBER(hitsMin); + ACTS_PYTHON_MEMBER(hitsMax); ACTS_PYTHON_MEMBER(measurementsMin); ACTS_PYTHON_MEMBER(measurementsMax); ACTS_PYTHON_MEMBER(removeCharged); @@ -159,7 +161,7 @@ void addTruthTracking(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::TruthSeedingAlgorithm, mex, "TruthSeedingAlgorithm", - inputParticles, inputMeasurementParticlesMap, inputSpacePoints, + inputParticles, inputParticleMeasurementsMap, inputSpacePoints, outputParticles, outputSeeds, outputProtoTracks, deltaRMin, deltaRMax); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::HitSelector, mex, "HitSelector", diff --git a/Examples/Python/tests/conftest.py b/Examples/Python/tests/conftest.py index 4bd8a430730..c1eb489bb62 100644 --- a/Examples/Python/tests/conftest.py +++ b/Examples/Python/tests/conftest.py @@ -237,14 +237,17 @@ def _basic_prop_seq_factory(geo, s=None): @pytest.fixture def trk_geo(): - detector, geo, contextDecorators = acts.examples.GenericDetector.create() - yield geo + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() + yield trackingGeometry DetectorConfig = namedtuple( "DetectorConfig", [ - "detectorTuple", + "detector", + "trackingGeometry", + "decorators", "geometrySelection", "digiConfigFile", "name", @@ -257,9 +260,13 @@ def detector_config(request): srcdir = Path(__file__).resolve().parent.parent.parent.parent if request.param == "generic": - detectorTuple = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() return DetectorConfig( - detectorTuple, + detector, + trackingGeometry, + decorators, geometrySelection=( srcdir / "Examples/Algorithms/TrackFinding/share/geoSelection-genericDetector.json" @@ -278,9 +285,13 @@ def detector_config(request): srcdir / "thirdparty/OpenDataDetector/data/odd-material-maps.root", level=acts.logging.INFO, ) - detectorTuple = getOpenDataDetector(matDeco) + detector = getOpenDataDetector(matDeco) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() return DetectorConfig( - detectorTuple, + detector, + trackingGeometry, + decorators, digiConfigFile=( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" @@ -375,14 +386,8 @@ def _do_material_recording(d: Path): s = acts.examples.Sequencer(events=2, numThreads=1) - with getOpenDataDetector() as (detector, trackingGeometry, decorators): - detectorConstructionFactory = ( - acts.examples.geant4.dd4hep.DDG4DetectorConstructionFactory(detector) - ) - - runMaterialRecording( - detectorConstructionFactory, str(d), tracksPerEvent=100, s=s - ) + with getOpenDataDetector() as detector: + runMaterialRecording(detector, str(d), tracksPerEvent=100, s=s) s.run() @@ -396,7 +401,9 @@ def material_recording_session(): pytest.skip("DD4hep recording requested, but DD4hep is not set up") with tempfile.TemporaryDirectory() as d: - p = multiprocessing.Process(target=_do_material_recording, args=(d,)) + # explicitly ask for "spawn" as CI failures were observed with "fork" + spawn_context = multiprocessing.get_context("spawn") + p = spawn_context.Process(target=_do_material_recording, args=(d,)) p.start() p.join() if p.exitcode != 0: diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index 6eeaaebff44..07d42c66ad1 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -72,25 +72,17 @@ test_root_clusters_writer[configKwConstructor]__clusters.root: e842df4fe04eefff3 test_root_clusters_writer[kwargsConstructor]__clusters.root: e842df4fe04eefff3df5f32cd1026e93286be62b8040dc700a2aff557c56dec8 test_exatrkx[cpu-torch]__performance_track_finding.root: 36b3045589c4c17c038dbc87943366f4af4440f7eea6887afb763871ac149b05 test_exatrkx[gpu-onnx]__performance_track_finding.root: 9090de10ffb1489d3f1993e2a3081a3038227e3e5c453e98a9a4f33ea3d6d817 -test_exatrkx[gpu-torch]__performance_track_finding.root: 36b3045589c4c17c038dbc87943366f4af4440f7eea6887afb763871ac149b05 +test_exatrkx[gpu-torch]__performance_track_finding.root: 6b658fa22c7532e082eaab7aa4b71b852f1c324adcc59d1156aff45124b222d9 test_ML_Ambiguity_Solver__performance_finding_ambiML.root: 166dd8bb189097c4957b7b02c04c41267868d72d9a08c4bb892985b06849cb76 test_refitting[odd]__trackstates_gsf_refit.root: e297749dc1e7eda3b8dea13defa0499986c584740d93e723a901b498b8e90c71 test_refitting[odd]__tracksummary_gsf_refit.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 test_refitting[generic]__trackstates_gsf_refit.root: 4424fdf2f27575db825c1a59f8e53a1595946211cbd5b2c8d3a2f71cdcc77ae9 test_refitting[generic]__tracksummary_gsf_refit.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 -test_truth_tracking_kalman[generic-False-0.0]__trackstates_kf.root: 9f77962b92037cb760b1629a602b1dae61f45e659c45d9a87baa784f6190960e -test_truth_tracking_kalman[generic-False-0.0]__tracksummary_kf.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 -test_truth_tracking_kalman[generic-False-1000.0]__trackstates_kf.root: 56a1bd989b9c1316b9098c65fa75df9e6683e62e35ae68d8f72d27220be0fd7d -test_truth_tracking_kalman[generic-False-1000.0]__tracksummary_kf.root: 2d85be3a5dff01a1076e80f8c225aca32f65b30dc9c3551b610ac2f12d006a39 -test_truth_tracking_kalman[generic-True-0.0]__trackstates_kf.root: 9f77962b92037cb760b1629a602b1dae61f45e659c45d9a87baa784f6190960e -test_truth_tracking_kalman[generic-True-0.0]__tracksummary_kf.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 -test_truth_tracking_kalman[generic-True-1000.0]__trackstates_kf.root: 56a1bd989b9c1316b9098c65fa75df9e6683e62e35ae68d8f72d27220be0fd7d -test_truth_tracking_kalman[generic-True-1000.0]__tracksummary_kf.root: 2d85be3a5dff01a1076e80f8c225aca32f65b30dc9c3551b610ac2f12d006a39 -test_truth_tracking_kalman[odd-False-0.0]__trackstates_kf.root: 7e144571b19aaf00002aef4f5bec5d488b96fb9ed8e1b2904c3756b31be83513 -test_truth_tracking_kalman[odd-False-0.0]__tracksummary_kf.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 -test_truth_tracking_kalman[odd-False-1000.0]__trackstates_kf.root: efdf37f56fa3ef85265cda61853f4c0f989e3d4f4745b5e351c9bcca78cd93cc -test_truth_tracking_kalman[odd-False-1000.0]__tracksummary_kf.root: b84fabd6c1b71c360019cd719400746ccff1e565a983ca23d0621790b8843e44 -test_truth_tracking_kalman[odd-True-0.0]__trackstates_kf.root: 7e144571b19aaf00002aef4f5bec5d488b96fb9ed8e1b2904c3756b31be83513 -test_truth_tracking_kalman[odd-True-0.0]__tracksummary_kf.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 -test_truth_tracking_kalman[odd-True-1000.0]__trackstates_kf.root: efdf37f56fa3ef85265cda61853f4c0f989e3d4f4745b5e351c9bcca78cd93cc -test_truth_tracking_kalman[odd-True-1000.0]__tracksummary_kf.root: b84fabd6c1b71c360019cd719400746ccff1e565a983ca23d0621790b8843e44 +test_truth_tracking_kalman[generic-0.0]__trackstates_kf.root: 9f77962b92037cb760b1629a602b1dae61f45e659c45d9a87baa784f6190960e +test_truth_tracking_kalman[generic-0.0]__tracksummary_kf.root: 562deecee4cfb97ceee72eff53d63da079e3249fb62d6bcd556e6f27d495dfd9 +test_truth_tracking_kalman[generic-1000.0]__trackstates_kf.root: 56a1bd989b9c1316b9098c65fa75df9e6683e62e35ae68d8f72d27220be0fd7d +test_truth_tracking_kalman[generic-1000.0]__tracksummary_kf.root: 2d85be3a5dff01a1076e80f8c225aca32f65b30dc9c3551b610ac2f12d006a39 +test_truth_tracking_kalman[odd-0.0]__trackstates_kf.root: 7e144571b19aaf00002aef4f5bec5d488b96fb9ed8e1b2904c3756b31be83513 +test_truth_tracking_kalman[odd-0.0]__tracksummary_kf.root: d5085882e45a0b699194dff9f40a36e9291227bf65f9aaaf9087f9242ef5ae22 +test_truth_tracking_kalman[odd-1000.0]__trackstates_kf.root: efdf37f56fa3ef85265cda61853f4c0f989e3d4f4745b5e351c9bcca78cd93cc +test_truth_tracking_kalman[odd-1000.0]__tracksummary_kf.root: b84fabd6c1b71c360019cd719400746ccff1e565a983ca23d0621790b8843e44 diff --git a/Examples/Python/tests/test_algorithms.py b/Examples/Python/tests/test_algorithms.py index 6311dc6460a..0f1e1f01a61 100644 --- a/Examples/Python/tests/test_algorithms.py +++ b/Examples/Python/tests/test_algorithms.py @@ -17,7 +17,6 @@ TrackParameterSmearing, TrackSelectorAlgorithm, TrackFittingAlgorithm, - SurfaceSortingAlgorithm, ParticlesPrinter, TrackParametersPrinter, PropagationAlgorithm, @@ -47,7 +46,6 @@ TrackParameterSmearing, TrackSelectorAlgorithm, TrackFittingAlgorithm, - SurfaceSortingAlgorithm, ParticlesPrinter, TrackParametersPrinter, PropagationAlgorithm, diff --git a/Examples/Python/tests/test_blueprint.py b/Examples/Python/tests/test_blueprint.py new file mode 100644 index 00000000000..8471bf52105 --- /dev/null +++ b/Examples/Python/tests/test_blueprint.py @@ -0,0 +1,80 @@ +import pytest + +import acts + +mm = acts.UnitConstants.mm +m = acts.UnitConstants.m +degree = acts.UnitConstants.degree + +bv = acts.BinningValue + +gctx = acts.GeometryContext() +logLevel = acts.logging.VERBOSE + + +def test_zdirection_container_blueprint(tmp_path): + + def write(root: acts.BlueprintNode, stage: int): + gz = tmp_path / f"blueprint_{stage}.dot" + print(gz) + with gz.open("w") as fh: + root.graphviz(fh) + + base = acts.Transform3.Identity() + + root = acts.Blueprint(envelope=acts.ExtentEnvelope(r=[10 * mm, 10 * mm])) + assert root.depth == 0 + + barrel = root.addCylinderContainer("Barrel", direction=bv.binR) + + assert barrel.depth == 1 + + r = 25 * mm + for i in range(1, 3): + r += 50 * mm + bounds = acts.CylinderVolumeBounds(r, r + 20 * mm, 200 * mm) + vol = barrel.addStaticVolume(base, bounds, name=f"Barrel_{i}") + assert vol.depth == 2 + + write(barrel, 1) + + root.clearChildren() + + assert barrel.depth == 0 + + det = root.addCylinderContainer("Detector", direction=bv.binZ) + + assert det.depth == 1 + + with det.CylinderContainer("nEC", direction=bv.binZ) as ec: + assert ec.depth == 2 + z = -200 + for i in range(1, 3): + z -= 200 * mm + bounds = acts.CylinderVolumeBounds(100 * mm, 150 * mm, 50 * mm) + + trf = base * acts.Translation3(acts.Vector3(0, 0, z)) + + vol = ec.addStaticVolume(trf, bounds, name=f"nEC_{i}") + assert vol.depth == 3 + + write(ec, 2) + + det.addChild(barrel) + assert barrel.depth == 2 + + write(det, 3) + + with det.CylinderContainer("pEC", direction=bv.binZ) as ec: + assert ec.depth == 2 + z = 200 + for i in range(1, 3): + z += 200 * mm + bounds = acts.CylinderVolumeBounds(100 * mm, 150 * mm, 50 * mm) + + trf = base * acts.Translation3(acts.Vector3(0, 0, z)) + + vol = ec.addStaticVolume(trf, bounds, name=f"pEC_{i}") + assert vol.depth == 3 + + write(root, 4) diff --git a/Examples/Python/tests/test_detectors.py b/Examples/Python/tests/test_detectors.py index 3de27524db9..bbfb715697f 100644 --- a/Examples/Python/tests/test_detectors.py +++ b/Examples/Python/tests/test_detectors.py @@ -27,41 +27,50 @@ def check_extra_odd(srf): def test_generic_geometry(): - detector, geo, contextDecorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() + contextDecorators = detector.contextDecorators() assert detector is not None - assert geo is not None + assert trackingGeometry is not None assert contextDecorators is not None - assert count_surfaces(geo) == 18728 + assert count_surfaces(trackingGeometry) == 18728 def test_telescope_geometry(): n_surfaces = 10 - detector, geo, contextDecorators = acts.examples.TelescopeDetector.create( + config = acts.examples.TelescopeDetector.Config( bounds=[100, 100], positions=[10 * i for i in range(n_surfaces)], stereos=[0] * n_surfaces, binValue=0, ) + detector = acts.examples.TelescopeDetector(config) + trackingGeometry = detector.trackingGeometry() + contextDecorators = detector.contextDecorators() assert detector is not None - assert geo is not None + assert trackingGeometry is not None assert contextDecorators is not None - assert count_surfaces(geo) == n_surfaces + assert count_surfaces(trackingGeometry) == n_surfaces @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep is not set up") def test_odd(): - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector() as detector: + trackingGeometry = detector.trackingGeometry() + trackingGeometry.visitSurfaces(check_extra_odd) assert count_surfaces(trackingGeometry) == 18824 def test_aligned_detector(): - detector, trackingGeometry, decorators = acts.examples.AlignedDetector.create() + detector = acts.examples.AlignedDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() assert detector is not None assert trackingGeometry is not None diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index 2261e58cea4..4e7ea9b14c3 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -149,7 +149,7 @@ def test_geant4(tmp_path, assert_root_hash): # This test literally only ensures that the geant 4 example can run without erroring out # just to make sure it can build the odd - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector(): pass csv = tmp_path / "csv" @@ -427,7 +427,7 @@ def test_itk_seeding(tmp_path, trk_geo, field, assert_root_hash): postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), eta=(-4, 4), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) @@ -579,9 +579,8 @@ def test_event_recording(tmp_path): @pytest.mark.parametrize("revFiltMomThresh", [0 * u.GeV, 1 * u.TeV]) -@pytest.mark.parametrize("directNavigation", [False, True]) def test_truth_tracking_kalman( - tmp_path, assert_root_hash, revFiltMomThresh, directNavigation, detector_config + tmp_path, assert_root_hash, revFiltMomThresh, detector_config ): root_files = [ ("trackstates_kf.root", "trackstates", 19), @@ -593,8 +592,7 @@ def test_truth_tracking_kalman( fp = tmp_path / fn assert not fp.exists() - print("with") - with detector_config.detectorTuple as (detector, trackingGeometry, decorators): + with detector_config.detector: from truth_tracking_kalman import runTruthTrackingKalman field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) @@ -602,17 +600,15 @@ def test_truth_tracking_kalman( seq = Sequencer(events=10, numThreads=1) runTruthTrackingKalman( - trackingGeometry=trackingGeometry, + trackingGeometry=detector_config.trackingGeometry, field=field, digiConfigFile=detector_config.digiConfigFile, outputDir=tmp_path, reverseFilteringMomThreshold=revFiltMomThresh, - directNavigation=directNavigation, s=seq, ) seq.run() - print("done") for fn, tn, ee in root_files: fp = tmp_path / fn @@ -659,10 +655,10 @@ def test_truth_tracking_gsf(tmp_path, assert_root_hash, detector_config): fp = tmp_path / fn assert not fp.exists() - with detector_config.detectorTuple as (detector, trackingGeometry, decorators): + with detector_config.detector: runTruthTrackingGsf( - trackingGeometry=trackingGeometry, - decorators=decorators, + trackingGeometry=detector_config.trackingGeometry, + decorators=detector_config.decorators, field=field, digiConfigFile=detector_config.digiConfigFile, outputDir=tmp_path, @@ -691,11 +687,11 @@ def test_refitting(tmp_path, detector_config, assert_root_hash): numThreads=1, ) - with detector_config.detectorTuple as (detector, trackingGeometry, decorators): + with detector_config.detector: # Only check if it runs without errors right known # Changes in fitter behaviour should be caught by other tests runRefittingGsf( - trackingGeometry=trackingGeometry, + trackingGeometry=detector_config.trackingGeometry, field=field, digiConfigFile=detector_config.digiConfigFile, outputDir=tmp_path, @@ -759,7 +755,10 @@ def test_material_mapping(material_recording, tmp_path, assert_root_hash): s = Sequencer(numThreads=1) - with getOpenDataDetector(mdecorator) as (detector, trackingGeometry, decorators): + with getOpenDataDetector(mdecorator) as detector: + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() + runMaterialMapping( trackingGeometry, decorators, @@ -792,11 +791,12 @@ def test_material_mapping(material_recording, tmp_path, assert_root_hash): s = Sequencer(events=10, numThreads=1) - with getOpenDataDetector(mdecorator=acts.IMaterialDecorator.fromFile(mat_file)) as ( - detector, - trackingGeometry, - decorators, - ): + with getOpenDataDetector( + mdecorator=acts.IMaterialDecorator.fromFile(mat_file) + ) as detector: + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() + runMaterialValidation( 10, 1000, trackingGeometry, decorators, field, outputDir=str(tmp_path), s=s ) @@ -826,11 +826,12 @@ def test_volume_material_mapping(material_recording, tmp_path, assert_root_hash) s = Sequencer(numThreads=1) - with getOpenDataDetector(mdecorator=acts.IMaterialDecorator.fromFile(geo_map)) as ( - detector, - trackingGeometry, - decorators, - ): + with getOpenDataDetector( + mdecorator=acts.IMaterialDecorator.fromFile(geo_map) + ) as detector: + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() + runMaterialMapping( trackingGeometry, decorators, @@ -864,11 +865,12 @@ def test_volume_material_mapping(material_recording, tmp_path, assert_root_hash) s = Sequencer(events=10, numThreads=1) - with getOpenDataDetector(mdecorator=acts.IMaterialDecorator.fromFile(mat_file)) as ( - detector, - trackingGeometry, - decorators, - ): + with getOpenDataDetector( + mdecorator=acts.IMaterialDecorator.fromFile(mat_file) + ) as detector: + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() + runMaterialValidation( 10, 1000, @@ -888,11 +890,12 @@ def test_volume_material_mapping(material_recording, tmp_path, assert_root_hash) @pytest.mark.parametrize( - "geoFactory,nobj", + "detectorFactory,aligned,nobj", [ - (GenericDetector.create, 450), + (GenericDetector, True, 450), pytest.param( getOpenDataDetector, + True, 540, marks=[ pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep not set up"), @@ -900,12 +903,14 @@ def test_volume_material_mapping(material_recording, tmp_path, assert_root_hash) pytest.mark.odd, ], ), - (functools.partial(AlignedDetector.create, iovSize=1), 450), + (functools.partial(AlignedDetector, iovSize=1), False, 450), ], ) @pytest.mark.slow -def test_geometry_example(geoFactory, nobj, tmp_path): - detector, trackingGeometry, decorators = geoFactory() +def test_geometry_example(detectorFactory, aligned, nobj, tmp_path): + detector = detectorFactory() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() from geometry import runGeometry @@ -942,12 +947,12 @@ def test_geometry_example(geoFactory, nobj, tmp_path): contents = [f.read_text() for f in detector_files] ref = contents[0] for c in contents[1:]: - if isinstance(detector, AlignedDetector): - assert c != ref, "Detector writeout is expected to be different" - else: + if aligned: assert c == ref, "Detector writeout is expected to be identical" + else: + assert c != ref, "Detector writeout is expected to be different" - if not isinstance(detector, AlignedDetector): + if aligned: for f in [json_dir / f"event{i:>09}-detector.json" for i in range(events)]: assert detector_file.exists() with f.open() as fh: @@ -1165,10 +1170,10 @@ def test_ckf_tracks_example( from ckf_tracks import runCKFTracks - with detector_config.detectorTuple as (detector, trackingGeometry, decorators): + with detector_config.detector: runCKFTracks( - trackingGeometry, - decorators, + detector_config.trackingGeometry, + detector_config.decorators, field=field, outputCsv=True, outputDir=tmp_path, @@ -1201,7 +1206,7 @@ def test_full_chain_odd_example(tmp_path): # This test literally only ensures that the full chain example can run without erroring out # just to make sure it can build the odd - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector(): pass script = ( @@ -1234,7 +1239,7 @@ def test_full_chain_odd_example_pythia_geant4(tmp_path): # This test literally only ensures that the full chain example can run without erroring out # just to make sure it can build the odd - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector(): pass script = ( @@ -1288,7 +1293,7 @@ def test_ML_Ambiguity_Solver(tmp_path, assert_root_hash): assert not (tmp_path / root_file).exists() # just to make sure it can build the odd - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector(): pass script = ( diff --git a/Examples/Python/tests/test_reader.py b/Examples/Python/tests/test_reader.py index 104a4e0f98f..46b1c9c229f 100644 --- a/Examples/Python/tests/test_reader.py +++ b/Examples/Python/tests/test_reader.py @@ -290,7 +290,9 @@ def test_edm4hep_simhit_particle_reader(tmp_path): tmp_file = str(tmp_path / "output_edm4hep.root") odd_xml_file = str(getOpenDataDetectorDirectory() / "xml" / "OpenDataDetector.xml") - p = multiprocessing.Process( + # explicitly ask for "spawn" as CI failures were observed with "fork" + spawn_context = multiprocessing.get_context("spawn") + p = spawn_context.Process( target=generate_input_test_edm4hep_simhit_reader, args=(odd_xml_file, tmp_file) ) p.start() @@ -300,7 +302,9 @@ def test_edm4hep_simhit_particle_reader(tmp_path): s = Sequencer(numThreads=1) - with getOpenDataDetector() as (detector, trackingGeometry, decorators): + with getOpenDataDetector() as detector: + trackingGeometry = detector.trackingGeometry() + s.addReader( EDM4hepReader( level=acts.logging.INFO, @@ -386,7 +390,9 @@ def test_edm4hep_measurement_reader(tmp_path, fatras, conf_const): def test_edm4hep_tracks_reader(tmp_path): from acts.examples.edm4hep import EDM4hepTrackWriter, EDM4hepTrackReader - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() + field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) from truth_tracking_kalman import runTruthTrackingKalman diff --git a/Examples/Python/tests/test_writer.py b/Examples/Python/tests/test_writer.py index ac64fa4048f..2de90c931ab 100644 --- a/Examples/Python/tests/test_writer.py +++ b/Examples/Python/tests/test_writer.py @@ -199,7 +199,8 @@ def test_root_simhits_writer(tmp_path, fatras, conf_const, assert_root_hash): @pytest.mark.root def test_root_tracksummary_writer(tmp_path, fatras, conf_const): - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) s = Sequencer(numThreads=1, events=10) @@ -361,9 +362,10 @@ def test_csv_writer_interface(writer, conf_const, tmp_path, trk_geo): def test_root_material_writer(tmp_path, assert_root_hash): from acts.examples.dd4hep import DD4hepDetector - detector, trackingGeometry, _ = DD4hepDetector.create( + detector = DD4hepDetector( xmlFileNames=[str(getOpenDataDetectorDirectory() / "xml/OpenDataDetector.xml")] ) + trackingGeometry = detector.trackingGeometry() out = tmp_path / "material.root" @@ -385,9 +387,10 @@ def test_root_material_writer(tmp_path, assert_root_hash): def test_json_material_writer(tmp_path, fmt): from acts.examples.dd4hep import DD4hepDetector - detector, trackingGeometry, _ = DD4hepDetector.create( + detector = DD4hepDetector( xmlFileNames=[str(getOpenDataDetectorDirectory() / "xml/OpenDataDetector.xml")] ) + trackingGeometry = detector.trackingGeometry() out = (tmp_path / "material").with_suffix("." + fmt.name.lower()) @@ -404,7 +407,8 @@ def test_json_material_writer(tmp_path, fmt): @pytest.mark.csv def test_csv_multitrajectory_writer(tmp_path): - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) from truth_tracking_kalman import runTruthTrackingKalman @@ -602,7 +606,8 @@ def test_edm4hep_particle_writer(tmp_path, conf_const, ptcl_gun): def test_edm4hep_multitrajectory_writer(tmp_path): from acts.examples.edm4hep import EDM4hepMultiTrajectoryWriter - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) from truth_tracking_kalman import runTruthTrackingKalman @@ -651,7 +656,8 @@ def test_edm4hep_multitrajectory_writer(tmp_path): def test_edm4hep_tracks_writer(tmp_path): from acts.examples.edm4hep import EDM4hepTrackWriter - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) from truth_tracking_kalman import runTruthTrackingKalman diff --git a/Examples/Scripts/GsfDebugger/make_gsf_verbose_log.py b/Examples/Scripts/GsfDebugger/make_gsf_verbose_log.py index c72e183feae..7f4b466ee84 100755 --- a/Examples/Scripts/GsfDebugger/make_gsf_verbose_log.py +++ b/Examples/Scripts/GsfDebugger/make_gsf_verbose_log.py @@ -1,7 +1,6 @@ #!/bin/python3 -import sys + from pathlib import Path -import os import acts import acts.examples @@ -20,7 +19,8 @@ ) assert digiConfigFile.exists() - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Optimization/ckf.py b/Examples/Scripts/Optimization/ckf.py index e0cd5fd9870..7de27b98465 100755 --- a/Examples/Scripts/Optimization/ckf.py +++ b/Examples/Scripts/Optimization/ckf.py @@ -171,7 +171,7 @@ def runCKFTracks( rnd=rnd, postSelectParticles=ParticleSelectorConfig( pt=(0.5 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) @@ -259,7 +259,9 @@ def runCKFTracks( srcdir = Path(__file__).resolve().parent.parent.parent.parent - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py b/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py index 821320a33a2..d64c7056d7c 100755 --- a/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py +++ b/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py @@ -167,7 +167,8 @@ def runMaterialMappingVariance( matDeco = acts.IMaterialDecorator.fromFile( str(os.path.join(inputPath, "geometry-map.json")) ) - detectorTemp, trackingGeometryTemp, decoratorsTemp = getOpenDataDetector(matDeco) + detectorTemp = getOpenDataDetector(matDeco) + trackingGeometryTemp = detectorTemp.trackingGeometry() matMapDeco = acts.MappingMaterialDecorator( tGeometry=trackingGeometryTemp, level=acts.logging.ERROR ) @@ -175,7 +176,9 @@ def runMaterialMappingVariance( matMapDeco.setBinningMap(binMap) # Decorate the detector with the MappingMaterialDecorator - detector, trackingGeometry, decorators = getOpenDataDetector(matMapDeco) + detector = getOpenDataDetector(matMapDeco) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() # Sequence for the mapping, only use one thread when mapping material sMap = acts.examples.Sequencer( @@ -207,7 +210,9 @@ def runMaterialMappingVariance( # Use the material map from the previous mapping as an input cborMap = os.path.join(pathExp, (mapName + ".cbor")) matDecoVar = acts.IMaterialDecorator.fromFile(cborMap) - detectorVar, trackingGeometryVar, decoratorsVar = getOpenDataDetector(matDecoVar) + detectorVar = getOpenDataDetector(matDecoVar) + trackingGeometryVar = detectorVar.trackingGeometry() + decoratorsVar = detectorVar.contextDecorators() s = acts.examples.Sequencer(events=events, numThreads=1, logLevel=acts.logging.INFO) for decorator in decoratorsVar: s.addContextDecorator(decorator) @@ -463,7 +468,9 @@ def surfaceExperiment(key, nbJobs, pathDB, pathResult, pipeBin, pipeResult, doPl matDeco = acts.IMaterialDecorator.fromFile( str(os.path.join(args.inputPath, "geometry-map.json")) ) - detector, trackingGeometry, decorators = getOpenDataDetector(matDeco) + detector = getOpenDataDetector(matDeco) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() # Use the MappingMaterialDecorator to create a binning map that can be optimised matMapDeco = acts.MappingMaterialDecorator( diff --git a/Examples/Scripts/Python/MLAmbiguityResolution/ambiguity_solver_network.py b/Examples/Scripts/Python/MLAmbiguityResolution/ambiguity_solver_network.py index 15f596414f7..5e241fdf717 100644 --- a/Examples/Scripts/Python/MLAmbiguityResolution/ambiguity_solver_network.py +++ b/Examples/Scripts/Python/MLAmbiguityResolution/ambiguity_solver_network.py @@ -16,7 +16,7 @@ def prepareDataSet(data: pd.DataFrame) -> pd.DataFrame: data = data # Remove tracks with less than 7 measurements data = data[data["nMeasurements"] > 6] - # data = data.sort_values("good/duplicate/fake", ascending=False) + data = data.sort_values("good/duplicate/fake", ascending=False) # Remove pure duplicate (tracks purely identical) keep the ones good one if among them. data = data.drop_duplicates( subset=[ diff --git a/Examples/Scripts/Python/blueprint.py b/Examples/Scripts/Python/blueprint.py new file mode 100755 index 00000000000..0dca3a6629f --- /dev/null +++ b/Examples/Scripts/Python/blueprint.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +from pathlib import Path + +import acts + +mm = acts.UnitConstants.mm +degree = acts.UnitConstants.degree + +root = acts.Blueprint(envelope=acts.ExtentEnvelope(r=[10 * mm, 10 * mm])) + + +pixel = root.addCylinderContainer(direction=acts.BinningValue.binZ, name="Pixel") +print(repr(pixel)) + +trf = acts.Transform3.Identity() * acts.Translation3(acts.Vector3(0, 0, 0 * mm)) + + +if True: + barrel = acts.CylinderContainerBlueprintNode( + "PixelBarrel", + acts.BinningValue.binR, + attachmentStrategy=acts.CylinderVolumeStack.AttachmentStrategy.Gap, + resizeStrategy=acts.CylinderVolumeStack.ResizeStrategy.Gap, + ) + pixel.addChild(barrel) + + print("Barrel") + r = 25 * mm + for i in range(0, 4): + r += 50 * mm + bounds = acts.CylinderVolumeBounds(r, r + 20 * mm, 200 * mm) + print(bounds) + brlLayer = barrel.addStaticVolume(trf, bounds, name=f"PixelBarrelLayer{i}") + assert brlLayer.name == f"PixelBarrelLayer{i}" + + +if True: + + with pixel.CylinderContainer("PixelPosEndcap", acts.BinningValue.binZ) as ec: + print("Positive Endcap") + + ec.attachmentStrategy = acts.CylinderVolumeStack.AttachmentStrategy.Gap + ec.resizeStrategy = acts.CylinderVolumeStack.ResizeStrategy.Gap + + z = 200 + for i in range(0, 4): + z += 200 * mm + bounds = acts.CylinderVolumeBounds(100 * mm, 150 * mm, 50 * mm) + print(bounds) + + trf = acts.Transform3.Identity() * acts.Translation3(acts.Vector3(0, 0, z)) + + with ec.StaticVolume(trf, bounds, name=f"PixelPosEndcapDisk{i}") as disc: + print("Add disk", i) + + assert disc.name == f"PixelPosEndcapDisk{i}" + + +if True: + with pixel.Material() as mat: + with mat.CylinderContainer( + direction=acts.BinningValue.binZ, name="PixelNegEndcap" + ) as ec: + ec.attachmentStrategy = acts.CylinderVolumeStack.AttachmentStrategy.Gap + + print("Negative Endcap") + + z = -200 + for i in range(0, 4): + z -= 200 * mm + bounds = acts.CylinderVolumeBounds(200 * mm, 300 * mm, 50 * mm) + print(bounds) + + trf = acts.Transform3.Identity() * acts.Translation3( + acts.Vector3(0, 0, z) + ) + + with ec.StaticVolume( + trf, bounds, name=f"PixelNegEndcapDisk{i}" + ) as disk: + print("Add disk", i) + assert disk.name == f"PixelNegEndcapDisk{i}" + + +with open("blueprint.dot", "w") as fh: + root.graphviz(fh) + + +gctx = acts.GeometryContext() +trackingGeometry = root.construct( + options=acts.BlueprintNode.Options(), gctx=gctx, level=acts.logging.VERBOSE +) + +vis = acts.ObjVisualization3D() +trackingGeometry.visualize(vis, gctx) +with Path("blueprint.obj").open("w") as fh: + vis.write(fh) +# print("DONE") diff --git a/Examples/Scripts/Python/ckf_tracks.py b/Examples/Scripts/Python/ckf_tracks.py index 75ec8a7c316..1ef32c844ee 100755 --- a/Examples/Scripts/Python/ckf_tracks.py +++ b/Examples/Scripts/Python/ckf_tracks.py @@ -82,7 +82,7 @@ def runCKFTracks( rnd=rnd, postSelectParticles=ParticleSelectorConfig( pt=(0.5 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) @@ -179,7 +179,9 @@ def runCKFTracks( if "__main__" == __name__: srcdir = Path(__file__).resolve().parent.parent.parent.parent - detector, trackingGeometry, decorators = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Python/digitization.py b/Examples/Scripts/Python/digitization.py index a69a8f01c45..3e2479983af 100755 --- a/Examples/Scripts/Python/digitization.py +++ b/Examples/Scripts/Python/digitization.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 + from pathlib import Path from typing import Optional @@ -75,7 +76,8 @@ def runDigitization( if "__main__" == __name__: - detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() digiConfigFile = ( Path(__file__).resolve().parent.parent.parent.parent diff --git a/Examples/Scripts/Python/digitization_config.py b/Examples/Scripts/Python/digitization_config.py index 1b6de07f477..13e41dc1898 100755 --- a/Examples/Scripts/Python/digitization_config.py +++ b/Examples/Scripts/Python/digitization_config.py @@ -33,7 +33,8 @@ def runDigitizationConfig( if "__main__" == __name__: - detector, trackingGeometry, _ = GenericDetector.create() + detector = GenericDetector() + trackingGeometry = detector.trackingGeometry() runDigitizationConfig( trackingGeometry=trackingGeometry, diff --git a/Examples/Scripts/Python/event_recording.py b/Examples/Scripts/Python/event_recording.py index 04cbb135dbe..8c31799c768 100755 --- a/Examples/Scripts/Python/event_recording.py +++ b/Examples/Scripts/Python/event_recording.py @@ -7,7 +7,6 @@ import acts.examples.hepmc3 import acts.examples.dd4hep import acts.examples.geant4 -import acts.examples.geant4.dd4hep import acts.examples.geant4.hepmc3 from acts.examples.odd import getOpenDataDetector @@ -15,7 +14,7 @@ u = acts.UnitConstants -def runEventRecording(detectorConstructionFactory, outputDir, s=None): +def runEventRecording(detector, outputDir, s=None): hepmc_dir = os.path.join(outputDir, "hepmc3") if not os.path.exists(hepmc_dir): os.mkdir(hepmc_dir) @@ -54,7 +53,7 @@ def runEventRecording(detectorConstructionFactory, outputDir, s=None): outputHepMcTracks="geant-event", seed1=43, seed2=44, - detectorConstructionFactory=detectorConstructionFactory, + detector=detector, ) erAlg = acts.examples.geant4.hepmc3.EventRecording( @@ -76,13 +75,9 @@ def runEventRecording(detectorConstructionFactory, outputDir, s=None): if "__main__" == __name__: - detector, trackingGeometry, decorators = getOpenDataDetector() - - detectorConstructionFactory = ( - acts.examples.geant4.dd4hep.DDG4DetectorConstructionFactory(detector) - ) + detector = getOpenDataDetector() runEventRecording( - detectorConstructionFactory=detectorConstructionFactory, + detector=detector, outputDir=os.getcwd(), ).run() diff --git a/Examples/Scripts/Python/exatrkx.py b/Examples/Scripts/Python/exatrkx.py index b2a151fc4e7..77cb9b1a785 100755 --- a/Examples/Scripts/Python/exatrkx.py +++ b/Examples/Scripts/Python/exatrkx.py @@ -1,16 +1,53 @@ #!/usr/bin/env python3 + from pathlib import Path +import os +import sys import acts.examples import acts +from acts.examples.reconstruction import addExaTrkX, ExaTrkXBackend from acts import UnitConstants as u +from digitization import runDigitization + + +def runGNNTrackFinding( + trackingGeometry, + field, + outputDir, + digiConfigFile, + geometrySelection, + backend, + modelDir, + outputRoot=False, + outputCsv=False, + s=None, +): + s = runDigitization( + trackingGeometry, + field, + outputDir, + digiConfigFile=digiConfigFile, + particlesInput=None, + outputRoot=outputRoot, + outputCsv=outputCsv, + s=s, + ) + + addExaTrkX( + s, + trackingGeometry, + geometrySelection, + modelDir, + backend=backend, + outputDirRoot=outputDir if outputRoot else None, + ) + + s.run() + if "__main__" == __name__: - import os - import sys - from digitization import runDigitization - from acts.examples.reconstruction import addExaTrkX, ExaTrkXBackend backend = ExaTrkXBackend.Torch @@ -19,16 +56,11 @@ if "torch" in sys.argv: backend = ExaTrkXBackend.Torch - srcdir = Path(__file__).resolve().parent.parent.parent.parent - - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) - inputParticlePath = Path("particles.root") - if not inputParticlePath.exists(): - inputParticlePath = None - srcdir = Path(__file__).resolve().parent.parent.parent.parent geometrySelection = ( @@ -60,24 +92,15 @@ rnd = acts.examples.RandomNumbers() outputDir = Path(os.getcwd()) - s = runDigitization( + runGNNTrackFinding( trackingGeometry, field, outputDir, - digiConfigFile=digiConfigFile, - particlesInput=inputParticlePath, - outputRoot=True, - outputCsv=True, - s=s, - ) - - addExaTrkX( - s, - trackingGeometry, + digiConfigFile, geometrySelection, + backend, modelDir, - outputDir, - backend=backend, + outputRoot=True, + outputCsv=False, + s=s, ) - - s.run() diff --git a/Examples/Scripts/Python/fatras.py b/Examples/Scripts/Python/fatras.py index 58fb8b5f565..9a28ed8eed2 100755 --- a/Examples/Scripts/Python/fatras.py +++ b/Examples/Scripts/Python/fatras.py @@ -31,8 +31,8 @@ def runFatras(trackingGeometry, field, outputDir, s: acts.examples.Sequencer = N if "__main__" == __name__: gdc = acts.examples.GenericDetector.Config() - detector = acts.examples.GenericDetector() - trackingGeometry, contextDecorators = detector.finalize(gdc, None) + gd = acts.examples.GenericDetector(gdc) + trackingGeometry, decorators, _ = gd.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Python/full_chain_itk.py b/Examples/Scripts/Python/full_chain_itk.py index b5c17278a1e..afb1df6a12b 100755 --- a/Examples/Scripts/Python/full_chain_itk.py +++ b/Examples/Scripts/Python/full_chain_itk.py @@ -74,7 +74,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-4.0, 4.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), outputDirRoot=outputDir, diff --git a/Examples/Scripts/Python/full_chain_itk_Gbts.py b/Examples/Scripts/Python/full_chain_itk_Gbts.py index ac9cc8ae8fc..a939bd7e930 100755 --- a/Examples/Scripts/Python/full_chain_itk_Gbts.py +++ b/Examples/Scripts/Python/full_chain_itk_Gbts.py @@ -69,7 +69,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-4.0, 4.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), outputDirRoot=outputDir, diff --git a/Examples/Scripts/Python/full_chain_odd.py b/Examples/Scripts/Python/full_chain_odd.py index 7ddaa02054f..3b7261c47dd 100755 --- a/Examples/Scripts/Python/full_chain_odd.py +++ b/Examples/Scripts/Python/full_chain_odd.py @@ -168,9 +168,9 @@ oddSeedingSel = geoDir / "config/odd-seeding-config.json" oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap) -detector, trackingGeometry, decorators = getOpenDataDetector( - odd_dir=geoDir, mdecorator=oddMaterialDeco -) +detector = getOpenDataDetector(odd_dir=geoDir, mdecorator=oddMaterialDeco) +trackingGeometry = detector.trackingGeometry() +decorators = detector.contextDecorators() field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T)) rnd = acts.examples.RandomNumbers(seed=42) @@ -278,7 +278,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), outputDirRoot=outputDir if args.output_root else None, @@ -306,7 +306,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), enableInteractions=True, diff --git a/Examples/Scripts/Python/full_chain_odd_LRT.py b/Examples/Scripts/Python/full_chain_odd_LRT.py index cfa03b5336c..36a552af0c8 100644 --- a/Examples/Scripts/Python/full_chain_odd_LRT.py +++ b/Examples/Scripts/Python/full_chain_odd_LRT.py @@ -160,9 +160,9 @@ oddSeedingSel = geoDir / "config/odd-seeding-config.json" oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap) -detector, trackingGeometry, decorators = getOpenDataDetector( - odd_dir=geoDir, mdecorator=oddMaterialDeco -) +detector = getOpenDataDetector(odd_dir=geoDir, mdecorator=oddMaterialDeco) +trackingGeometry = detector.trackingGeometry() +decorators = detector.contextDecorators() field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T)) rnd = acts.examples.RandomNumbers(seed=42) @@ -273,7 +273,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), outputDirRoot=outputDir if args.output_root else None, @@ -300,7 +300,7 @@ postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), enableInteractions=True, diff --git a/Examples/Scripts/Python/full_chain_test.py b/Examples/Scripts/Python/full_chain_test.py index 1ae8961c52f..f09be753d46 100755 --- a/Examples/Scripts/Python/full_chain_test.py +++ b/Examples/Scripts/Python/full_chain_test.py @@ -295,7 +295,9 @@ def full_chain(args): args.digi_config = geo_dir / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" seedingConfigFile = geo_dir / "Examples/Algorithms/TrackFinding/share/geoSelection-genericDetector.json" args.bf_constant = True - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() elif args.odd: import acts.examples.odd etaRange = (-3.0, 3.0) @@ -311,10 +313,12 @@ def full_chain(args): if args.material_config is None: args.material_config = geo_dir / "data/odd-material-maps.root" args.bf_constant = True - detector, trackingGeometry, decorators = acts.examples.odd.getOpenDataDetector( + detector = getOpenDataDetector( odd_dir=geo_dir, mdecorator=acts.IMaterialDecorator.fromFile(args.material_config), ) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() elif args.itk: import acts.examples.itk as itk etaRange = (-4.0, 4.0) @@ -380,7 +384,7 @@ def full_chain(args): postSelectParticles = ParticleSelectorConfig( pt=(ptMin, None), eta=etaRange if not args.generic_detector else (None, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ) diff --git a/Examples/Scripts/Python/geant4.py b/Examples/Scripts/Python/geant4.py index 65e154c2704..49a41cc32fd 100755 --- a/Examples/Scripts/Python/geant4.py +++ b/Examples/Scripts/Python/geant4.py @@ -81,5 +81,7 @@ def runGeant4( [detector, contextors, store] = dd4hepDetector.finalize(geoContext, cOptions) runGeant4(detector, detector, field, Path.cwd()).run() else: - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() runGeant4(detector, trackingGeometry, field, Path.cwd()).run() diff --git a/Examples/Scripts/Python/geant4_parallel.py b/Examples/Scripts/Python/geant4_parallel.py index d9a8f59f8c2..7f39fc18ec8 100755 --- a/Examples/Scripts/Python/geant4_parallel.py +++ b/Examples/Scripts/Python/geant4_parallel.py @@ -61,7 +61,9 @@ def runGeant4EventRange(detector, trackingGeometry, beginEvent, endEvent, output if "__main__" == __name__: from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() n_events = 100 n_jobs = 8 diff --git a/Examples/Scripts/Python/geometry.py b/Examples/Scripts/Python/geometry.py index 396fec3151a..e3a9f14b99a 100755 --- a/Examples/Scripts/Python/geometry.py +++ b/Examples/Scripts/Python/geometry.py @@ -7,8 +7,6 @@ from acts import MaterialMapJsonConverter from acts.examples.odd import getOpenDataDetector from acts.examples import ( - GenericDetector, - AlignedDetector, WhiteBoard, AlgorithmContext, ProcessCode, @@ -90,9 +88,11 @@ def runGeometry( if "__main__" == __name__: - # detector, trackingGeometry, decorators = AlignedDetector.create() - # detector, trackingGeometry, decorators = GenericDetector.create() - detector, trackingGeometry, decorators = getOpenDataDetector() + # detector = AlignedDetector() + # detector = GenericDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() runGeometry(trackingGeometry, decorators, outputDir=os.getcwd()) diff --git a/Examples/Scripts/Python/hashing_seeding.py b/Examples/Scripts/Python/hashing_seeding.py index 770c34e5185..b13ee204604 100755 --- a/Examples/Scripts/Python/hashing_seeding.py +++ b/Examples/Scripts/Python/hashing_seeding.py @@ -125,9 +125,8 @@ def getDetectorInfo(self): oddSeedingSel = geoDir / "config/odd-seeding-config.json" oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap) - detector, trackingGeometry, decorators = getOpenDataDetector( - odd_dir=geoDir, mdecorator=oddMaterialDeco - ) + detector = getOpenDataDetector(odd_dir=geoDir, mdecorator=oddMaterialDeco) + trackingGeometry = detector.trackingGeometry() digiConfig = oddDigiConfig @@ -136,7 +135,8 @@ def getDetectorInfo(self): elif self.detector == DetectorName.generic: print("Create detector and tracking geometry") - detector, trackingGeometry, a = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() digiConfig = ( actsExamplesDir / "Algorithms/Digitization/share/default-smearing-config-generic.json" @@ -215,7 +215,7 @@ def runHashingSeeding( postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-eta, eta), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), enableInteractions=True, diff --git a/Examples/Scripts/Python/material_mapping.py b/Examples/Scripts/Python/material_mapping.py index 775115cf61b..559c3b7a663 100755 --- a/Examples/Scripts/Python/material_mapping.py +++ b/Examples/Scripts/Python/material_mapping.py @@ -155,7 +155,9 @@ def runMaterialMapping( mapName = args.outFile.split(".")[0] - detector, trackingGeometry, decorators = getOpenDataDetector(None) + detector = getOpenDataDetector(None) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() runMaterialMapping( trackingGeometry, diff --git a/Examples/Scripts/Python/material_mapping_core.py b/Examples/Scripts/Python/material_mapping_core.py index 0bd9a7415de..e5a6e865246 100644 --- a/Examples/Scripts/Python/material_mapping_core.py +++ b/Examples/Scripts/Python/material_mapping_core.py @@ -300,7 +300,8 @@ def runMaterialMapping(surfaces, inputFile, outputFile, outputMap, loglevel): if args.matconfig != "": matDeco = acts.IMaterialDecorator.fromFile(args.matconfig) - [detector, trackingGeometry, decorators] = getOpenDataDetector(matDeco) + detector = getOpenDataDetector(matDeco) + trackingGeometry = detector.trackingGeometry() materialSurfaces = trackingGeometry.extractMaterialSurfaces() diff --git a/Examples/Scripts/Python/material_recording.py b/Examples/Scripts/Python/material_recording.py index 9de5914d45a..fe4ff238ca5 100755 --- a/Examples/Scripts/Python/material_recording.py +++ b/Examples/Scripts/Python/material_recording.py @@ -15,7 +15,6 @@ import acts.examples.dd4hep import acts.examples.geant4 -import acts.examples.geant4.dd4hep from acts.examples.odd import getOpenDataDetector try: @@ -30,7 +29,7 @@ def runMaterialRecording( - detectorConstructionFactory, + detector, outputDir, tracksPerEvent=10000, s=None, @@ -73,7 +72,7 @@ def runMaterialRecording( g4Alg = acts.examples.geant4.Geant4MaterialRecording( level=acts.logging.INFO, - detectorConstructionFactory=detectorConstructionFactory, + detector=detector, randomNumbers=rnd, inputParticles=evGen.config.outputParticles, outputMaterialTracks="material-tracks", @@ -108,27 +107,16 @@ def main(): args = p.parse_args() - detectorConstructionFactory = None + detector = None if args.input == "": - detector, trackingGeometry, decorators = getOpenDataDetector() - - detectorConstructionFactory = ( - acts.examples.geant4.dd4hep.DDG4DetectorConstructionFactory(detector) - ) + detector = getOpenDataDetector() elif args.input.endswith(".gdml"): - detectorConstructionFactory = ( - acts.examples.geant4.GdmlDetectorConstructionFactory(args.input) - ) + detector = acts.examples.geant4.GdmlDetector(path=args.input) elif args.input.endswith(".sqlite") or args.input.endswith(".db"): - geoModelTree = acts.geomodel.readFromDb(args.input) - detectorConstructionFactory = ( - acts.examples.geant4.geomodel.GeoModelDetectorConstructionFactory( - geoModelTree - ) - ) + detector = acts.examples.GeoModelDetector(path=args.input) runMaterialRecording( - detectorConstructionFactory=detectorConstructionFactory, + detector=detector, tracksPerEvent=args.tracks, outputDir=os.getcwd(), s=acts.examples.Sequencer(events=args.events, numThreads=1), diff --git a/Examples/Scripts/Python/material_validation.py b/Examples/Scripts/Python/material_validation.py index 94c0f21b94a..a516262d2d4 100755 --- a/Examples/Scripts/Python/material_validation.py +++ b/Examples/Scripts/Python/material_validation.py @@ -97,9 +97,9 @@ def runMaterialValidation( acts.IMaterialDecorator.fromFile(args.map) if args.map != None else None ) - detector, trackingGeometry, decorators = getOpenDataDetector( - mdecorator=materialDecorator - ) + detector = getOpenDataDetector(materialDecorator) + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() field = acts.ConstantBField(acts.Vector3(0, 0, 0 * acts.UnitConstants.T)) diff --git a/Examples/Scripts/Python/material_validation_core.py b/Examples/Scripts/Python/material_validation_core.py index 7e2ae129dcf..1d81054c091 100644 --- a/Examples/Scripts/Python/material_validation_core.py +++ b/Examples/Scripts/Python/material_validation_core.py @@ -244,9 +244,8 @@ def runMaterialValidation(s, ntracks, surfaces, outputFile, seed, loglevel): materialSurfaces = detector.extractMaterialSurfaces() else: - [detector, trackingGeometry, decorators] = getOpenDataDetector( - materialDecorator - ) + detector = getOpenDataDetector(materialDecorator) + trackingGeometry = detector.trackingGeometry() materialSurfaces = trackingGeometry.extractMaterialSurfaces() diff --git a/Examples/Scripts/Python/propagation.py b/Examples/Scripts/Python/propagation.py index 59be3385ca0..72401d8113e 100755 --- a/Examples/Scripts/Python/propagation.py +++ b/Examples/Scripts/Python/propagation.py @@ -72,14 +72,10 @@ def runPropagation(trackingGeometry, field, outputDir, s=None, decorators=[]): # matDeco = acts.IMaterialDecorator.fromFile("material.root") ## Generic detector: Default - ( - detector, - trackingGeometry, - contextDecorators, - ) = GenericDetector.create(mdecorator=matDeco) + detector = GenericDetector(mdecorator=matDeco) ## Alternative: Aligned detector in a couple of modes - # detector, trackingGeometry, contextDecorators = AlignedDetector.create( + # detector = AlignedDetector( # decoratorLogLevel=acts.logging.INFO, # # These parameters need to be tuned so that GC doesn't break # # with multiple threads @@ -94,8 +90,10 @@ def runPropagation(trackingGeometry, field, outputDir, s=None, decorators=[]): ## Alternative: DD4hep detector # dd4hepCfg = acts.examples.DD4hepDetector.Config() # dd4hepCfg.xmlFileNames = [str(getOpenDataDetectorDirectory()/"xml/OpenDataDetector.xml")] - # detector = acts.examples.DD4hepDetector() - # trackingGeometry, contextDecorators = detector.finalize(dd4hepCfg, None) + # detector = acts.examples.DD4hepDetector(dd4hepCfg) + + trackingGeometry = detector.trackingGeometry() + contextDecorators = detector.contextDecorators() ## Magnetic field setup: Default: constant 2T longitudinal field field = acts.ConstantBField(acts.Vector3(0, 0, 2 * acts.UnitConstants.T)) diff --git a/Examples/Scripts/Python/seeding.py b/Examples/Scripts/Python/seeding.py index 3beb6071aae..ce1914accc7 100755 --- a/Examples/Scripts/Python/seeding.py +++ b/Examples/Scripts/Python/seeding.py @@ -90,7 +90,7 @@ def runSeeding( postSelectParticles=ParticleSelectorConfig( pt=(1.0 * u.GeV, None), eta=(-2.5, 2.5), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) @@ -152,8 +152,9 @@ def runSeeding( ) args = p.parse_args() - # detector, trackingGeometry, decorators = getOpenDataDetector() - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + # detector = getOpenDataDetector() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Python/telescope_simulation.py b/Examples/Scripts/Python/telescope_simulation.py index e47c7969232..97e6214c893 100755 --- a/Examples/Scripts/Python/telescope_simulation.py +++ b/Examples/Scripts/Python/telescope_simulation.py @@ -16,12 +16,13 @@ u = acts.UnitConstants if "__main__" == __name__: - detector, trackingGeometry, decorators = acts.examples.TelescopeDetector.create( + detector = acts.examples.TelescopeDetector( bounds=[200, 200], positions=[30, 60, 90, 120, 150, 180, 210, 240, 270], stereos=[0, 0, 0, 0, 0, 0, 0, 0, 0], binValue=2, ) + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/Python/telescope_track_params_lookup_generation.py b/Examples/Scripts/Python/telescope_track_params_lookup_generation.py index ecdffc20ec3..4f3eada72b1 100644 --- a/Examples/Scripts/Python/telescope_track_params_lookup_generation.py +++ b/Examples/Scripts/Python/telescope_track_params_lookup_generation.py @@ -99,13 +99,14 @@ def estimateLookup(trackingGeometry, numEvents, outputPath): args = p.parse_args() # Initialize the geometry - detector, trackingGeometry, decorators = acts.examples.TelescopeDetector.create( + detector = acts.examples.TelescopeDetector( bounds=[4, 10], positions=[30, 60, 90], stereos=[0, 0, 0], binValue=2, surfaceType=0, ) + trackingGeometry = detector.trackingGeometry() # Estimate the lookup estimateLookup(trackingGeometry, args.events, args.output) diff --git a/Examples/Scripts/Python/truth_tracking_gsf.py b/Examples/Scripts/Python/truth_tracking_gsf.py index ae8aca06d6c..7d6e05c07c7 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf.py +++ b/Examples/Scripts/Python/truth_tracking_gsf.py @@ -79,7 +79,7 @@ def runTruthTrackingGsf( enableInteractions=True, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(7, None), + hits=(7, None), removeNeutral=True, removeSecondaries=True, ), @@ -163,13 +163,15 @@ def runTruthTrackingGsf( # ODD from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() digiConfigFile = ( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" ) ## GenericDetector - # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # detector = acts.examples.GenericDetector() + # trackingGeometry = detector.trackingGeometry() # digiConfigFile = ( # srcdir # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Scripts/Python/truth_tracking_gsf_refitting.py b/Examples/Scripts/Python/truth_tracking_gsf_refitting.py index 686f4af06c2..79bd5c41b50 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf_refitting.py +++ b/Examples/Scripts/Python/truth_tracking_gsf_refitting.py @@ -102,13 +102,15 @@ def runRefittingGsf( # ODD from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() digiConfigFile = ( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" ) ## GenericDetector - # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # detector = acts.examples.GenericDetector() + # trackingGeometry = detector.trackingGeometry() # digiConfigFile = ( # srcdir # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Scripts/Python/truth_tracking_gx2f.py b/Examples/Scripts/Python/truth_tracking_gx2f.py index 8503dc982f4..2c692ca87cc 100644 --- a/Examples/Scripts/Python/truth_tracking_gx2f.py +++ b/Examples/Scripts/Python/truth_tracking_gx2f.py @@ -76,7 +76,7 @@ def runTruthTrackingGx2f( enableInteractions=True, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(7, None), + hits=(7, None), removeNeutral=True, removeSecondaries=True, ), @@ -163,13 +163,15 @@ def runTruthTrackingGx2f( # ODD from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() digiConfigFile = ( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" ) ## GenericDetector - # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # detector = acts.examples.GenericDetector() + # trackingGeometry = detector.trackingGeometry() # digiConfigFile = ( # srcdir # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index 91c18f1dd28..0102f1db8ee 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -17,7 +17,6 @@ def runTruthTrackingKalman( inputParticlePath: Optional[Path] = None, inputHitsPath: Optional[Path] = None, decorators=[], - directNavigation=False, reverseFilteringMomThreshold=0 * u.GeV, s: acts.examples.Sequencer = None, ): @@ -84,7 +83,7 @@ def runTruthTrackingKalman( enableInteractions=True, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(7, None), + hits=(7, None), removeNeutral=True, removeSecondaries=True, ), @@ -122,7 +121,6 @@ def runTruthTrackingKalman( s, trackingGeometry, field, - directNavigation, reverseFilteringMomThreshold, ) @@ -179,13 +177,15 @@ def runTruthTrackingKalman( # ODD from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() digiConfigFile = ( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" ) ## GenericDetector - # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # detector = acts.examples.GenericDetector() + # trackingGeometry = detector.trackingGeometry() # digiConfigFile = ( # srcdir # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Scripts/Python/truth_tracking_kalman_refitting.py b/Examples/Scripts/Python/truth_tracking_kalman_refitting.py index 46578423f33..2d84808a15c 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman_refitting.py +++ b/Examples/Scripts/Python/truth_tracking_kalman_refitting.py @@ -100,13 +100,16 @@ def runRefittingKf( # ODD from acts.examples.odd import getOpenDataDetector - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() digiConfigFile = ( srcdir / "thirdparty/OpenDataDetector/config/odd-digi-smearing-config.json" ) ## GenericDetector - # detector, trackingGeometry, _ = acts.examples.GenericDetector.create() + # detector = acts.examples.GenericDetector() + # trackingGeometry = detector.trackingGeometry() # digiConfigFile = ( # srcdir # / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Scripts/Python/truth_tracking_telescope.py b/Examples/Scripts/Python/truth_tracking_telescope.py index b179fb9afa7..503c05b0a19 100755 --- a/Examples/Scripts/Python/truth_tracking_telescope.py +++ b/Examples/Scripts/Python/truth_tracking_telescope.py @@ -10,11 +10,12 @@ u = acts.UnitConstants if "__main__" == __name__: - detector, trackingGeometry, decorators = acts.examples.TelescopeDetector.create( + detector = acts.examples.TelescopeDetector( bounds=[200, 200], positions=[30, 60, 90, 120, 150, 180, 210, 240, 270], stereos=[0] * 9, ) + trackingGeometry = detector.trackingGeometry() srcdir = Path(__file__).resolve().parent.parent.parent.parent diff --git a/Examples/Scripts/Python/vertex_fitting.py b/Examples/Scripts/Python/vertex_fitting.py index c6088f618a6..5add3b8f1c1 100755 --- a/Examples/Scripts/Python/vertex_fitting.py +++ b/Examples/Scripts/Python/vertex_fitting.py @@ -119,7 +119,8 @@ def runVertexFitting( if "__main__" == __name__: - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) diff --git a/Examples/Scripts/compareRootFiles.hpp b/Examples/Scripts/compareRootFiles.hpp index 464230373be..a7043b15e96 100644 --- a/Examples/Scripts/compareRootFiles.hpp +++ b/Examples/Scripts/compareRootFiles.hpp @@ -34,8 +34,7 @@ class AnyVector { static std::pair*> create(Args&&... args) { std::vector* vector = new std::vector(std::forward(args)...); std::function deleter = [vector] { delete vector; }; - return std::make_pair( - AnyVector{static_cast(vector), std::move(deleter)}, vector); + return {AnyVector{static_cast(vector), std::move(deleter)}, vector}; } // Default-construct a null type-erased vector diff --git a/Examples/Scripts/vertex_mu_scan.py b/Examples/Scripts/vertex_mu_scan.py index ee8977fe9df..0de15b20097 100755 --- a/Examples/Scripts/vertex_mu_scan.py +++ b/Examples/Scripts/vertex_mu_scan.py @@ -39,7 +39,7 @@ def main(files: List[Path], output: str, title: str = ""): if time_file.exists(): time = numpy.append(time, float(time_file.read_text())) else: - fime.append(float("nan")) + time.append(float("nan")) rf = uproot.open(f"{file}:vertexing") diff --git a/Fatras/include/ActsFatras/Digitization/UncorrelatedHitSmearer.hpp b/Fatras/include/ActsFatras/Digitization/UncorrelatedHitSmearer.hpp index caf5efa8c61..a4825b2a99b 100644 --- a/Fatras/include/ActsFatras/Digitization/UncorrelatedHitSmearer.hpp +++ b/Fatras/include/ActsFatras/Digitization/UncorrelatedHitSmearer.hpp @@ -86,7 +86,7 @@ struct BoundParametersSmearer { ParametersVector par = ParametersVector::Zero(); CovarianceMatrix cov = CovarianceMatrix::Zero(); - for (int i = 0; i < static_cast(kSize); ++i) { + for (std::size_t i = 0; i < kSize; ++i) { auto res = smearFunctions[i](boundParams[indices[i]], rng); if (!res.ok()) { return Result::failure(res.error()); diff --git a/Fatras/include/ActsFatras/Physics/ElectroMagnetic/PhotonConversion.hpp b/Fatras/include/ActsFatras/Physics/ElectroMagnetic/PhotonConversion.hpp index af45481258e..582cc073bc4 100644 --- a/Fatras/include/ActsFatras/Physics/ElectroMagnetic/PhotonConversion.hpp +++ b/Fatras/include/ActsFatras/Physics/ElectroMagnetic/PhotonConversion.hpp @@ -129,8 +129,8 @@ std::pair PhotonConversion::generatePathLimits( // Fast exit if not a photon or the energy is too low if (particle.pdg() != Acts::PdgParticle::eGamma || particle.absoluteMomentum() < (2 * kElectronMass)) { - return std::make_pair(std::numeric_limits::infinity(), - std::numeric_limits::infinity()); + return {std::numeric_limits::infinity(), + std::numeric_limits::infinity()}; } // Use for the moment only Al data - Yung Tsai - Rev.Mod.Particle Physics Vol. @@ -155,11 +155,11 @@ std::pair PhotonConversion::generatePathLimits( std::uniform_real_distribution uniformDistribution{0., 1.}; // This is a transformation of eq. 3.75 - return std::make_pair(-9. / 7. * - std::log(conversionProbScaleFactor * - (1 - uniformDistribution(generator))) / - (1. - xi), - std::numeric_limits::infinity()); + return {-9. / 7. * + std::log(conversionProbScaleFactor * + (1 - uniformDistribution(generator))) / + (1. - xi), + std::numeric_limits::infinity()}; } template diff --git a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp b/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp index d372cf8ffc9..d0a0d84c36c 100644 --- a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp +++ b/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp @@ -58,8 +58,8 @@ struct NuclearInteraction { const Particle& particle) const { // Fast exit: No parameterisation provided if (multiParticleParameterisation.empty()) { - return std::make_pair(std::numeric_limits::infinity(), - std::numeric_limits::infinity()); + return {std::numeric_limits::infinity(), + std::numeric_limits::infinity()}; } // Find the parametrisation that corresponds to the particle type for (const auto& particleParametrisation : multiParticleParameterisation) { @@ -77,15 +77,13 @@ struct NuclearInteraction { // Set the L0 limit if not done already const auto& distribution = parametrisation.nuclearInteractionProbability; - auto limits = - std::make_pair(std::numeric_limits::infinity(), - sampleContinuousValues( - uniformDistribution(generator), distribution)); - return limits; + return {std::numeric_limits::infinity(), + sampleContinuousValues(uniformDistribution(generator), + distribution)}; } } - return std::make_pair(std::numeric_limits::infinity(), - std::numeric_limits::infinity()); + return {std::numeric_limits::infinity(), + std::numeric_limits::infinity()}; } /// This method performs a nuclear interaction. @@ -483,14 +481,14 @@ NuclearInteraction::sampleKinematics( if (trials == nMatchingTrialsTotal) { return std::nullopt; } - // Re-sampole invariant masses if no fitting momenta were found + // Re-sample invariant masses if no fitting momenta were found if (trials++ % nMatchingTrials == 0) { invariantMasses = sampleInvariantMasses(generator, parameters); } else { momenta = sampleMomenta(generator, parameters, momentum); } } - return std::make_pair(momenta, invariantMasses); + return std::pair(momenta, invariantMasses); } template diff --git a/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp b/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp index 84ab93921fe..ad1e0bbb6a6 100644 --- a/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp +++ b/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp @@ -141,7 +141,7 @@ std::pair NuclearInteraction::globalAngle(double phi1, const float theta = std::acos(vectorSum.z() / vectorSum.norm()); const float phi = std::atan2(vectorSum.y(), vectorSum.x()); - return std::make_pair(phi, theta); + return {phi, theta}; } bool NuclearInteraction::match(const Acts::ActsDynamicVector& momenta, diff --git a/Plugins/DD4hep/src/DD4hepBlueprintFactory.cpp b/Plugins/DD4hep/src/DD4hepBlueprintFactory.cpp index bf653fff801..06ff97153dd 100644 --- a/Plugins/DD4hep/src/DD4hepBlueprintFactory.cpp +++ b/Plugins/DD4hep/src/DD4hepBlueprintFactory.cpp @@ -207,7 +207,7 @@ Acts::Experimental::DD4hepBlueprintFactory::extractExternals( aux += "vol. binning : " + binningString; } // Return the tuple - return std::make_tuple(transform, bValueType, bValues, bBinning, aux); + return {transform, bValueType, bValues, bBinning, aux}; } std::tuple, @@ -309,6 +309,5 @@ Acts::Experimental::DD4hepBlueprintFactory::extractInternals( std::make_shared(geoIdCfg); } - return std::make_tuple(internalsBuilder, rootsFinderBuilder, geoIdGenerator, - aux, ext); + return {internalsBuilder, rootsFinderBuilder, geoIdGenerator, aux, ext}; } diff --git a/Plugins/Geant4/src/Geant4Converters.cpp b/Plugins/Geant4/src/Geant4Converters.cpp index 1608f45bb10..6db950f570a 100644 --- a/Plugins/Geant4/src/Geant4Converters.cpp +++ b/Plugins/Geant4/src/Geant4Converters.cpp @@ -110,7 +110,7 @@ Acts::Geant4ShapeConverter::cylinderBounds(const G4Tubs& g4Tubs) { } double thickness = g4Tubs.GetOuterRadius() - g4Tubs.GetInnerRadius(); auto cBounds = std::make_shared(tArray); - return std::make_tuple(std::move(cBounds), thickness); + return {std::move(cBounds), thickness}; } std::tuple, double> @@ -133,7 +133,7 @@ Acts::Geant4ShapeConverter::radialBounds(const G4Tubs& g4Tubs) { } double thickness = g4Tubs.GetZHalfLength() * 2; auto rBounds = std::make_shared(tArray); - return std::make_tuple(std::move(rBounds), thickness); + return {std::move(rBounds), thickness}; } std::shared_ptr Acts::Geant4ShapeConverter::lineBounds( @@ -173,7 +173,7 @@ Acts::Geant4ShapeConverter::rectangleBounds(const G4Box& g4Box) { } auto rBounds = std::make_shared(hG4XYZ[std::abs(rAxes[0u])], hG4XYZ[std::abs(rAxes[1u])]); - return std::make_tuple(std::move(rBounds), rAxes, thickness); + return {std::move(rBounds), rAxes, thickness}; } std::tuple, std::array, double> @@ -226,7 +226,7 @@ Acts::Geant4ShapeConverter::trapezoidBounds(const G4Trd& g4Trd) { auto tBounds = std::make_shared( halfLengthXminY, halfLengthXmaxY, halfLengthY); - return std::make_tuple(std::move(tBounds), rAxes, thickness); + return {std::move(tBounds), rAxes, thickness}; } std::tuple, std::array, double> @@ -234,19 +234,19 @@ Acts::Geant4ShapeConverter::planarBounds(const G4VSolid& g4Solid) { const G4Box* box = dynamic_cast(&g4Solid); if (box != nullptr) { auto [rBounds, axes, thickness] = rectangleBounds(*box); - return std::make_tuple(std::move(rBounds), axes, thickness); + return {std::move(rBounds), axes, thickness}; } const G4Trd* trd = dynamic_cast(&g4Solid); if (trd != nullptr) { auto [tBounds, axes, thickness] = trapezoidBounds(*trd); - return std::make_tuple(std::move(tBounds), axes, thickness); + return {std::move(tBounds), axes, thickness}; } std::shared_ptr pBounds = nullptr; std::array rAxes = {}; double rThickness = 0.; - return std::make_tuple(std::move(pBounds), rAxes, rThickness); + return {std::move(pBounds), rAxes, rThickness}; } namespace { diff --git a/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp b/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp index 429321828d6..081834d7069 100644 --- a/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp +++ b/Plugins/GeoModel/src/GeoModelBlueprintCreater.cpp @@ -320,7 +320,7 @@ Acts::GeoModelBlueprintCreater::createInternalStructureBuilder( const std::vector& internalConstraints) const { // Check if the internals entry is empty if (entry.internals.empty()) { - return std::make_tuple(nullptr, Extent()); + return {nullptr, Extent()}; } // Build a layer structure @@ -393,10 +393,10 @@ Acts::GeoModelBlueprintCreater::createInternalStructureBuilder( lsbCfg.nMinimalSurfaces = surfaces.size() + 1u; } - return std::make_tuple( + return { std::make_shared( lsbCfg, m_logger->clone(entry.name + "_LayerStructureBuilder")), - internalExtent); + internalExtent}; } else { throw std::invalid_argument( @@ -404,7 +404,7 @@ Acts::GeoModelBlueprintCreater::createInternalStructureBuilder( entry.internals[1u] + "' / or now kdt surfaces provided."); } } - return std::make_tuple(nullptr, Extent()); + return {nullptr, Extent()}; } std::tuple, @@ -434,5 +434,5 @@ Acts::GeoModelBlueprintCreater::parseBounds( "supported for the moment."); } - return std::make_tuple(boundsType, extent, boundValues, translation); + return {boundsType, extent, boundValues, translation}; } diff --git a/Plugins/GeoModel/src/detail/GeoModelExtentHelper.cpp b/Plugins/GeoModel/src/detail/GeoModelExtentHelper.cpp index daab4fd10e6..3734aefb8fe 100644 --- a/Plugins/GeoModel/src/detail/GeoModelExtentHelper.cpp +++ b/Plugins/GeoModel/src/detail/GeoModelExtentHelper.cpp @@ -111,7 +111,7 @@ Acts::detail::GeoModelExentHelper::extentFromTable( BinningValue bValue = bvCyl.at(iv); double val = std::numeric_limits::max(); bool isMin = (iv % 2 == 0); - // Case "e" : exxternal extent + // Case "e" : external extent if (value == "e") { // External parameters do not constrain it if (!externalExtent.constrains(bValue)) { @@ -157,5 +157,5 @@ Acts::detail::GeoModelExentHelper::extentFromTable( } } - return std::make_tuple(boundsType, extent); + return {boundsType, extent}; } diff --git a/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp b/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp index 3e1450f2b85..b0ddbd09bc7 100644 --- a/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp +++ b/Plugins/Onnx/include/Acts/Plugins/Onnx/AmbiguityTrackClassifier.hpp @@ -102,23 +102,6 @@ class AmbiguityTrackClassifier { return goodTracks; } - /// Select the track associated with each cluster - /// - /// @param clusters is a map of clusters, each cluster correspond to a vector of track ID - /// @param tracks is the input track container - /// @return a vector of trackID corresponding tho the good tracks - template - std::vector solveAmbiguity( - std::unordered_map>& clusters, - const track_container_t& tracks) const { - std::vector> outputTensor = - inferScores(clusters, tracks); - std::vector goodTracks = - trackSelection(clusters, outputTensor); - - return goodTracks; - } - private: // ONNX environment Ort::Env m_env; diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index eff298adb84..0c51456d7e4 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -1,4 +1,4 @@ -add_unittest(Blueprint BlueprintTests.cpp) +add_unittest(Gen2Blueprint BlueprintTests.cpp) add_unittest(BlueprintHelper BlueprintHelperTests.cpp) add_unittest(CylindricalContainerBuilder CylindricalContainerBuilderTests.cpp) add_unittest(CylindricalDetectorFromBlueprint CylindricalDetectorFromBlueprintTests.cpp) diff --git a/Tests/UnitTests/Core/Geometry/BlueprintApiTests.cpp b/Tests/UnitTests/Core/Geometry/BlueprintApiTests.cpp new file mode 100644 index 00000000000..737f410e24b --- /dev/null +++ b/Tests/UnitTests/Core/Geometry/BlueprintApiTests.cpp @@ -0,0 +1,409 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Detector/ProtoBinning.hpp" +#include "Acts/Geometry/Blueprint.hpp" +#include "Acts/Geometry/CylinderContainerBlueprintNode.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" +#include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Visualization/GeometryView3D.hpp" +#include "Acts/Visualization/ObjVisualization3D.hpp" + +#include +#include +#include + +using namespace Acts::UnitLiterals; + +namespace Acts::Test { + +auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::DEBUG); + +GeometryContext gctx; + +inline std::vector> makeFanLayer( + const Transform3& base, + std::vector>& elements, + double r = 300_mm, std::size_t nSensors = 8, double thickness = 0) { + auto recBounds = std::make_shared(40_mm, 60_mm); + + double deltaPhi = 2 * std::numbers::pi / nSensors; + std::vector> surfaces; + for (std::size_t i = 0; i < nSensors; i++) { + // Create a fan of sensors + + Transform3 trf = base * AngleAxis3{deltaPhi * i, Vector3::UnitZ()} * + Translation3(Vector3::UnitX() * r); + + if (i % 2 == 0) { + trf = trf * Translation3{Vector3::UnitZ() * 5_mm}; + } + + auto& element = elements.emplace_back( + std::make_unique(trf, recBounds, thickness)); + + element->surface().assignDetectorElement(*element); + + surfaces.push_back(element->surface().getSharedPtr()); + } + return surfaces; +} + +inline std::vector> makeBarrelLayer( + const Transform3& base, + std::vector>& elements, + double r = 300_mm, std::size_t nStaves = 10, int nSensorsPerStave = 8, + double thickness = 0, double hlPhi = 40_mm, double hlZ = 60_mm) { + auto recBounds = std::make_shared(hlPhi, hlZ); + + double deltaPhi = 2 * std::numbers::pi / nStaves; + std::vector> surfaces; + + for (std::size_t istave = 0; istave < nStaves; istave++) { + for (int isensor = -nSensorsPerStave; isensor <= nSensorsPerStave; + isensor++) { + double z = isensor * (2 * hlZ + 5_mm); + + Transform3 trf = base * Translation3(Vector3::UnitZ() * z) * + AngleAxis3{deltaPhi * istave, Vector3::UnitZ()} * + Translation3(Vector3::UnitX() * r) * + AngleAxis3{10_degree, Vector3::UnitZ()} * + AngleAxis3{90_degree, Vector3::UnitY()} * + AngleAxis3{90_degree, Vector3::UnitZ()}; + auto& element = elements.emplace_back( + std::make_unique(trf, recBounds, thickness)); + element->surface().assignDetectorElement(*element); + surfaces.push_back(element->surface().getSharedPtr()); + } + } + + return surfaces; +} + +BOOST_AUTO_TEST_SUITE(Geometry); + +BOOST_AUTO_TEST_SUITE(BlueprintApiTest); + +void pseudoNavigation(const TrackingGeometry& trackingGeometry, + Vector3 position, const Vector3& direction, + std::ostream& csv, std::size_t run, + std::size_t substepsPerCm, const Logger& logger) { + ACTS_VERBOSE("start navigation " << run); + ACTS_VERBOSE("dir: " << direction.transpose()); + ACTS_VERBOSE(direction.norm()); + + std::mt19937 rng{static_cast(run)}; + std::uniform_real_distribution<> dist{0.01, 0.99}; + + const auto* volume = trackingGeometry.lowestTrackingVolume(gctx, position); + BOOST_REQUIRE_NE(volume, nullptr); + ACTS_VERBOSE(volume->volumeName()); + + NavigationStream main; + const TrackingVolume* currentVolume = volume; + + csv << run << "," << position[0] << "," << position[1] << "," << position[2]; + csv << "," << volume->geometryId().volume(); + csv << "," << volume->geometryId().boundary(); + csv << "," << volume->geometryId().sensitive(); + csv << std::endl; + + ACTS_VERBOSE("start pseudo navigation"); + + for (std::size_t i = 0; i < 100; i++) { + main = NavigationStream{}; + AppendOnlyNavigationStream stream{main}; + + currentVolume->initializeNavigationCandidates( + {.position = position, .direction = direction}, stream, logger); + + ACTS_VERBOSE(main.candidates().size() << " candidates"); + + for (const auto& candidate : main.candidates()) { + ACTS_VERBOSE(" -> " << candidate.surface().geometryId()); + ACTS_VERBOSE(" " << candidate.surface().toStream(gctx)); + } + + ACTS_VERBOSE("initializing candidates"); + main.initialize(gctx, {position, direction}, BoundaryTolerance::None()); + + ACTS_VERBOSE(main.candidates().size() << " candidates remaining"); + + for (const auto& candidate : main.candidates()) { + ACTS_VERBOSE(" -> " << candidate.surface().geometryId()); + ACTS_VERBOSE(" " << candidate.surface().toStream(gctx)); + } + + if (main.currentCandidate().surface().isOnSurface(gctx, position, + direction)) { + ACTS_VERBOSE("Already on surface at initialization, skipping candidate"); + + auto id = main.currentCandidate().surface().geometryId(); + csv << run << "," << position[0] << "," << position[1] << "," + << position[2]; + csv << "," << id.volume(); + csv << "," << id.boundary(); + csv << "," << id.sensitive(); + csv << std::endl; + if (!main.switchToNextCandidate()) { + ACTS_WARNING("candidates exhausted unexpectedly"); + break; + } + } + + auto writeIntersection = [&](const Vector3& pos, const Surface& surface) { + csv << run << "," << pos[0] << "," << pos[1] << "," << pos[2]; + csv << "," << surface.geometryId().volume(); + csv << "," << surface.geometryId().boundary(); + csv << "," << surface.geometryId().sensitive(); + csv << std::endl; + }; + + bool terminated = false; + while (main.remainingCandidates() > 0) { + const auto& candidate = main.currentCandidate(); + + ACTS_VERBOSE(candidate.portal); + ACTS_VERBOSE(candidate.intersection.position().transpose()); + + ACTS_VERBOSE("moving to position: " << position.transpose() << " (r=" + << VectorHelpers::perp(position) + << ")"); + + Vector3 delta = candidate.intersection.position() - position; + + std::size_t substeps = + std::max(1l, std::lround(delta.norm() / 10_cm * substepsPerCm)); + + for (std::size_t j = 0; j < substeps; j++) { + // position += delta / (substeps + 1); + Vector3 subpos = position + dist(rng) * delta; + csv << run << "," << subpos[0] << "," << subpos[1] << "," << subpos[2]; + csv << "," << currentVolume->geometryId().volume(); + csv << ",0,0"; // zero boundary and sensitive ids + csv << std::endl; + } + + position = candidate.intersection.position(); + ACTS_VERBOSE(" -> " + << position.transpose() + << " (r=" << VectorHelpers::perp(position) << ")"); + + writeIntersection(position, candidate.surface()); + + if (candidate.portal != nullptr) { + ACTS_VERBOSE( + "On portal: " << candidate.portal->surface().toStream(gctx)); + currentVolume = + candidate.portal->resolveVolume(gctx, position, direction).value(); + + if (currentVolume == nullptr) { + ACTS_VERBOSE("switched to nullptr -> we're done"); + terminated = true; + } + break; + + } else { + ACTS_VERBOSE("Not on portal"); + } + + main.switchToNextCandidate(); + } + + if (terminated) { + ACTS_VERBOSE("Terminate pseudo navigation"); + break; + } + + ACTS_VERBOSE("switched to " << currentVolume->volumeName()); + + ACTS_VERBOSE("-----"); + } +} + +BOOST_AUTO_TEST_CASE(NodeApiTestContainers) { + // Transform3 base{AngleAxis3{30_degree, Vector3{1, 0, 0}}}; + Transform3 base{Transform3::Identity()}; + + std::vector> detectorElements; + auto makeFan = [&](const Transform3& layerBase, auto&&..., double r, + std::size_t nSensors, double thickness) { + return makeFanLayer(layerBase, detectorElements, r, nSensors, thickness); + }; + + Blueprint::Config cfg; + cfg.envelope[BinningValue::binZ] = {20_mm, 20_mm}; + cfg.envelope[BinningValue::binR] = {0_mm, 20_mm}; + auto root = std::make_unique(cfg); + + root->addMaterial("GlobalMaterial", [&](MaterialDesignatorBlueprintNode& + mat) { + Experimental::ProtoBinning zBinning{BinningValue::binZ, + AxisBoundaryType::Bound, 20}; + + Experimental::ProtoBinning rPhiBinning{BinningValue::binRPhi, + AxisBoundaryType::Bound, 20}; + + mat.setBinning(std::vector{std::tuple{ + CylinderVolumeBounds::Face::OuterCylinder, rPhiBinning, zBinning}}); + + mat.addCylinderContainer("Detector", BinningValue::binR, [&](auto& det) { + det.addCylinderContainer("Pixel", BinningValue::binZ, [&](auto& cyl) { + cyl.setAttachmentStrategy(CylinderVolumeStack::AttachmentStrategy::Gap) + .setResizeStrategy(CylinderVolumeStack::ResizeStrategy::Gap); + + cyl.addCylinderContainer( + "PixelNegativeEndcap", BinningValue::binZ, [&](auto& ec) { + ec.setAttachmentStrategy( + CylinderVolumeStack::AttachmentStrategy::Gap); + + auto makeLayer = [&](const Transform3& trf, auto& layer) { + std::vector> surfaces; + auto layerSurfaces = makeFan(trf, 300_mm, 10, 2_mm); + std::copy(layerSurfaces.begin(), layerSurfaces.end(), + std::back_inserter(surfaces)); + layerSurfaces = makeFan(trf, 500_mm, 16, 2_mm); + std::copy(layerSurfaces.begin(), layerSurfaces.end(), + std::back_inserter(surfaces)); + + layer.setSurfaces(surfaces) + .setLayerType(LayerBlueprintNode::LayerType::Disc) + .setEnvelope(ExtentEnvelope{{ + .z = {5_mm, 5_mm}, + .r = {10_mm, 20_mm}, + }}) + .setTransform(base); + }; + + ec.addLayer("PixelNeg1", [&](auto& layer) { + makeLayer(base * Translation3{Vector3{0, 0, -700_mm}}, layer); + }); + + ec.addLayer("PixelNeg2", [&](auto& layer) { + makeLayer(base * Translation3{Vector3{0, 0, -500_mm}}, layer); + }); + }); + + cyl.addCylinderContainer( + "PixelBarrel", BinningValue::binR, [&](auto& brl) { + brl.setAttachmentStrategy( + CylinderVolumeStack::AttachmentStrategy::Gap) + .setResizeStrategy(CylinderVolumeStack::ResizeStrategy::Gap); + + auto makeLayer = [&](const std::string& name, double r, + std::size_t nStaves, int nSensorsPerStave) { + brl.addLayer(name, [&](auto& layer) { + std::vector> surfaces = + makeBarrelLayer(base, detectorElements, r, nStaves, + nSensorsPerStave, 2.5_mm, 10_mm, 20_mm); + + layer.setSurfaces(surfaces) + .setLayerType(LayerBlueprintNode::LayerType::Cylinder) + .setEnvelope(ExtentEnvelope{{ + .z = {5_mm, 5_mm}, + .r = {1_mm, 1_mm}, + }}) + .setTransform(base); + }); + }; + + makeLayer("PixelLayer0", 30_mm, 18, 5); + makeLayer("PixelLayer1", 90_mm, 30, 6); + + brl.addStaticVolume(base, + std::make_shared( + 100_mm, 110_mm, 250_mm), + "PixelSupport"); + + makeLayer("PixelLayer2", 150_mm, 40, 7); + makeLayer("PixelLayer3", 250_mm, 70, 8); + }); + + auto& ec = + cyl.addCylinderContainer("PixelPosWrapper", BinningValue::binR); + ec.setResizeStrategy(CylinderVolumeStack::ResizeStrategy::Gap); + ec.addStaticVolume(std::make_unique( + base * Translation3{Vector3{0, 0, 600_mm}}, + std::make_shared(150_mm, 390_mm, 200_mm), + "PixelPositiveEndcap")); + }); + + det.addStaticVolume( + base, std::make_shared(0_mm, 23_mm, 1000_mm), + "BeamPipe"); + }); + }); + + std::ofstream dot{"api_test_container.dot"}; + root->graphviz(dot); + + auto trackingGeometry = root->construct({}, gctx, *logger); + + trackingGeometry->visitVolumes([&](const TrackingVolume* volume) { + std::cout << volume->volumeName() << std::endl; + std::cout << " -> id: " << volume->geometryId() << std::endl; + std::cout << " -> " << volume->portals().size() << " portals" << std::endl; + }); + + ObjVisualization3D vis; + + trackingGeometry->visualize(vis, gctx, {}, {}); + + vis.write("api_test_container.obj"); + + Vector3 position = Vector3::Zero(); + std::ofstream csv{"api_test_container.csv"}; + csv << "x,y,z,volume,boundary,sensitive" << std::endl; + + std::mt19937 rnd{42}; + + std::uniform_real_distribution<> dist{-1, 1}; + + double etaWidth = 3; + double thetaMin = 2 * std::atan(std::exp(-etaWidth)); + double thetaMax = 2 * std::atan(std::exp(etaWidth)); + std::uniform_real_distribution<> thetaDist{thetaMin, thetaMax}; + + using namespace Acts::UnitLiterals; + + for (std::size_t i = 0; i < 5000; i++) { + double theta = thetaDist(rnd); + double phi = 2 * std::numbers::pi * dist(rnd); + + Vector3 direction; + direction[0] = std::sin(theta) * std::cos(phi); + direction[1] = std::sin(theta) * std::sin(phi); + direction[2] = std::cos(theta); + + pseudoNavigation(*trackingGeometry, position, direction, csv, i, 2, + *logger->clone(std::nullopt, Logging::DEBUG)); + } +} + +BOOST_AUTO_TEST_SUITE_END(); + +BOOST_AUTO_TEST_SUITE_END(); + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Geometry/BlueprintTests.cpp b/Tests/UnitTests/Core/Geometry/BlueprintTests.cpp new file mode 100644 index 00000000000..c3a0ecc028f --- /dev/null +++ b/Tests/UnitTests/Core/Geometry/BlueprintTests.cpp @@ -0,0 +1,531 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/Blueprint.hpp" +#include "Acts/Geometry/BlueprintNode.hpp" +#include "Acts/Geometry/CylinderContainerBlueprintNode.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeStack.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" +#include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp" +#include "Acts/Geometry/StaticBlueprintNode.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Material/BinnedSurfaceMaterial.hpp" +#include "Acts/Material/ProtoSurfaceMaterial.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +using namespace Acts::UnitLiterals; + +namespace Acts::Test { + +auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::INFO); + +GeometryContext gctx; + +namespace { + +auto nameLookup(const TrackingGeometry& geo) { + return [&](const std::string& name) -> const TrackingVolume& { + const TrackingVolume* volume = nullptr; + + geo.visitVolumes([&](const TrackingVolume* v) { + if (v->volumeName() == name) { + volume = v; + } + }); + + if (volume == nullptr) { + throw std::runtime_error("Volume not found: " + name); + } + return *volume; + }; +} + +std::size_t countVolumes(const TrackingGeometry& geo) { + std::size_t nVolumes = 0; + geo.visitVolumes([&](const TrackingVolume* /*volume*/) { nVolumes++; }); + return nVolumes; +} + +} // namespace + +BOOST_AUTO_TEST_SUITE(Geometry); + +BOOST_AUTO_TEST_SUITE(BlueprintNodeTest); + +BOOST_AUTO_TEST_CASE(InvalidRoot) { + Logging::ScopedFailureThreshold threshold{Logging::Level::FATAL}; + + Blueprint::Config cfg; + Blueprint root{cfg}; + BOOST_CHECK_THROW(root.construct({}, gctx, *logger), std::logic_error); + + // More than one child is also invalid + auto cylBounds = std::make_shared(10_mm, 20_mm, 100_mm); + root.addChild( + std::make_unique(std::make_unique( + Transform3::Identity(), cylBounds, "child1"))); + root.addChild( + std::make_unique(std::make_unique( + Transform3::Identity(), cylBounds, "child2"))); + + BOOST_CHECK_THROW(root.construct({}, gctx, *logger), std::logic_error); +} + +class DummyNode : public BlueprintNode { + public: + explicit DummyNode(const std::string& name) : m_name(name) {} + + const std::string& name() const override { return m_name; } + + Volume& build(const BlueprintOptions& /*options*/, + const GeometryContext& /*gctx*/, + const Acts::Logger& /*logger*/) override { + throw std::logic_error("Not implemented"); + } + + PortalShellBase& connect(const BlueprintOptions& /*options*/, + const GeometryContext& /*gctx*/, + const Logger& /*logger */) override { + throw std::logic_error("Not implemented"); + } + + void finalize(const BlueprintOptions& /*options*/, + const GeometryContext& /*gctx*/, TrackingVolume& /*parent*/, + const Logger& /*logger*/) override { + throw std::logic_error("Not implemented"); + } + + private: + std::string m_name; +}; + +BOOST_AUTO_TEST_CASE(RootCannotBeChild) { + auto node = std::make_shared("node"); + Blueprint::Config cfg; + auto root = std::make_shared(cfg); + + BOOST_CHECK_THROW(node->addChild(root), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(AddChildInvalid) { + auto node = std::make_shared("node"); + + // Add self + BOOST_CHECK_THROW(node->addChild(node), std::invalid_argument); + + // Add nullptr + BOOST_CHECK_THROW(node->addChild(nullptr), std::invalid_argument); + + auto nodeB = std::make_shared("nodeB"); + auto nodeC = std::make_shared("nodeC"); + + node->addChild(nodeB); + nodeB->addChild(nodeC); + BOOST_CHECK_THROW(nodeC->addChild(node), std::invalid_argument); + + // already has parent, can't be added as a child anywhere else + BOOST_CHECK_THROW(node->addChild(nodeC), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(Depth) { + auto node1 = std::make_shared("node1"); + auto node2 = std::make_shared("node2"); + auto node3 = std::make_shared("node3"); + + BOOST_CHECK_EQUAL(node1->depth(), 0); + BOOST_CHECK_EQUAL(node2->depth(), 0); + BOOST_CHECK_EQUAL(node3->depth(), 0); + + node2->addChild(node3); + BOOST_CHECK_EQUAL(node2->depth(), 0); + BOOST_CHECK_EQUAL(node3->depth(), 1); + + node1->addChild(node2); + BOOST_CHECK_EQUAL(node1->depth(), 0); + BOOST_CHECK_EQUAL(node2->depth(), 1); + BOOST_CHECK_EQUAL(node3->depth(), 2); +} + +BOOST_AUTO_TEST_CASE(Static) { + Blueprint::Config cfg; + cfg.envelope[BinningValue::binZ] = {20_mm, 20_mm}; + cfg.envelope[BinningValue::binR] = {1_mm, 2_mm}; + Blueprint root{cfg}; + + double hlZ = 30_mm; + auto cylBounds = std::make_shared(10_mm, 20_mm, hlZ); + auto cyl = std::make_unique(Transform3::Identity(), cylBounds, + "child"); + + root.addStaticVolume(std::move(cyl)); + + BOOST_CHECK_EQUAL(root.children().size(), 1); + + auto tGeometry = root.construct({}, gctx, *logger); + + BOOST_REQUIRE(tGeometry); + + BOOST_CHECK_EQUAL(tGeometry->highestTrackingVolume()->volumes().size(), 1); + + BOOST_CHECK_EQUAL(countVolumes(*tGeometry), 2); + + auto lookup = nameLookup(*tGeometry); + auto actCyl = + dynamic_cast(lookup("child").volumeBounds()); + // Size as given + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 10_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 20_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), hlZ); + + auto worldCyl = + dynamic_cast(lookup("World").volumeBounds()); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 9_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 22_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), + hlZ + 20_mm); + + BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8); +} + +BOOST_AUTO_TEST_CASE(CylinderContainer) { + Blueprint::Config cfg; + cfg.envelope[BinningValue::binZ] = {20_mm, 20_mm}; + cfg.envelope[BinningValue::binR] = {2_mm, 20_mm}; + auto root = std::make_unique(cfg); + + auto& cyl = root->addCylinderContainer("Container", BinningValue::binZ); + cyl.setAttachmentStrategy(CylinderVolumeStack::AttachmentStrategy::Gap); + + double z0 = -200_mm; + double hlZ = 30_mm; + auto cylBounds = std::make_shared(10_mm, 20_mm, hlZ); + for (std::size_t i = 0; i < 3; i++) { + auto childCyl = std::make_unique( + Transform3::Identity() * + Translation3{Vector3{0, 0, z0 + i * 2 * hlZ * 1.2}}, + cylBounds, "child" + std::to_string(i)); + cyl.addStaticVolume(std::move(childCyl)); + } + + auto tGeometry = root->construct({}, gctx, *logger); + + // 4 real volumes + 2 gaps + BOOST_CHECK_EQUAL(countVolumes(*tGeometry), 6); + + auto lookup = nameLookup(*tGeometry); + auto worldCyl = + dynamic_cast(lookup("World").volumeBounds()); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 8_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 40_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), 122_mm); + + BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8); + + for (std::size_t i = 0; i < 3; i++) { + auto actCyl = dynamic_cast( + lookup("child" + std::to_string(i)).volumeBounds()); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 10_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 20_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), hlZ); + } + + for (std::size_t i = 0; i < 2; i++) { + auto gapCyl = dynamic_cast( + lookup("Container::Gap" + std::to_string(i + 1)).volumeBounds()); + BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eMinR), 10_mm); + BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eMaxR), 20_mm); + BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eHalfLengthZ), 6_mm); + } +} + +BOOST_AUTO_TEST_CASE(Confined) { + Transform3 base{Transform3::Identity()}; + + Blueprint::Config cfg; + cfg.envelope[BinningValue::binZ] = {20_mm, 20_mm}; + cfg.envelope[BinningValue::binR] = {2_mm, 20_mm}; + auto root = std::make_unique(cfg); + + root->addStaticVolume( + base, std::make_shared(50_mm, 400_mm, 1000_mm), + "PixelWrapper", [&](auto& wrap) { + double rMin = 100_mm; + double rMax = 350_mm; + double hlZ = 100_mm; + + wrap.addStaticVolume( + base * Translation3{Vector3{0, 0, -600_mm}}, + std::make_shared(rMin, rMax, hlZ), + "PixelNeg1"); + + wrap.addStaticVolume( + base * Translation3{Vector3{0, 0, -200_mm}}, + std::make_shared(rMin, rMax, hlZ), + "PixelNeg2"); + + wrap.addStaticVolume( + base * Translation3{Vector3{0, 0, 200_mm}}, + std::make_shared(rMin, rMax, hlZ), + "PixelPos1"); + + wrap.addStaticVolume( + base * Translation3{Vector3{0, 0, 600_mm}}, + std::make_shared(rMin, rMax, hlZ), + "PixelPos2"); + }); + + auto trackingGeometry = root->construct({}, gctx, *logger); + + // overall dimensions are the wrapper volume + envelope + auto lookup = nameLookup(*trackingGeometry); + auto worldCyl = + dynamic_cast(lookup("World").volumeBounds()); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 48_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 420_mm); + BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), 1020_mm); + + // 4 outer portals and 4 inner + BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8); + BOOST_CHECK_EQUAL(lookup("World").volumes().size(), 1); + + auto wrapperCyl = dynamic_cast( + lookup("PixelWrapper").volumeBounds()); + BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eMinR), 50_mm); + BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eMaxR), 400_mm); + BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eHalfLengthZ), + 1000_mm); + BOOST_CHECK_EQUAL(lookup("PixelWrapper").portals().size(), 4 + 4 * 4); + BOOST_CHECK_EQUAL(lookup("PixelWrapper").volumes().size(), 4); + + for (const auto& name : + {"PixelNeg1", "PixelNeg2", "PixelPos1", "PixelPos2"}) { + auto actCyl = + dynamic_cast(lookup(name).volumeBounds()); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 100_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 350_mm); + BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), 100_mm); + BOOST_CHECK_EQUAL(lookup(name).portals().size(), 4); + } +} + +BOOST_AUTO_TEST_CASE(DiscLayer) { + double yrot = 45_degree; + Transform3 base = Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()}; + + std::vector> surfaces; + std::vector> elements; + double r = 300_mm; + std::size_t nSensors = 8; + double thickness = 2.5_mm; + auto recBounds = std::make_shared(40_mm, 60_mm); + + double deltaPhi = 2 * std::numbers::pi / nSensors; + for (std::size_t i = 0; i < nSensors; i++) { + // Create a fan of sensors + + Transform3 trf = base * AngleAxis3{deltaPhi * i, Vector3::UnitZ()} * + Translation3(Vector3::UnitX() * r); + + if (i % 2 == 0) { + trf = trf * Translation3{Vector3::UnitZ() * 5_mm}; + } + + auto& element = elements.emplace_back( + std::make_unique(trf, recBounds, thickness)); + + element->surface().assignDetectorElement(*element); + + surfaces.push_back(element->surface().getSharedPtr()); + } + + Blueprint root{{.envelope = ExtentEnvelope{{ + .z = {2_mm, 2_mm}, + .r = {3_mm, 5_mm}, + }}}}; + + root.addLayer("Layer0", [&](auto& layer) { + layer.setSurfaces(surfaces) + .setLayerType(LayerBlueprintNode::LayerType::Disc) + .setEnvelope(ExtentEnvelope{{ + .z = {0.1_mm, 0.1_mm}, + .r = {1_mm, 1_mm}, + }}) + .setTransform(base); + }); + + auto trackingGeometry = root.construct({}, gctx, *logger); + + std::size_t nSurfaces = 0; + + trackingGeometry->visitSurfaces([&](const Surface* surface) { + if (surface->associatedDetectorElement() != nullptr) { + nSurfaces++; + } + }); + + BOOST_CHECK_EQUAL(nSurfaces, surfaces.size()); + BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2); + auto lookup = nameLookup(*trackingGeometry); + auto layerCyl = dynamic_cast( + lookup("Layer0").volumeBounds()); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 258.9999999_mm, + 1e-6); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR), 346.25353003_mm, + 1e-6); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 3.85_mm, + 1e-6); +} + +BOOST_AUTO_TEST_CASE(CylinderLayer) { + double yrot = 0_degree; + Transform3 base = Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()}; + + std::vector> surfaces; + std::vector> elements; + + double r = 300_mm; + std::size_t nStaves = 10; + int nSensorsPerStave = 8; + double thickness = 0; + double hlPhi = 40_mm; + double hlZ = 60_mm; + auto recBounds = std::make_shared(hlPhi, hlZ); + + double deltaPhi = 2 * std::numbers::pi / nStaves; + + for (std::size_t istave = 0; istave < nStaves; istave++) { + for (int isensor = -nSensorsPerStave; isensor <= nSensorsPerStave; + isensor++) { + double z = isensor * (2 * hlZ + 5_mm); + + Transform3 trf = base * Translation3(Vector3::UnitZ() * z) * + AngleAxis3{deltaPhi * istave, Vector3::UnitZ()} * + Translation3(Vector3::UnitX() * r) * + AngleAxis3{10_degree, Vector3::UnitZ()} * + AngleAxis3{90_degree, Vector3::UnitY()} * + AngleAxis3{90_degree, Vector3::UnitZ()}; + auto& element = elements.emplace_back( + std::make_unique(trf, recBounds, thickness)); + element->surface().assignDetectorElement(*element); + surfaces.push_back(element->surface().getSharedPtr()); + } + } + + Blueprint root{{.envelope = ExtentEnvelope{{ + .z = {2_mm, 2_mm}, + .r = {3_mm, 5_mm}, + }}}}; + + root.addLayer("Layer0", [&](auto& layer) { + layer.setSurfaces(surfaces) + .setLayerType(LayerBlueprintNode::LayerType::Cylinder) + .setEnvelope(ExtentEnvelope{{ + .z = {10_mm, 10_mm}, + .r = {20_mm, 10_mm}, + }}) + .setTransform(base); + }); + + auto trackingGeometry = root.construct({}, gctx, *logger); + + std::size_t nSurfaces = 0; + + trackingGeometry->visitSurfaces([&](const Surface* surface) { + if (surface->associatedDetectorElement() != nullptr) { + nSurfaces++; + } + }); + + BOOST_CHECK_EQUAL(nSurfaces, surfaces.size()); + BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2); + auto lookup = nameLookup(*trackingGeometry); + auto layerCyl = dynamic_cast( + lookup("Layer0").volumeBounds()); + BOOST_CHECK_EQUAL(lookup("Layer0").portals().size(), 4); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 275.6897761_mm, + 1e-6); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR), 319.4633358_mm, + 1e-6); + BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 1070_mm, + 1e-6); +} + +BOOST_AUTO_TEST_CASE(Material) { + Blueprint::Config cfg; + cfg.envelope[BinningValue::binZ] = {20_mm, 20_mm}; + cfg.envelope[BinningValue::binR] = {1_mm, 2_mm}; + Blueprint root{cfg}; + + double hlZ = 30_mm; + auto cylBounds = std::make_shared(10_mm, 20_mm, hlZ); + auto cyl = std::make_unique(Transform3::Identity(), cylBounds, + "child"); + + using enum BinningValue; + using enum CylinderVolumeBounds::Face; + using enum AxisBoundaryType; + + root.addMaterial("Material", [&](auto& mat) { + // @TODO: This API is not great + mat.setBinning(std::vector{ + std::tuple{NegativeDisc, Experimental::ProtoBinning{binR, Bound, 5}, + Experimental::ProtoBinning{binPhi, Bound, 10}}, + std::tuple{PositiveDisc, Experimental::ProtoBinning{binR, Bound, 15}, + Experimental::ProtoBinning{binPhi, Bound, 20}}, + }); + + mat.addStaticVolume(std::move(cyl)); + }); + + auto trackingGeometry = root.construct({}, gctx, *logger); + + BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2); + auto lookup = nameLookup(*trackingGeometry); + auto& child = lookup("child"); + + const auto* negDisc = child.portals().at(0).surface().surfaceMaterial(); + const auto* posDisc = child.portals().at(1).surface().surfaceMaterial(); + BOOST_CHECK_NE(negDisc, nullptr); + BOOST_CHECK_NE(posDisc, nullptr); + + const auto& negDiscMat = + dynamic_cast(*negDisc); + const auto& posDiscMat = + dynamic_cast(*posDisc); + + BOOST_CHECK_EQUAL(negDiscMat.binning().binning.at(0).bins(), 5); + BOOST_CHECK_EQUAL(negDiscMat.binning().binning.at(1).bins(), 10); + BOOST_CHECK_EQUAL(posDiscMat.binning().binning.at(0).bins(), 15); + BOOST_CHECK_EQUAL(posDiscMat.binning().binning.at(1).bins(), 20); + + for (std::size_t i = 2; i < child.portals().size(); i++) { + BOOST_CHECK_EQUAL(child.portals().at(i).surface().surfaceMaterial(), + nullptr); + } +} + +BOOST_AUTO_TEST_SUITE_END(); + +BOOST_AUTO_TEST_SUITE_END(); + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Geometry/CMakeLists.txt b/Tests/UnitTests/Core/Geometry/CMakeLists.txt index 0226e46d7d0..41c218129fe 100644 --- a/Tests/UnitTests/Core/Geometry/CMakeLists.txt +++ b/Tests/UnitTests/Core/Geometry/CMakeLists.txt @@ -35,3 +35,5 @@ add_unittest(CylinderVolumeStack CylinderVolumeStackTests.cpp) add_unittest(PortalLink PortalLinkTests.cpp) add_unittest(Portal PortalTests.cpp) add_unittest(PortalShell PortalShellTests.cpp) +add_unittest(Blueprint BlueprintTests.cpp) +add_unittest(BlueprintApi BlueprintApiTests.cpp) diff --git a/Tests/UnitTests/Core/Geometry/LayerCreatorTests.cpp b/Tests/UnitTests/Core/Geometry/LayerCreatorTests.cpp index 13aec3ff545..20f43a3b68e 100644 --- a/Tests/UnitTests/Core/Geometry/LayerCreatorTests.cpp +++ b/Tests/UnitTests/Core/Geometry/LayerCreatorTests.cpp @@ -230,7 +230,7 @@ struct LayerCreatorFixture { } } - return std::make_pair(res, pairs); + return {res, pairs}; } }; diff --git a/Tests/UnitTests/Core/Geometry/SurfaceArrayCreatorTests.cpp b/Tests/UnitTests/Core/Geometry/SurfaceArrayCreatorTests.cpp index 00b6509ef25..6d69c1667da 100644 --- a/Tests/UnitTests/Core/Geometry/SurfaceArrayCreatorTests.cpp +++ b/Tests/UnitTests/Core/Geometry/SurfaceArrayCreatorTests.cpp @@ -228,7 +228,7 @@ struct SurfaceArrayCreatorFixture { } } - return std::make_pair(res, pairs); + return {res, pairs}; } }; diff --git a/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp b/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp index c20239bb078..6d043178320 100644 --- a/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp +++ b/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp @@ -93,7 +93,7 @@ std::pair stripEnds( auto gPos1 = surface->localToGlobal(gctx, lpos1, globalFakeMom); auto gPos2 = surface->localToGlobal(gctx, lpos2, globalFakeMom); - return std::make_pair(gPos1, gPos2); + return {gPos1, gPos2}; } // Create a test context diff --git a/Tests/UnitTests/Core/Utilities/ResultTests.cpp b/Tests/UnitTests/Core/Utilities/ResultTests.cpp index 0a873f16d4c..748e0d7211e 100644 --- a/Tests/UnitTests/Core/Utilities/ResultTests.cpp +++ b/Tests/UnitTests/Core/Utilities/ResultTests.cpp @@ -333,6 +333,94 @@ BOOST_AUTO_TEST_CASE(BoolResult) { BOOST_CHECK_EQUAL(res.error(), MyError::Failure); } +BOOST_AUTO_TEST_CASE(ValueOrResult) { + using Result = Result; + + Result res = Result::success(5); + BOOST_CHECK_EQUAL(res.value_or(42), 5); + + res = Result::failure(MyError::Failure); + BOOST_CHECK_EQUAL(res.value_or(42), 42); + + BOOST_CHECK_EQUAL(Result::success(5).value_or(42), 5); + BOOST_CHECK_EQUAL(Result::failure(MyError::Failure).value_or(42), 42); + + int val = 25; + const int cval = 30; + + BOOST_CHECK_EQUAL(Result::success(5).value_or(val), 5); + BOOST_CHECK_EQUAL(Result::success(5).value_or(cval), 5); + BOOST_CHECK_EQUAL(Result::failure(MyError::Failure).value_or(val), 25); + BOOST_CHECK_EQUAL(Result::failure(MyError::Failure).value_or(cval), 30); + + res = Result::success(5); + + BOOST_CHECK_EQUAL(res.value_or(val), 5); + BOOST_CHECK_EQUAL(&(res.value_or(val)), &res.value()); + BOOST_CHECK_EQUAL(res.value_or(cval), 5); + BOOST_CHECK_EQUAL(&(res.value_or(cval)), &res.value()); + + res = Result::failure(MyError::Failure); + + BOOST_CHECK_EQUAL(res.value_or(val), 25); + BOOST_CHECK_EQUAL(res.value_or(cval), 30); + BOOST_CHECK_EQUAL(&(res.value_or(val)), &val); + BOOST_CHECK_EQUAL(&(res.value_or(cval)), &cval); +} + +BOOST_AUTO_TEST_CASE(TransformResult) { + using Result = Result; + + auto f1 = [](int x) { return 2 * x; }; + + Result res = Result::success(5); + Result res2 = res.transform(f1); + BOOST_CHECK(res2.ok()); + BOOST_CHECK_EQUAL(*res2, 10); + + res = Result::failure(MyError::Failure); + res2 = res.transform(f1); + BOOST_CHECK(!res2.ok()); + + BOOST_CHECK(Result::success(5).transform(f1).ok()); + BOOST_CHECK_EQUAL(Result::success(5).transform(f1).value(), 10); + + BOOST_CHECK(!Result::failure(MyError::Failure).transform(f1).ok()); +} + +BOOST_AUTO_TEST_CASE(AndThenResult) { + using Result1 = Result; + using Result2 = Result; + + auto f1 = [](int x) -> Result2 { + return Result2::success("hello " + std::to_string(x)); + }; + auto f2 = [](int) -> Result2 { return Result2::failure(MyError::Failure); }; + + Result1 res = Result1::success(5); + Result2 res2 = res.and_then(f1); + BOOST_CHECK(res2.ok()); + BOOST_CHECK_EQUAL(*res2, "hello 5"); + + res2 = res.and_then(f2); + BOOST_CHECK(!res2.ok()); + + res = Result1::failure(MyError::Failure); + res2 = res.and_then(f1); + BOOST_CHECK(!res2.ok()); + + res2 = res.and_then(f2); + BOOST_CHECK(!res2.ok()); + + BOOST_CHECK(Result1::success(5).and_then(f1).ok()); + BOOST_CHECK_EQUAL(Result1::success(5).and_then(f1).value(), "hello 5"); + + BOOST_CHECK(!Result1::success(5).and_then(f2).ok()); + + BOOST_CHECK(!Result1::failure(MyError::Failure).and_then(f1).ok()); + + BOOST_CHECK(!Result1::failure(MyError::Failure).and_then(f2).ok()); +} BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp b/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp index d7ce6b56877..3b369d56307 100644 --- a/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp +++ b/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp @@ -6,10 +6,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +#include #include #include "Acts/Utilities/TransformRange.hpp" +#include + using namespace Acts; BOOST_AUTO_TEST_SUITE(TransformRangeTests) @@ -99,6 +102,14 @@ BOOST_AUTO_TEST_CASE(TransformRangeDeref) { static_assert(std::is_same_v); checkSameAddresses(v, r); + std::vector unpacked; + std::ranges::transform(r, std::back_inserter(unpacked), + [](auto val) { return val; }); + std::vector exp = {1, 2, 4}; + + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), unpacked.begin(), + unpacked.end()); + auto cr = detail::TransformRange{detail::ConstDereference{}, raw_v}; static_assert(std::is_same_v); static_assert(std::is_same_v); @@ -108,6 +119,13 @@ BOOST_AUTO_TEST_CASE(TransformRangeDeref) { static_assert(std::is_same_v); static_assert(std::is_same_v); checkSameAddresses(v, r); + + unpacked.clear(); + std::ranges::transform(cr, std::back_inserter(unpacked), + [](auto val) { return val; }); + + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), unpacked.begin(), + unpacked.end()); } } @@ -127,6 +145,12 @@ BOOST_AUTO_TEST_CASE(TransformRangeDeref) { static_assert(std::is_same_v); static_assert(std::is_same_v); checkSameAddresses(v, r); + + std::vector unpacked; + std::ranges::transform(r, std::back_inserter(unpacked), + [](auto val) { return val; }); + + BOOST_CHECK(unpacked == std::vector({1, 2, 3})); } std::vector raw_v; diff --git a/Tests/UnitTests/Core/Vertexing/VertexingDataHelper.hpp b/Tests/UnitTests/Core/Vertexing/VertexingDataHelper.hpp index d984b4c8c44..383b5b84d2c 100644 --- a/Tests/UnitTests/Core/Vertexing/VertexingDataHelper.hpp +++ b/Tests/UnitTests/Core/Vertexing/VertexingDataHelper.hpp @@ -143,7 +143,7 @@ readTracksAndVertexCSV(const std::string& toolString, vertices.push_back(vertexInfo); } - return std::make_tuple(beamspotConstraint, vertices, tracks); + return {beamspotConstraint, vertices, tracks}; } } // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Visualization/CMakeLists.txt b/Tests/UnitTests/Core/Visualization/CMakeLists.txt index ca6768e876b..97d76217917 100644 --- a/Tests/UnitTests/Core/Visualization/CMakeLists.txt +++ b/Tests/UnitTests/Core/Visualization/CMakeLists.txt @@ -1,5 +1,6 @@ add_unittest(Visualization3D Visualization3DTests.cpp) add_unittest(EventDataView3D EventDataView3DTests.cpp) +add_unittest(Interpolation3D Interpolation3DTests.cpp) add_unittest(PrimitivesView3D PrimitivesView3DTests.cpp) add_unittest(SurfaceView3D SurfaceView3DTests.cpp) add_unittest(TrackingGeometryView3D TrackingGeometryView3DTests.cpp) diff --git a/Tests/UnitTests/Core/Visualization/Interpolation3DTests.cpp b/Tests/UnitTests/Core/Visualization/Interpolation3DTests.cpp new file mode 100644 index 00000000000..ba19c55b03f --- /dev/null +++ b/Tests/UnitTests/Core/Visualization/Interpolation3DTests.cpp @@ -0,0 +1,92 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Visualization/Interpolation3D.hpp" + +#include + +namespace Acts::Test { + +BOOST_AUTO_TEST_SUITE(Visualization) + +BOOST_AUTO_TEST_CASE(SplineInterpolationEigen) { + /// Define the input vector + double R = 10.; + std::vector inputs; + + // Interpolate the points options + std::vector trajectory; + + // Empty in empty out check + trajectory = Acts::Interpolation3D::spline(inputs, 10); + BOOST_CHECK(trajectory.empty()); + + for (double phi = 0; phi < 2 * std::numbers::pi; + phi += std::numbers::pi / 4) { + inputs.push_back(Acts::Vector3(R * cos(phi), R * sin(phi), 0.)); + } + + // (0) - nothing happens + trajectory = Acts::Interpolation3D::spline(inputs, 1); + // Check input and output size are the same + BOOST_CHECK_EQUAL(trajectory.size(), inputs.size()); + + // (1) - interpolate between the points with 12 points in total + trajectory = Acts::Interpolation3D::spline(inputs, 12); + // Check the output size is correct + BOOST_CHECK_EQUAL(trajectory.size(), 12); + + for (const auto& point : trajectory) { + // Check the interpolated points are on the circle + // with a tolerance of course + CHECK_CLOSE_ABS(point.norm(), R, 0.1); + // Verify points remain in the XY plane + CHECK_CLOSE_ABS(point.z(), 0., 0.1); + } +} + +BOOST_AUTO_TEST_CASE(SplineInterpolationArray) { + /// Define the input vector + std::vector> inputs; + + for (double x = 0; x < 10; x += 1) { + inputs.push_back({x, x * x, 0.}); + } + + // This time we keep the original hits + auto trajectory = Acts::Interpolation3D::spline(inputs, 100, true); + + // Check the outpu type is correct + constexpr bool isOutput = + std::is_same_v; + BOOST_CHECK(isOutput); + + // Check the output size is correct + BOOST_CHECK_EQUAL(trajectory.size(), 108); +} + +BOOST_AUTO_TEST_CASE(SplineInterpolationErrors) { + std::vector> inputs; + + // Test with single point + inputs.push_back({0., 0., 0.}); + auto result = Acts::Interpolation3D::spline(inputs, 10); + BOOST_CHECK_EQUAL(result.size(), 1); + + // Test with two points + inputs.push_back({1., 1., 1.}); + result = Acts::Interpolation3D::spline(inputs, 10); + BOOST_CHECK_EQUAL(result.size(), 2); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace Acts::Test diff --git a/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp b/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp index 1b31f083fd8..87d61b1df71 100644 --- a/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp +++ b/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp @@ -231,7 +231,7 @@ int main(int argc, char** argv) { -> std::tuple> { Acts::Vector3 position(sp.x(), sp.y(), sp.z()); Acts::Vector2 variance(sp.varianceR, sp.varianceZ); - return std::make_tuple(position, variance, std::nullopt); + return {position, variance, std::nullopt}; }; // setup spacepoint grid config diff --git a/Tests/UnitTests/Plugins/Cuda/Seeding2/main.cpp b/Tests/UnitTests/Plugins/Cuda/Seeding2/main.cpp index b63d549e37f..3b134aca9a0 100644 --- a/Tests/UnitTests/Plugins/Cuda/Seeding2/main.cpp +++ b/Tests/UnitTests/Plugins/Cuda/Seeding2/main.cpp @@ -113,7 +113,7 @@ int main(int argc, char* argv[]) { -> std::tuple> { Acts::Vector3 position(sp.x(), sp.y(), sp.z()); Acts::Vector2 covariance(sp.m_varianceR, sp.m_varianceZ); - return std::make_tuple(position, covariance, std::nullopt); + return {position, covariance, std::nullopt}; }; // extent used to store r range for middle spacepoint diff --git a/Tests/UnitTests/Plugins/Geant4/Geant4SurfaceProviderTests.cpp b/Tests/UnitTests/Plugins/Geant4/Geant4SurfaceProviderTests.cpp index 49521ae786d..7508fe9a148 100644 --- a/Tests/UnitTests/Plugins/Geant4/Geant4SurfaceProviderTests.cpp +++ b/Tests/UnitTests/Plugins/Geant4/Geant4SurfaceProviderTests.cpp @@ -123,7 +123,7 @@ ConstructGeant4World() { parser.SetOutputFileOverwrite(true); parser.Write(gdmlPath.string(), physWorld); - return std::make_tuple(physWorld, names); + return {physWorld, names}; } auto gctx = Acts::GeometryContext(); diff --git a/docs/core/geometry/concepts.md b/docs/core/geometry/concepts.md new file mode 100644 index 00000000000..abd2d31951a --- /dev/null +++ b/docs/core/geometry/concepts.md @@ -0,0 +1,75 @@ +# Concepts + +:::{todo} +Not complete yet +::: + +## Tracking geometry + +## Volume + +### Volume bounds + +## Tracking volume + +## Portals + +:::{doxygenclass} Acts::Portal +::: + +### Portal links + +:::{doxygenclass} Acts::PortalLinkBase +::: + +#### Trivial portal link + +:::{doxygenclass} Acts::TrivialPortalLink +::: + +#### Grid portal link + +:::{doxygenclass} Acts::GridPortalLink +::: + +:::{doxygenclass} Acts::GridPortalLinkT +::: + +#### Composite portal link + +:::{doxygenclass} Acts::CompositePortalLink +::: + +### Portal shells + +:::{doxygenclass} Acts::PortalShellBase +::: + +:::{doxygenclass} Acts::CylinderPortalShell +::: + +:::{doxygenclass} Acts::SingleCylinderPortalShell +::: + +:::{doxygenclass} Acts::CylinderStackPortalShell +::: + +### Navigation policy + +:::{doxygenclass} Acts::INavigationPolicy +::: + +:::{doxygenclass} Acts::MultiNavigationPolicyBase +::: + +:::{doxygenclass} Acts::MultiNavigationPolicy +::: + +:::{doxygenclass} Acts::SurfaceArrayNavigationPolicy +::: + +:::{doxygenclass} Acts::TryAllNavigationPolicy +::: + +:::{doxygenstruct} Acts::ProtoLayer +::: diff --git a/docs/core/geometry/construction.md b/docs/core/geometry/construction.md new file mode 100644 index 00000000000..5bcebdb24be --- /dev/null +++ b/docs/core/geometry/construction.md @@ -0,0 +1,43 @@ +# Construction + +:::{todo} +Not complete yet +::: + +## Blueprint tracking geometry construction + +:::{doxygenclass} Acts::BlueprintNode +::: + +:::{doxygenclass} Acts::Blueprint +::: + +### Container nodes + +:::{doxygenclass} Acts::CylinderContainerBlueprintNode +::: + +### Material nodes + +:::{doxygenclass} Acts::MaterialDesignatorBlueprintNode +::: + +### Geometry identification specification + +### *Layers* and other nodes + +:::{doxygenclass} Acts::StaticBlueprintNode +::: + +:::{doxygenclass} Acts::LayerBlueprintNode +::: + +## API + +### C++ API Example + +### Python API Examples + +### Plugin usage + +### Extension capabilities diff --git a/docs/core/geometry/index.md b/docs/core/geometry/index.md index b0009a36ef1..2bcb5cdec3f 100644 --- a/docs/core/geometry/index.md +++ b/docs/core/geometry/index.md @@ -1,4 +1,5 @@ (geometry_impl)= + # Geometry module The ACTS geometry model is strongly based on the ATLAS Tracking geometry. Its @@ -15,9 +16,11 @@ logical layers will be modelled as volumes, see [](layerless_geometry). :::{toctree} :maxdepth: 1 +concepts geometry_id material surfaces legacy/legacy +construction layerless/layerless ::: diff --git a/docs/core/geometry/layerless/layerless.md b/docs/core/geometry/layerless/layerless.md index b9784ac1f74..98e1624592a 100644 --- a/docs/core/geometry/layerless/layerless.md +++ b/docs/core/geometry/layerless/layerless.md @@ -1,13 +1,13 @@ (layerless_geometry)= + # Layerless geometry ## Geometry module rosetta stone -:::{todo} -Describe replacements of `TrackingGeometry`, `TrackingVolume` etc. and how the classes map to one another. +:::{note} +The combination of the original (Gen 1) geometry classes and the new *layerless* modelling (Gen 2, this page) will result in a combined Gen 3 geometry model. ::: - :::{toctree} building ::: diff --git a/docs/examples/full_chain_odd.md b/docs/examples/full_chain_odd.md index 5589d9f18d8..24f7dd8ca52 100644 --- a/docs/examples/full_chain_odd.md +++ b/docs/examples/full_chain_odd.md @@ -12,9 +12,9 @@ oddDir = getOpenDataDetectorDirectory() oddMaterialMap = oddDir / "data/odd-material-maps.root" oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap) -detector, trackingGeometry, decorators = getOpenDataDetector( - mdecorator=oddMaterialDeco, -) +detector = getOpenDataDetector(mdecorator=oddMaterialDeco) +trackingGeometry = detector.trackingGeometry() +decorators = detector.contextDecorators() ``` In our simple example we assume a homogeneous magnetic field along the beam axis with 2 T. The magnetic field is passed to all the different algorithms in our simulation and the reconstruction pipeline. diff --git a/docs/examples/howto/material_mapping.rst b/docs/examples/howto/material_mapping.rst index a8657a470a6..601d0b25ba9 100644 --- a/docs/examples/howto/material_mapping.rst +++ b/docs/examples/howto/material_mapping.rst @@ -55,7 +55,9 @@ For the following example we will be remapping the material of the ODD, we will .. code-block:: console - detector, trackingGeometry, decorators = getOpenDataDetector() + detector = getOpenDataDetector() + trackingGeometry = detector.trackingGeometry() + decorators = detector.contextDecorators() This algorithm is useful to obtain a visualisation of your detector using the different types of output available (``output-obj`` gives ``.obj`` with a 3D representation of the different subdetectors, for example). Here, we use ``output-json`` to obtain a map of all the surfaces and volumes in the detector with a ``ProtoSurfaceMaterial`` (or a ``ProtoVolumeMaterial``), ``mat-output-allmaterial`` ensure that a ``ProtoSurfaceMaterial`` (or a ``ProtoVolumeMaterial``) is associated to all the surfaces (or volumes), enforcing all of them to be written. Four types of surfaces exist: diff --git a/docs/examples/python_bindings.rst b/docs/examples/python_bindings.rst index fe0cd1dcb9f..69cead57aa7 100644 --- a/docs/examples/python_bindings.rst +++ b/docs/examples/python_bindings.rst @@ -22,7 +22,8 @@ sets up the particle propagation and runs a few events. import acts import acts.examples - detector, trackingGeometry, contextDecorators = acts.examples.GenericDetector.create() + detector = acts.examples.GenericDetector() + trackingGeometry = detector.trackingGeometry() s = acts.examples.Sequencer(events=10) rnd = acts.examples.RandomNumbers(seed=42) diff --git a/docs/getting_started.md b/docs/getting_started.md index aff8e38a3b0..03365dd9f40 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -375,7 +375,9 @@ You can now use the ODD in the python binding by using: ```python oddMaterialDeco = acts.IMaterialDecorator.fromFile("PATH_TO_Acts/thirdparty/OpenDataDetector/data/odd-material-maps.root") -detector, trackingGeometry, decorators = getOpenDataDetector(oddMaterialDeco) +detector = getOpenDataDetector(oddMaterialDeco) +trackingGeometry = detector.trackingGeometry() +decorators = detector.contextDecorators() ``` ## Using ACTS diff --git a/docs/known-warnings.txt b/docs/known-warnings.txt index 2138dc11bb9..2c23b8a9319 100644 --- a/docs/known-warnings.txt +++ b/docs/known-warnings.txt @@ -5,5 +5,6 @@ .*Duplicate explicit target name: .* .*undefined label: .*class_acts_1_1_convex_polygon_bounds_3_01_polygon_dynamic_01_4.* .*undefined label: .*class_acts_1_1_grid_surface_material_t.* +.*undefined label: .*namespace_acts_.* # I think these are because we use specialization .*undefined label: .*bdt.*