From 2862173924e232c0ed3800ba6fc0f9abb59f3d6e Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Fri, 6 Dec 2024 17:42:32 +0100 Subject: [PATCH 01/24] ci: Lint updates (#3958) 1. Remove redundant extra CI jobs that are covered by the pre-commit job 2. Make codespell write changes (more convenient to use from pre-commit) --- .github/workflows/checks.yml | 81 ++---------------------------------- .pre-commit-config.yaml | 3 +- 2 files changed, 5 insertions(+), 79 deletions(-) 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/.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/.*$ From 12ea68c11818c0a95ab0687719cbda781037870c Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:24:21 +0100 Subject: [PATCH 02/24] ci: Reenable some ExaTrkX tests (#3395) These tests should work even though cugraph is not available anymore... ## Summary by CodeRabbit - **New Features** - Introduced new test jobs for enhanced testing capabilities. - Added a new algorithm for converting prototracks to tracks, improving data processing. - **Bug Fixes** - Updated input parameters for existing algorithms to ensure consistency and accuracy in data flow. - **Chores** - Improved cache management in the CI pipeline for better build efficiency. - Updated hash values for test files to reflect recent changes. --- .gitlab-ci.yml | 67 ++++++++++------- .../TrackFindingExaTrkX/CMakeLists.txt | 1 + .../python/acts/examples/reconstruction.py | 24 ++++--- Examples/Python/tests/root_file_hashes.txt | 2 +- Examples/Scripts/Python/exatrkx.py | 71 ++++++++++++------- 5 files changed, 102 insertions(+), 63 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c897c947010..e278b18eade 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -142,33 +142,46 @@ 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 + - source src/CI/dependencies.sh + - 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/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/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index 727bf9e9785..43792827e8d 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -1786,13 +1786,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 = [ @@ -1824,11 +1823,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", @@ -1843,14 +1849,12 @@ 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", + inputTracks="tracks", + inputParticles="particles", inputParticleMeasurementsMap="particle_measurements_map", inputTrackParticleMatching=matchAlg.config.outputTrackParticleMatching, filePath=str(Path(outputDirRoot) / "performance_track_finding.root"), diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index 6eeaaebff44..e1b2833c2eb 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -72,7 +72,7 @@ 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 diff --git a/Examples/Scripts/Python/exatrkx.py b/Examples/Scripts/Python/exatrkx.py index b2a151fc4e7..5a8920a318a 100755 --- a/Examples/Scripts/Python/exatrkx.py +++ b/Examples/Scripts/Python/exatrkx.py @@ -1,16 +1,52 @@ #!/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 +55,10 @@ if "torch" in sys.argv: backend = ExaTrkXBackend.Torch - srcdir = Path(__file__).resolve().parent.parent.parent.parent - detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() 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 +90,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() From 03315f87b1c6a1d4ff17341e160f484753af81e1 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 6 Dec 2024 23:41:36 +0100 Subject: [PATCH 03/24] fix: Distinguish between hits and measurements in `ParticleSelector` in Examples (#3947) This clears out an oversight in a previous PR https://github.com/acts-project/acts/pull/3742 where I replaced numbers of measurements with number of hits. This number can be different and should not be confused. I add an optional input for the particle to measurement map which is then queried if present and used for the number of measurement cuts. blocked by: - https://github.com/acts-project/acts/pull/3944 ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced particle selection with new configuration options for minimum and maximum hit counts. - Updated particle selection criteria to focus on hit counts instead of measurement counts. - **Bug Fixes** - Improved validation checks for input measurements to ensure proper initialization. - **Documentation** - Updated comments and documentation to reflect changes in parameter naming from "measurements" to "hits." - **Tests** - Adjusted test cases to align with the new parameter naming conventions. - **Chores** - Minor code formatting and organization improvements throughout various scripts. --- .../workflows/physmon_trackfinding_1muon.py | 2 +- .../physmon_trackfinding_4muon_50vertices.py | 2 +- .../physmon_trackfinding_ttbar_pu200.py | 2 +- .../TruthTracking/ParticleSelector.cpp | 43 +++++++++++++++---- .../TruthTracking/ParticleSelector.hpp | 11 ++++- .../Python/python/acts/examples/simulation.py | 5 ++- Examples/Python/src/TruthTracking.cpp | 2 + Examples/Python/tests/test_examples.py | 2 +- Examples/Scripts/Optimization/ckf.py | 2 +- Examples/Scripts/Python/ckf_tracks.py | 2 +- Examples/Scripts/Python/full_chain_itk.py | 2 +- .../Scripts/Python/full_chain_itk_Gbts.py | 2 +- Examples/Scripts/Python/full_chain_odd.py | 4 +- Examples/Scripts/Python/full_chain_odd_LRT.py | 4 +- Examples/Scripts/Python/full_chain_test.py | 2 +- Examples/Scripts/Python/hashing_seeding.py | 2 +- Examples/Scripts/Python/seeding.py | 2 +- Examples/Scripts/Python/truth_tracking_gsf.py | 2 +- .../Scripts/Python/truth_tracking_gx2f.py | 2 +- .../Scripts/Python/truth_tracking_kalman.py | 2 +- 20 files changed, 69 insertions(+), 28 deletions(-) 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 4b4c75e1f36..de4021a5055 100755 --- a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py +++ b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py @@ -71,7 +71,7 @@ ), postSelectParticles=ParticleSelectorConfig( pt=(0.5 * u.GeV, None), - measurements=(9, None), + hits=(9, None), removeNeutral=True, ), ) 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/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index 411f4410361..ff690e2a777 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, diff --git a/Examples/Python/src/TruthTracking.cpp b/Examples/Python/src/TruthTracking.cpp index 4799d93dbdb..4d3569f4656 100644 --- a/Examples/Python/src/TruthTracking.cpp +++ b/Examples/Python/src/TruthTracking.cpp @@ -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); diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index 2261e58cea4..8441514c26b 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -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, ), ) diff --git a/Examples/Scripts/Optimization/ckf.py b/Examples/Scripts/Optimization/ckf.py index e0cd5fd9870..0077f35317b 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, ), ) diff --git a/Examples/Scripts/Python/ckf_tracks.py b/Examples/Scripts/Python/ckf_tracks.py index 75ec8a7c316..15a50466799 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, ), ) 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..50a13219afa 100755 --- a/Examples/Scripts/Python/full_chain_odd.py +++ b/Examples/Scripts/Python/full_chain_odd.py @@ -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..57f7ce85876 100644 --- a/Examples/Scripts/Python/full_chain_odd_LRT.py +++ b/Examples/Scripts/Python/full_chain_odd_LRT.py @@ -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 595dbd43a4e..49d7dacfeed 100755 --- a/Examples/Scripts/Python/full_chain_test.py +++ b/Examples/Scripts/Python/full_chain_test.py @@ -370,7 +370,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/hashing_seeding.py b/Examples/Scripts/Python/hashing_seeding.py index 770c34e5185..10f68b5cea9 100755 --- a/Examples/Scripts/Python/hashing_seeding.py +++ b/Examples/Scripts/Python/hashing_seeding.py @@ -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/seeding.py b/Examples/Scripts/Python/seeding.py index 3beb6071aae..1e5b9e50d6b 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, ), ) diff --git a/Examples/Scripts/Python/truth_tracking_gsf.py b/Examples/Scripts/Python/truth_tracking_gsf.py index ae8aca06d6c..4f194a65662 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, ), diff --git a/Examples/Scripts/Python/truth_tracking_gx2f.py b/Examples/Scripts/Python/truth_tracking_gx2f.py index 8503dc982f4..31ebb447d42 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, ), diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index 91c18f1dd28..971f0c4bab4 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -84,7 +84,7 @@ def runTruthTrackingKalman( enableInteractions=True, postSelectParticles=ParticleSelectorConfig( pt=(0.9 * u.GeV, None), - measurements=(7, None), + hits=(7, None), removeNeutral=True, removeSecondaries=True, ), From 5e8efd662189ef54fddbd328c535309a97d4c857 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sat, 7 Dec 2024 00:58:52 +0100 Subject: [PATCH 04/24] refactor: Remove `SurfaceSortingAlgorithm` from Examples (#3952) In https://github.com/acts-project/acts/pull/3944 we found out that this algorithm is not needed anymore ## Summary by CodeRabbit - **New Features** - Enhanced configurability for machine learning-based seed filtering with the addition of an ONNX model parameter. - Streamlined seeding and track fitting algorithms for improved modularity. - **Bug Fixes** - Removed unnecessary complexity by eliminating the `directNavigation` parameter from multiple functions. - **Documentation** - Updated test suite to improve clarity and maintainability. - **Chores** - Removed obsolete files and references related to the `SurfaceSortingAlgorithm` class to streamline the codebase. --- .../Algorithms/TrackFitting/CMakeLists.txt | 1 - .../TrackFitting/SurfaceSortingAlgorithm.hpp | 55 ------------ .../src/SurfaceSortingAlgorithm.cpp | 84 ------------------- .../python/acts/examples/reconstruction.py | 12 --- Examples/Python/src/TrackFitting.cpp | 17 ---- Examples/Python/tests/root_file_hashes.txt | 24 ++---- Examples/Python/tests/test_algorithms.py | 2 - Examples/Python/tests/test_examples.py | 4 +- .../Scripts/Python/truth_tracking_kalman.py | 2 - 9 files changed, 9 insertions(+), 192 deletions(-) delete mode 100644 Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp delete mode 100644 Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp 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 6315e192e67..00000000000 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/SurfaceSortingAlgorithm.hpp +++ /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/. - -#pragma once - -#include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/EventData/Measurement.hpp" -#include "ActsExamples/EventData/ProtoTrack.hpp" -#include "ActsExamples/EventData/SimHit.hpp" -#include "ActsExamples/Framework/DataHandle.hpp" -#include "ActsExamples/Framework/IAlgorithm.hpp" -#include "ActsExamples/Framework/ProcessCode.hpp" - -#include - -namespace ActsExamples { - -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/SurfaceSortingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp deleted file mode 100644 index 57a79ab2db0..00000000000 --- a/Examples/Algorithms/TrackFitting/src/SurfaceSortingAlgorithm.cpp +++ /dev/null @@ -1,84 +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 "ActsFatras/EventData/Hit.hpp" - -#include -#include -#include -#include - -namespace ActsExamples { - -SurfaceSortingAlgorithm::SurfaceSortingAlgorithm(Config cfg, - Acts::Logging::Level level) - : 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); -} - -ProcessCode SurfaceSortingAlgorithm::execute( - const 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()); - std::map 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 ProcessCode::SUCCESS; -} - -} // namespace ActsExamples diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index 43792827e8d..0e30961fe39 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -1288,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, @@ -1299,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, 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/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index e1b2833c2eb..07d42c66ad1 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -78,19 +78,11 @@ test_refitting[odd]__trackstates_gsf_refit.root: e297749dc1e7eda3b8dea13defa0499 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_examples.py b/Examples/Python/tests/test_examples.py index 8441514c26b..daf40b6685a 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -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), @@ -607,7 +606,6 @@ def test_truth_tracking_kalman( digiConfigFile=detector_config.digiConfigFile, outputDir=tmp_path, reverseFilteringMomThreshold=revFiltMomThresh, - directNavigation=directNavigation, s=seq, ) diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index 971f0c4bab4..3b857fd41da 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, ): @@ -122,7 +121,6 @@ def runTruthTrackingKalman( s, trackingGeometry, field, - directNavigation, reverseFilteringMomThreshold, ) From 1ebd328a63591dd259e9f5f65e7a56bb227c1bf3 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Sat, 7 Dec 2024 12:48:54 +0100 Subject: [PATCH 05/24] fix: typo in variable in vertex muon scan script (#3959) ## Summary by CodeRabbit - **Bug Fixes** - Corrected error handling in the vertex_mu_scan script to properly append NaN values to the time array. - **Chores** - Made spelling corrections in the CI/codespell_ignore.txt file to enhance clarity and correctness. --- CI/codespell_ignore.txt | 1 - Examples/Scripts/vertex_mu_scan.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CI/codespell_ignore.txt b/CI/codespell_ignore.txt index 13a3328440f..40dd321d0f5 100644 --- a/CI/codespell_ignore.txt +++ b/CI/codespell_ignore.txt @@ -5,7 +5,6 @@ coner dthe iself sortings -fime gaus te parm 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") From f16ed67a706a0b1ab3ea84291a8d737f705881e6 Mon Sep 17 00:00:00 2001 From: Stephen Nicholas Swatman Date: Sat, 7 Dec 2024 14:57:20 +0100 Subject: [PATCH 06/24] fix: Checkout branch in ExaTrk CI (#3967) Currently, the ExaTrk CI is trying to run a dependency script before having properly checked out the right branch, possible causing a CI error. This commit ensures that the target branch is properly checked out before trying to use any files in the repository. ## Summary by CodeRabbit - **Chores** - Improved CI/CD pipeline configuration for better build and test job management. - Standardized caching mechanisms and dependency handling across jobs. - Enhanced clarity and maintainability of the configuration. --- .gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e278b18eade..25aff340111 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -155,7 +155,10 @@ test_exatrkx_unittests: - apt-get update -y - git clone $CLONE_URL src - - source src/CI/dependencies.sh + - cd src + - git checkout $HEAD_SHA + - source CI/dependencies.sh + - cd .. - ctest --test-dir build -R ExaTrkX test_exatrkx_python: From 9067679f3908b08e8ce55c5c61d75fde0b9061a9 Mon Sep 17 00:00:00 2001 From: Stephen Nicholas Swatman Date: Sat, 7 Dec 2024 16:29:01 +0100 Subject: [PATCH 07/24] feat: Add monadic functions to `Result` (#3957) In order to make the `Result` type easier to use, this commit adds three new functions: * `Result::value_or` allows the user to obtain the value or a provided default value. * `Result::transform` models functorial mapping, allowing users to modify values inside results. * `Result::and_then` models monadic binding, allowing users to build complex chains of actions on results. Implemented are lvalue and rvalue versions of these functions as well as tests. ![image](https://github.com/user-attachments/assets/591e64a2-e4fb-4ce6-9d34-166b28f7a837) ## Summary by CodeRabbit - **New Features** - Enhanced `Result` class with new methods for improved error handling and value transformation: - `value_or`: Retrieve valid value or default. - `transform`: Apply a function to the valid value. - `and_then`: Chain functions based on the result's validity. - **Tests** - Added comprehensive test cases to validate the new functionalities of the `Result` class, ensuring reliability and correctness: - `ValueOrResult`: Validates the `value_or` method. - `TransformResult`: Assesses the `transform` method. - `AndThenResult`: Evaluates the `and_then` method. --- Core/include/Acts/Utilities/Result.hpp | 141 ++++++++++++++++++ .../UnitTests/Core/Utilities/ResultTests.cpp | 88 +++++++++++ 2 files changed, 229 insertions(+) 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/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 From 341d2b0b64cd4ccfc26cb69dea2006a875e5eab5 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sat, 7 Dec 2024 18:22:45 +0100 Subject: [PATCH 08/24] refactor: Rework detector handling in Examples (#3498) - Concrete detector interface for examples - Align interfaces of different detectors - Remove `DD4hepGeometryService` - Improve DDG4 construction - `unique_ptr` for DD4hep detector with dedicated name - Concrete `Geant4Mapping` instance for conversion - Simplify CMake for optional Geant4 construction for different detectors - Move Geant4 construction code to detector folder --- CI/physmon/physmon_common.py | 4 +- Core/include/Acts/Utilities/Logger.hpp | 4 +- Examples/Algorithms/Geant4/CMakeLists.txt | 38 +--- .../DDG4/DDG4DetectorConstruction.hpp | 74 -------- ...tory.hpp => Geant4ConstructionOptions.hpp} | 16 +- .../ActsExamples/Geant4/Geant4Simulation.hpp | 22 +-- .../ActsExamples/Geant4/RegionCreator.hpp | 25 +-- .../TelescopeG4DetectorConstruction.hpp | 56 ------ .../Geant4/src/DDG4DetectorConstruction.cpp | 76 -------- .../Geant4/src/GdmlDetectorConstruction.cpp | 49 ----- .../Geant4/src/Geant4Simulation.cpp | 8 +- .../Algorithms/Geant4/src/RegionCreator.cpp | 19 +- .../Geant4HepMC/EventRecording.hpp | 9 +- .../Geant4HepMC/src/EventRecording.cpp | 6 +- Examples/Detectors/CMakeLists.txt | 2 + Examples/Detectors/Common/CMakeLists.txt | 14 ++ .../ActsExamples/DetectorCommons/Detector.hpp | 69 +++++++ Examples/Detectors/Common/src/Detector.cpp | 58 ++++++ .../ContextualDetector/CMakeLists.txt | 8 +- .../ContextualDetector/AlignedDetector.hpp | 32 +--- .../src/AlignedDetector.cpp | 91 +++++---- .../Detectors/DD4hepDetector/CMakeLists.txt | 37 +++- .../DD4hepDetector/DD4hepDetector.hpp | 150 ++++++++------- .../DD4hepDetector/DD4hepGeometryService.hpp | 137 -------------- .../DDG4DetectorConstruction.hpp | 50 +++++ .../DD4hepDetector/src/DD4hepDetector.cpp | 163 ++++++++++------ .../src/DD4hepDetectorGeant4.cpp | 22 +++ .../src/DD4hepDetectorGeant4Stub.cpp | 19 ++ .../src/DD4hepGeometryService.cpp | 174 ------------------ .../src/DDG4DetectorConstruction.cpp | 52 ++++++ .../Detectors/Geant4Detector/CMakeLists.txt | 17 +- .../Geant4Detector/GdmlDetector.hpp | 36 ++++ .../GdmlDetectorConstruction.hpp | 28 +-- .../Geant4Detector/Geant4Detector.hpp | 51 +---- .../Geant4Detector/src/GdmlDetector.cpp | 27 +++ .../src/GdmlDetectorConstruction.cpp | 41 +++++ .../Geant4Detector/src/Geant4Detector.cpp | 77 +++----- .../Detectors/GenericDetector/CMakeLists.txt | 8 +- .../GenericDetector/GenericDetector.hpp | 37 ++-- .../GenericDetector/src/GenericDetector.cpp | 32 ++-- .../Detectors/GeoModelDetector/CMakeLists.txt | 41 +++++ .../GeoModelDetector/GeoModelDetector.hpp | 37 ++++ .../GeoModelGeant4DetectorConstruction.hpp} | 31 +--- .../GeoModelDetector/src/GeoModelDetector.cpp | 24 +++ .../src/GeoModelDetectorGeant4.cpp | 21 +++ .../src/GeoModelDetectorGeant4Stub.cpp | 19 ++ .../GeoModelGeant4DetectorConstruction.cpp} | 33 ++-- .../src/MockupSectorBuilder.cpp | 17 +- .../Detectors/TGeoDetector/CMakeLists.txt | 2 + .../TGeoDetector/JsonTGeoDetectorConfig.hpp | 10 +- .../TGeoDetector/TGeoDetector.hpp | 32 ++-- .../TGeoDetector/src/TGeoDetector.cpp | 26 ++- .../TelescopeDetector/CMakeLists.txt | 23 ++- .../TelescopeDetector/TelescopeDetector.hpp | 27 ++- .../TelescopeG4DetectorConstruction.hpp | 38 ++++ .../src/TelescopeDetector.cpp | 43 ++--- .../src/TelescopeDetectorGeant4.cpp | 20 ++ .../src/TelescopeDetectorGeant4Stub.cpp | 19 ++ .../src/TelescopeG4DetectorConstruction.cpp | 29 +-- .../ActsExamples/Io/EDM4hep/EDM4hepReader.hpp | 2 +- Examples/Io/EDM4hep/src/EDM4hepReader.cpp | 5 +- Examples/Python/CMakeLists.txt | 55 +----- Examples/Python/python/acts/_adapter.py | 50 ----- .../Python/python/acts/examples/__init__.py | 4 +- .../Python/python/acts/examples/dd4hep.py | 7 +- .../python/acts/examples/geant4/dd4hep.py | 20 -- .../python/acts/examples/geant4/geomodel.py | 6 - Examples/Python/python/acts/examples/itk.py | 6 +- Examples/Python/python/acts/examples/odd.py | 36 +--- .../Python/python/acts/examples/simulation.py | 44 +---- Examples/Python/src/Covfie.cpp | 2 + Examples/Python/src/DD4hepComponent.cpp | 50 ++--- Examples/Python/src/Detector.cpp | 171 ++++++++--------- Examples/Python/src/Geant4Component.cpp | 110 ++++------- Examples/Python/src/Geant4DD4hepComponent.cpp | 34 ---- .../Python/src/Geant4GeoModelComponent.cpp | 36 ---- Examples/Python/src/Geant4HepMC3.cpp | 6 +- Examples/Python/src/GeoModel.cpp | 18 ++ Examples/Python/src/GeometryBuildingGen1.cpp | 23 ++- Examples/Python/src/ModuleEntry.cpp | 17 +- Examples/Python/tests/conftest.py | 39 ++-- Examples/Python/tests/test_detectors.py | 25 ++- Examples/Python/tests/test_examples.py | 89 ++++----- Examples/Python/tests/test_reader.py | 12 +- Examples/Python/tests/test_writer.py | 18 +- .../GsfDebugger/make_gsf_verbose_log.py | 6 +- Examples/Scripts/Optimization/ckf.py | 4 +- .../Orion/material_mapping_optimisation.py | 15 +- Examples/Scripts/Python/ckf_tracks.py | 4 +- Examples/Scripts/Python/digitization.py | 4 +- .../Scripts/Python/digitization_config.py | 3 +- Examples/Scripts/Python/event_recording.py | 13 +- Examples/Scripts/Python/exatrkx.py | 4 +- Examples/Scripts/Python/fatras.py | 4 +- Examples/Scripts/Python/full_chain_odd.py | 6 +- Examples/Scripts/Python/full_chain_odd_LRT.py | 6 +- Examples/Scripts/Python/full_chain_test.py | 8 +- Examples/Scripts/Python/geant4.py | 4 +- Examples/Scripts/Python/geant4_parallel.py | 4 +- Examples/Scripts/Python/geometry.py | 10 +- Examples/Scripts/Python/hashing_seeding.py | 8 +- Examples/Scripts/Python/material_mapping.py | 4 +- .../Scripts/Python/material_mapping_core.py | 3 +- Examples/Scripts/Python/material_recording.py | 26 +-- .../Scripts/Python/material_validation.py | 6 +- .../Python/material_validation_core.py | 5 +- Examples/Scripts/Python/propagation.py | 14 +- Examples/Scripts/Python/seeding.py | 5 +- .../Scripts/Python/telescope_simulation.py | 3 +- ...elescope_track_params_lookup_generation.py | 3 +- Examples/Scripts/Python/truth_tracking_gsf.py | 6 +- .../Python/truth_tracking_gsf_refitting.py | 6 +- .../Scripts/Python/truth_tracking_gx2f.py | 6 +- .../Scripts/Python/truth_tracking_kalman.py | 6 +- .../Python/truth_tracking_kalman_refitting.py | 7 +- .../Python/truth_tracking_telescope.py | 3 +- Examples/Scripts/Python/vertex_fitting.py | 3 +- docs/examples/full_chain_odd.md | 6 +- docs/examples/howto/material_mapping.rst | 4 +- docs/examples/python_bindings.rst | 3 +- docs/getting_started.md | 4 +- 121 files changed, 1584 insertions(+), 1857 deletions(-) delete mode 100644 Examples/Algorithms/Geant4/include/ActsExamples/DDG4/DDG4DetectorConstruction.hpp rename Examples/Algorithms/Geant4/include/ActsExamples/Geant4/{DetectorConstructionFactory.hpp => Geant4ConstructionOptions.hpp} (53%) delete mode 100644 Examples/Algorithms/Geant4/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp delete mode 100644 Examples/Algorithms/Geant4/src/DDG4DetectorConstruction.cpp delete mode 100644 Examples/Algorithms/Geant4/src/GdmlDetectorConstruction.cpp create mode 100644 Examples/Detectors/Common/CMakeLists.txt create mode 100644 Examples/Detectors/Common/include/ActsExamples/DetectorCommons/Detector.hpp create mode 100644 Examples/Detectors/Common/src/Detector.cpp delete mode 100644 Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp create mode 100644 Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DDG4DetectorConstruction.hpp create mode 100644 Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4.cpp create mode 100644 Examples/Detectors/DD4hepDetector/src/DD4hepDetectorGeant4Stub.cpp delete mode 100644 Examples/Detectors/DD4hepDetector/src/DD4hepGeometryService.cpp create mode 100644 Examples/Detectors/DD4hepDetector/src/DDG4DetectorConstruction.cpp create mode 100644 Examples/Detectors/Geant4Detector/include/ActsExamples/Geant4Detector/GdmlDetector.hpp rename Examples/{Algorithms/Geant4/include/ActsExamples/Geant4 => Detectors/Geant4Detector/include/ActsExamples/Geant4Detector}/GdmlDetectorConstruction.hpp (56%) create mode 100644 Examples/Detectors/Geant4Detector/src/GdmlDetector.cpp create mode 100644 Examples/Detectors/Geant4Detector/src/GdmlDetectorConstruction.cpp create mode 100644 Examples/Detectors/GeoModelDetector/CMakeLists.txt create mode 100644 Examples/Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelDetector.hpp rename Examples/{Algorithms/Geant4/include/ActsExamples/GeoModelG4/GeoModelDetectorConstruction.hpp => Detectors/GeoModelDetector/include/ActsExamples/GeoModelDetector/GeoModelGeant4DetectorConstruction.hpp} (52%) create mode 100644 Examples/Detectors/GeoModelDetector/src/GeoModelDetector.cpp create mode 100644 Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4.cpp create mode 100644 Examples/Detectors/GeoModelDetector/src/GeoModelDetectorGeant4Stub.cpp rename Examples/{Algorithms/Geant4/src/GeoModelDetectorConstruction.cpp => Detectors/GeoModelDetector/src/GeoModelGeant4DetectorConstruction.cpp} (53%) create mode 100644 Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeG4DetectorConstruction.hpp create mode 100644 Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4.cpp create mode 100644 Examples/Detectors/TelescopeDetector/src/TelescopeDetectorGeant4Stub.cpp rename Examples/{Algorithms/Geant4 => Detectors/TelescopeDetector}/src/TelescopeG4DetectorConstruction.cpp (87%) delete mode 100644 Examples/Python/python/acts/examples/geant4/dd4hep.py delete mode 100644 Examples/Python/python/acts/examples/geant4/geomodel.py delete mode 100644 Examples/Python/src/Geant4DD4hepComponent.cpp delete mode 100644 Examples/Python/src/Geant4GeoModelComponent.cpp 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/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/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..089c1c2b3cd 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 final; /// 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/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..0859c174e5a 100644 --- a/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp +++ b/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp @@ -10,31 +10,18 @@ #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; @@ -59,21 +46,14 @@ class AlignedDetector { enum class Mode { Internal, External }; Mode mode = Mode::Internal; - }; - std::pair finalize( - const Config& cfg, - std::shared_ptr mdecorator); + std::shared_ptr materialDecorator; + }; - 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 9a9c5901543..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,94 +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::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 a2c12dfaaa7..a411a32a028 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp @@ -8,77 +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::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 (auto& file : m_cfg.xmlFileNames) { + detector->fromCompact(file.c_str()); + } + 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 (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); + } + } + sort(muon.begin(), muon.end(), + [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + sort(eCal.begin(), eCal.end(), + [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + sort(hCal.begin(), hCal.end(), + [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { + return (a.id() < b.id()); + }); + sort(tracker.begin(), tracker.end(), + [](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 b80b769708d..f996d225e79 100644 --- a/Examples/Detectors/GenericDetector/src/GenericDetector.cpp +++ b/Examples/Detectors/GenericDetector/src/GenericDetector.cpp @@ -8,26 +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::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 f594b681d49..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,18 +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::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 ccba63a1c34..414658204b8 100644 --- a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp @@ -9,59 +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::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/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/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index dc359db0d0d..b7e7a5b4112 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -91,7 +91,10 @@ 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) @@ -177,56 +180,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/simulation.py b/Examples/Python/python/acts/examples/simulation.py index ff690e2a777..a82c16e4198 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -585,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 @@ -629,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", @@ -697,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() @@ -717,7 +675,7 @@ def addGeant4( alg = Geant4Simulation( level=customLogLevel(), geant4Handle=__geant4Handle, - detectorConstructionFactory=g4DetectorConstructionFactory, + detector=detector, randomNumbers=rnd, inputParticles=particlesPreSelected, outputParticles=outputParticles, 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/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_ #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 a78f04f9565..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 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/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 daf40b6685a..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" @@ -592,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)) @@ -601,7 +600,7 @@ 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, @@ -610,7 +609,6 @@ def test_truth_tracking_kalman( ) seq.run() - print("done") for fn, tn, ee in root_files: fp = tmp_path / fn @@ -657,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, @@ -689,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, @@ -757,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, @@ -790,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 ) @@ -824,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, @@ -862,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, @@ -886,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"), @@ -898,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 @@ -940,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: @@ -1163,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, @@ -1199,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 = ( @@ -1232,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 = ( @@ -1286,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 0077f35317b..7de27b98465 100755 --- a/Examples/Scripts/Optimization/ckf.py +++ b/Examples/Scripts/Optimization/ckf.py @@ -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/ckf_tracks.py b/Examples/Scripts/Python/ckf_tracks.py index 15a50466799..1ef32c844ee 100755 --- a/Examples/Scripts/Python/ckf_tracks.py +++ b/Examples/Scripts/Python/ckf_tracks.py @@ -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 5a8920a318a..77cb9b1a785 100755 --- a/Examples/Scripts/Python/exatrkx.py +++ b/Examples/Scripts/Python/exatrkx.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 + from pathlib import Path import os import sys @@ -55,7 +56,8 @@ def runGNNTrackFinding( if "torch" in sys.argv: backend = ExaTrkXBackend.Torch - 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/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_odd.py b/Examples/Scripts/Python/full_chain_odd.py index 50a13219afa..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) diff --git a/Examples/Scripts/Python/full_chain_odd_LRT.py b/Examples/Scripts/Python/full_chain_odd_LRT.py index 57f7ce85876..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) diff --git a/Examples/Scripts/Python/full_chain_test.py b/Examples/Scripts/Python/full_chain_test.py index 49d7dacfeed..e7c945094f6 100755 --- a/Examples/Scripts/Python/full_chain_test.py +++ b/Examples/Scripts/Python/full_chain_test.py @@ -285,7 +285,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) @@ -301,10 +303,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) 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 10f68b5cea9..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" 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 1e5b9e50d6b..ce1914accc7 100755 --- a/Examples/Scripts/Python/seeding.py +++ b/Examples/Scripts/Python/seeding.py @@ -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 4f194a65662..7d6e05c07c7 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf.py +++ b/Examples/Scripts/Python/truth_tracking_gsf.py @@ -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 31ebb447d42..2c692ca87cc 100644 --- a/Examples/Scripts/Python/truth_tracking_gx2f.py +++ b/Examples/Scripts/Python/truth_tracking_gx2f.py @@ -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 3b857fd41da..0102f1db8ee 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -177,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/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 From 47db0b917b575cc1b956fff0dbc3b09f925ed3c5 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 9 Dec 2024 10:46:03 +0100 Subject: [PATCH 09/24] chore: Sonar fixes after detector refactor (#3968) Blocked by: - #3498 ## Summary by CodeRabbit - **New Features** - Enhanced safety in the `DD4hepDetector` class by using const references in loop iterations. - Updated sorting mechanism for several vectors to utilize modern C++ practices. - **Bug Fixes** - Adjusted data types in the `Config` struct of the `AlignedDetector` class for better performance and accuracy. - Removed obsolete member variable from the `Config` struct in the `AlignedDetector` class. Co-authored-by: Andreas Stefl <487211+andiwand@users.noreply.github.com> --- .../ActsExamples/Geant4/Geant4Simulation.hpp | 2 +- .../ContextualDetector/AlignedDetector.hpp | 10 ++--- .../DD4hepDetector/src/DD4hepDetector.cpp | 38 +++++++++---------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp index 089c1c2b3cd..df9dd637eca 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp @@ -208,7 +208,7 @@ class Geant4MaterialRecording final : public Geant4SimulationBase { /// Algorithm execute method, called once per event with context /// /// @param ctx the AlgorithmContext for this event - 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/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp b/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp index 0859c174e5a..7a184f16fc1 100644 --- a/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp +++ b/Examples/Detectors/ContextualDetector/include/ActsExamples/ContextualDetector/AlignedDetector.hpp @@ -13,8 +13,6 @@ #include "ActsExamples/DetectorCommons/Detector.hpp" #include "ActsExamples/GenericDetector/GenericDetector.hpp" -#include - namespace ActsExamples { class InternallyAlignedDetectorElement; @@ -24,11 +22,11 @@ class AlignedDetector : public Detector { public: 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 @@ -46,8 +44,6 @@ class AlignedDetector : public Detector { enum class Mode { Internal, External }; Mode mode = Mode::Internal; - - std::shared_ptr materialDecorator; }; explicit AlignedDetector(const Config& cfg); diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp index a411a32a028..6ad70fa7d70 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp @@ -86,8 +86,8 @@ std::unique_ptr DD4hepDetector::buildDD4hepGeometry() const { std::unique_ptr detector = dd4hep::Detector::make_unique(m_cfg.name); - for (auto& file : m_cfg.xmlFileNames) { - detector->fromCompact(file.c_str()); + for (const auto& file : m_cfg.xmlFileNames) { + detector->fromCompact(file); } detector->volumeManager(); detector->apply("DD4hepVolumeManager", 0, nullptr); @@ -106,7 +106,7 @@ void ActsExamples::sortFCChhDetElements(std::vector& det) { std::vector eCal; std::vector hCal; std::vector muon; - for (auto& detElement : det) { + for (const auto& detElement : det) { std::string detName = detElement.name(); if (detName.find("Muon") != std::string::npos) { muon.push_back(detElement); @@ -118,22 +118,22 @@ void ActsExamples::sortFCChhDetElements(std::vector& det) { tracker.push_back(detElement); } } - sort(muon.begin(), muon.end(), - [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { - return (a.id() < b.id()); - }); - sort(eCal.begin(), eCal.end(), - [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { - return (a.id() < b.id()); - }); - sort(hCal.begin(), hCal.end(), - [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { - return (a.id() < b.id()); - }); - sort(tracker.begin(), tracker.end(), - [](const dd4hep::DetElement& a, const dd4hep::DetElement& b) { - return (a.id() < b.id()); - }); + 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; From 0a11443eef9ff85e9a0b5f1a6b15f6f424eaef98 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 9 Dec 2024 12:31:07 +0100 Subject: [PATCH 10/24] chore: Clean event generator includes in Examples (#3964) --- .../Generators/ActsExamples/Generators/EventGenerator.cpp | 5 ----- .../Generators/ActsExamples/Generators/EventGenerator.hpp | 1 - .../ActsExamples/Generators/ParametricParticleGenerator.cpp | 1 - .../ActsExamples/Generators/Pythia8ProcessGenerator.cpp | 2 -- 4 files changed, 9 deletions(-) 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 From 94ae506310982400b4c8b0739a3fa6cec89c9527 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 9 Dec 2024 14:20:57 +0100 Subject: [PATCH 11/24] fix: Workaround Pythia8 race condition in ttbar physmon (#3965) Until https://github.com/acts-project/acts/issues/3963 is fixed we have to stick to single threaded processing with ttbar ## Summary by CodeRabbit - **Bug Fixes** - Adjusted threading configuration to single-threaded execution to enhance stability and prevent race conditions during simulations. --- CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py index de4021a5055..d26e5e78873 100755 --- a/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py +++ b/CI/physmon/workflows/physmon_trackfinding_ttbar_pu200.py @@ -35,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, ) From bd88be8f5b2064aa28e543b402ada978f0816b44 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:15:51 +0100 Subject: [PATCH 12/24] fix: typo in json digitization config (#3960) ## Summary by CodeRabbit - **Bug Fixes** - Corrected spelling errors in the spell-check ignore list. - Fixed a typo from "Digitial" to "Digital" in JSON serialization and deserialization functions, ensuring accurate representation of smearing configuration. - **Chores** - Updated internal documentation for spelling corrections. --- CI/codespell_ignore.txt | 1 - Examples/Algorithms/Digitization/scripts/smearing-config.py | 2 +- Examples/Io/Json/src/JsonDigitizationConfig.cpp | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CI/codespell_ignore.txt b/CI/codespell_ignore.txt index 40dd321d0f5..e6ab8123413 100644 --- a/CI/codespell_ignore.txt +++ b/CI/codespell_ignore.txt @@ -11,7 +11,6 @@ parm writet localy lastr -digitial exprot pring aline 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/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); From d3cedd0be4dd54deeb846bd5f48f509b4540b416 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 10 Dec 2024 16:49:45 +0100 Subject: [PATCH 13/24] feat: Make TransformRange fulfill range concept (#3971) This allows using it with range algorithms --- .../include/Acts/Utilities/TransformRange.hpp | 16 +++++++++++++ .../Core/Utilities/TransformRangeTests.cpp | 24 +++++++++++++++++++ 2 files changed, 40 insertions(+) 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/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; From 6c910a6640dd1b66ab95b612d0c57bbfc3b8dd0b Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 10 Dec 2024 21:46:35 +0100 Subject: [PATCH 14/24] feat: Gen3 blueprint geometry construction (#3869) This PR implements building a tracking geometry using the blueprint building mechanism. ``` +---------------+ +-----------+ | | | | | 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. Part of: - #3502 Blocked by: - #3816 - #3818 - https://github.com/acts-project/acts/pull/3971 --- Core/include/Acts/Geometry/Blueprint.hpp | 105 ++++ Core/include/Acts/Geometry/BlueprintNode.hpp | 295 ++++++++++ .../Acts/Geometry/BlueprintOptions.hpp | 28 + .../Acts/Geometry/CompositePortalLink.hpp | 2 + .../CylinderContainerBlueprintNode.hpp | 157 ++++++ Core/include/Acts/Geometry/GridPortalLink.hpp | 12 + .../Acts/Geometry/LayerBlueprintNode.hpp | 141 +++++ .../MaterialDesignatorBlueprintNode.hpp | 101 ++++ Core/include/Acts/Geometry/Portal.hpp | 4 + .../Acts/Geometry/StaticBlueprintNode.hpp | 67 +++ .../Acts/Material/ProtoVolumeMaterial.hpp | 3 +- Core/include/Acts/Seeding/PathSeeder.hpp | 2 - Core/include/Acts/Utilities/Delegate.hpp | 9 +- Core/src/Geometry/Blueprint.cpp | 157 ++++++ Core/src/Geometry/BlueprintNode.cpp | 173 ++++++ Core/src/Geometry/BlueprintOptions.cpp | 29 + Core/src/Geometry/CMakeLists.txt | 7 + .../CylinderContainerBlueprintNode.cpp | 257 +++++++++ Core/src/Geometry/LayerBlueprintNode.cpp | 149 +++++ .../MaterialDesignatorBlueprintNode.cpp | 191 +++++++ Core/src/Geometry/PortalLinkBase.cpp | 10 +- Core/src/Geometry/StaticBlueprintNode.cpp | 170 ++++++ Examples/Python/CMakeLists.txt | 1 + Examples/Python/src/Blueprint.cpp | 432 ++++++++++++++ Examples/Python/src/Geometry.cpp | 15 + Examples/Python/tests/test_blueprint.py | 80 +++ Examples/Scripts/Python/blueprint.py | 99 ++++ Tests/UnitTests/Core/Detector/CMakeLists.txt | 2 +- .../Core/Geometry/BlueprintApiTests.cpp | 409 ++++++++++++++ .../Core/Geometry/BlueprintTests.cpp | 531 ++++++++++++++++++ Tests/UnitTests/Core/Geometry/CMakeLists.txt | 2 + docs/core/geometry/concepts.md | 75 +++ docs/core/geometry/construction.md | 43 ++ docs/core/geometry/index.md | 3 + docs/core/geometry/layerless/layerless.md | 6 +- docs/known-warnings.txt | 1 + 36 files changed, 3754 insertions(+), 14 deletions(-) create mode 100644 Core/include/Acts/Geometry/Blueprint.hpp create mode 100644 Core/include/Acts/Geometry/BlueprintNode.hpp create mode 100644 Core/include/Acts/Geometry/BlueprintOptions.hpp create mode 100644 Core/include/Acts/Geometry/CylinderContainerBlueprintNode.hpp create mode 100644 Core/include/Acts/Geometry/LayerBlueprintNode.hpp create mode 100644 Core/include/Acts/Geometry/MaterialDesignatorBlueprintNode.hpp create mode 100644 Core/include/Acts/Geometry/StaticBlueprintNode.hpp create mode 100644 Core/src/Geometry/Blueprint.cpp create mode 100644 Core/src/Geometry/BlueprintNode.cpp create mode 100644 Core/src/Geometry/BlueprintOptions.cpp create mode 100644 Core/src/Geometry/CylinderContainerBlueprintNode.cpp create mode 100644 Core/src/Geometry/LayerBlueprintNode.cpp create mode 100644 Core/src/Geometry/MaterialDesignatorBlueprintNode.cpp create mode 100644 Core/src/Geometry/StaticBlueprintNode.cpp create mode 100644 Examples/Python/src/Blueprint.cpp create mode 100644 Examples/Python/tests/test_blueprint.py create mode 100755 Examples/Scripts/Python/blueprint.py create mode 100644 Tests/UnitTests/Core/Geometry/BlueprintApiTests.cpp create mode 100644 Tests/UnitTests/Core/Geometry/BlueprintTests.cpp create mode 100644 docs/core/geometry/concepts.md create mode 100644 docs/core/geometry/construction.md 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/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/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/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/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index b7e7a5b4112..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 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/Geometry.cpp b/Examples/Python/src/Geometry.cpp index 4c15e0dcf94..e9bbbfa350e 100644 --- a/Examples/Python/src/Geometry.cpp +++ b/Examples/Python/src/Geometry.cpp @@ -30,6 +30,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/ProtoLayer.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Geometry/VolumeBounds.hpp" @@ -91,6 +92,9 @@ struct IdentifierSurfacesCollector { } // namespace namespace Acts::Python { + +void addBlueprint(Context& ctx); + void addGeometry(Context& ctx) { auto m = ctx.get("main"); @@ -301,6 +305,8 @@ void addGeometry(Context& ctx) { .value("Gap", CylinderVolumeStack::ResizeStrategy::Gap) .value("Expand", CylinderVolumeStack::ResizeStrategy::Expand); } + + addBlueprint(ctx); } void addExperimentalGeometry(Context& ctx) { @@ -702,6 +708,15 @@ void addExperimentalGeometry(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::VolumeAssociationTest, mex, "VolumeAssociationTest", name, ntests, randomNumbers, randomRange, detector); + + 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/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/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/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/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/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.* From 08a3953b3197282ac8a1e0b98309193b7c166867 Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:47:19 +0100 Subject: [PATCH 15/24] fix: Ensure correct order of uniform distribution limits (#3976) Pulled out of #3759 ## Summary by CodeRabbit - **Bug Fixes** - Enhanced validation for direction generation parameters to ensure they are within acceptable ranges. - Adjusted calculations for cosine and eta values to align with the properties of the cosine function. - **Tests** - Modified random distribution ranges in unit tests for the `CylinderVolumeBuilder` to improve test coverage and accuracy. --- .../Acts/EventData/detail/GenerateParameters.hpp | 13 +++++++++---- .../Core/Geometry/CylinderVolumeBuilderTests.cpp | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Core/include/Acts/EventData/detail/GenerateParameters.hpp b/Core/include/Acts/EventData/detail/GenerateParameters.hpp index b06d853faa6..630878e7d4a 100644 --- a/Core/include/Acts/EventData/detail/GenerateParameters.hpp +++ b/Core/include/Acts/EventData/detail/GenerateParameters.hpp @@ -102,19 +102,24 @@ template inline std::pair generateBoundDirection( generator_t& rng, const GenerateBoundDirectionOptions& options) { using UniformReal = std::uniform_real_distribution; + assert(options.thetaMin >= 0.f); + assert(options.thetaMax <= std::numbers::pi); + assert(options.thetaMin <= options.thetaMax); // since we want to draw the direction uniform on the unit sphere, we must // draw from cos(theta) instead of theta. see e.g. // https://mathworld.wolfram.com/SpherePointPicking.html - double cosThetaMin = std::cos(options.thetaMin); + // Get cosThetaMin from thetaMax and vice versa, because cos is + // monothonical decreasing between [0, pi] + double cosThetaMin = std::cos(options.thetaMax); // ensure upper bound is included. see e.g. // https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution - double cosThetaMax = std::nextafter(std::cos(options.thetaMax), + double cosThetaMax = std::nextafter(std::cos(options.thetaMin), std::numeric_limits::max()); // in case we force uniform eta generation - double etaMin = Acts::AngleHelpers::etaFromTheta(options.thetaMin); - double etaMax = Acts::AngleHelpers::etaFromTheta(options.thetaMax); + double etaMin = Acts::AngleHelpers::etaFromTheta(options.thetaMax); + double etaMax = Acts::AngleHelpers::etaFromTheta(options.thetaMin); UniformReal phiDist(options.phiMin, options.phiMax); UniformReal cosThetaDist(cosThetaMin, cosThetaMax); diff --git a/Tests/UnitTests/Core/Geometry/CylinderVolumeBuilderTests.cpp b/Tests/UnitTests/Core/Geometry/CylinderVolumeBuilderTests.cpp index e7a96099bb0..9ab6f7392dc 100644 --- a/Tests/UnitTests/Core/Geometry/CylinderVolumeBuilderTests.cpp +++ b/Tests/UnitTests/Core/Geometry/CylinderVolumeBuilderTests.cpp @@ -22,7 +22,7 @@ BOOST_DATA_TEST_CASE( CylinderVolumeBuilder_wraps, bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1, bdata::distribution = - std::uniform_real_distribution(-11., -15.))) ^ + std::uniform_real_distribution(-15., -11.))) ^ bdata::random((bdata::engine = std::mt19937(), bdata::seed = 2, bdata::distribution = std::uniform_real_distribution(11., 15.))) ^ @@ -114,7 +114,7 @@ BOOST_DATA_TEST_CASE( CylinderVolumeBuilder_containes, bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1, bdata::distribution = - std::uniform_real_distribution(-11., -15.))) ^ + std::uniform_real_distribution(-15., -11.))) ^ bdata::random((bdata::engine = std::mt19937(), bdata::seed = 2, bdata::distribution = std::uniform_real_distribution(11., 15.))) ^ @@ -285,7 +285,7 @@ BOOST_DATA_TEST_CASE( CylinderVolumeBuilder_overlapsInZ, bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1, bdata::distribution = - std::uniform_real_distribution(-11., -15.))) ^ + std::uniform_real_distribution(-15., -11.))) ^ bdata::random((bdata::engine = std::mt19937(), bdata::seed = 2, bdata::distribution = std::uniform_real_distribution(11., 15.))) ^ From 93e3b27d35b751a47f239997d44dcf768638ecf2 Mon Sep 17 00:00:00 2001 From: Tim Adye Date: Wed, 11 Dec 2024 13:07:28 +0000 Subject: [PATCH 16/24] fix: update full_chain_test.py to follow full_chain_odd.py (#3956) * support `--output-obj` like #3180. Can also still use `-cc` to output all formats. * Fuse initial and final sim particles when `--edm4hep` input specified (sync with #3804) Next, we should remove the command-line options for `full_chain_odd.py`. ## Summary by CodeRabbit - **New Features** - Introduced a new `--output-obj` argument for enabling object file output. - **Improvements** - Updated help text for the `--output-csv` argument to clarify output options. - Enhanced output directory handling to support multiple output formats. --- Examples/Scripts/Python/full_chain_test.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Examples/Scripts/Python/full_chain_test.py b/Examples/Scripts/Python/full_chain_test.py index e7c945094f6..f09be753d46 100755 --- a/Examples/Scripts/Python/full_chain_test.py +++ b/Examples/Scripts/Python/full_chain_test.py @@ -81,7 +81,12 @@ def parse_args(): "--output-csv", action="count", default=0, - help="Use CSV output instead of ROOT. Specify -cc to output both.", + help="Use CSV output instead of ROOT. Specify -cc to output all formats (ROOT, CSV, and obj).", + ) + parser.add_argument( + "--output-obj", + action="store_true", + help="Enable obj output", ) parser.add_argument( "-n", @@ -272,6 +277,11 @@ def full_chain(args): outputDirCsv = outputDir if args.output_csv != 0 else None outputDirLessCsv = outputDirLess if args.output_csv != 0 else None outputDirMoreCsv = outputDirMore if args.output_csv != 0 else None + outputDirObj = ( + outputDirLess + if args.output_obj + else outputDir if args.output_csv == 2 else None + ) # fmt: off if args.generic_detector: @@ -392,8 +402,7 @@ def full_chain(args): "LongStripEndcapReadout", ], outputParticlesGenerator="particles_input", - outputParticlesInitial="particles_initial", - outputParticlesFinal="particles_final", + outputParticlesSimulation="particles_simulated", outputSimHits="simhits", graphvizOutput="graphviz", dd4hepDetector=detector, @@ -483,6 +492,7 @@ def full_chain(args): postSelectParticles=postSelectParticles, outputDirRoot=outputDirRoot, outputDirCsv=outputDirCsv, + outputDirObj=outputDirObj, ) else: if s.config.numThreads != 1: @@ -508,6 +518,7 @@ def full_chain(args): killAfterTime=25 * u.ns, outputDirRoot=outputDirRoot, outputDirCsv=outputDirCsv, + outputDirObj=outputDirObj, ) addDigitization( From 595f2e67788ad608381ac4c75db5377865d0f2dd Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:58:42 +0100 Subject: [PATCH 17/24] ci: Enable standard library assertions in debug builds (#3759) Should add assertions that check preconditions of calls to standardlibrary functions. ## Summary by CodeRabbit - **Chores** - Enhanced configuration for compiler flags to conditionally enable assertions. - Addressed compilation issues with GCC 13 by undefining specific preprocessor directives. - Maintained existing logic for compiler type checks without altering public entity signatures. - **Bug Fixes** - Adjusted the logic for generating random directions to ensure correct bounds for angles. - **Documentation** - Improved comments for clarity on the calculations in the random direction generation function. --- Core/src/Visualization/ObjVisualization3D.cpp | 2 +- Plugins/Json/src/DetrayJsonHelper.cpp | 7 +++++++ .../UnitTests/Core/Utilities/AlgebraHelpersTests.cpp | 11 +++-------- cmake/ActsCompilerOptions.cmake | 5 +++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Core/src/Visualization/ObjVisualization3D.cpp b/Core/src/Visualization/ObjVisualization3D.cpp index 777e883b3d3..6895ad56f03 100644 --- a/Core/src/Visualization/ObjVisualization3D.cpp +++ b/Core/src/Visualization/ObjVisualization3D.cpp @@ -63,7 +63,7 @@ void ObjVisualization3D::faces(const std::vector& vtxs, o.vertices.insert(o.vertices.end(), vtxs.begin(), vtxs.end()); for (const auto& face : faces) { if (face.size() == 2) { - o.lines.push_back({face[0] + vtxoffs, face[2] + vtxoffs}); + o.lines.push_back({face[0] + vtxoffs, face[1] + vtxoffs}); } else { FaceType rawFace; std::ranges::transform( diff --git a/Plugins/Json/src/DetrayJsonHelper.cpp b/Plugins/Json/src/DetrayJsonHelper.cpp index b04d3e62b11..d17e6cee1d5 100644 --- a/Plugins/Json/src/DetrayJsonHelper.cpp +++ b/Plugins/Json/src/DetrayJsonHelper.cpp @@ -6,6 +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/. +// For whatever reason, this compilation unit does not compile +// with those assertions and GCC 13. For now just disable the +// flags in this case. +#if defined(_GLIBCXX_ASSERTIONS) && __GNUC__ == 13 +#undef _GLIBCXX_ASSERTIONS +#endif + #include "Acts/Plugins/Json/DetrayJsonHelper.hpp" namespace Acts::DetrayJsonHelper { diff --git a/Tests/UnitTests/Core/Utilities/AlgebraHelpersTests.cpp b/Tests/UnitTests/Core/Utilities/AlgebraHelpersTests.cpp index 87e47d3e3a9..a821682d795 100644 --- a/Tests/UnitTests/Core/Utilities/AlgebraHelpersTests.cpp +++ b/Tests/UnitTests/Core/Utilities/AlgebraHelpersTests.cpp @@ -82,18 +82,15 @@ BOOST_AUTO_TEST_CASE(SafeInverseFPESmallMatrix) { m(1, 1) = 1; auto mInv = Acts::safeInverse(m); + BOOST_REQUIRE(mInv.has_value()); auto mInvInv = Acts::safeInverse(*mInv); - - BOOST_CHECK(mInv); BOOST_CHECK(!mInvInv); ACTS_VERBOSE("Test: SafeInverseFPESmallMatrix" << "\n" << "m:\n" << m << "\n" << "mInv:\n" - << *mInv << "\n" - << "mInvInv [garbage]:\n" - << *mInvInv); + << *mInv); } BOOST_AUTO_TEST_CASE(SafeInverseFPELargeMatrix) { @@ -107,9 +104,7 @@ BOOST_AUTO_TEST_CASE(SafeInverseFPELargeMatrix) { ACTS_VERBOSE("Test: SafeInverseFPELargeMatrix" << "\n" << "m:\n" - << m << "\n" - << "mInv [garbage]:\n" - << *mInv); + << m); } /// This test should not compile diff --git a/cmake/ActsCompilerOptions.cmake b/cmake/ActsCompilerOptions.cmake index 64467c64665..44c215ca8d5 100644 --- a/cmake/ActsCompilerOptions.cmake +++ b/cmake/ActsCompilerOptions.cmake @@ -13,6 +13,11 @@ set(cxx_flags "-Wall -Wextra -Wpedantic -Wshadow -Wzero-as-null-pointer-constant -Wold-style-cast" ) +# Add assertions to standard libraries +if(ACTS_FORCE_ASSERTIONS) + set(cxx_flags "${cxx_flags} -D_GLIBCXX_ASSERTIONS -D_LIBCPP_DEBUG") +endif() + # This adds some useful conversion checks like float-to-bool, float-to-int, etc. # However, at the moment this is only added to clang builds, since GCC's -Wfloat-conversion # is much more aggressive and also triggers on e.g., double-to-float From 72fc7cb3c886aac01e5e0edc74e449abc7d0b320 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 11 Dec 2024 18:52:19 +0100 Subject: [PATCH 18/24] chore: Update .gitignore (#3979) - Un-ignore the `.github` folder - Restrict the `build` exclude pattern to the repository root. --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c0a676728ea..dba5bbf393b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ genConf x86_64-slc6-gcc48-opt x86_64-slc6-gcc49-opt .* -build* +/build* .asetup.save #json output @@ -30,7 +30,7 @@ build* # dont ignore hidden configs !.clang-format -!.github/** +!.github !.gitignore !.gitlab-ci.yml !.clang-tidy From 2d5ed729db9cba874d560670f8f035400feb1fee Mon Sep 17 00:00:00 2001 From: Doga Elitez <108287101+delitez@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:57:18 +0100 Subject: [PATCH 19/24] fix: Update full_chain_odd_LRT.py to follow full_chain_odd.py (#3977) In this PR full_chain_odd_LRT.py is updated according to full_chain_odd.py. ## Summary by CodeRabbit - **New Features** - Introduced a command-line argument `--output-obj` to toggle object output. - **Enhancements** - Updated ambiguity resolution configuration to use mathematical constants for `phiMax` and `phiMin`. - Added new parameters for seeding and track selection to improve configurability and precision. - Changed the vertex fitting method to a more advanced algorithm. These updates enhance the script's functionality and improve the particle tracking and reconstruction processes. --- Examples/Scripts/Python/full_chain_odd_LRT.py | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/Examples/Scripts/Python/full_chain_odd_LRT.py b/Examples/Scripts/Python/full_chain_odd_LRT.py index 36a552af0c8..9c1f57074f6 100644 --- a/Examples/Scripts/Python/full_chain_odd_LRT.py +++ b/Examples/Scripts/Python/full_chain_odd_LRT.py @@ -3,6 +3,7 @@ import os import argparse import pathlib +import math import acts import acts.examples @@ -12,6 +13,7 @@ EtaConfig, PhiConfig, ParticleConfig, + ParticleSelectorConfig, addPythia8, addFatras, addGeant4, @@ -134,6 +136,12 @@ default=True, action=argparse.BooleanOptionalAction, ) +parser.add_argument( + "--output-obj", + help="Switch obj output on/off", + default=True, + action=argparse.BooleanOptionalAction, +) args = parser.parse_args() @@ -224,7 +232,6 @@ ParticleConfig( args.gun_particles, acts.PdgParticle.eMuon, randomizeCharge=True ), - outputDirRoot=pathlib.Path("/home/aicha/Atlas/NewVertices"), vtxGen=acts.examples.GaussianDisplacedVertexPositionGenerator( rMean=2, rStdDev=0.0125 * u.mm, @@ -278,6 +285,7 @@ ), outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, + outputDirObj=outputDir if args.output_obj else None, rnd=rnd, killVolume=trackingGeometry.highestTrackingVolume, killAfterTime=25 * u.ns, @@ -306,6 +314,7 @@ enableInteractions=True, outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, + outputDirObj=outputDir if args.output_obj else None, rnd=rnd, ) @@ -324,6 +333,16 @@ s, trackingGeometry, field, + initialSigmas=[ + 1 * u.mm, + 1 * u.mm, + 1 * u.degree, + 1 * u.degree, + 0.1 * u.e / u.GeV, + 1 * u.ns, + ], + initialSigmaPtRel=0.1, + initialVarInflation=[1.0] * 6, geoSelectionConfigFile=oddSeedingSel, outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, @@ -354,12 +373,32 @@ maxOutliers=2, ), CkfConfig( + chi2CutOffMeasurement=15.0, + chi2CutOffOutlier=25.0, + numMeasurementsCutOff=10, seedDeduplication=True, stayOnSeed=True, - pixelVolumes={16, 17, 18}, - stripVolumes={23, 24, 25}, + pixelVolumes=[16, 17, 18], + stripVolumes=[23, 24, 25], maxPixelHoles=1, maxStripHoles=2, + constrainToVolumes=[ + 2, # beam pipe + 32, + 4, # beam pip gap + 16, + 17, + 18, # pixel + 20, # PST + 23, + 24, + 25, # short strip + 26, + 8, # long strip gap + 28, + 29, + 30, # long strip + ], ), outputDirRoot=outputDir if args.output_root else None, outputDirCsv=outputDir if args.output_csv else None, @@ -388,8 +427,8 @@ maxSharedTracksPerMeasurement=2, pTMax=1400, pTMin=0.5, - phiMax=3.14, - phiMin=-3.14, + phiMax=math.pi, + phiMin=-math.pi, etaMax=4, etaMin=-4, useAmbiguityFunction=False, @@ -413,7 +452,7 @@ addVertexFitting( s, field, - vertexFinder=VertexFinder.Iterative, + vertexFinder=VertexFinder.AMVF, outputDirRoot=outputDir if args.output_root else None, ) From 9a2b74def57154015e846797923f611cfcc21281 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 13 Dec 2024 10:55:21 +0100 Subject: [PATCH 20/24] chore: Some magnetic field code cleanup (#3982) - cleanup includes - cleanup some `using namespace` pulled out of https://github.com/acts-project/acts/pull/3951 ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced new classes for enhanced magnetic field handling in Python. - Added methods for creating magnetic field maps from files. - **Bug Fixes** - Improved error handling in `getField` function to provide clearer feedback on failures. - **Documentation** - Enhanced comments for clarity in several files. - **Chores** - Removed unused include directives across multiple files to streamline code. --- .../Acts/MagneticField/InterpolatedBFieldMap.hpp | 1 - .../Acts/MagneticField/MagneticFieldProvider.hpp | 3 --- Core/include/Acts/Material/SurfaceMaterialMapper.hpp | 4 +--- Core/include/Acts/Navigation/DetectorNavigator.hpp | 5 ----- Core/include/Acts/Propagator/StraightLineStepper.hpp | 1 - Core/src/MagneticField/SolenoidBField.cpp | 1 - Core/src/Propagator/SympyStepper.cpp | 1 - Core/src/Vertexing/HelicalTrackLinearizer.cpp | 1 - .../include/ActsExamples/Io/Root/RootBFieldWriter.hpp | 10 ---------- Examples/Io/Root/src/RootBFieldWriter.cpp | 9 +++------ Examples/Python/src/MagneticField.cpp | 1 - .../Core/MagneticField/ConstantBFieldTests.cpp | 2 -- .../Core/MagneticField/InterpolatedBFieldMapTests.cpp | 5 ----- .../Core/MagneticField/SolenoidBFieldTests.cpp | 1 - Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp | 7 ------- Tests/UnitTests/Core/Utilities/BFieldMapUtilsTests.cpp | 1 - 16 files changed, 4 insertions(+), 49 deletions(-) diff --git a/Core/include/Acts/MagneticField/InterpolatedBFieldMap.hpp b/Core/include/Acts/MagneticField/InterpolatedBFieldMap.hpp index d65f8113d9e..92cdc0090c7 100644 --- a/Core/include/Acts/MagneticField/InterpolatedBFieldMap.hpp +++ b/Core/include/Acts/MagneticField/InterpolatedBFieldMap.hpp @@ -12,7 +12,6 @@ #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/MagneticField/MagneticFieldError.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" -#include "Acts/Utilities/Grid.hpp" #include "Acts/Utilities/Interpolation.hpp" #include "Acts/Utilities/Result.hpp" diff --git a/Core/include/Acts/MagneticField/MagneticFieldProvider.hpp b/Core/include/Acts/MagneticField/MagneticFieldProvider.hpp index 068c2ed8e33..44596b73d6e 100644 --- a/Core/include/Acts/MagneticField/MagneticFieldProvider.hpp +++ b/Core/include/Acts/MagneticField/MagneticFieldProvider.hpp @@ -13,9 +13,6 @@ #include "Acts/Utilities/Any.hpp" #include "Acts/Utilities/Result.hpp" -#include -#include - namespace Acts { /// @defgroup MagneticField Magnetic field diff --git a/Core/include/Acts/Material/SurfaceMaterialMapper.hpp b/Core/include/Acts/Material/SurfaceMaterialMapper.hpp index 305d293542c..1fece817478 100644 --- a/Core/include/Acts/Material/SurfaceMaterialMapper.hpp +++ b/Core/include/Acts/Material/SurfaceMaterialMapper.hpp @@ -18,12 +18,9 @@ #include "Acts/Material/AccumulatedSurfaceMaterial.hpp" #include "Acts/Material/ISurfaceMaterial.hpp" #include "Acts/Material/MaterialInteraction.hpp" -#include "Acts/Propagator/MaterialInteractor.hpp" #include "Acts/Propagator/Navigator.hpp" #include "Acts/Propagator/Propagator.hpp" #include "Acts/Propagator/StraightLineStepper.hpp" -#include "Acts/Propagator/SurfaceCollector.hpp" -#include "Acts/Propagator/VolumeCollector.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" @@ -225,4 +222,5 @@ class SurfaceMaterialMapper { /// The logging instance std::unique_ptr m_logger; }; + } // namespace Acts diff --git a/Core/include/Acts/Navigation/DetectorNavigator.hpp b/Core/include/Acts/Navigation/DetectorNavigator.hpp index 3bc324e187f..56f588dfb47 100644 --- a/Core/include/Acts/Navigation/DetectorNavigator.hpp +++ b/Core/include/Acts/Navigation/DetectorNavigator.hpp @@ -8,24 +8,19 @@ #pragma once -#include "Acts/Definitions/Units.hpp" #include "Acts/Detector/Detector.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/Portal.hpp" -#include "Acts/Geometry/BoundarySurfaceT.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Geometry/Layer.hpp" #include "Acts/Navigation/NavigationState.hpp" #include "Acts/Propagator/NavigatorOptions.hpp" #include "Acts/Propagator/NavigatorStatistics.hpp" -#include "Acts/Propagator/Propagator.hpp" #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" #include -#include -#include #include #include diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp index 1227397fefb..f43728e7802 100644 --- a/Core/include/Acts/Propagator/StraightLineStepper.hpp +++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp @@ -32,7 +32,6 @@ #include "Acts/Utilities/MathHelpers.hpp" #include "Acts/Utilities/Result.hpp" -#include #include #include #include diff --git a/Core/src/MagneticField/SolenoidBField.cpp b/Core/src/MagneticField/SolenoidBField.cpp index 6fefe017b38..44cb12d3403 100644 --- a/Core/src/MagneticField/SolenoidBField.cpp +++ b/Core/src/MagneticField/SolenoidBField.cpp @@ -10,7 +10,6 @@ #include "Acts/Utilities/VectorHelpers.hpp" -#include #include #include diff --git a/Core/src/Propagator/SympyStepper.cpp b/Core/src/Propagator/SympyStepper.cpp index d55c073a851..b209d395a42 100644 --- a/Core/src/Propagator/SympyStepper.cpp +++ b/Core/src/Propagator/SympyStepper.cpp @@ -12,7 +12,6 @@ #include "Acts/Propagator/detail/SympyJacobianEngine.hpp" #include -#include #include "codegen/sympy_stepper_math.hpp" diff --git a/Core/src/Vertexing/HelicalTrackLinearizer.cpp b/Core/src/Vertexing/HelicalTrackLinearizer.cpp index e42bc9d0aad..bc12feacbcb 100644 --- a/Core/src/Vertexing/HelicalTrackLinearizer.cpp +++ b/Core/src/Vertexing/HelicalTrackLinearizer.cpp @@ -9,7 +9,6 @@ #include "Acts/Vertexing/HelicalTrackLinearizer.hpp" #include "Acts/Propagator/PropagatorOptions.hpp" -#include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Utilities/MathHelpers.hpp" #include "Acts/Vertexing/LinearizerTrackParameters.hpp" diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootBFieldWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootBFieldWriter.hpp index a8685f809c9..109fbf7888e 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootBFieldWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootBFieldWriter.hpp @@ -8,29 +8,19 @@ #pragma once -#include "Acts/Definitions/Units.hpp" #include "Acts/MagneticField/InterpolatedBFieldMap.hpp" #include "Acts/Utilities/Logger.hpp" -#include "ActsExamples/Framework/ProcessCode.hpp" #include #include -#include #include -#include #include -#include -#include #include #include #include #include -namespace Acts { -class InterpolatedMagneticField; -} // namespace Acts - namespace ActsExamples { /// @class RootBFieldWriter diff --git a/Examples/Io/Root/src/RootBFieldWriter.cpp b/Examples/Io/Root/src/RootBFieldWriter.cpp index 894636d61c9..bb1d2f2e3b7 100644 --- a/Examples/Io/Root/src/RootBFieldWriter.cpp +++ b/Examples/Io/Root/src/RootBFieldWriter.cpp @@ -10,7 +10,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/MagneticField/InterpolatedBFieldMap.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Utilities/VectorHelpers.hpp" @@ -30,9 +29,6 @@ namespace ActsExamples { void RootBFieldWriter::run(const Config& config, std::unique_ptr p_logger) { // Set up (local) logging - // @todo Remove dangerous using declaration once the logger macro - // tolerates it - using namespace Acts; ACTS_LOCAL_LOGGER(std::move(p_logger)) Acts::MagneticFieldContext bFieldContext; @@ -179,7 +175,7 @@ void RootBFieldWriter::run(const Config& config, for (std::size_t k = 0; k < nBinsZ; k++) { double raw_z = minZ + k * stepZ; Acts::Vector3 position(raw_x, raw_y, raw_z); - Vector3 bField = config.bField->getFieldUnchecked(position); + Acts::Vector3 bField = config.bField->getFieldUnchecked(position); x = raw_x / Acts::UnitConstants::mm; y = raw_y / Acts::UnitConstants::mm; @@ -268,7 +264,7 @@ void RootBFieldWriter::run(const Config& config, z = raw_z / Acts::UnitConstants::mm; r = raw_r / Acts::UnitConstants::mm; Bz = bField.z() / Acts::UnitConstants::T; - Br = VectorHelpers::perp(bField) / Acts::UnitConstants::T; + Br = Acts::VectorHelpers::perp(bField) / Acts::UnitConstants::T; outputTree->Fill(); } // for R } // for z @@ -278,4 +274,5 @@ void RootBFieldWriter::run(const Config& config, ACTS_INFO("Closing and Writing ROOT output File : " << config.fileName); outputTree->Write(); } + } // namespace ActsExamples diff --git a/Examples/Python/src/MagneticField.cpp b/Examples/Python/src/MagneticField.cpp index 15463386402..93d69f1a4b8 100644 --- a/Examples/Python/src/MagneticField.cpp +++ b/Examples/Python/src/MagneticField.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/Tests/UnitTests/Core/MagneticField/ConstantBFieldTests.cpp b/Tests/UnitTests/Core/MagneticField/ConstantBFieldTests.cpp index f6d8486e054..d7afdd11555 100644 --- a/Tests/UnitTests/Core/MagneticField/ConstantBFieldTests.cpp +++ b/Tests/UnitTests/Core/MagneticField/ConstantBFieldTests.cpp @@ -15,8 +15,6 @@ #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Utilities/Result.hpp" -#include - namespace bdata = boost::unit_test::data; using namespace Acts::UnitLiterals; diff --git a/Tests/UnitTests/Core/MagneticField/InterpolatedBFieldMapTests.cpp b/Tests/UnitTests/Core/MagneticField/InterpolatedBFieldMapTests.cpp index 1671aa7a429..9e246b6c326 100644 --- a/Tests/UnitTests/Core/MagneticField/InterpolatedBFieldMapTests.cpp +++ b/Tests/UnitTests/Core/MagneticField/InterpolatedBFieldMapTests.cpp @@ -11,22 +11,17 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/MagneticField/InterpolatedBFieldMap.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" -#include "Acts/MagneticField/MagneticFieldProvider.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/Axis.hpp" #include "Acts/Utilities/AxisFwd.hpp" #include "Acts/Utilities/Grid.hpp" #include "Acts/Utilities/Result.hpp" #include "Acts/Utilities/VectorHelpers.hpp" -#include "Acts/Utilities/detail/grid_helper.hpp" #include #include -#include #include -#include #include -#include using Acts::VectorHelpers::perp; diff --git a/Tests/UnitTests/Core/MagneticField/SolenoidBFieldTests.cpp b/Tests/UnitTests/Core/MagneticField/SolenoidBFieldTests.cpp index 885e9ee59ec..05d2fb6dd8b 100644 --- a/Tests/UnitTests/Core/MagneticField/SolenoidBFieldTests.cpp +++ b/Tests/UnitTests/Core/MagneticField/SolenoidBFieldTests.cpp @@ -16,7 +16,6 @@ #include "Acts/Utilities/Result.hpp" #include -#include using namespace Acts::UnitLiterals; diff --git a/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp b/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp index e8171faf004..46cfdcc9065 100644 --- a/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp @@ -10,7 +10,6 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" @@ -23,25 +22,19 @@ #include "Acts/Propagator/MaterialInteractor.hpp" #include "Acts/Propagator/Navigator.hpp" #include "Acts/Propagator/Propagator.hpp" -#include "Acts/Propagator/StandardAborters.hpp" #include "Acts/Propagator/SurfaceCollector.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Tests/CommonHelpers/CylindricalTrackingGeometry.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/Result.hpp" -#include -#include #include #include -#include #include #include #include #include -#include #include -#include namespace bdata = boost::unit_test::data; using namespace Acts::UnitLiterals; diff --git a/Tests/UnitTests/Core/Utilities/BFieldMapUtilsTests.cpp b/Tests/UnitTests/Core/Utilities/BFieldMapUtilsTests.cpp index 5dd21ff38de..e175e23466c 100644 --- a/Tests/UnitTests/Core/Utilities/BFieldMapUtilsTests.cpp +++ b/Tests/UnitTests/Core/Utilities/BFieldMapUtilsTests.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include namespace bdata = boost::unit_test::data; From 1960d7a6b305a19cdab9fa69b0de67cf7166d547 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 16 Dec 2024 11:03:36 +0100 Subject: [PATCH 21/24] refactor: Combine GSF actor and aborter (#3984) Currently the GSF actor communicates the abort flag via the navigation break which is not ideal. I want to remove setting this flag from outside in https://github.com/acts-project/acts/pull/3449. The GSF can carry this flag by itself and after the actor+aborter refactor we do not need a separate aborter anymore. pulled out of https://github.com/acts-project/acts/pull/3449 ## Summary by CodeRabbit - **New Features** - Introduced a new method to check if navigation should be aborted based on processed measurements. - **Improvements** - Updated navigation handling in the fitting process by removing the `NavigationBreakAborter` struct. - Refined geo context management in multiple methods for better clarity and consistency. --- .../Acts/TrackFitting/GaussianSumFitter.hpp | 19 ++----------------- .../Acts/TrackFitting/detail/GsfActor.hpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp index 4efcd82e4c4..fcada49abc2 100644 --- a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp +++ b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp @@ -77,20 +77,6 @@ struct GaussianSumFitter { /// The actor type using GsfActor = detail::GsfActor; - /// This allows to break the propagation by setting the navigationBreak - /// TODO refactor once we can do this more elegantly - struct NavigationBreakAborter { - NavigationBreakAborter() = default; - - template - bool checkAbort(propagator_state_t& state, const stepper_t& /*stepper*/, - const navigator_t& navigator, - const Logger& /*logger*/) const { - return navigator.navigationBreak(state.navigation); - } - }; - /// @brief The fit function for the Direct navigator template @@ -105,7 +91,7 @@ struct GaussianSumFitter { // Initialize the forward propagation with the DirectNavigator auto fwdPropInitializer = [&sSequence, this](const auto& opts) { - using Actors = ActorList; + using Actors = ActorList; using PropagatorOptions = typename propagator_t::template Options; PropagatorOptions propOptions(opts.geoContext, opts.magFieldContext); @@ -151,8 +137,7 @@ struct GaussianSumFitter { // Initialize the forward propagation with the DirectNavigator auto fwdPropInitializer = [this](const auto& opts) { - using Actors = - ActorList; + using Actors = ActorList; using PropagatorOptions = typename propagator_t::template Options; PropagatorOptions propOptions(opts.geoContext, opts.magFieldContext); diff --git a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp index 6d5ac68d8b9..3c5d602acdb 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp @@ -317,13 +317,20 @@ struct GsfActor { applyMultipleScattering(state, stepper, navigator, MaterialUpdateStage::PostUpdate); } + } - // Break the navigation if we found all measurements + template + bool checkAbort(propagator_state_t& /*state*/, const stepper_t& /*stepper*/, + const navigator_t& /*navigator*/, const result_type& result, + const Logger& /*logger*/) const { if (m_cfg.numberMeasurements && result.measurementStates == m_cfg.numberMeasurements) { ACTS_VERBOSE("Stop navigation because all measurements are found"); - navigator.navigationBreak(state.navigation, true); + return true; } + + return false; } template Date: Mon, 16 Dec 2024 12:38:22 +0100 Subject: [PATCH 22/24] chore: clean codespell ignore list (#3961) - remove duplicates (2x `dthe`) - remove outdated (`compres`) - sort alphabetically ## Summary by CodeRabbit - **Chores** - Updated the list of terms ignored by the spell checker to enhance code quality. New terms have been added, and outdated ones removed. --- CI/codespell_ignore.txt | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/CI/codespell_ignore.txt b/CI/codespell_ignore.txt index e6ab8123413..ca114b4f26e 100644 --- a/CI/codespell_ignore.txt +++ b/CI/codespell_ignore.txt @@ -1,24 +1,21 @@ +aline +ans +boxs bu -strack -compres coner +dependees dthe -iself -sortings +exprot gaus -te -parm -writet -localy +iself lastr -exprot +localy +millepede +parm +pixelx pring -aline -boxs -ans -dthe -dthe +sortings +strack +te vart -pixelx -millepede -dependees +writet From ba4cc8c8eb8c4aab5d97f1b1fd68308c64ebfbe5 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:00:07 +0100 Subject: [PATCH 23/24] refactor(fatras): remove nuclear interaction (#3935) This seems to be unused. --- .../ActsExamples/Fatras/FatrasSimulation.hpp | 3 - Examples/Python/src/ExampleAlgorithms.cpp | 9 +- Fatras/CMakeLists.txt | 1 - .../NuclearInteraction/NuclearInteraction.hpp | 540 ------------------ .../NuclearInteractionParameters.hpp | 106 ---- .../NuclearInteraction/NuclearInteraction.cpp | 165 ------ 6 files changed, 4 insertions(+), 820 deletions(-) delete mode 100644 Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp delete mode 100644 Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteractionParameters.hpp delete mode 100644 Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp diff --git a/Examples/Algorithms/Fatras/include/ActsExamples/Fatras/FatrasSimulation.hpp b/Examples/Algorithms/Fatras/include/ActsExamples/Fatras/FatrasSimulation.hpp index 75395218a9e..8e3d891d3b5 100644 --- a/Examples/Algorithms/Fatras/include/ActsExamples/Fatras/FatrasSimulation.hpp +++ b/Examples/Algorithms/Fatras/include/ActsExamples/Fatras/FatrasSimulation.hpp @@ -18,7 +18,6 @@ #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/RandomNumbers.hpp" #include "ActsExamples/MagneticField/MagneticField.hpp" -#include "ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp" #include #include @@ -48,8 +47,6 @@ class FatrasSimulation final : public IAlgorithm { /// The simulated hits output collection. std::string outputSimHits; /// Parametrisation of nuclear interaction - std::string imputParametrisationNuclearInteraction = - "nuclearInteractionParameters"; /// Random number service. std::shared_ptr randomNumbers; /// The tracking geometry that should be used. diff --git a/Examples/Python/src/ExampleAlgorithms.cpp b/Examples/Python/src/ExampleAlgorithms.cpp index 411973d147e..863059d78b3 100644 --- a/Examples/Python/src/ExampleAlgorithms.cpp +++ b/Examples/Python/src/ExampleAlgorithms.cpp @@ -35,11 +35,10 @@ void addExampleAlgorithms(Context& ctx) { ACTS_PYTHON_DECLARE_ALGORITHM( ActsExamples::FatrasSimulation, mex, "FatrasSimulation", inputParticles, - outputParticles, outputSimHits, imputParametrisationNuclearInteraction, - randomNumbers, trackingGeometry, magneticField, pMin, emScattering, - emEnergyLossIonisation, emEnergyLossRadiation, emPhotonConversion, - generateHitsOnSensitive, generateHitsOnMaterial, generateHitsOnPassive, - averageHitsPerParticle); + outputParticles, outputSimHits, randomNumbers, trackingGeometry, + magneticField, pMin, emScattering, emEnergyLossIonisation, + emEnergyLossRadiation, emPhotonConversion, generateHitsOnSensitive, + generateHitsOnMaterial, generateHitsOnPassive, averageHitsPerParticle); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::ParticlesPrinter, mex, "ParticlesPrinter", inputParticles); diff --git a/Fatras/CMakeLists.txt b/Fatras/CMakeLists.txt index 4e998b1ea17..bdb54e93a7f 100644 --- a/Fatras/CMakeLists.txt +++ b/Fatras/CMakeLists.txt @@ -10,7 +10,6 @@ add_library( src/EventData/ProcessType.cpp src/Kernel/SimulationError.cpp src/Physics/BetheHeitler.cpp - src/Physics/NuclearInteraction/NuclearInteraction.cpp src/Physics/PhotonConversion.cpp src/Physics/StandardInteractions.cpp src/Utilities/LandauDistribution.cpp diff --git a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp b/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp deleted file mode 100644 index d0a0d84c36c..00000000000 --- a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp +++ /dev/null @@ -1,540 +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/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" -#include "Acts/Definitions/PdgParticle.hpp" -#include "Acts/Material/MaterialSlab.hpp" -#include "Acts/Utilities/UnitVectors.hpp" -#include "Acts/Utilities/VectorHelpers.hpp" -#include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Particle.hpp" -#include "ActsFatras/EventData/ProcessType.hpp" -#include "ActsFatras/Physics/NuclearInteraction/NuclearInteractionParameters.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ActsFatras { - -/// This class provides a parametrised nuclear interaction. The thereby -/// required parametrisation needs to be set and is not provided by default. -/// -/// @note This class differs between two different processes labelled as nuclear -/// interaction. Either the initial particle survives (soft) or it gets -/// destroyed (hard) by this process. -struct NuclearInteraction { - /// The storage of the parameterisation - detail::MultiParticleNuclearInteractionParametrisation - multiParticleParameterisation; - /// The number of trials to match momenta and invariant masses - //~ unsigned int nMatchingTrials = std::numeric_limits::max(); - unsigned int nMatchingTrials = 100; - unsigned int nMatchingTrialsTotal = 1000; - - /// This method evaluates the nuclear interaction length L0. - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] particle The ingoing particle - /// - /// @return valid X0 limit and no limit on L0 - template - std::pair generatePathLimits(generator_t& generator, - const Particle& particle) const { - // Fast exit: No parameterisation provided - if (multiParticleParameterisation.empty()) { - return {std::numeric_limits::infinity(), - std::numeric_limits::infinity()}; - } - // Find the parametrisation that corresponds to the particle type - for (const auto& particleParametrisation : multiParticleParameterisation) { - if (particleParametrisation.first == particle.pdg()) { - std::uniform_real_distribution uniformDistribution{0., 1.}; - - // Get the parameters - const detail::NuclearInteractionParametrisation& - singleParticleParametrisation = particleParametrisation.second; - const detail::NuclearInteractionParameters& parametrisation = - findParameters(uniformDistribution(generator), - singleParticleParametrisation, - particle.absoluteMomentum()); - - // Set the L0 limit if not done already - const auto& distribution = - parametrisation.nuclearInteractionProbability; - return {std::numeric_limits::infinity(), - sampleContinuousValues(uniformDistribution(generator), - distribution)}; - } - } - return {std::numeric_limits::infinity(), - std::numeric_limits::infinity()}; - } - - /// This method performs a nuclear interaction. - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in, out] particle The ingoing particle - /// @param [out] generated Additional generated particles - /// - /// @return True if the particle was killed, false otherwise - template - bool run(generator_t& generator, Particle& particle, - std::vector& generated) const { - // Fast exit: No paramtrization provided - if (multiParticleParameterisation.empty()) { - return false; - } - - // Find the parametrisation that corresponds to the particle type - for (const auto& particleParametrisation : multiParticleParameterisation) { - if (particleParametrisation.first == particle.pdg()) { - std::uniform_real_distribution uniformDistribution{0., 1.}; - - // Get the parameters - const detail::NuclearInteractionParametrisation& - singleParticleParametrisation = particleParametrisation.second; - const detail::NuclearInteractionParameters& parametrisation = - findParameters(uniformDistribution(generator), - singleParticleParametrisation, - particle.absoluteMomentum()); - - std::normal_distribution normalDistribution{0., 1.}; - // Dice the interaction type - const bool interactSoft = - softInteraction(normalDistribution(generator), - parametrisation.softInteractionProbability); - - // Get the final state multiplicity - const unsigned int multiplicity = finalStateMultiplicity( - uniformDistribution(generator), - interactSoft ? parametrisation.softMultiplicity - : parametrisation.hardMultiplicity); - - // Get the parameters for the kinematics - const std::vector& - parametrisationOfType = - interactSoft ? parametrisation.softKinematicParameters - : parametrisation.hardKinematicParameters; - const detail::NuclearInteractionParameters:: - ParametersWithFixedMultiplicity& parametrisationOfMultiplicity = - parametrisationOfType[multiplicity]; - if (!parametrisationOfMultiplicity.validParametrisation) { - return false; - } - - // Get the kinematics - const auto kinematics = sampleKinematics( - generator, parametrisationOfMultiplicity, parametrisation.momentum); - if (!kinematics.has_value()) { - return run(generator, particle, generated); - } - - // Get the particle types - const std::vector pdgIds = - samplePdgIds(generator, parametrisation.pdgMap, multiplicity, - particle.pdg(), interactSoft); - - // Construct the particles - const auto particles = convertParametersToParticles( - generator, pdgIds, kinematics->first, kinematics->second, particle, - parametrisation.momentum, interactSoft); - - // Kill the particle in a hard process - if (!interactSoft) { - particle.setAbsoluteMomentum(0); - } - - generated.insert(generated.end(), particles.begin(), particles.end()); - return !interactSoft; - } - } - // Fast exit if no parametrisation for the particle was provided - return false; - } - - private: - /// Retrieves the parametrisation for the particle - /// - /// @param [in] rnd A random number - /// @param [in] parametrisation The storage of parametrisations - /// @param [in] particleMomentum The particles momentum - /// - /// @return The parametrisation - const detail::NuclearInteractionParameters& findParameters( - double rnd, - const detail::NuclearInteractionParametrisation& parametrisation, - float particleMomentum) const; - - /// Estimates the interaction type - /// - /// @param [in] rnd Random number - /// @param [in] probability The probability for a soft interaction - /// - /// @return True if a soft interaction occurs - inline bool softInteraction(double rnd, float probability) const { - return rnd <= probability; - } - - /// Evaluates the multiplicity of the final state - /// - /// @param [in] rnd Random number - /// @param [in] distribution The multiplicity distribution - /// - /// @return The final state multiplicity - unsigned int finalStateMultiplicity( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution) const; - - /// Evaluates the final state PDG IDs - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] pdgMap The branching probability map - /// @param [in] multiplicity The final state multiplicity - /// @param [in] particlePdg The PDG ID of the initial particle - /// @param [in] soft Treat it as soft or hard nuclear interaction - /// - /// @return Vector containing the PDG IDs - template - std::vector samplePdgIds( - generator_t& generator, - const detail::NuclearInteractionParameters::PdgMap& pdgMap, - unsigned int multiplicity, int particlePdg, bool soft) const; - - /// Evaluates the final state invariant masses - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] parametrisation Parametrisation of kinematic properties - /// - /// @return Vector containing the invariant masses - template - Acts::ActsDynamicVector sampleInvariantMasses( - generator_t& generator, - const detail::NuclearInteractionParameters:: - ParametersWithFixedMultiplicity& parametrisation) const; - - /// Evaluates the final state momenta - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] parametrisation Parametrisation of kinematic properties - /// @param [in] initialMomentum The initial momentum - /// - /// @return Vector containing the momenta - template - Acts::ActsDynamicVector sampleMomenta( - generator_t& generator, - const detail::NuclearInteractionParameters:: - ParametersWithFixedMultiplicity& parametrisation, - float initialMomentum) const; - - /// Tests whether the final state momenta and invariant masses are - /// matching to each other to allow the evaluation of particle directions. - /// - /// @param [in] momenta The final state momenta - /// @param [in] invariantMasses The final state invariant masses - /// @param [in] parametrizedMomentum The momentum of the parametrized particle - /// - /// @return Decision whether the parameters can be matched to each other or - /// not. - bool match(const Acts::ActsDynamicVector& momenta, - const Acts::ActsDynamicVector& invariantMasses, - float parametrizedMomentum) const; - - /// This method samples the kinematics of the final state particles - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] parameters The parametrisation - /// @param [in] momentum The momentum of the parametrisation - /// - /// @return The final state momenta and invariant masses - template - std::optional> - sampleKinematics(generator_t& generator, - const detail::NuclearInteractionParameters:: - ParametersWithFixedMultiplicity& parameters, - float momentum) const; - - /// Converts relative angles to absolute angles wrt the global - /// coordinate system. - /// @note It is assumed that the angles of the first particle are provided in - /// the context of the global coordinate system whereas the angles of the - /// second particle are provided relatively to the first particle. - /// - /// @param [in] phi1 The azimuthal angle of the first particle - /// @param [in] theta1 The polar angle of the first particle - /// @param [in] phi2 The azimuthal angle of the second particle - /// @param [in] theta2 The polar angle of the second particle - /// - /// @return Azimuthal and polar angle of the second particle in the global - /// coordinate system - std::pair globalAngle(double phi1, double theta1, float phi2, - float theta2) const; - - /// Converter from sampled numbers to a vector of particles - /// - /// @tparam generator_t The random number generator type - /// @param [in, out] generator The random number generator - /// @param [in] pdgId The PDG IDs - /// @param [in] momenta The momenta - /// @param [in] invariantMasses The invariant masses - /// @param [in] initialParticle The initial particle - /// @param [in] parametrizedMomentum Momentum of the parametrisation - /// @param [in] soft Treat it as soft or hard nuclear interaction - /// - /// @return Vector containing the final state particles - template - std::vector convertParametersToParticles( - generator_t& generator, const std::vector& pdgId, - const Acts::ActsDynamicVector& momenta, - const Acts::ActsDynamicVector& invariantMasses, Particle& initialParticle, - float parametrizedMomentum, bool soft) const; - - /// This function performs an inverse sampling to provide a discrete - /// value from a distribution. - /// - /// @param [in] rnd A random number in [0,1] - /// @param [in] distribution The distribution to sample from - /// - /// @return The sampled value - unsigned int sampleDiscreteValues( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution) const; - - /// This function performs an inverse sampling to provide a continuous - /// value from a distribition. - /// - /// @param [in] rnd A random number in [0,1] - /// @param [in] distribution The distribution to sample from - /// @param [in] interpolate Flag to steer whether an interpolation between - /// neighbouring bins should be performed instead of a bin lookup - /// - /// @return The sampled value - double sampleContinuousValues( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution, - bool interpolate = false) const; -}; - -template -std::vector NuclearInteraction::samplePdgIds( - generator_t& generator, - const detail::NuclearInteractionParameters::PdgMap& pdgMap, - unsigned int multiplicity, int particlePdg, bool soft) const { - // Fast exit in case of no final state particles - if (multiplicity == 0) { - return {}; - } - - // The final state PDG IDs - std::vector pdgIds; - pdgIds.reserve(multiplicity); - - std::uniform_real_distribution uniformDistribution{0., 1.}; - - // Find the producers probability distribution - auto citProducer = pdgMap.cbegin(); - while (citProducer->first != particlePdg && citProducer != pdgMap.end()) { - citProducer++; - } - - const std::vector>& mapInitial = citProducer->second; - // Set the first particle depending on the interaction type - if (soft) { - // Store the initial particle if the interaction is soft - pdgIds.push_back(particlePdg); - } else { - // Otherwise dice the particle - const float rndInitial = uniformDistribution(generator); - - pdgIds.push_back( - std::lower_bound(mapInitial.begin(), mapInitial.end(), rndInitial, - [](const std::pair& element, - float random) { return element.second < random; }) - ->first); - } - - // Set the remaining particles - for (unsigned int i = 1; i < multiplicity; i++) { - // Find the producers probability distribution from the last produced - // particle - citProducer = pdgMap.cbegin(); - while (citProducer->first != pdgIds[i - 1] && citProducer != pdgMap.end()) { - citProducer++; - } - - // Set the next particle - const std::vector>& map = citProducer->second; - const float rnd = uniformDistribution(generator); - pdgIds.push_back( - std::lower_bound(map.begin(), map.end(), rnd, - [](const std::pair& element, - float random) { return element.second < random; }) - ->first); - } - return pdgIds; -} - -template -Acts::ActsDynamicVector NuclearInteraction::sampleInvariantMasses( - generator_t& generator, - const detail::NuclearInteractionParameters::ParametersWithFixedMultiplicity& - parametrisation) const { - // The resulting vector - Acts::ActsDynamicVector parameters; - const unsigned int size = parametrisation.eigenvaluesInvariantMass.size(); - parameters.resize(size); - - // Sample in the eigenspace - for (unsigned int i = 0; i < size; i++) { - float variance = parametrisation.eigenvaluesInvariantMass[i]; - std::normal_distribution dist{parametrisation.meanInvariantMass[i], - std::sqrt(variance)}; - parameters[i] = dist(generator); - } - // Transform to multivariate normal distribution - parameters = parametrisation.eigenvectorsInvariantMass * parameters; - - // Perform the inverse sampling from the distributions - for (unsigned int i = 0; i < size; i++) { - const double cdf = (std::erff(parameters[i]) + 1) * 0.5; - parameters[i] = sampleContinuousValues( - cdf, parametrisation.invariantMassDistributions[i]); - } - return parameters; -} - -template -Acts::ActsDynamicVector NuclearInteraction::sampleMomenta( - generator_t& generator, - const detail::NuclearInteractionParameters::ParametersWithFixedMultiplicity& - parametrisation, - float initialMomentum) const { - // The resulting vector - Acts::ActsDynamicVector parameters; - const unsigned int size = parametrisation.eigenvaluesMomentum.size(); - parameters.resize(size); - - // Sample in the eigenspace - for (unsigned int i = 0; i < size; i++) { - float variance = parametrisation.eigenvaluesMomentum[i]; - std::normal_distribution dist{parametrisation.meanMomentum[i], - std::sqrt(variance)}; - parameters[i] = dist(generator); - } - - // Transform to multivariate normal distribution - parameters = parametrisation.eigenvectorsMomentum * parameters; - - // Perform the inverse sampling from the distributions - for (unsigned int i = 0; i < size; i++) { - const float cdf = (std::erff(parameters[i]) + 1) * 0.5; - parameters[i] = - sampleContinuousValues(cdf, parametrisation.momentumDistributions[i]); - } - - // Scale the momenta - Acts::ActsDynamicVector momenta = parameters.head(size - 1); - const float sum = momenta.sum(); - const float scale = parameters.template tail<1>()(0, 0) / sum; - momenta *= scale * initialMomentum; - return momenta; -} - -template -std::optional> -NuclearInteraction::sampleKinematics( - generator_t& generator, - const detail::NuclearInteractionParameters::ParametersWithFixedMultiplicity& - parameters, - float momentum) const { - unsigned int trials = 0; - Acts::ActsDynamicVector invariantMasses = - sampleInvariantMasses(generator, parameters); - Acts::ActsDynamicVector momenta = - sampleMomenta(generator, parameters, momentum); - // Repeat momentum evaluation until the parameters match - while (!match(momenta, invariantMasses, momentum)) { - if (trials == nMatchingTrialsTotal) { - return std::nullopt; - } - // 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::pair(momenta, invariantMasses); -} - -template -std::vector NuclearInteraction::convertParametersToParticles( - generator_t& generator, const std::vector& pdgId, - const Acts::ActsDynamicVector& momenta, - const Acts::ActsDynamicVector& invariantMasses, Particle& initialParticle, - float parametrizedMomentum, bool soft) const { - std::uniform_real_distribution uniformDistribution{0., 1.}; - const auto& initialDirection = initialParticle.direction(); - const double phi = Acts::VectorHelpers::phi(initialDirection); - const double theta = Acts::VectorHelpers::theta(initialDirection); - const unsigned int size = momenta.size(); - - std::vector result; - result.reserve(size); - - // Build the particles - for (unsigned int i = 0; i < size; i++) { - const float momentum = momenta[i]; - const float invariantMass = invariantMasses[i]; - const float p1p2 = 2. * momentum * parametrizedMomentum; - const float costheta = 1. - invariantMass * invariantMass / p1p2; - - const auto phiTheta = globalAngle( - phi, theta, uniformDistribution(generator) * 2. * std::numbers::pi, - std::acos(costheta)); - const auto direction = - Acts::makeDirectionFromPhiTheta(phiTheta.first, phiTheta.second); - - Particle p = Particle(initialParticle.particleId().makeDescendant(i), - static_cast(pdgId[i])); - p.setProcess(ProcessType::eNuclearInteraction) - .setPosition4(initialParticle.fourPosition()) - .setAbsoluteMomentum(momentum) - .setDirection(direction) - .setReferenceSurface(initialParticle.referenceSurface()); - - // Store the particle - if (i == 0 && soft) { - initialParticle = p; - } else { - result.push_back(std::move(p)); - } - } - - return result; -} -} // namespace ActsFatras diff --git a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteractionParameters.hpp b/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteractionParameters.hpp deleted file mode 100644 index 617acd5ba94..00000000000 --- a/Fatras/include/ActsFatras/Physics/NuclearInteraction/NuclearInteractionParameters.hpp +++ /dev/null @@ -1,106 +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/Definitions/Common.hpp" - -#include - -namespace ActsFatras::detail { - -/// @brief Data storage of the parametrized nuclear interaction -struct NuclearInteractionParameters { - using CumulativeDistribution = - std::pair, std::vector>; - using Distributions = std::vector; - using PdgMap = - std::vector>>>; - - /// @brief Nested struct for the storage of the kinematic parametrisations for - /// a given final state multiplicity - struct ParametersWithFixedMultiplicity { - ParametersWithFixedMultiplicity() = default; - - ParametersWithFixedMultiplicity( - Distributions& momenta, Acts::ActsDynamicVector& eValMom, - Acts::ActsDynamicVector& eVecMom, Acts::ActsDynamicVector& meanMom, - Distributions& invariantMasses, Acts::ActsDynamicVector& eValIM, - Acts::ActsDynamicVector& eVecIM, Acts::ActsDynamicVector& meanIM) - : validParametrisation(true), - momentumDistributions(momenta), - eigenvaluesMomentum(eValMom), - meanMomentum(meanMom), - invariantMassDistributions(invariantMasses), - eigenvaluesInvariantMass(eValIM), - meanInvariantMass(meanIM) { - const unsigned int sizeMom = eigenvaluesMomentum.size(); - eigenvectorsMomentum.resize(sizeMom, sizeMom); - for (unsigned int i = 0; i < sizeMom; i++) { - for (unsigned int j = 0; j < sizeMom; j++) { - eigenvectorsMomentum(i, j) = eVecMom[i * sizeMom + j]; - } - } - - const unsigned int sizeInvMass = eigenvaluesInvariantMass.size(); - eigenvectorsInvariantMass.resize(sizeInvMass, sizeInvMass); - for (unsigned int i = 0; i < sizeInvMass; i++) { - for (unsigned int j = 0; j < sizeInvMass; j++) { - eigenvectorsInvariantMass(i, j) = eVecIM[i * sizeInvMass + j]; - } - } - } - - bool validParametrisation = false; - - /// Momentum parameters - /// Generation-wise distributions - Distributions momentumDistributions; - /// Eigenvalues - Acts::ActsDynamicVector eigenvaluesMomentum; - /// Eigenvectors - Acts::ActsDynamicMatrix eigenvectorsMomentum; - /// Mean in eigenspace - Acts::ActsDynamicVector meanMomentum; - - /// Invariant mass parameters - /// Generation-wise distributions - Distributions invariantMassDistributions; - /// Eigenvalues - Acts::ActsDynamicVector eigenvaluesInvariantMass; - /// Eigenvectors - Acts::ActsDynamicMatrix eigenvectorsInvariantMass; - /// Mean in eigenspace - Acts::ActsDynamicVector meanInvariantMass; - }; - - /// Initial momentum - float momentum = 0; - /// Probability for soft nuclear interacion - float softInteractionProbability = 0; - /// PDG ID based branching probabilities - PdgMap pdgMap; - /// Probability for nuclear interaction - CumulativeDistribution nuclearInteractionProbability; - /// Multiplicity in soft interactions - CumulativeDistribution softMultiplicity; - /// Multiplicity in hard interactions - CumulativeDistribution hardMultiplicity; - /// Kinematic distributions in soft interactions - std::vector softKinematicParameters; - /// Kinematic distributions in hard interactions - std::vector hardKinematicParameters; -}; - -/// Parametrisation of a single particle -using NuclearInteractionParametrisation = - std::vector>; -/// Parametrisation of multiple particles -using MultiParticleNuclearInteractionParametrisation = - std::vector>; -} // namespace ActsFatras::detail diff --git a/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp b/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp deleted file mode 100644 index ad1e0bbb6a6..00000000000 --- a/Fatras/src/Physics/NuclearInteraction/NuclearInteraction.cpp +++ /dev/null @@ -1,165 +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 "ActsFatras/Physics/NuclearInteraction/NuclearInteraction.hpp" - -#include -#include -#include -#include -#include -#include - -namespace ActsFatras { - -const detail::NuclearInteractionParameters& NuclearInteraction::findParameters( - double rnd, - const detail::NuclearInteractionParametrisation& parametrisation, - float particleMomentum) const { - // Return lowest/highest if momentum outside the boundary - if (particleMomentum <= parametrisation.front().first) { - return parametrisation.front().second; - } - if (particleMomentum >= parametrisation.back().first) { - return parametrisation.back().second; - } - - // Find the two neighbouring parametrisations - const auto lowerBound = std::lower_bound( - parametrisation.begin(), parametrisation.end(), particleMomentum, - [](const std::pair& - params, - const float mom) { return params.first < mom; }); - const float momentumUpperNeighbour = lowerBound->first; - const float momentumLowerNeighbour = std::prev(lowerBound, 1)->first; - - // Pick one randomly - const float weight = (momentumUpperNeighbour - particleMomentum) / - (momentumUpperNeighbour - momentumLowerNeighbour); - return (rnd < weight) ? std::prev(lowerBound, 1)->second : lowerBound->second; -} // namespace ActsFatras - -unsigned int NuclearInteraction::sampleDiscreteValues( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution) const { - // Fast exit - if (distribution.second.empty()) { - return 0; - } - - // Find the bin - const std::uint32_t int_rnd = static_cast( - std::numeric_limits::max() * rnd); - const auto it = std::upper_bound(distribution.second.begin(), - distribution.second.end(), int_rnd); - std::size_t iBin = std::min( - static_cast(std::distance(distribution.second.begin(), it)), - distribution.second.size() - 1); - - // Return the corresponding bin - return static_cast(distribution.first[iBin]); -} - -double NuclearInteraction::sampleContinuousValues( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution, - bool interpolate) const { - // Fast exit - if (distribution.second.empty()) { - return std::numeric_limits::infinity(); - } - - // Find the bin - const std::uint32_t int_rnd = static_cast( - std::numeric_limits::max() * rnd); - // Fast exit for non-normalised CDFs like interaction probability - if (int_rnd > distribution.second.back()) { - return std::numeric_limits::infinity(); - } - const auto it = std::upper_bound(distribution.second.begin(), - distribution.second.end(), int_rnd); - std::size_t iBin = std::min( - static_cast(std::distance(distribution.second.begin(), it)), - distribution.second.size() - 1); - - if (interpolate) { - // Interpolate between neighbouring bins and return a diced intermediate - // value - const std::uint32_t basecont = - (iBin > 0 ? distribution.second[iBin - 1] : 0); - const std::uint32_t dcont = distribution.second[iBin] - basecont; - return distribution.first[iBin] + - (distribution.first[iBin + 1] - distribution.first[iBin]) * - (dcont > 0 ? (int_rnd - basecont) / dcont : 0.5); - } else { - return distribution.first[iBin]; - } -} - -unsigned int NuclearInteraction::finalStateMultiplicity( - double rnd, - const detail::NuclearInteractionParameters::CumulativeDistribution& - distribution) const { - return sampleDiscreteValues(rnd, distribution); -} - -std::pair NuclearInteraction::globalAngle(double phi1, - double theta1, - float phi2, - float theta2) const { - // Rotation around the global y-axis - Acts::SquareMatrix3 rotY = Acts::SquareMatrix3::Zero(); - rotY(0, 0) = std::cos(theta1); - rotY(0, 2) = std::sin(theta1); - rotY(1, 1) = 1.; - rotY(2, 0) = -std::sin(theta1); - rotY(2, 2) = std::cos(theta1); - - // Rotation around the global z-axis - Acts::SquareMatrix3 rotZ = Acts::SquareMatrix3::Zero(); - rotZ(0, 0) = std::cos(phi1); - rotZ(0, 1) = -std::sin(phi1); - rotZ(1, 0) = std::sin(phi1); - rotZ(1, 1) = std::cos(phi1); - rotZ(2, 2) = 1.; - - // Rotate the direction vector of the second particle - const Acts::Vector3 vector2(std::sin(theta2) * std::cos(phi2), - std::sin(theta2) * std::sin(phi2), - std::cos(theta2)); - const Acts::Vector3 vectorSum = rotZ * rotY * vector2; - - // Calculate the global angles - const float theta = std::acos(vectorSum.z() / vectorSum.norm()); - const float phi = std::atan2(vectorSum.y(), vectorSum.x()); - - return {phi, theta}; -} - -bool NuclearInteraction::match(const Acts::ActsDynamicVector& momenta, - const Acts::ActsDynamicVector& invariantMasses, - float parametrizedMomentum) const { - const unsigned int size = momenta.size(); - for (unsigned int i = 0; i < size; i++) { - // Calculate the invariant masses - const float momentum = momenta[i]; - const float invariantMass = invariantMasses[i]; - const float p1p2 = 2. * momentum * parametrizedMomentum; - const float costheta = 1. - invariantMass * invariantMass / p1p2; - - // Abort if an angle cannot be calculated - if (std::abs(costheta) > 1) { - return false; - } - } - return true; -} -} // namespace ActsFatras From 2bb167c0ba1be5c66b55ca76308f8b84e609f489 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 16 Dec 2024 16:36:13 +0100 Subject: [PATCH 24/24] chore: Adjust target limit after reset in Core CKF (#3985) After a reset in the CKF we need to adjust the target limit again to make sure the next step picks it up. I don't think we hit this case yet but it is good to have it anyways. pulled out of https://github.com/acts-project/acts/pull/3449 ## Summary by CodeRabbit - **New Features** - Enhanced functionality for managing path limits and target surfaces in the filtering process. - Improved error handling and state management during track finding. - **Bug Fixes** - Refined logic for processing new track states and handling outlier states. - **Documentation** - Minor adjustments to comments and logging for improved clarity. --- Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 1ab019b59ce..e2ffcf9bc11 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -689,8 +689,12 @@ class CombinatorialKalmanFilter { materialInteractor(navigator.currentSurface(state.navigation), state, stepper, navigator, MaterialUpdateStage::PostUpdate); + // Set path limit based on loop protection detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, logger()); + + // Set path limit based on target surface + targetReached.checkAbort(state, stepper, navigator, logger()); } /// @brief CombinatorialKalmanFilter actor operation: