diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 9a4d14899bd..9a9a99e1219 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -130,7 +130,7 @@ jobs: ACTS_LOG_FAILURE_THRESHOLD: WARNING steps: - name: Install git lfs - run: apt-get install -y git-lfs + run: apt-get update && apt-get install -y git-lfs - uses: actions/checkout@v3 with: @@ -175,7 +175,7 @@ jobs: run: ccache -s - name: Unit tests - run: cmake --build build --target test + run: ctest --test-dir build -j$(nproc) - name: Integration tests run: cmake --build build --target integrationtests @@ -212,7 +212,7 @@ jobs: needs: [linux_ubuntu] steps: - name: Install git lfs - run: apt-get install -y git-lfs + run: apt-get update && apt-get install -y git-lfs - uses: actions/checkout@v3 with: @@ -253,7 +253,7 @@ jobs: steps: - name: Install git lfs - run: apt-get install -y git-lfs + run: apt-get update && apt-get install -y git-lfs - uses: actions/checkout@v3 with: @@ -289,7 +289,7 @@ jobs: && export LD_LIBRARY_PATH=$PWD/build/thirdparty/OpenDataDetector/factory:$LD_LIBRARY_PATH && echo "::endgroup::" && export PYTHONPATH="${PYTHONPATH}":"${GITHUB_WORKSPACE}/Examples/Scripts/Python" - && CI/physmon/phys_perf_mon.sh physmon + && CI/physmon/phys_perf_mon.sh all physmon - uses: actions/upload-artifact@v3 if: always() @@ -314,7 +314,7 @@ jobs: ACTS_LOG_FAILURE_THRESHOLD: WARNING steps: - name: Install git lfs - run: apt-get install -y git-lfs + run: apt-get update && apt-get install -y git-lfs - uses: actions/checkout@v3 with: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e93c739929b..88d8d7a0ff7 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,7 +15,7 @@ concurrency: jobs: format: runs-on: ubuntu-latest - container: ghcr.io/acts-project/format10:v41 + container: ghcr.io/acts-project/format14:v41 steps: - uses: actions/checkout@v3 - name: Check diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87eda808147..1f740c8aca6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -65,9 +65,9 @@ build_exatrkx: - docker cache: - key: ccache-exatrkx-${HEAD_REF}-${CCACHE_KEY_SUFFIX} + key: ccache-exatrkx-$CI_COMMIT_REF_SLUG paths: - - ${CCACHE_DIR} + - ${CI_PROJECT_DIR}/ccache_${CCACHE_KEY_SUFFIX} artifacts: paths: @@ -98,10 +98,7 @@ build_exatrkx: -DACTS_EXATRKX_ENABLE_ONNX=ON -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON -DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON - - - ccache -z - cmake --build build -- - - ccache -s test_exatrkx: stage: test @@ -122,3 +119,280 @@ test_exatrkx: - nvidia-smi - pytest -rFsv -k test_exatrkx +build_linux_ubuntu: + stage: build + image: ghcr.io/acts-project/ubuntu2204:v41 + tags: + - docker + + cache: + key: ccache-${CI_JOB_NAME_SLUG}-${HEAD_REF}-${CCACHE_KEY_SUFFIX} + when: 'always' + paths: + - ${CI_PROJECT_DIR}/ccache + + artifacts: + paths: + - build/ + exclude: + - build/**/*.o + + script: + - echo $PATH + - git clone $CLONE_URL src + + - cd src + - git checkout $HEAD_SHA + - git submodule init + - git submodule update + + - cd .. + - mkdir build + - > + cmake -B build -S src + -GNinja + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_CXX_FLAGS=-Werror + -DCMAKE_CXX_STANDARD=17 + -DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON + -DACTS_BUILD_EVERYTHING=ON + -DACTS_BUILD_ODD=ON + -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON + -DACTS_BUILD_EXAMPLES_BINARIES=ON + -DACTS_BUILD_EXAMPLES_EDM4HEP=ON + -DACTS_FORCE_ASSERTIONS=ON + -DACTS_BUILD_ANALYSIS_APPS=ON + -DACTS_BUILD_PLUGIN_ONNX=ON + + - ccache -z + - cmake --build build -- + - ccache -s + +linux_test_examples: + stage: test + image: ghcr.io/acts-project/ubuntu2204:v41 + needs: [build_linux_ubuntu] + tags: + - docker + + script: + - apt-get update && apt-get install -y git-lfs + + - git clone $CLONE_URL src + - cd src + - git checkout $HEAD_SHA + - git submodule init + - git submodule update + - cd .. + + - /usr/local/bin/geant4-config --install-datasets + - "source /usr/local/bin/thisroot.sh || true" + - "source /usr/local/bin/thisdd4hep_only.sh || true" + - "source /usr/local/bin/geant4.sh || true" + - source build/python/setup.sh + - export PYTHONPATH=/usr/local/python:$PYTHONPATH + - export LD_LIBRARY_PATH=$PWD/build/thirdparty/OpenDataDetector/factory:$LD_LIBRARY_PATH + - cd src + - pip3 install -r Examples/Python/tests/requirements.txt + - pytest -rFsv -k "not exatrkx" -v -n auto + + +############################### +### UBUNTU EXTRA JOB MATRIX ### +############################### + +.linux_ubuntu_extra: &linux_ubuntu_extra + variables: + INSTALL_DIR: ${CI_PROJECT_DIR}/install + + stage: build + tags: + - docker + + cache: + key: ccache-${CI_JOB_NAME_SLUG}-${HEAD_REF}-${CCACHE_KEY_SUFFIX} + when: 'always' + paths: + - ${CI_PROJECT_DIR}/ccache + + script: + - git clone $CLONE_URL src + + - cd src + - git checkout $HEAD_SHA + - git submodule init + - git submodule update + + - cd .. + - mkdir build + - > + cmake -B build -S src + -GNinja + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_CXX_FLAGS=-Werror + -DCMAKE_CXX_STANDARD=${CXXSTD} + -DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON + -DACTS_BUILD_EVERYTHING=ON + -DACTS_BUILD_ODD=ON + -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON + -DACTS_BUILD_EXAMPLES_EDM4HEP=ON + -DACTS_FORCE_ASSERTIONS=ON + -DACTS_BUILD_ANALYSIS_APPS=ON + + - ccache -z + - cmake --build build -- + - ccache -s + + - ctest --test-dir build -j$(nproc) + - cmake --build build --target integrationtests + + # Install main project + - cmake --install build + + # Downstream configure + - > + cmake -B build-downstream -S src/Tests/DownstreamProject + -GNinja + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_CXX_FLAGS=-Werror + -DCMAKE_CXX_STANDARD=${CXXSTD} + -DCMAKE_PREFIX_PATH="${INSTALL_DIR}" + + # Downstream build + - cmake --build build-downstream + + # Downstream run + - ./build-downstream/bin/ShowActsVersion + +linux_ubuntu_2004_cpp17: + <<: *linux_ubuntu_extra + variables: + CXXSTD: 17 + image: ghcr.io/acts-project/ubuntu2004:v41 + +linux_ubuntu_2204_cpp20: + <<: *linux_ubuntu_extra + variables: + CXXSTD: 20 + image: ghcr.io/acts-project/ubuntu2204_cpp20:v41 + +linux_ubuntu_2204_clang: + <<: *linux_ubuntu_extra + variables: + CXXSTD: 17 + image: ghcr.io/acts-project/ubuntu2204_clang:v41 + + +###################### +### LCG JOB MATRIX ### +###################### + +.lcg: &lcg_base_job + image: ghcr.io/acts-project/${OS}-base:sha-ca76a1f2 + stage: build + tags: + - docker + - cvmfs + + variables: + INSTALL_DIR: ${{ github.workspace }}/install + + SETUP: + + cache: + key: ccache-${CI_JOB_NAME_SLUG}-${HEAD_REF}-${CCACHE_KEY_SUFFIX} + when: 'always' + paths: + - ${CI_PROJECT_DIR}/ccache + + before_script: + - 'echo "LCG_VERSION: ${LCG_VERSION}"' + - 'echo "COMPILER: ${COMPILER}"' + - 'if [ "$OS" = "alma9" ]; then export LCG_PLATFORM="centos9"; else export LCG_PLATFORM="$OS"; fi' + - 'echo "LCG_PLATFORM: ${LCG_PLATFORM}"' + - source /cvmfs/sft.cern.ch/lcg/views/LCG_${LCG_VERSION}/x86_64-${LCG_PLATFORM}-${COMPILER}-opt/setup.sh + + - git clone $CLONE_URL src + + - cd src + - git checkout $HEAD_SHA + - git submodule init + - git submodule update + - cd .. + + - ccache --version + + script: + - > + cmake -B build -S src + -GNinja + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_CXX_FLAGS=-Werror + -DCMAKE_CXX_STANDARD=17 + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" + -DACTS_LOG_FAILURE_THRESHOLD=WARNING + -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON + -DACTS_FORCE_ASSERTIONS=ON + -DACTS_BUILD_UNITTESTS=ON + -DACTS_BUILD_INTEGRATIONTESTS=ON + -DACTS_BUILD_BENCHMARKS=ON + -DACTS_BUILD_EXAMPLES=ON + -DACTS_BUILD_PLUGIN_DD4HEP=OFF + -DACTS_BUILD_PLUGIN_TGEO=ON + -DACTS_BUILD_PLUGIN_IDENTIFICATION=ON + -DACTS_BUILD_PLUGIN_JSON=ON + -DACTS_BUILD_FATRAS=ON + -DACTS_BUILD_PLUGIN_LEGACY=ON + -DACTS_BUILD_PLUGIN_AUTODIFF=ON + -DACTS_BUILD_EXAMPLES_DD4HEP=OFF + -DACTS_BUILD_PLUGIN_EDM4HEP=OFF + -DACTS_BUILD_EXAMPLES_GEANT4=ON + -DACTS_BUILD_EXAMPLES_HEPMC3=ON + -DACTS_BUILD_EXAMPLES_PYTHIA8=ON + -DACTS_BUILD_FATRAS_GEANT4=ON + -DACTS_BUILD_FATRAS=ON + -DACTS_BUILD_ALIGNMENT=ON + -DACTS_BUILD_ANALYSIS_APPS=ON + + - ccache -z + - cmake --build build + - ccache -s + + - ctest --test-dir build -j$(nproc) + + +lcg_102b: + <<: *lcg_base_job + + variables: + LCG_VERSION: "102b" + + parallel: + matrix: + - OS: [centos7] + COMPILER: [gcc11] + + - OS: [centos8, alma9] + COMPILER: [gcc11] + +lcg_103: + <<: *lcg_base_job + + variables: + LCG_VERSION: "103" + + parallel: + matrix: + - OS: [centos7] + COMPILER: [gcc11, gcc12, clang12, clang15] + + - OS: [alma9] + COMPILER: [gcc11, gcc12] + + rules: + - if: '$COMPILER == "clang12" || $COMPILER == "gcc12" || $COMPILER == "clang15"' + allow_failure: true + - when: on_success diff --git a/CI/check_format_local b/CI/check_format_local index cc0c765f13a..c4886393e61 100755 --- a/CI/check_format_local +++ b/CI/check_format_local @@ -17,5 +17,5 @@ docker run --rm -ti \ -v ${WD}:/work_dir:rw \ --user "${USER_ID}:${GROUP_ID}" \ -w "/work_dir" \ - ghcr.io/acts-project/format10:v6 \ + ghcr.io/acts-project/format14:v41 \ CI/check_format . diff --git a/CI/physmon/comment_template.md b/CI/physmon/comment_template.md index 37093c23ba3..6717aa480ad 100644 --- a/CI/physmon/comment_template.md +++ b/CI/physmon/comment_template.md @@ -4,20 +4,26 @@ > This is likely a physmon job failure {% endif %} +[Summary]({{ url }}/summary.html) [Full report]({{ url }}/) Seeding: {{ make_url("seeded", "seeding_seeded.html") }}, {{ make_url("truth estimated", "seeding_truth_estimated.html") }}, {{ make_url("orthogonal", "seeding_orthogonal.html") }} CKF: {{ make_url("seeded", "ckf_seeded.html") }}, {{ make_url("truth smeared", "ckf_truth_smeared.html") }}, {{ make_url("truth estimated", "ckf_truth_estimated.html") }}, {{ make_url("orthogonal", "ckf_orthogonal.html") }} IVF: {{ make_url("seeded", "ivf_seeded.html") }}, {{ make_url("truth smeared", "ivf_truth_smeared.html") }}, {{ make_url("truth estimated", "ivf_truth_estimated.html") }}, {{ make_url("orthogonal", "ivf_orthogonal.html") }} +AMVF: {{ make_url("seeded", "amvf_seeded.html") }}, {{ make_url("truth smeared", "amvf_truth_smeared.html") }}, {{ make_url("truth estimated", "amvf_truth_estimated.html") }}, {{ make_url("orthogonal", "amvf_orthogonal.html") }} Ambiguity resolution: {{ make_url("seeded", "ambi_seeded.html") }}, {{ make_url("orthogonal", "ambi_orthogonal.html") }} {{ make_url("Truth tracking", "truth_tracking.html") }} {{ make_url("Truth tracking (GSF)", "gsf.html")}} ### Vertexing {{ "" if all_exist( - "vertexing_mu_scan.pdf", + "vertexing_mu_scan.pdf", "ivf_seeded_plots", "ivf_truth_smeared_plots", "ivf_truth_estimated_plots", "ivf_orthogonal_plots", + "amvf_seeded_plots", + "amvf_truth_smeared_plots", + "amvf_truth_estimated_plots", + "amvf_orthogonal_plots", ) else ":x: "}} {% call detail_block("Vertexing vs. mu", "vertexing_mu_scan.pdf") %} @@ -27,13 +33,14 @@ Ambiguity resolution: {{ make_url("seeded", "ambi_seeded.html") }}, {{ make_url( {% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} {% call detail_block("IVF "+mode, "ivf_"+mode+"_plots") %} - + {% for url in [ "covXX.pdf", "covYY.pdf", - "diffx.pdf", - "diffy.pdf", - "diffz.pdf", + "covZZ.pdf", + "resX.pdf", + "resY.pdf", + "resZ.pdf", "recoOverTrue.pdf", ] -%} {{- make_image("ivf_"+mode+"_plots/"+url, "50%") -}} @@ -43,6 +50,26 @@ Ambiguity resolution: {{ make_url("seeded", "ambi_seeded.html") }}, {{ make_url( {% endfor %} +{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} + +{% call detail_block("AMVF "+mode, "amvf_"+mode+"_plots") %} + +{% for url in [ + "covXX.pdf", + "covYY.pdf", + "covZZ.pdf", + "resX.pdf", + "resY.pdf", + "resZ.pdf", + "recoOverTrue.pdf", +] -%} +{{- make_image("amvf_"+mode+"_plots/"+url, "50%") -}} +{%- endfor %} + +{% endcall %} + +{% endfor %} + ### Seeding {{ "" if all_exist( "seeding_seeded_plots", "seeding_truth_estimated_plots", diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index a9087b54198..3249ff4443a 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -2,7 +2,14 @@ set -e -outdir=$1 + +mode=$1 +if ! [[ $mode = @(all|kalman|gsf|fullchains|vertexing) ]]; then + echo "Usage: $0 (outdir)" + exit 1 +fi + +outdir=$2 [ -z "$outdir" ] && outdir=physmon mkdir -p $outdir @@ -11,7 +18,7 @@ refcommit=$(cat $refdir/commit) commit=$(git rev-parse --short HEAD) echo "::group::Generate validation dataset" -CI/physmon/physmon.py $outdir 2>&1 > $outdir/run.log +CI/physmon/physmon.py $mode $outdir 2>&1 > $outdir/run.log echo "::endgroup::" set +e @@ -63,60 +70,112 @@ function full_chain() { -p $outdir/ckf_${suffix}_plots Examples/Scripts/generic_plotter.py \ - $outdir/performance_vertexing_${suffix}.root \ + $outdir/performance_ivf_${suffix}.root \ vertexing \ - $outdir/performance_vertexing_${suffix}_hist.root \ + $outdir/performance_ivf_${suffix}_hist.root \ --silent \ --config CI/physmon/vertexing_config.yml ec=$(($ec | $?)) + # remove ntuple file because it's large + rm $outdir/performance_ivf_${suffix}.root + run \ - $outdir/performance_vertexing_${suffix}_hist.root \ - $refdir/performance_vertexing_${suffix}_hist.root \ + $outdir/performance_ivf_${suffix}_hist.root \ + $refdir/performance_ivf_${suffix}_hist.root \ --title "IVF ${suffix}" \ -o $outdir/ivf_${suffix}.html \ -p $outdir/ivf_${suffix}_plots + + Examples/Scripts/generic_plotter.py \ + $outdir/performance_amvf_${suffix}.root \ + vertexing \ + $outdir/performance_amvf_${suffix}_hist.root \ + --silent \ + --config CI/physmon/vertexing_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/performance_amvf_${suffix}.root + + run \ + $outdir/performance_amvf_${suffix}_hist.root \ + $refdir/performance_amvf_${suffix}_hist.root \ + --title "AMVF ${suffix}" \ + -o $outdir/amvf_${suffix}.html \ + -p $outdir/amvf_${suffix}_plots + + Examples/Scripts/generic_plotter.py \ + $outdir/tracksummary_ckf_${suffix}.root \ + tracksummary \ + $outdir/tracksummary_ckf_${suffix}_hist.root \ + --silent \ + --config CI/physmon/tracksummary_ckf_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/tracksummary_ckf_${suffix}.root + + run \ + $outdir/tracksummary_ckf_${suffix}_hist.root \ + $refdir/tracksummary_ckf_${suffix}_hist.root \ + --title "Track Summary CKF ${suffix}" \ + -o $outdir/tracksummary_ckf_${suffix}.html \ + -p $outdir/tracksummary_ckf_${suffix}_plots + + } -full_chain truth_smeared -full_chain truth_estimated -full_chain seeded -full_chain orthogonal - -run \ - $outdir/performance_gsf.root \ - $refdir/performance_gsf.root \ - --title "Truth tracking (GSF)" \ - -c CI/physmon/gsf.yml \ - -o $outdir/gsf.html \ - -p $outdir/gsf_plots - -run \ - $outdir/performance_truth_tracking.root \ - $refdir/performance_truth_tracking.root \ - --title "Truth tracking" \ - -c CI/physmon/truth_tracking.yml \ - -o $outdir/truth_tracking.html \ - -p $outdir/truth_tracking_plots - -run \ - $outdir/performance_ambi_seeded.root \ - $refdir/performance_ambi_seeded.root \ - --title "Ambisolver seeded" \ - -o $outdir/ambi_seeded.html \ - -p $outdir/ambi_seeded_plots - -run \ - $outdir/performance_ambi_orthogonal.root \ - $refdir/performance_ambi_orthogonal.root \ - --title "Ambisolver orthogonal" \ - -o $outdir/ambi_orthogonal.html \ - -p $outdir/ambi_orthogonal_plots - -Examples/Scripts/vertex_mu_scan.py \ - $outdir/performance_vertexing_*mu*.root \ - $outdir/vertexing_mu_scan.pdf - -rm $outdir/performance_vertexing_*mu* +if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then + full_chain truth_smeared + full_chain truth_estimated + full_chain seeded + full_chain orthogonal + + run \ + $outdir/performance_ambi_seeded.root \ + $refdir/performance_ambi_seeded.root \ + --title "Ambisolver seeded" \ + -o $outdir/ambi_seeded.html \ + -p $outdir/ambi_seeded_plots + + run \ + $outdir/performance_ambi_orthogonal.root \ + $refdir/performance_ambi_orthogonal.root \ + --title "Ambisolver orthogonal" \ + -o $outdir/ambi_orthogonal.html \ + -p $outdir/ambi_orthogonal_plots +fi + +if [[ "$mode" == "all" || "$mode" == "gsf" ]]; then + run \ + $outdir/performance_gsf.root \ + $refdir/performance_gsf.root \ + --title "Truth tracking (GSF)" \ + -c CI/physmon/gsf.yml \ + -o $outdir/gsf.html \ + -p $outdir/gsf_plots +fi + +if [[ "$mode" == "all" || "$mode" == "kalman" ]]; then + run \ + $outdir/performance_truth_tracking.root \ + $refdir/performance_truth_tracking.root \ + --title "Truth tracking" \ + -c CI/physmon/truth_tracking.yml \ + -o $outdir/truth_tracking.html \ + -p $outdir/truth_tracking_plots +fi + +if [[ "$mode" == "all" || "$mode" == "vertexing" ]]; then + Examples/Scripts/vertex_mu_scan.py \ + $outdir/performance_vertexing_*mu*.root \ + $outdir/vertexing_mu_scan.pdf + + rm $outdir/performance_vertexing_*mu* +fi + +CI/physmon/summary.py $outdir/*.html $outdir/summary.html +ec=$(($ec | $?)) exit $ec diff --git a/CI/physmon/physmon.py b/CI/physmon/physmon.py index 6258dbfc941..0111628b185 100755 --- a/CI/physmon/physmon.py +++ b/CI/physmon/physmon.py @@ -49,6 +49,7 @@ parser = argparse.ArgumentParser() +parser.add_argument("mode", choices=["all", "kalman", "gsf", "fullchains", "vertexing"]) parser.add_argument("outdir") args = parser.parse_args() @@ -221,14 +222,40 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): associatedParticles=None if label in ["seeded", "orthogonal"] else "particles_input", + outputProtoVertices="ivf_protovertices", + outputVertices="ivf_fittedVertices", vertexFinder=VertexFinder.Iterative, - outputDirRoot=tp, + outputDirRoot=tp / "ivf", + ) + + addVertexFitting( + s, + field, + associatedParticles=None + if label in ["seeded", "orthogonal"] + else "particles_input", + outputProtoVertices="amvf_protovertices", + outputVertices="amvf_fittedVertices", + outputTime="amvf_outputTime", + vertexFinder=VertexFinder.AMVF, + outputDirRoot=tp / "amvf", ) s.run() del s - for stem in ["performance_ckf", "performance_vertexing"] + ( + for vertexing in ["ivf", "amvf"]: + shutil.move( + tp / f"{vertexing}/performance_vertexing.root", + tp / f"performance_{vertexing}.root", + ) + + for stem in [ + "performance_ckf", + "tracksummary_ckf", + "performance_ivf", + "performance_amvf", + ] + ( ["performance_seeding", "performance_ambi"] if label in ["seeded", "orthogonal"] else ["performance_seeding"] @@ -342,36 +369,36 @@ def run_vertexing(fitter, mu, events): with acts.FpeMonitor(): ### Truth tracking with Kalman Filter - - truth_tracking_kalman() + if args.mode == "all" or args.mode == "kalman": + truth_tracking_kalman() ### GSF - - truth_tracking_gsf() + if args.mode == "all" or args.mode == "gsf": + truth_tracking_gsf() ### CKF track finding variations - - for truthSmearedSeeded, truthEstimatedSeeded, label in [ - (True, False, "truth_smeared"), # if first is true, second is ignored - (False, True, "truth_estimated"), - (False, False, "seeded"), - (False, False, "orthogonal"), - ]: - run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label) + if args.mode == "all" or args.mode == "fullchains": + for truthSmearedSeeded, truthEstimatedSeeded, label in [ + (True, False, "truth_smeared"), # if first is true, second is ignored + (False, True, "truth_estimated"), + (False, False, "seeded"), + (False, False, "orthogonal"), + ]: + run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label) ### VERTEX MU SCAN + if args.mode == "all" or args.mode == "vertexing": + for fitter in (VertexFinder.Iterative, VertexFinder.AMVF): + for mu in (1, 10, 25, 50, 75, 100, 125, 150, 175, 200): + start = datetime.datetime.now() - for fitter in (VertexFinder.Iterative, VertexFinder.AMVF): - for mu in (1, 10, 25, 50, 75, 100, 125, 150, 175, 200): - start = datetime.datetime.now() - - events = 5 - run_vertexing(fitter, mu, events) + events = 5 + run_vertexing(fitter, mu, events) - delta = datetime.datetime.now() - start + delta = datetime.datetime.now() - start - duration = delta.total_seconds() / events + duration = delta.total_seconds() / events - ( - outdir / f"performance_vertexing_{fitter.name}_mu{mu}_time.txt" - ).write_text(str(duration)) + ( + outdir / f"performance_vertexing_{fitter.name}_mu{mu}_time.txt" + ).write_text(str(duration)) diff --git a/CI/physmon/reference/performance_amvf_orthogonal_hist.root b/CI/physmon/reference/performance_amvf_orthogonal_hist.root new file mode 100644 index 00000000000..ac2a16aa673 Binary files /dev/null and b/CI/physmon/reference/performance_amvf_orthogonal_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_seeded_hist.root b/CI/physmon/reference/performance_amvf_seeded_hist.root new file mode 100644 index 00000000000..3253b8983c4 Binary files /dev/null and b/CI/physmon/reference/performance_amvf_seeded_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_truth_estimated_hist.root b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root new file mode 100644 index 00000000000..a9aceb7b03e Binary files /dev/null and b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root new file mode 100644 index 00000000000..b0dce9f3030 Binary files /dev/null and b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_orthogonal_hist.root b/CI/physmon/reference/performance_ivf_orthogonal_hist.root new file mode 100644 index 00000000000..9719d1e2d20 Binary files /dev/null and b/CI/physmon/reference/performance_ivf_orthogonal_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_seeded_hist.root b/CI/physmon/reference/performance_ivf_seeded_hist.root new file mode 100644 index 00000000000..db7ddfd335c Binary files /dev/null and b/CI/physmon/reference/performance_ivf_seeded_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_truth_estimated_hist.root b/CI/physmon/reference/performance_ivf_truth_estimated_hist.root new file mode 100644 index 00000000000..87067a7d08f Binary files /dev/null and b/CI/physmon/reference/performance_ivf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root new file mode 100644 index 00000000000..8fa957f3202 Binary files /dev/null and b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_vertexing_orthogonal_hist.root b/CI/physmon/reference/performance_vertexing_orthogonal_hist.root deleted file mode 100644 index 49f6fe0b741..00000000000 Binary files a/CI/physmon/reference/performance_vertexing_orthogonal_hist.root and /dev/null differ diff --git a/CI/physmon/reference/performance_vertexing_seeded_hist.root b/CI/physmon/reference/performance_vertexing_seeded_hist.root deleted file mode 100644 index c505b39f35e..00000000000 Binary files a/CI/physmon/reference/performance_vertexing_seeded_hist.root and /dev/null differ diff --git a/CI/physmon/reference/performance_vertexing_truth_estimated_hist.root b/CI/physmon/reference/performance_vertexing_truth_estimated_hist.root deleted file mode 100644 index f47aa5da387..00000000000 Binary files a/CI/physmon/reference/performance_vertexing_truth_estimated_hist.root and /dev/null differ diff --git a/CI/physmon/reference/performance_vertexing_truth_smeared_hist.root b/CI/physmon/reference/performance_vertexing_truth_smeared_hist.root deleted file mode 100644 index 69520ab2d0f..00000000000 Binary files a/CI/physmon/reference/performance_vertexing_truth_smeared_hist.root and /dev/null differ diff --git a/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root b/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root new file mode 100644 index 00000000000..5763428f8b9 Binary files /dev/null and b/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_seeded_hist.root b/CI/physmon/reference/tracksummary_ckf_seeded_hist.root new file mode 100644 index 00000000000..64be088a9c4 Binary files /dev/null and b/CI/physmon/reference/tracksummary_ckf_seeded_hist.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_truth_estimated_hist.root b/CI/physmon/reference/tracksummary_ckf_truth_estimated_hist.root new file mode 100644 index 00000000000..c3c9e3718cc Binary files /dev/null and b/CI/physmon/reference/tracksummary_ckf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_truth_smeared_hist.root b/CI/physmon/reference/tracksummary_ckf_truth_smeared_hist.root new file mode 100644 index 00000000000..dfcc9570703 Binary files /dev/null and b/CI/physmon/reference/tracksummary_ckf_truth_smeared_hist.root differ diff --git a/CI/physmon/summary.py b/CI/physmon/summary.py new file mode 100755 index 00000000000..9525a3ed44c --- /dev/null +++ b/CI/physmon/summary.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import argparse +import re +import functools +import os + + +parser = argparse.ArgumentParser() +parser.add_argument("html", nargs="+") +parser.add_argument("output") +args = parser.parse_args() + +re_title = re.compile(r'

\s*(.*)\s*<\/p>', re.RegexFlag.MULTILINE) +re_check = re.compile(r'\s*(.)\s*<\/a>', re.RegexFlag.MULTILINE) + +summary = {} + +for h in args.html: + with open(h, mode="r", encoding="utf-8") as f: + try: + content = f.read() + print(h, re_title.findall(content)) + title = re_title.findall(content)[0] + checks = re_check.findall(content) + parsed_checks = list(map(lambda c: c[1] == "✅", checks)) + summary[h] = { + "title": title, + "checks": checks, + "parsed_checks": parsed_checks, + "total": functools.reduce(lambda a, b: a and b, parsed_checks), + } + except Exception as e: + print(r"could not parse {h}", e) + +with open(args.output, mode="w", encoding="utf-8") as f: + f.write( + """ + + + physmon summary + + + +

physmon summary

+ + +""" + ) diff --git a/CI/physmon/tracksummary_ckf_config.yml b/CI/physmon/tracksummary_ckf_config.yml new file mode 100644 index 00000000000..8364e47bc9a --- /dev/null +++ b/CI/physmon/tracksummary_ckf_config.yml @@ -0,0 +1,173 @@ +histograms: + "chi2Sum": + min: 0 + max: 100 + + "pull_.*": + nbins: 50 + min: -6 + max: 6 + + "eLOC0_fit": + min: -5 + max: 5 + + eLOC1_fit: + min: -200 + max: 200 + + eQOP_fit: + min: -2 + max: 2 + + eTHETA_fit: + min: 0 + max: 3.14 + + eT_fit: + min: -150 + max: 150 + + err_eLOC0_fit: + min: 0 + max: 0.2 + + err_eLOC1_fit: + min: 0 + max: 0.8 + + err_ePHI_fit: + min: 0 + max: 0.008 + + err_eQOP_fit: + min: 0 + max: 0.1 + + err_eT_fit: + min: 0 + max: 1 + + err_eTHETA_fit: + min: 0 + max: 0.0004 + + ".*Chi2": + min: 0 + max: 10 + + measurementLayer|measurementVolume|outlierLayer|outlierVolume: + nbins: 30 + min: 0 + max: 30 + + measurementVolume: + nbins: 30 + min: 0 + max: 30 + + NDF: + nbins: 50 + min: 0 + max: 50 + + nHoles|nMajorityHits|nMeasurements|nOutliers|nSharedHits|nStates: + nbins: 10 + min: 0 + max: 10 + + + "res_eLOC0_fit|res_eLOC1_fit": + nbins: 80 + min: -2 + max: 2 + + "res_ePHI_fit|res_eTHETA_fit": + nbins: 80 + min: -0.01 + max: 0.01 + + res_eTHETA_fit: + nbins: 80 + min: -0.001 + max: 0.001 + + res_eQOP_fit: + nbins: 80 + min: -0.01 + max: 0.01 + + res_eT_fit: + nbins: 80 + min: -100.0 + max: 100 + + t_charge: + nbins: 100 + min: -1.0 + max: 1.0 + + t_time: + nbins: 100 + min: -1.0 + max: 1.0 + + "t_vx|t_vy": + nbins: 100 + min: -0.04 + max: 0.04 + + t_vz: + nbins: 100 + min: -200.0 + max: 200 + + "t_px|t_py|t_pz": + nbins: 100 + min: -10.0 + max: 10 + + t_theta: + nbins: 100 + min: 0 + max: 3.14 + + t_phi: + nbins: 100 + min: -3.14 + max: 3.14 + + t_eta: + nbins: 100 + min: -2.7 + max: 2.7 + + "t_p$": + nbins: 100 + min: 1 + max: 10 + + t_pT: + nbins: 100 + min: 0 + max: 10 + + t_d0: + nbins: 100 + min: -0.05 + max: 0.05 + + t_z0: + nbins: 100 + min: -200.0 + max: 200 + +exclude: + - event_nr + - hasFittedParams + - majorityParticleId + - multiTraj_nr + - subTraj_nr + + + diff --git a/CI/physmon/vertexing_config.yml b/CI/physmon/vertexing_config.yml index a4c55face83..2a1b79dd90b 100644 --- a/CI/physmon/vertexing_config.yml +++ b/CI/physmon/vertexing_config.yml @@ -1,20 +1,25 @@ histograms: - "diff.*": + "res.*": nbins: 100 min: -0.1 max: 0.1 - diffz: + resZ: nbins: 50 min: -0.3 max: 0.3 - "covXX|covYY": + "pull.*": + nbins: 50 + min: -6 + max: 6 + + "covXX|covYY|covZZ": nbins: 100 min: -0.0005 max: 0.0005 - "covXY|covYX": + "covXY|covXZ|covYX": nbins: 100 min: -0.0001 max: 0.0001 @@ -34,7 +39,6 @@ histograms: min: -200 max: 200 nbins: 100 - extra_histograms: - expression: df["nRecoVtx"] / df["nTrueVtx"] @@ -53,3 +57,5 @@ extra_histograms: min: 0.0 max: 1.0 +exclude: + - timeMS diff --git a/CMakeLists.txt b/CMakeLists.txt index 18a877ad4b2..3198e719a68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ option(ACTS_BUILD_ANALYSIS_APPS "Build Analysis applications in the examples" OF option(ACTS_BUILD_BENCHMARKS "Build benchmarks" OFF) option(ACTS_BUILD_INTEGRATIONTESTS "Build integration tests" OFF) option(ACTS_BUILD_UNITTESTS "Build unit tests" OFF) +option(ACTS_BUILD_NONCOMPILE_TESTS "Build tests that check build failure invariants" OFF) option(ACTS_RUN_CLANG_TIDY "Run clang-tidy static analysis" OFF) # other options option(ACTS_BUILD_DOCS "Build documentation" OFF) @@ -93,6 +94,7 @@ include(ActsOptionHelpers) # options without inter-dependencies set_option_if(ACTS_BUILD_BENCHMARKS ACTS_BUILD_EVERYTHING) set_option_if(ACTS_BUILD_UNITTESTS ACTS_BUILD_EVERYTHING) +set_option_if(ACTS_BUILD_NONCOMPILE_TESTS ACTS_BUILD_EVERYTHING) set_option_if(ACTS_BUILD_INTEGRATIONTESTS ACTS_BUILD_EVERYTHING) set_option_if(ACTS_BUILD_EXAMPLES_DD4HEP ACTS_BUILD_EVERYTHING) set_option_if(ACTS_BUILD_EXAMPLES_GEANT4 ACTS_BUILD_EVERYTHING) diff --git a/Core/include/Acts/Definitions/Common.hpp b/Core/include/Acts/Definitions/Common.hpp index 9fd159e811f..538de54a945 100644 --- a/Core/include/Acts/Definitions/Common.hpp +++ b/Core/include/Acts/Definitions/Common.hpp @@ -8,144 +8,10 @@ #pragma once -#include "Acts/Definitions/Algebra.hpp" - #include -#include namespace Acts { -/// Tolerance for being numerical equal for geometry building -static constexpr ActsScalar s_epsilon = - 3 * std::numeric_limits::epsilon(); - -/// Tolerance for being on Surface -/// -/// @note This is intentionally given w/o an explicit unit to avoid having -/// to include the units header unneccessarily. With the native length -/// unit of mm this corresponds to 0.1um. -static constexpr ActsScalar s_onSurfaceTolerance = 1e-4; - -/// Tolerance for not being within curvilinear projection -/// this allows using the same curvilinear frame to eta = 6, -/// validity tested with IntegrationTests/PropagationTest -static constexpr ActsScalar s_curvilinearProjTolerance = 0.999995; - -/// @enum NavigationDirection -/// The navigation direction is always with -/// respect to a given momentum or direction -enum class NavigationDirection : int { Backward = -1, Forward = 1 }; - -/// Convert navigation dir to index [0,1] which allows to -/// store direction dependent objects in std::array -/// -/// @param nDir is the navigation direction at input -/// -/// returns either 0 or 1 -inline constexpr size_t indexFromDirection(NavigationDirection nDir) { - if (nDir == NavigationDirection::Backward) { - return 0u; - } - return 1u; -} - -/// Convert and ndex [0,1] to a navigation direction -/// for sorting in std::array -/// -/// @param index is the navigation direction at input -/// -/// returns either 0 or 1 -inline constexpr NavigationDirection directionFromIndex(size_t index) { - if (index == 0u) { - return NavigationDirection::Backward; - } - return NavigationDirection::Forward; -} - -/// This turns a signed value into a navigation direction -/// -/// @param value is the signed value -/// -/// @return a navigation direciton enum -inline constexpr NavigationDirection directionFromStepSize(ActsScalar value) { - assert(value != 0); - return value > 0 ? NavigationDirection::Forward - : NavigationDirection::Backward; -} - -/// Invert a navigation direction enum -/// -/// @param nDir is the navigation direction at input -/// -/// return an opposite navigation direction -inline constexpr NavigationDirection invertDirection(NavigationDirection nDir) { - return (nDir == NavigationDirection::Forward) ? NavigationDirection::Backward - : NavigationDirection::Forward; -} - -std::ostream& operator<<(std::ostream& os, NavigationDirection navDir); - -// NavigationDirection * T - -inline constexpr auto operator*(NavigationDirection dir, int value) { - return static_cast>(dir) * value; -} - -inline constexpr auto operator*(NavigationDirection dir, float value) { - return static_cast>(dir) * value; -} - -inline constexpr auto operator*(NavigationDirection dir, double value) { - return static_cast>(dir) * value; -} - -inline Acts::Vector3 operator*(NavigationDirection dir, - const Acts::Vector3& value) { - return static_cast>(dir) * value; -} - -// T * NavigationDirection - -inline constexpr auto operator*(int value, NavigationDirection dir) { - return value * static_cast>(dir); -} - -inline constexpr auto operator*(float value, NavigationDirection dir) { - return value * static_cast>(dir); -} - -inline constexpr auto operator*(double value, NavigationDirection dir) { - return value * static_cast>(dir); -} - -inline Acts::Vector3 operator*(const Acts::Vector3& value, - NavigationDirection dir) { - return value * static_cast>(dir); -} - -// T *= NavigationDirection - -inline constexpr auto operator*=(int& value, NavigationDirection dir) { - value *= static_cast>(dir); - return value; -} - -inline constexpr auto operator*=(float& value, NavigationDirection dir) { - value *= static_cast>(dir); - return value; -} - -inline constexpr auto operator*=(double& value, NavigationDirection dir) { - value *= static_cast>(dir); - return value; -} - -inline Acts::Vector3& operator*=(Acts::Vector3& value, - NavigationDirection dir) { - value *= static_cast>(dir); - return value; -} - /// This is a steering enum to tell which material update stage: /// - PreUpdate : update on approach of a surface /// - FullUpdate : update when passing a surface diff --git a/Core/include/Acts/Definitions/Direction.hpp b/Core/include/Acts/Definitions/Direction.hpp new file mode 100644 index 00000000000..9b05eb2f91f --- /dev/null +++ b/Core/include/Acts/Definitions/Direction.hpp @@ -0,0 +1,169 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2016-2018 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" + +#include +#include + +namespace Acts { + +/// The direction is always with respect to a given momentum, surface normal or +/// other general axes +class Direction final { + private: + enum class Value : int { + Negative = -1, + Positive = 1, + }; + + public: + static constexpr auto Negative = Value::Negative; + static constexpr auto Positive = Value::Positive; + + static constexpr auto Backward = Value::Negative; + static constexpr auto Forward = Value::Positive; + + /// This turns a signed value into a direction. Will assert on zero. + /// + /// @param scalar is the signed value + /// + /// @return a direciton enum + static inline constexpr Direction fromScalar(ActsScalar scalar) { + assert(scalar != 0); + return scalar >= 0 ? Value::Positive : Value::Negative; + } + + /// This turns a signed value into a direction and 0 will be handled as a + /// positive direction. Only use this when you are convinced that the 0 case + /// is properly handled downstream. + /// + /// @param scalar is the signed value + /// + /// @return a direciton enum + static inline constexpr Direction fromScalarZeroAsPositive( + ActsScalar scalar) { + return scalar >= 0 ? Value::Positive : Value::Negative; + } + + /// Convert and index [0,1] to a direction e.g. for sorting in + /// std::array + /// + /// @param index is the direction at input + static inline constexpr Direction fromIndex(std::size_t index) { + if (index == 0u) { + return Value::Negative; + } + return Value::Positive; + } + + /// Convert dir to index [0,1] which allows to store direction dependent + /// objects in std::array + /// + /// @return either 0 or 1 + inline constexpr std::size_t index() const { + if (m_value == Value::Negative) { + return 0u; + } + return 1u; + } + + /// Turns the direction into a signed value + /// + /// @return a signed value + inline constexpr int sign() const { return static_cast(m_value); } + + /// Reverse the direction + /// + /// @param dir is the direction at input + /// + /// @return an opposite direction + inline constexpr Direction invert() const { + return (m_value == Value::Positive) ? Value::Negative : Value::Positive; + } + + std::string toString() const; + + inline constexpr Direction() = default; + inline constexpr Direction(Value value) : m_value(value) {} + + inline constexpr bool operator==(Direction other) const { + return m_value == other.m_value; + } + + inline constexpr bool operator!=(Direction other) const { + return m_value != other.m_value; + } + + private: + Value m_value = Value::Positive; +}; + +std::ostream& operator<<(std::ostream& os, Direction dir); + +// Direction * T + +inline constexpr int operator*(Direction dir, int value) { + return dir.sign() * value; +} + +inline constexpr float operator*(Direction dir, float value) { + return dir.sign() * value; +} + +inline constexpr double operator*(Direction dir, double value) { + return dir.sign() * value; +} + +inline Acts::Vector3 operator*(Direction dir, const Acts::Vector3& value) { + return dir.sign() * value; +} + +// T * Direction + +inline constexpr int operator*(int value, Direction dir) { + return value * dir.sign(); +} + +inline constexpr float operator*(float value, Direction dir) { + return value * dir.sign(); +} + +inline constexpr double operator*(double value, Direction dir) { + return value * dir.sign(); +} + +inline Acts::Vector3 operator*(const Acts::Vector3& value, Direction dir) { + return value * dir.sign(); +} + +// T *= Direction + +inline constexpr int operator*=(int& value, Direction dir) { + value *= dir.sign(); + return value; +} + +inline constexpr float operator*=(float& value, Direction dir) { + value *= dir.sign(); + return value; +} + +inline constexpr double operator*=(double& value, Direction dir) { + value *= dir.sign(); + return value; +} + +inline Acts::Vector3& operator*=(Acts::Vector3& value, Direction dir) { + value *= dir.sign(); + return value; +} + +} // namespace Acts diff --git a/Core/include/Acts/Definitions/Tolerance.hpp b/Core/include/Acts/Definitions/Tolerance.hpp new file mode 100644 index 00000000000..b3ed98ccb01 --- /dev/null +++ b/Core/include/Acts/Definitions/Tolerance.hpp @@ -0,0 +1,33 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" + +#include + +namespace Acts { + +/// Tolerance for being numerical equal for geometry building +static constexpr ActsScalar s_epsilon = + 3 * std::numeric_limits::epsilon(); + +/// Tolerance for being on Surface +/// +/// @note This is intentionally given w/o an explicit unit to avoid having +/// to include the units header unneccessarily. With the native length +/// unit of mm this corresponds to 0.1um. +static constexpr ActsScalar s_onSurfaceTolerance = 1e-4; + +/// Tolerance for not being within curvilinear projection +/// this allows using the same curvilinear frame to eta = 6, +/// validity tested with IntegrationTests/PropagationTest +static constexpr ActsScalar s_curvilinearProjTolerance = 0.999995; + +} // namespace Acts diff --git a/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp new file mode 100644 index 00000000000..4dd1e512f56 --- /dev/null +++ b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp @@ -0,0 +1,76 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022-2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +namespace Acts { +namespace Experimental { + +/// @brief A dedicated container builder for cylindrical detector containers +/// +/// It relies on the detailed implementation of the CylindricalDetectorHelper +/// and allows for DetectorVolume attachment in z/r/phi, such as wrapping +/// of bevelled cylinder objects in z/r +/// +/// @note the builder expects a fully consistent set of sub volume builders +/// that will be executed in a chain +/// +/// @note allowed BinningValue(s) for the cylindrical container builder are +/// {binZ}, {binR}, {binPhi}, {binZ, binR}, whereas the last option indicates +/// a wrapping setup. +class CylindricalContainerBuilder : public IDetectorComponentBuilder { + public: + /// Nested configuration object + struct Config { + /// The configured volume builders + std::vector> builders = {}; + /// Binning prescription of attachment + std::vector binning = {}; + /// Auxilliary information, mainly for screen output + std::string auxilliary = ""; + }; + + /// Constructor with configuration arguments + /// + /// @param cfg is the configuration struct + /// @param logger logging instance for screen output + CylindricalContainerBuilder( + const Config& cfg, + std::unique_ptr logger = + getDefaultLogger("CylindricalContainerBuilder", Logging::INFO)); + + /// The final implementation of the cylindrical container builder + /// + /// @param roots [in,out] the detector root volumes + /// @param gctx The geometry context for this call + /// + /// @return an outgoing detector component + DetectorComponent construct(RootDetectorVolumes& roots, + const GeometryContext& gctx) const final; + + private: + /// configuration object + Config m_cfg; + + /// Private acces method to the logger + const Logger& logger() const { return *m_logger; } + + /// logging instance + std::unique_ptr m_logger; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/DetectorComponents.hpp b/Core/include/Acts/Detector/DetectorComponents.hpp new file mode 100644 index 00000000000..d227a2b176c --- /dev/null +++ b/Core/include/Acts/Detector/DetectorComponents.hpp @@ -0,0 +1,85 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Navigation/DetectorVolumeUpdators.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" + +#include +#include +#include + +namespace Acts { + +class VolumeBounds; +class Surface; + +namespace Experimental { + +class DetectorVolume; +class Portal; + +/// @brief The currently built detector components +/// including the constructed volumes and the current +/// shell/coating, i.e. portals ordered in a map +/// +/// @note the map indices of the shell coating represent +/// their respective index in the portal vector of the +/// VolumeBounds derivative that is described by the given +/// component. +struct DetectorComponent { + using PortalContainer = std::map>; + /// The vector of construced volume(s) + std::vector> volumes = {}; + /// The current map of outside portals + PortalContainer portals; +}; + +/// @brief Holder struct for the external structure components +/// required to construct a detectorVolume +struct ExternalStructure { + /// A 3D transform where the volume should be positioned + Transform3 transform = Transform3::Identity(); + /// A shape definition + std::unique_ptr bounds = nullptr; + /// And a portal generator + PortalGenerator portalGenerator; +}; + +/// @brief Holder struct for the internal structure components of a DetectorVolume +/// +/// @note the surface surfacesUpdator needs to handle also portal providing +/// of contained volumes. +struct InternalStructure { + /// Contained surfaces of this volume, handled by the surfacesUpdator + std::vector> surfaces = {}; + /// Contained volumes of this volume, handled by the volumeUpdator + std::vector> volumes = {}; + /// Navigation delegate for surfaces + SurfaceCandidatesUpdator surfacesUpdator; + // Navigation delegate for volumes + DetectorVolumeUpdator volumeUpdator; +}; + +/// @brief Container to collect root volumes for the +/// construction of a Detector +/// +/// @note each root volume is expected to contain a full +/// search tree for eventually contained sub volumes +/// +/// This struct collects all root volumes that will then +/// be provided to the Detector object +struct RootDetectorVolumes { + std::vector> volumes = {}; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/DetectorVolume.hpp b/Core/include/Acts/Detector/DetectorVolume.hpp index e31aed32009..d4d08d4c08a 100644 --- a/Core/include/Acts/Detector/DetectorVolume.hpp +++ b/Core/include/Acts/Detector/DetectorVolume.hpp @@ -101,7 +101,7 @@ class DetectorVolume : public std::enable_shared_from_this { /// state updator delegates are not connected DetectorVolume( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, DetectorVolumeUpdator&& detectorVolumeUpdator, @@ -120,7 +120,7 @@ class DetectorVolume : public std::enable_shared_from_this { /// state updator delegates are not connected DetectorVolume( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, SurfaceCandidatesUpdator&& surfaceCandidateUpdator) noexcept(false); /// Factory method for producing memory managed instances of DetectorVolume. @@ -128,7 +128,7 @@ class DetectorVolume : public std::enable_shared_from_this { /// @note This is called by the @class DetectorVolumeFactory static std::shared_ptr makeShared( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, DetectorVolumeUpdator&& detectorVolumeUpdator, @@ -139,7 +139,7 @@ class DetectorVolume : public std::enable_shared_from_this { /// @note This is called by the @class DetectorVolumeFactory static std::shared_ptr makeShared( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, SurfaceCandidatesUpdator&& surfaceCandidateUpdator); public: @@ -362,7 +362,7 @@ class DetectorVolume : public std::enable_shared_from_this { Transform3 m_transform = Transform3::Identity(); /// Volume boundaries - std::unique_ptr m_bounds = nullptr; + std::shared_ptr m_bounds = nullptr; /// Portal store (internal/external) ObjectStore> m_portals; @@ -400,7 +400,7 @@ class DetectorVolumeFactory { static std::shared_ptr construct( const PortalGenerator& portalGenerator, const GeometryContext& gctx, const std::string& name, const Transform3& transform, - std::unique_ptr bounds, + std::shared_ptr bounds, const std::vector>& surfaces, const std::vector>& volumes, DetectorVolumeUpdator&& detectorVolumeUpdator, @@ -416,7 +416,7 @@ class DetectorVolumeFactory { static std::shared_ptr construct( const PortalGenerator& portalGenerator, const GeometryContext& gctx, const std::string& name, const Transform3& transform, - std::unique_ptr bounds, + std::shared_ptr bounds, SurfaceCandidatesUpdator&& surfaceCandidateUpdator) { auto dVolume = DetectorVolume::makeShared(gctx, name, transform, std::move(bounds), @@ -474,6 +474,8 @@ struct IndexedSurfacesExtractor { /// @param nState is the current navigation state /// @param indices are access indices into the surfaces store /// + /// @note no out of boudns checking is done + /// /// @return a vector of raw Surface pointers inline static const std::vector extract( [[maybe_unused]] const GeometryContext& gctx, diff --git a/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp new file mode 100644 index 00000000000..9c3e37c8ea2 --- /dev/null +++ b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp @@ -0,0 +1,78 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022-2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IInternalStructureBuilder.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { + +/// @brief A generic detector volume builder that uses +/// an external builder for shape and portals and an internal +/// structure builder for volume internals. +/// +/// @note Although this helper builds only a single volume, +/// it is to the outside presented as a DetectorComponent with +/// shell; like this it can be transparently be used for the +/// downstream detector construction process. +class DetectorVolumeBuilder : public IDetectorComponentBuilder { + public: + /// Nested configuration object + struct Config { + /// The name of the volume to be built + std::string name = "unnamed"; + /// An external builder + std::shared_ptr externalsBuilder = nullptr; + /// An internal builder + std::shared_ptr internalsBuilder = nullptr; + /// Add to the root volumes: the current volume + bool addToRoot = true; + /// Add eventual internal volume to root + bool addInternalsToRoot = false; + /// Auxilliary information + std::string auxilliary = ""; + }; + + /// Constructor with configuration arguments + /// + /// @param cfg is the configuration struct + /// @param logger logging instance for screen output + DetectorVolumeBuilder(const Config& cfg, + std::unique_ptr logger = getDefaultLogger( + "DetectorVolumeBuilder", Logging::INFO)); + + /// Final implementation of a volume builder that is purely defined + /// by an internal and external structure builder + /// + /// @param roots [in,out] the detector root volumes + /// @param gctx The geometry context for this call + /// + /// @return an outgoing detector component + DetectorComponent construct(RootDetectorVolumes& roots, + const GeometryContext& gctx) const final; + + private: + /// configuration object + Config m_cfg; + + /// Private acces method to the logger + const Logger& logger() const { return *m_logger; } + + /// logging instance + std::unique_ptr m_logger; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/GridAxisGenerators.hpp b/Core/include/Acts/Detector/GridAxisGenerators.hpp deleted file mode 100644 index d43d54f3237..00000000000 --- a/Core/include/Acts/Detector/GridAxisGenerators.hpp +++ /dev/null @@ -1,291 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2023 CERN for the benefit of the Acts project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Utilities/detail/Axis.hpp" -#include "Acts/Utilities/detail/Grid.hpp" - -#include -#include -#include - -namespace Acts { -namespace Experimental { - -/// Axis generators are used to allow defining different grid types -/// for indexed geometry objects. -namespace GridAxisGenerators { - -/// @brief Templated base generator for equidistant axis as a tuple - 1D -/// -/// @tparam aType the type of the axis (Bound, Closed, Open) -template -struct Eq { - /// Broadcast the return_type - using return_type = - std::tuple>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid>; - - std::array range = {}; - std::size_t nBins = 0u; - - /// Call operator that generates the Axis - return_type operator()() const { - detail::Axis eAxis(range[0u], - range[1u], nBins); - return {eAxis}; - } -}; - -// All 1D equidistant options -using EqBound = Eq; -using EqOpen = Eq; -using EqClosed = Eq; - -/// @brief Templated base generator for vairable axis as a tuple - 1D -/// -/// @tparam aType the type of the axis (Bound, Closed, Open) -template -struct Var { - /// Broadcast the return_type - using return_type = - std::tuple>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid>; - - std::vector edges = {}; - - /// Call operator that generates the Axis - return_type operator()() const { - detail::Axis vAxis(edges); - return {vAxis}; - } -}; - -// All 1D variable options -using VarBound = Var; -using VarOpen = Var; -using VarClosed = Var; - -/// @brief Templated base generator for two equidistant axes as a tuple - 2D -/// -/// @tparam aType the type of the first axis (Bound, Closed, Open) -/// @tparam bType the type of the second axis (Bound, Closed, Open) -/// -template -struct EqEq { - /// Broadcast the return_type - using return_type = - std::tuple, - detail::Axis>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid, - detail::Axis>; - - std::array range0 = {}; - std::size_t nBins0 = 0u; - std::array range1 = {}; - std::size_t nBins1 = 1u; - - /// Call operator that generates the Axis - return_type operator()() const { - // Create the two axis - detail::Axis aEq(range0[0u], - range0[1u], nBins0); - detail::Axis bEq(range1[0u], - range1[1u], nBins1); - return std::tie(aEq, bEq); - } -}; - -// All 2D EqEq options -using EqBoundEqBound = - EqEq; -using EqBoundEqOpen = - EqEq; -using EqBoundEqClosed = - EqEq; -using EqOpenEqBound = - EqEq; -using EqOpenEqOpen = - EqEq; -using EqOpenEqClosed = - EqEq; -using EqClosedEqBound = - EqEq; -using EqClosedEqOpen = - EqEq; -using EqClosedEqClosed = - EqEq; - -/// @brief Templated base generator for equidistant / variable axes as a tuple - 2D -/// -/// @tparam aType the type of the first axis (Bound, Closed, Open) -/// @tparam bType the type of the second axis (Bound, Closed, Open) -/// -template -struct EqVar { - /// Broadcast the return_type - using return_type = - std::tuple, - detail::Axis>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid, - detail::Axis>; - - std::array range = {}; - std::size_t nBins = 0u; - std::vector edges = {}; - - /// Call operator that generates the Axis - return_type operator()() const { - detail::Axis eqA(range[0u], range[1u], - nBins); - detail::Axis varB(edges); - return std::tie(eqA, varB); - } -}; - -// All 2D EqVar options -using EqBoundVarBound = - EqVar; -using EqBoundVarOpen = - EqVar; -using EqBoundVarClosed = - EqVar; -using EqOpenVarBound = - EqVar; -using EqOpenVarOpen = - EqVar; -using EqOpenVarClosed = - EqVar; -using EqClosedVarBound = - EqVar; -using EqClosedVarOpen = - EqVar; -using EqClosedVarClosed = - EqVar; - -/// @brief Templated base generator for a variable, equidistant axes tuple - 2D -/// -/// @tparam aType the type of the first axis (Bound, Closed, Open) -/// @tparam bType the type of the second axis (Bound, Closed, Open) -/// -template -struct VarEq { - /// Broadcast the return_type - using return_type = - std::tuple, - detail::Axis>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid, - detail::Axis>; - - std::vector edges = {}; - std::array range = {}; - std::size_t nBins = 0u; - - /// Call operator that generates the Axis - return_type operator()() const { - detail::Axis varA(edges); - detail::Axis eqB(range[0u], range[1u], - nBins); - return std::tie(varA, eqB); - } -}; - -// All 2D VarEq options -using VarBoundEqBound = - VarEq; -using VarBoundEqOpen = - VarEq; -using VarBoundEqClosed = - VarEq; -using VarOpenEqBound = - VarEq; -using VarOpenEqOpen = - VarEq; -using VarOpenEqClosed = - VarEq; -using VarClosedEqBound = - VarEq; -using VarClosedEqOpen = - VarEq; -using VarClosedEqClosed = - VarEq; - -/// @brief Templated base generator for a two variable axes tuple - 2D -/// -/// @tparam aType the type of the first axis (Bound, Closed, Open) -/// @tparam bType the type of the second axis (Bound, Closed, Open) -/// -template -struct VarVar { - /// Broadcast the return_type - using return_type = - std::tuple, - detail::Axis>; - - /// Broadcast the grid type - template - using grid_type = - detail::Grid, - detail::Axis>; - - std::vector edges0 = {}; - std::vector edges1 = {}; - - /// Call operator that generates the Axis - return_type operator()() const { - detail::Axis varA(edges0); - detail::Axis varB(edges1); - return std::tie(varA, varB); - } -}; - -// All 2D VarVar options -using VarBoundVarBound = - VarVar; -using VarBoundVarOpen = - VarVar; -using VarBoundVarClosed = - VarVar; -using VarOpenVarBound = - VarVar; -using VarOpenVarOpen = - VarVar; -using VarOpenVarClosed = - VarVar; -using VarClosedVarBound = - VarVar; -using VarClosedVarOpen = - VarVar; -using VarClosedVarClosed = - VarVar; - -} // namespace GridAxisGenerators - -} // namespace Experimental -} // namespace Acts diff --git a/Core/include/Acts/Detector/LayerStructureBuilder.hpp b/Core/include/Acts/Detector/LayerStructureBuilder.hpp new file mode 100644 index 00000000000..71c41424609 --- /dev/null +++ b/Core/include/Acts/Detector/LayerStructureBuilder.hpp @@ -0,0 +1,113 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/interface/IInternalStructureBuilder.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { + +/// @brief This is a builder of layer structures to be contained +/// within a DetectorVolume, it extends the IInternalStructureBuilder +/// interface and provides the internal structure components of +/// DetectorVolume objects to be constructed. +/// +/// It uses the IndexedSurfaceGrid to bin the internal surfaces, +/// and allows for additional support surfaces that are added to the +/// structure and indexing mechanism. Those support structures can +/// also be approximated by planar surfaces, in order to facilitate +/// vectorization of surface intersection calls. +/// +/// The binning can be chosen with a so called `expansion`, a number +/// which indicates the configured expanded bin window in which the +/// surfaces are going to be filled, the details to this strategy +/// can be found in the IndexedGridFiller and IndexedSurfacesGenerator +/// classes. +/// +/// No sub volumes are added to this structure builders, hence, +/// the DetectorVolumeFinder navigation delegate uses the "NoopFinder" +/// breakpoint to indicate the bottom of the volume hierarchy. +/// +class LayerStructureBuilder : public IInternalStructureBuilder { + public: + /// @brief Support parameter defintions + struct Support { + /// Define whether you want to build support structures + std::array values = {}; + /// The surface type to be built + Surface::SurfaceType type = Surface::SurfaceType::Other; + /// Define in which values the support should be constrained + std::vector constraints = s_binningValues; + /// Potential splits into planar approximations + unsigned int splits = 1u; + /// The (optional) layer transform + std::optional transform = std::nullopt; + }; + + /// @brief The surface binning definition + struct Binning { + /// Define the binning of the surfaces + BinningData data; + /// An expansion for the filling (in bins) + size_t expansion = 0u; + }; + + /// @brief Configuration struct for the LayerStructureBuilder + /// + /// It contain: + /// - a source of the surfaces to be built + /// - a definition of surface binning on this layer + /// - a definition of supports to be built + struct Config { + /// Connection point for a function to provide surfaces + std::function>()> surfaces; + /// Definition of Supports + std::vector supports = {}; + /// Definition of Binnings + std::vector binnings = {}; + /// Polyhedron approximations + unsigned int nSegments = 1u; + /// Extra information, mainly for screen output + std::string auxilliary = ""; + }; + + /// Constructor + /// + /// @param cfg is the configuration struct + /// @param logger logging instance for screen output + LayerStructureBuilder(const Config& cfg, + std::unique_ptr logger = getDefaultLogger( + "LayerStructureBuilder", Logging::INFO)); + + /// The interface definition for internal structure creation + /// + /// @param gctx the geometry context at the creation of the internal structure + /// + /// @return a consistent set of detector volume internals + InternalStructure construct(const GeometryContext& gctx) const final; + + private: + /// configuration object + Config m_cfg; + + /// Private acces method to the logger + const Logger& logger() const { return *m_logger; } + + /// logging instance + std::unique_ptr m_logger; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/Portal.hpp b/Core/include/Acts/Detector/Portal.hpp index b83bc83ca96..8b2e07e97b5 100644 --- a/Core/include/Acts/Detector/Portal.hpp +++ b/Core/include/Acts/Detector/Portal.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/NavigationDelegates.hpp" @@ -120,7 +120,7 @@ class Portal : public std::enable_shared_from_this { /// /// @note this overwrites the existing link void assignDetectorVolumeUpdator( - NavigationDirection dir, DetectorVolumeUpdator&& dVolumeUpdator, + Direction dir, DetectorVolumeUpdator&& dVolumeUpdator, const std::vector>& attachedVolumes); /// Update the volume link, w/o directive, i.e. it relies that there's only diff --git a/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp b/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp new file mode 100644 index 00000000000..addd1f1e363 --- /dev/null +++ b/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp @@ -0,0 +1,180 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Common.hpp" +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include +#include + +namespace Acts { + +namespace Experimental { + +class DetectorVolume; +class Portal; + +namespace detail { +namespace CylindricalDetectorHelper { + +/// @brief Connect detector volumes in R +/// +/// @param gctx The geometry context +/// @param volumes the volumes +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note a fair amount of consistency checking is done, +/// and exceptions are thrown if any of the tests fail +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInR( + const GeometryContext& gctx, + std::vector>& volumes, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect detector volumes in Z +/// +/// @param gctx The geometry context +/// @param volumes the volumes +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note a fair amount of consistency checking is done, +/// and exceptions are thrown if any of the tests fail +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInZ( + const GeometryContext& gctx, + std::vector>& volumes, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect detector volumes in phi +/// +/// @param gctx The geometry context +/// @param volumes the volumes +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note a fair amount of consistency checking is done, +/// and exceptions are thrown if any of the tests fail +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInPhi( + const GeometryContext& gctx, + std::vector>& volumes, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Wrap detector volumes in R,Z +/// +/// @param gctx The geometry context +/// @param volumes the volumes +/// @param logLevel is the screen logging level +/// +/// @note a fair amount of consistency checking is done, +/// and exceptions are thrown if any of the tests fail +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer wrapInZR( + const GeometryContext& gctx, + std::vector>& volumes, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect containers in R +/// +/// @param gctx The geometry context +/// @param containers the containers +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note not much checking is done anymore, as the DetectorComponent::PortalContainer +/// are assumed to come properly formed from the prior methods +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInR( + const GeometryContext& gctx, + const std::vector& containers, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect containers in Z +/// +/// @param gctx The geometry context +/// @param containers the containers +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note not much checking is done anymore, as the DetectorComponent::PortalContainer +/// are assumed to come properly formed from the prior methods +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInZ( + const GeometryContext& gctx, + const std::vector& containers, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect containers in Phi +/// +/// @param gctx The geometry context +/// @param containers the containers +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note not much checking is done anymore, as the DetectorComponent::PortalContainer +/// are assumed to come properly formed from the prior methods +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connectInPhi( + const GeometryContext& gctx, + const std::vector& containers, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Wrap container in R,Z - this uses the cutout cylinder bounds +/// +/// @param gctx The geometry context +/// @param containers the containers, i.e. the inner volume and the wrapping container +/// @param logLevel is the screen logging level +/// +/// @note not much checking is done anymore, as the DetectorComponent::PortalContainer +/// are assumed to come properly formed from the prior methods +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer wrapInZR( + const GeometryContext& gctx, + const std::vector& containers, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Helper method to extract r,z,phi boundaries for +/// eventual grid volume search +/// +/// @param gctx the geometry context of the call +/// @param volumes the volumes at input +/// @param logLevel is the screen logging level +/// +/// @return extracted boundary values +std::array, 3u> rzphiBoundaries( + const GeometryContext& gctx, + const std::vector& volumes, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +} // namespace CylindricalDetectorHelper +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/GridAxisGenerators.hpp b/Core/include/Acts/Detector/detail/GridAxisGenerators.hpp new file mode 100644 index 00000000000..f14c28fb909 --- /dev/null +++ b/Core/include/Acts/Detector/detail/GridAxisGenerators.hpp @@ -0,0 +1,301 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/detail/Axis.hpp" +#include "Acts/Utilities/detail/Grid.hpp" + +#include +#include +#include + +namespace Acts { +namespace Experimental { +namespace detail { + +/// Axis generators are used to allow defining different grid types +/// for indexed geometry objects. +/// +/// The call operator() API allows to plug axis generators into +/// dedicated code snippets and create fitting axis types on the fly +/// which then turn into conrete Grid types. +/// +namespace GridAxisGenerators { + +/// @brief Templated base generator for equidistant axis as a tuple - 1D +/// +/// @tparam aType the type of the axis (Bound, Closed, Open) +template +struct Eq { + /// Broadcast the return_type + using return_type = std::tuple< + Acts::detail::Axis>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis>; + + std::array range = {}; + std::size_t nBins = 0u; + + /// Call operator that generates the Axis + return_type operator()() const { + Acts::detail::Axis eAxis( + range[0u], range[1u], nBins); + return {eAxis}; + } +}; + +// All 1D equidistant options +using EqBound = Eq; +using EqOpen = Eq; +using EqClosed = Eq; + +/// @brief Templated base generator for vairable axis as a tuple - 1D +/// +/// @tparam aType the type of the axis (Bound, Closed, Open) +template +struct Var { + /// Broadcast the return_type + using return_type = + std::tuple>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis>; + + std::vector edges = {}; + + /// Call operator that generates the Axis + return_type operator()() const { + Acts::detail::Axis vAxis(edges); + return {vAxis}; + } +}; + +// All 1D variable options +using VarBound = Var; +using VarOpen = Var; +using VarClosed = Var; + +/// @brief Templated base generator for two equidistant axes as a tuple - 2D +/// +/// @tparam aType the type of the first axis (Bound, Closed, Open) +/// @tparam bType the type of the second axis (Bound, Closed, Open) +/// +template +struct EqEq { + /// Broadcast the return_type + using return_type = std::tuple< + Acts::detail::Axis, + Acts::detail::Axis>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis, + Acts::detail::Axis>; + + std::array range0 = {}; + std::size_t nBins0 = 0u; + std::array range1 = {}; + std::size_t nBins1 = 1u; + + /// Call operator that generates the Axis + return_type operator()() const { + // Create the two axis + Acts::detail::Axis aEq( + range0[0u], range0[1u], nBins0); + Acts::detail::Axis bEq( + range1[0u], range1[1u], nBins1); + return std::tie(aEq, bEq); + } +}; + +// All 2D EqEq options +using EqBoundEqBound = EqEq; +using EqBoundEqOpen = EqEq; +using EqBoundEqClosed = EqEq; +using EqOpenEqBound = EqEq; +using EqOpenEqOpen = EqEq; +using EqOpenEqClosed = EqEq; +using EqClosedEqBound = EqEq; +using EqClosedEqOpen = EqEq; +using EqClosedEqClosed = EqEq; + +/// @brief Templated base generator for equidistant / variable axes as a tuple - 2D +/// +/// @tparam aType the type of the first axis (Bound, Closed, Open) +/// @tparam bType the type of the second axis (Bound, Closed, Open) +/// +template +struct EqVar { + /// Broadcast the return_type + using return_type = + std::tuple, + Acts::detail::Axis>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis, + Acts::detail::Axis>; + + std::array range = {}; + std::size_t nBins = 0u; + std::vector edges = {}; + + /// Call operator that generates the Axis + return_type operator()() const { + Acts::detail::Axis eqA( + range[0u], range[1u], nBins); + Acts::detail::Axis varB(edges); + return std::tie(eqA, varB); + } +}; + +// All 2D EqVar options +using EqBoundVarBound = EqVar; +using EqBoundVarOpen = EqVar; +using EqBoundVarClosed = EqVar; +using EqOpenVarBound = EqVar; +using EqOpenVarOpen = EqVar; +using EqOpenVarClosed = EqVar; +using EqClosedVarBound = EqVar; +using EqClosedVarOpen = EqVar; +using EqClosedVarClosed = EqVar; + +/// @brief Templated base generator for a variable, equidistant axes tuple - 2D +/// +/// @tparam aType the type of the first axis (Bound, Closed, Open) +/// @tparam bType the type of the second axis (Bound, Closed, Open) +/// +template +struct VarEq { + /// Broadcast the return_type + using return_type = std::tuple< + Acts::detail::Axis, + Acts::detail::Axis>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis, + Acts::detail::Axis>; + + std::vector edges = {}; + std::array range = {}; + std::size_t nBins = 0u; + + /// Call operator that generates the Axis + return_type operator()() const { + Acts::detail::Axis varA(edges); + Acts::detail::Axis eqB( + range[0u], range[1u], nBins); + return std::tie(varA, eqB); + } +}; + +// All 2D VarEq options +using VarBoundEqBound = VarEq; +using VarBoundEqOpen = VarEq; +using VarBoundEqClosed = VarEq; +using VarOpenEqBound = VarEq; +using VarOpenEqOpen = VarEq; +using VarOpenEqClosed = VarEq; +using VarClosedEqBound = VarEq; +using VarClosedEqOpen = VarEq; +using VarClosedEqClosed = VarEq; + +/// @brief Templated base generator for a two variable axes tuple - 2D +/// +/// @tparam aType the type of the first axis (Bound, Closed, Open) +/// @tparam bType the type of the second axis (Bound, Closed, Open) +/// +template +struct VarVar { + /// Broadcast the return_type + using return_type = + std::tuple, + Acts::detail::Axis>; + + /// Broadcast the grid type + template + using grid_type = Acts::detail::Grid< + T, Acts::detail::Axis, + Acts::detail::Axis>; + + std::vector edges0 = {}; + std::vector edges1 = {}; + + /// Call operator that generates the Axis + return_type operator()() const { + Acts::detail::Axis varA(edges0); + Acts::detail::Axis varB(edges1); + return std::tie(varA, varB); + } +}; + +// All 2D VarVar options +using VarBoundVarBound = VarVar; +using VarBoundVarOpen = VarVar; +using VarBoundVarClosed = VarVar; +using VarOpenVarBound = VarVar; +using VarOpenVarOpen = VarVar; +using VarOpenVarClosed = VarVar; +using VarClosedVarBound = VarVar; +using VarClosedVarOpen = VarVar; +using VarClosedVarClosed = VarVar; + +} // namespace GridAxisGenerators +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/IndexedGridFiller.hpp b/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp similarity index 78% rename from Core/include/Acts/Detector/IndexedGridFiller.hpp rename to Core/include/Acts/Detector/detail/IndexedGridFiller.hpp index 658df397e1a..27b4c1fd575 100644 --- a/Core/include/Acts/Detector/IndexedGridFiller.hpp +++ b/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp @@ -10,10 +10,10 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" +#include "Acts/Detector/detail/ReferenceGenerators.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/Polyhedron.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" -#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Delegate.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/IAxis.hpp" @@ -27,6 +27,7 @@ namespace Acts { namespace Experimental { +namespace detail { /// @brief Helper method to generate completely populated bin sequences /// that respect the boundary type of the axis @@ -41,7 +42,7 @@ namespace Experimental { /// @return a vector of bins to be filled std::vector binSequence(std::array minMaxBins, std::size_t expand, std::size_t nBins, - detail::AxisBoundaryType type) { + Acts::detail::AxisBoundaryType type) { // Return vector for iterations std::vector rBins; /// Helper method to fill a range @@ -57,7 +58,7 @@ std::vector binSequence(std::array minMaxBins, std::size_t bmax = minMaxBins[1u]; // Open/Bound cases - if (type != detail::AxisBoundaryType::Closed) { + if (type != Acts::detail::AxisBoundaryType::Closed) { rBins.reserve(bmax - bmin + 1u + 2 * expand); // handle bmin:/max expand it down (for bound, don't fill underflow) if (type == Acts::detail::AxisBoundaryType::Bound) { @@ -75,7 +76,7 @@ std::vector binSequence(std::array minMaxBins, if (2 * span < nBins and (bmax + expand <= nBins) and (int(bmin) - int(expand) > 0)) { return binSequence({bmin, bmax}, expand, nBins, - detail::AxisBoundaryType::Bound); + Acts::detail::AxisBoundaryType::Bound); } else if (2 * span < nBins) { bmin = int(bmin) - int(expand) > 0 ? bmin - expand : 1u; bmax = bmax + expand <= nBins ? bmax + expand : nBins; @@ -213,81 +214,6 @@ std::string outputIndices(const std::set& lbins) { return rString; } -/// A struct to access the center position -/// -/// This generator will provide only one filling point and hence -/// only a single bin in the indexed grid. -struct CenterReferenceGenerator { - /// Helper to access the Center point of for filling the grid - /// - /// @param gctx the geometry context of this operation - /// @param surface the surface for which the reference point is to be accessed - /// - /// @return a vector of referene points for filling - const std::vector references(const GeometryContext& gctx, - const Surface& surface) const { - return {surface.center(gctx)}; - } -}; - -/// A struct to access reference postions based on bin values -/// -/// This generator will provide only one filling point and hence -/// only a single bin in the indexed grid. -struct BinningValueReferenceGenerator { - /// The binning value - BinningValue bValue; - - /// Helper to access a reference postion based on binning value - /// - /// @param gctx the geometry context of this operation - /// @param surface the surface for which the reference point is to be accessed - /// - /// @return a vector of referene points for filling - const std::vector references(const GeometryContext& gctx, - const Surface& surface) const { - return {surface.binningPosition(gctx, bValue)}; - } -}; - -/// A struct to access generated vertices from surface polyhedrons -/// These vertices are then used to find the bin boundary box for the -/// indexed grid. -/// -/// The grid filling then completes the empty bins in between and -/// expands if necessary. -struct PolyhedronReferenceGenerator { - /// Also use the barycenter - bool addBarycenter = true; - - /// The number of segments to approximate (1 means extrema points only) - unsigned int nSegments = 1; - - /// Helper to access the Center point of for filling the grid - /// - /// @param gctx the geometry context of this operation - /// @param surface the surface for which the reference point is to be accessed - /// - /// @return a vector of referene points for filling - const std::vector references(const GeometryContext& gctx, - const Surface& surface) const { - // Create the return vector - std::vector rPositions; - auto pHedron = surface.polyhedronRepresentation(gctx, nSegments); - rPositions.insert(rPositions.end(), pHedron.vertices.begin(), - pHedron.vertices.end()); - // Add the barycenter if configured - if (addBarycenter) { - Vector3 bc(0., 0., 0.); - std::for_each(rPositions.begin(), rPositions.end(), - [&](const auto& p) { bc += p; }); - bc *= 1. / rPositions.size(); - rPositions.push_back(bc); - } - return rPositions; - } -}; - /// A helper class that fills surfaces into predefined grids struct IndexedGridFiller { /// Bin expansion where needed @@ -381,5 +307,6 @@ struct IndexedGridFiller { const Logger& logger() const { return (*oLogger); } }; +} // namespace detail } // namespace Experimental } // namespace Acts diff --git a/Core/include/Acts/Detector/IndexedSurfacesGenerator.hpp b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp similarity index 91% rename from Core/include/Acts/Detector/IndexedSurfacesGenerator.hpp rename to Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp index 2673b61d9f9..9a501a17025 100644 --- a/Core/include/Acts/Detector/IndexedSurfacesGenerator.hpp +++ b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/detail/IndexedGridFiller.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" #include "Acts/Utilities/Enumerate.hpp" @@ -18,12 +19,17 @@ namespace Acts { namespace Experimental { +namespace detail { /// @brief A templated indexed grid generator. /// /// This Generator creates a SurfaceCandidatesUpdator delegate /// which can then be used in the DetectorVolume class for updating -/// given surface candidates. +/// given surface candidates based on an index grid. +/// +/// It allows for: +/// - certain indices being forcly assigned to all bins +/// - a chosen expansion to fill indices in neighborhood bins /// /// @tparam objects_container the objects container template @@ -33,15 +39,12 @@ struct IndexedSurfacesGenerator { surface_container surfaces = {}; // Indices of surfaces that are to be assigned to all bins std::vector assignToAll = {}; - /// The binning + /// The binning for the indexing std::vector bValues = {}; - // Bin expansion std::vector binExpansion = {}; - /// The transform into the local binning schema Transform3 transform = Transform3::Identity(); - /// Screen output logger std::unique_ptr oLogger = getDefaultLogger("IndexedSurfacesGenerator", Logging::INFO); @@ -102,5 +105,6 @@ struct IndexedSurfacesGenerator { const Logger& logger() const { return *oLogger; } }; +} // namespace detail } // namespace Experimental } // namespace Acts diff --git a/Core/include/Acts/Detector/detail/KdtSurfacesProvider.hpp b/Core/include/Acts/Detector/detail/KdtSurfacesProvider.hpp new file mode 100644 index 00000000000..3419b69a697 --- /dev/null +++ b/Core/include/Acts/Detector/detail/KdtSurfacesProvider.hpp @@ -0,0 +1,172 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/IndexedGridFiller.hpp" +#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp" +#include "Acts/Detector/detail/ReferenceGenerators.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/KDTree.hpp" + +#include +#include +#include +#include + +namespace Acts { + +namespace Experimental { + +namespace detail { + +/// @brief A wrapper class around a KDTree of surfaces +/// +/// It also deals with the conversion from global query to +/// KDTree lookup positions +/// +template +class KdtSurfaces { + public: + /// Broadcast the surface KDT type + using KDTS = + KDTree, ActsScalar, std::array, bSize>; + + /// Broadcast the query definition + using Query = std::array; + + /// Broadcast the entry + using Entry = std::pair>; + + /// Constructor from a vector of surfaces + /// + /// @param gctx the geometry context of this call + /// @param surfaces the surfaces to be filled into the tree + /// @param casts the cast list from global position into kdtree local + /// @param rgen the reference point generator + KdtSurfaces(const GeometryContext& gctx, + const std::vector>& surfaces, + const std::array& casts, + const reference_generator& rgen = PolyhedronReferenceGenerator{}) + : m_kdt(nullptr), m_casts(casts), m_rGenerator(rgen) { + // Simple check if the dimension is correct + if (kDIM == 0u) { + throw std::invalid_argument( + "KdtSurfaces: dimension and/or cast rules are incorrect."); + } + // Fill the tree from surfaces + std::vector kdtEntries; + kdtEntries.reserve(surfaces.size()); + for (auto& s : surfaces) { + // Generate the references and the center of gravity from it + const auto references = m_rGenerator.references(gctx, *s); + const auto ref = cog(references); + // Now cast into the correct fill position + std::array fill = {}; + fillCasts(ref, fill, std::make_integer_sequence{}); + kdtEntries.push_back({fill, s}); + } + // Create the KDTree + m_kdt = std::make_unique(std::move(kdtEntries)); + } + + /// Query with a Range object + /// + /// @param range is the range to be queried + /// + /// @return the matching surfaces from the KDT structure + std::vector> surfaces( + const RangeXD& range) const { + // Strip the surfaces + std::vector> surfacePtrs; + auto surfaceQuery = m_kdt->rangeSearchWithKey(range); + std::for_each(surfaceQuery.begin(), surfaceQuery.end(), + [&](auto& s) { surfacePtrs.push_back(s.second); }); + return surfacePtrs; + } + + /// Query with an Extent object + /// + /// @param extent is the range Extent to be queried + /// + /// @return the matching surfaces fpulled from the KDT structure + std::vector> surfaces(const Extent& extent) const { + RangeXD qRange; + for (auto [ibv, v] : enumerate(m_casts)) { + qRange[ibv] = extent.range(v); + } + return surfaces(qRange); + } + + private: + /// The KDTree as single source for the surfaces (maybe shared) + std::unique_ptr m_kdt = nullptr; + + /// Cast values that turn a global position to lookup position + std::array m_casts = {}; + + /// Helper to generate refernce points for filling + reference_generator m_rGenerator; + + /// Unroll the cast loop + /// @param position is the position of the update call + /// @param a is the array to be filled + template + void fillCasts(const Vector3& position, Array& a, + std::index_sequence /*indices*/) const { + ((a[idx] = VectorHelpers::cast(position, m_casts[idx])), ...); + } + + /// Helper method to calculate the center of gravity + /// + /// @param positions are the reference positions that go in + /// @note will do nothing if vector size is equal to 1 + /// + /// @note no checking on positions.empty() is done as the + /// positions are to be provided by a generator which itself + /// is tested for consistency + /// + /// @return the center of gravity + Vector3 cog(const std::vector& positions) const { + // Build the center of gravity of the n positions + Vector3 c(0., 0., 0.); + ActsScalar weight = 1. / positions.size(); + std::for_each(positions.begin(), positions.end(), + [&](const auto& p) { c += weight * p; }); + return c; + } +}; + +/// @brief Callable struct wrapper around the KDT surface structure +/// +/// This allows to create small region based callable structs at +/// configuration level that are then connected to an InternalStructureBuilder +template +struct KdtSurfacesProvider { + /// The prefilled surfaces in a KD tree structure, it is generally shared + /// amongst different providers + std::shared_ptr> kdt = nullptr; + /// The query region + Extent region; + /// The call operator that provides the function call + std::vector> operator()() const { + return kdt->surfaces(region); + } +}; + +} // namespace detail + +} // namespace Experimental + +} // namespace Acts diff --git a/Core/include/Acts/Detector/PortalHelper.hpp b/Core/include/Acts/Detector/detail/PortalHelper.hpp similarity index 74% rename from Core/include/Acts/Detector/PortalHelper.hpp rename to Core/include/Acts/Detector/detail/PortalHelper.hpp index 4bf981f6889..99d3baf9b4a 100644 --- a/Core/include/Acts/Detector/PortalHelper.hpp +++ b/Core/include/Acts/Detector/detail/PortalHelper.hpp @@ -23,26 +23,32 @@ namespace Experimental { class DetectorVolume; -/// Definition of a portal replacement when building proto containers +/// @brief Definition of a portal replacement when building proto containers +/// /// It consists of the new portal, the index, the direction, the parameters /// gathered from the sub volumes, the binning description using PortalReplacement = - std::tuple, unsigned int, - NavigationDirection, std::vector, BinningValue>; + std::tuple, unsigned int, Direction, + std::vector, BinningValue>; + +namespace detail { +namespace PortalHelper { -/// @brief Create and attach the multi link updator +/// @brief Create and attach the multi link updator, the portal will get +/// a volume updator attached, that points to the different sub volumes +/// depending on the global position and binning /// /// @param gctx the geometry context /// @param volumes are the volumes that are pointed to /// @param pReplacements are the portal replacements that are newly connected /// -/// void attachDetectorVolumeUpdators( const GeometryContext& gctx, const std::vector>& volumes, std::vector& pReplacements); -/// @brief Method that strips out attached volumes from portals +/// @brief Method that strips out attached volumes from portals and +/// provides them back to the caller. /// /// @note it throws an exception if both sides are already taken /// @@ -52,5 +58,7 @@ void attachDetectorVolumeUpdators( std::vector> attachedDetectorVolumes( Portal& portal) noexcept(false); +} // namespace PortalHelper +} // namespace detail } // namespace Experimental } // namespace Acts diff --git a/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp b/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp new file mode 100644 index 00000000000..cd0c78add4f --- /dev/null +++ b/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp @@ -0,0 +1,100 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/Polyhedron.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include + +namespace Acts { +namespace Experimental { +namespace detail { + +/// A struct to access the center position +/// +/// This generator will provide only one filling point and hence +/// only a single bin in the indexed grid. +struct CenterReferenceGenerator { + /// Helper to access the Center point of for filling the grid + /// + /// @param gctx the geometry context of this operation + /// @param surface the surface for which the reference point is to be accessed + /// + /// @return a vector of referene points for filling + const std::vector references(const GeometryContext& gctx, + const Surface& surface) const { + return {surface.center(gctx)}; + } +}; + +/// A struct to access reference postions based on bin values +/// +/// This generator will provide only one filling point and hence +/// only a single bin in the indexed grid. +struct BinningValueReferenceGenerator { + /// The binning value + BinningValue bValue = BinningValue::binValues; + + /// Helper to access a reference postion based on binning value + /// + /// @param gctx the geometry context of this operation + /// @param surface the surface for which the reference point is to be accessed + /// + /// @return a vector of referene points for filling + const std::vector references(const GeometryContext& gctx, + const Surface& surface) const { + return {surface.binningPosition(gctx, bValue)}; + } +}; + +/// A struct to access generated vertices from surface polyhedrons +/// These vertices are then used to find the bin boundary box for the +/// indexed grid. +/// +/// The grid filling then completes the empty bins in between and +/// expands if necessary. +struct PolyhedronReferenceGenerator { + /// Also use the barycenter + bool addBarycenter = true; + + /// The number of segments to approximate (1 means extrema points only) + unsigned int nSegments = 1; + + /// Helper to access the Center point of for filling the grid + /// + /// @param gctx the geometry context of this operation + /// @param surface the surface for which the reference point is to be accessed + /// + /// @return a vector of referene points for filling + const std::vector references(const GeometryContext& gctx, + const Surface& surface) const { + // Create the return vector + std::vector rPositions; + auto pHedron = surface.polyhedronRepresentation(gctx, nSegments); + rPositions.insert(rPositions.end(), pHedron.vertices.begin(), + pHedron.vertices.end()); + // Add the barycenter if configured + if (addBarycenter) { + Vector3 bc(0., 0., 0.); + std::for_each(rPositions.begin(), rPositions.end(), + [&](const auto& p) { bc += p; }); + bc *= 1. / rPositions.size(); + rPositions.push_back(bc); + } + return rPositions; + } +}; + +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/SupportHelper.hpp b/Core/include/Acts/Detector/detail/SupportHelper.hpp new file mode 100644 index 00000000000..2eeb5dd4e26 --- /dev/null +++ b/Core/include/Acts/Detector/detail/SupportHelper.hpp @@ -0,0 +1,71 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Helpers.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { +namespace detail { +/// @brief This file contains helper methods to build common support structures +/// such as support cylinders or discs. +/// +/// It allows to model those as Disc/CylinderSurface objects, but also - if +/// configured such - as approximations built from palanr surfaces +namespace SupportHelper { + +/// @brief Helper method to build cylindrical support structure +/// +/// @param transform the transform where this cylinder should be located +/// @param bounds the boundary values: r, halfZ, halfPhi, avgPhi, bevell parameters +/// @param splits the number of surfaces through which the surface is approximated (1u ... cylinder) +/// +/// @return a vector of surfaces that represent this support +std::vector> cylindricalSupport( + const Transform3& transform, const std::array& bounds, + unsigned int splits = 1u); + +/// @brief Helper method to build disc support structure +/// +/// @param transform the transform where this disc should be located +/// @param bounds the boundary values: minR, maxR, halfPhi, avgPhi +/// @param splits the number of surfaces through which the surface is approximated (1u ... disc) +/// +/// @return a vector of surfaces that represent this support +std::vector> discSupport( + const Transform3& transform, const std::array& bounds, + unsigned int splits = 1u); + +/// Add support to already existing surfaces +/// +/// @param layerSurfaces [in, out] the surfaces to which those are added +/// @param assignToAll [in, out] indices that are assigned to all bins in the indexing +/// @param layerExtent the externally provided layer Extent +/// @param layerRepresentation the shape of the layer +/// @param layerSupportValues the support structue in numbers +/// @param layerTransform is an optional value of the layer transform +/// @param supportSplits the number of splits if splitting is configured +/// +/// @note this modifies the layerSurfaces and toAllIndices +void addSupport(std::vector>& layerSurfaces, + std::vector& assignToAll, const Extent& layerExtent, + Surface::SurfaceType layerRepresentation, + const std::array& layerSupportValues, + std::optional layerTransform = std::nullopt, + unsigned int supportSplits = 1u); + +} // namespace SupportHelper +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/interface/IDetectorComponentBuilder.hpp b/Core/include/Acts/Detector/interface/IDetectorComponentBuilder.hpp new file mode 100644 index 00000000000..57629b4fc4e --- /dev/null +++ b/Core/include/Acts/Detector/interface/IDetectorComponentBuilder.hpp @@ -0,0 +1,37 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Geometry/GeometryContext.hpp" + +namespace Acts { +namespace Experimental { + +/// @brief This is the interface for detector component builders; +/// such a builder could be a simple detector volume builder, with +/// or without internal structure, or more complicated objects. +/// +class IDetectorComponentBuilder { + public: + virtual ~IDetectorComponentBuilder() = default; + + /// The interface method to be implemented by all detector + /// component builder + /// + /// @param roots [in, out] the collection of root volumes + /// @param gctx The geometry context for this call + /// + /// @return an outgoing detector component + virtual DetectorComponent construct(RootDetectorVolumes& roots, + const GeometryContext& gctx) const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/interface/IExternalStructureBuilder.hpp b/Core/include/Acts/Detector/interface/IExternalStructureBuilder.hpp new file mode 100644 index 00000000000..efc55d7e1e7 --- /dev/null +++ b/Core/include/Acts/Detector/interface/IExternalStructureBuilder.hpp @@ -0,0 +1,32 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Geometry/GeometryContext.hpp" + +namespace Acts { +namespace Experimental { + +/// @brief This is the interface definition of external structure +/// builders for DetectorVolume construction. +class IExternalStructureBuilder { + public: + virtual ~IExternalStructureBuilder() = default; + + /// The virtual interface definition for external structure creation + /// + /// @param gctx the geometry context at the creation of the internal structure + /// + /// @return a consistent set of detector volume externals + virtual ExternalStructure construct(const GeometryContext& gctx) const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/interface/IInternalStructureBuilder.hpp b/Core/include/Acts/Detector/interface/IInternalStructureBuilder.hpp new file mode 100644 index 00000000000..6d306735ba3 --- /dev/null +++ b/Core/include/Acts/Detector/interface/IInternalStructureBuilder.hpp @@ -0,0 +1,35 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Geometry/GeometryContext.hpp" + +namespace Acts { +namespace Experimental { + +/// @brief This is the interface definition of internal structure +/// builders for DetectorVolume construction. +/// +/// It is assumed that each builder returns a consistent set of +/// DetectorVolume internals, which in turn can be directly provided +/// to a DetectorVolume constructor. +class IInternalStructureBuilder { + public: + virtual ~IInternalStructureBuilder() = default; + /// The interface definition for internal structure creation + /// + /// @param gctx the geometry context at the creation of the internal structure + /// + /// @return a consistent set of detector volume internals + virtual InternalStructure construct(const GeometryContext& gctx) const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Digitization/DigitizationCell.hpp b/Core/include/Acts/Digitization/DigitizationCell.hpp index 8dd700077aa..3f3215fef94 100644 --- a/Core/include/Acts/Digitization/DigitizationCell.hpp +++ b/Core/include/Acts/Digitization/DigitizationCell.hpp @@ -21,8 +21,6 @@ struct DigitizationCell final { // connstruct them DigitizationCell(size_t ch0, size_t ch1, float d = 0.) : channel0(ch0), channel1(ch1), data(d) {} - // copy them - DigitizationCell(const DigitizationCell& dc) = default; /// To merge cells in case they are at the same position /// @param dc the cell to be added to the current cell diff --git a/Core/include/Acts/EventData/MultiTrajectory.hpp b/Core/include/Acts/EventData/MultiTrajectory.hpp index 98f41e7c13e..af4ecf9dd97 100644 --- a/Core/include/Acts/EventData/MultiTrajectory.hpp +++ b/Core/include/Acts/EventData/MultiTrajectory.hpp @@ -44,14 +44,92 @@ enum TrackStateFlag { NumTrackStateFlags = 6 }; -using TrackStateType = std::bitset; +class ConstTrackStateType; + +/// View type over a bitset stored in a 64 bit integer +/// This view allows modifications. +class TrackStateType { + public: + using raw_type = std::uint64_t; + /// Constructor from a reference to the underlying value container + /// @param raw the value container + TrackStateType(raw_type& raw) : m_raw{&raw} {} + + /// Assign the value from another set of flags + /// @param other the other set of flags to assign + /// @return this object + TrackStateType& operator=(const TrackStateType& other) { + *m_raw = *other.m_raw; + return *this; + } + + /// Assign the value from another set of flags + /// @param other the other set of flags to assign + /// @return this object + TrackStateType& operator=(const ConstTrackStateType& other); + + /// Return if the bit at position @p pos is 1 + /// @param pos the bit position + /// @return if the bit at @p pos is one or not + bool test(std::size_t pos) const { + std::bitset bs{*m_raw}; + return bs.test(pos); + } + + /// Change the value of the bit at position @p pos to @p value. + /// @param pos the position of the bit to change + /// @param value the value to change the bit to + void set(std::size_t pos, bool value = true) { + std::bitset bs{*m_raw}; + bs.set(pos, value); + *m_raw = bs.to_ullong(); + } + + /// Change the value of the bit at position at @p pos to @c false + /// @param pos the position of the bit to change + void reset(std::size_t pos) { set(pos, false); } + + private: + raw_type* m_raw{nullptr}; +}; + +/// View type over a bitset stored in a 64 bit integer +/// This view does not allow modifications +class ConstTrackStateType { + public: + using raw_type = std::uint64_t; + + /// Constructor from a reference to the underlying value container + /// @param raw the value container + ConstTrackStateType(const raw_type& raw) : m_raw{&raw} {} + + /// Return if the bit at position @p pos is 1 + /// @param pos the bit position + /// @return if the bit at @p pos is one or not + bool test(std::size_t pos) const { + std::bitset bs{*m_raw}; + return bs.test(pos); + } + + private: + friend class TrackStateType; + const raw_type* m_raw{nullptr}; +}; + +inline TrackStateType& TrackStateType::operator=( + const ConstTrackStateType& other) { + *m_raw = *other.m_raw; + return *this; +} + +// using TrackStateType = std::bitset; // forward declarations template class MultiTrajectory; class Surface; -using ProjectorBitset = unsigned long long; +using ProjectorBitset = uint64_t; namespace detail_lt { /// Either type T or const T depending on the boolean. @@ -392,8 +470,9 @@ class TrackStateProxy { pathLength() = other.pathLength(); typeFlags() = other.typeFlags(); - // can be nullptr, but we just take that - setReferenceSurface(other.referenceSurfacePointer()); + if (other.hasReferenceSurface()) { + setReferenceSurface(other.referenceSurface().getSharedPtr()); + } } /// Unset an optional track state component @@ -406,9 +485,15 @@ class TrackStateProxy { /// Reference surface. /// @return the reference surface const Surface& referenceSurface() const { - assert(has()); - return *component, - hashString("referenceSurface")>(); + assert(hasReferenceSurface() && + "TrackState does not have reference surface"); + return *m_traj->referenceSurface(m_istate); + } + + /// Returns if the track state has a non nullptr surface associated + /// @return whether a surface exists or not + bool hasReferenceSurface() const { + return m_traj->referenceSurface(m_istate) != nullptr; } // NOLINTBEGIN(performance-unnecessary-value-param) @@ -419,8 +504,7 @@ class TrackStateProxy { /// @note This overload is only present in case @c ReadOnly is false. template > void setReferenceSurface(std::shared_ptr srf) { - component, - hashString("referenceSurface")>() = std::move(srf); + m_traj->setReferenceSurface(m_istate, std::move(srf)); } // NOLINTEND(performance-unnecessary-value-param) @@ -640,8 +724,7 @@ class TrackStateProxy { /// @note Const version ConstCovariance jacobian() const { assert(has()); - return m_traj->self().jacobian( - component()); + return m_traj->self().jacobian(m_istate); } /// Returns the jacobian from the previous trackstate to this one @@ -650,8 +733,7 @@ class TrackStateProxy { template > Covariance jacobian() { assert(has()); - return m_traj->self().jacobian( - component()); + return m_traj->self().jacobian(m_istate); } /// Returns whether a jacobian is set for this trackstate @@ -763,8 +845,7 @@ class TrackStateProxy { template ConstMeasurement calibrated() const { assert(has()); - return m_traj->self().template measurement( - component()); + return m_traj->self().template measurement(m_istate); } /// Full calibrated measurement vector. Might contain additional zeroed @@ -775,8 +856,7 @@ class TrackStateProxy { typename = std::enable_if_t> Measurement calibrated() { assert(has()); - return m_traj->self().template measurement( - component()); + return m_traj->self().template measurement(m_istate); } /// Full calibrated measurement covariance matrix. The effective covariance @@ -786,8 +866,7 @@ class TrackStateProxy { template ConstMeasurementCovariance calibratedCovariance() const { assert(has()); - return m_traj->self().template measurementCovariance( - component()); + return m_traj->self().template measurementCovariance(m_istate); } /// Full calibrated measurement covariance matrix. The effective covariance @@ -798,8 +877,7 @@ class TrackStateProxy { typename = std::enable_if_t> MeasurementCovariance calibratedCovariance() { assert(has()); - return m_traj->self().template measurementCovariance( - component()); + return m_traj->self().template measurementCovariance(m_istate); } /// Dynamic measurement vector with only the valid dimensions. @@ -944,14 +1022,16 @@ class TrackStateProxy { /// reference. /// @return reference to the type flags. template > - TrackStateType& typeFlags() { - return component(); + TrackStateType typeFlags() { + return TrackStateType{ + component()}; } /// Getter for the type flags. Returns a copy of the type flags value. /// @return The type flags of this track state - TrackStateType typeFlags() const { - return component(); + ConstTrackStateType typeFlags() const { + return ConstTrackStateType{ + component()}; } template > @@ -966,11 +1046,6 @@ class TrackStateProxy { TrackStateProxy(ConstIf, ReadOnly>& trajectory, IndexType istate); - const std::shared_ptr& referenceSurfacePointer() const { - return component, - hashString("referenceSurface")>(); - } - TransitiveConstPointer, ReadOnly>> m_traj; IndexType m_istate; @@ -1459,6 +1534,16 @@ class MultiTrajectory { return self().getUncalibratedSourceLink_impl(istate); } + const Surface* referenceSurface(IndexType istate) const { + return self().referenceSurface_impl(istate); + } + + template > + void setReferenceSurface(IndexType istate, + std::shared_ptr surface) { + self().setReferenceSurface_impl(istate, std::move(surface)); + } + private: friend class detail_lt::TrackStateProxy; friend class detail_lt::TrackStateProxy; diff --git a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp b/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp index 920366c0f73..65ad1c9363c 100644 --- a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp @@ -168,6 +168,8 @@ class SingleBoundTrackParameters { return m_params[kIndex]; } + /// Local spatial position two-vector. + Vector2 localPosition() const { return m_params.segment<2>(eBoundLoc0); } /// Space-time position four-vector. /// /// @param[in] geoCtx Geometry context for the local-to-global @@ -178,11 +180,9 @@ class SingleBoundTrackParameters { /// select the appropriate transformation and might be a computationally /// expensive operation. Vector4 fourPosition(const GeometryContext& geoCtx) const { - const Vector2 loc(m_params[eBoundLoc0], m_params[eBoundLoc1]); - const Vector3 dir = makeDirectionUnitFromPhiTheta(m_params[eBoundPhi], - m_params[eBoundTheta]); Vector4 pos4; - pos4.segment<3>(ePos0) = m_surface->localToGlobal(geoCtx, loc, dir); + pos4.segment<3>(ePos0) = + m_surface->localToGlobal(geoCtx, localPosition(), unitDirection()); pos4[eTime] = m_params[eBoundTime]; return pos4; } @@ -196,10 +196,7 @@ class SingleBoundTrackParameters { /// select the appropriate transformation and might be a computationally /// expensive operation. Vector3 position(const GeometryContext& geoCtx) const { - const Vector2 loc(m_params[eBoundLoc0], m_params[eBoundLoc1]); - const Vector3 dir = makeDirectionUnitFromPhiTheta(m_params[eBoundPhi], - m_params[eBoundTheta]); - return m_surface->localToGlobal(geoCtx, loc, dir); + return m_surface->localToGlobal(geoCtx, localPosition(), unitDirection()); } /// Time coordinate. Scalar time() const { return m_params[eBoundTime]; } diff --git a/Core/include/Acts/EventData/SourceLink.hpp b/Core/include/Acts/EventData/SourceLink.hpp index 074ccc08f74..4792217ea6f 100644 --- a/Core/include/Acts/EventData/SourceLink.hpp +++ b/Core/include/Acts/EventData/SourceLink.hpp @@ -117,10 +117,7 @@ struct SourceLinkAdapterIterator { return !(*this == other); } - Acts::SourceLink operator*() const { - const auto& sl = *m_iterator; - return Acts::SourceLink{sl}; - } + Acts::SourceLink operator*() const { return Acts::SourceLink{*m_iterator}; } auto operator-(const SourceLinkAdapterIterator& other) const { return m_iterator - other.m_iterator; diff --git a/Core/include/Acts/EventData/TrackContainer.hpp b/Core/include/Acts/EventData/TrackContainer.hpp index f6c052a3e1b..7f93f292570 100644 --- a/Core/include/Acts/EventData/TrackContainer.hpp +++ b/Core/include/Acts/EventData/TrackContainer.hpp @@ -97,7 +97,9 @@ class TrackContainer { /// Get a const track proxy for a track index /// @param itrack the track index in the container /// @return A const track proxy for the index - ConstTrackProxy getTrack(IndexType itrack) const { return {*this, itrack}; } + ConstTrackProxy getTrack(IndexType itrack) const { + return {*this, itrack}; + } /// Get a mutable track proxy for a track index /// @param itrack the track index in the container @@ -109,7 +111,9 @@ class TrackContainer { /// Get the size of the track container /// @return the sixe - constexpr IndexType size() const { return m_container->size_impl(); } + constexpr IndexType size() const { + return m_container->size_impl(); + } /// Add a track to the container. Note this only creates the logical track and /// allocates memory. You can combine this with @c getTrack to obtain a track proxy @@ -157,7 +161,9 @@ class TrackContainer { /// Get a const reference to the track container backend /// @return a const reference to the backend - const auto& container() const { return *m_container; } + const auto& container() const { + return *m_container; + } /// Get a mutable reference to the track state container backend /// @return a mutable reference to the backend @@ -175,11 +181,15 @@ class TrackContainer { /// Get a const reference to the track state container backend /// @return a const reference to the backend - const auto& trackStateContainer() const { return *m_traj; } + const auto& trackStateContainer() const { + return *m_traj; + } /// Retrieve the holder of the track state container /// @return The track state container including it's holder - const auto& trackStateContainerHolder() const { return m_traj; } + const auto& trackStateContainerHolder() const { + return m_traj; + } /// Get a mutable iterator to the first track in the container /// @return a mutable iterator to the first track diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index 55ad4fa67f9..6daa885f8d0 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -269,8 +269,7 @@ class TrackProxy { /// Get the reference surface of the track (e.g. the perigee) /// @return the reference surface const Surface& referenceSurface() const { - return *component, - hashString("referenceSurface")>(); + return *m_container->container().referenceSurface_impl(m_index); } // NOLINTBEGIN(performance-unnecessary-value-param) @@ -279,16 +278,15 @@ class TrackProxy { /// @param srf The surface to set template > void setReferenceSurface(std::shared_ptr srf) { - component, - hashString("referenceSurface")>() = std::move(srf); + m_container->container().setReferenceSurface_impl(m_index, std::move(srf)); } // NOLINTEND(performance-unnecessary-value-param) /// Return whether a reference surface is associated to this track /// @return whether a surface exists or not bool hasReferenceSurface() const { - return !!component, - hashString("referenceSurface")>(); + // @TODO: This could be more efficient + return m_container->container().referenceSurface_impl(m_index) != nullptr; } /// Get the parameters of the track at the reference surface (e.g. perigee). @@ -329,27 +327,39 @@ class TrackProxy { /// Access the theta parameter of the track at the reference surface /// @return The theta parameter - ActsScalar theta() const { return parameters()[eBoundTheta]; } + ActsScalar theta() const { + return parameters()[eBoundTheta]; + } /// Access the phi parameter of the track at the reference surface /// @return The phi parameter - ActsScalar phi() const { return parameters()[eBoundPhi]; } + ActsScalar phi() const { + return parameters()[eBoundPhi]; + } /// Access the loc0 parameter of the track at the reference surface /// @return The loc0 parameter - ActsScalar loc0() const { return parameters()[eBoundLoc0]; } + ActsScalar loc0() const { + return parameters()[eBoundLoc0]; + } /// Access the loc1 parameter of the track at the reference surface /// @return The loc1 parameter - ActsScalar loc1() const { return parameters()[eBoundLoc1]; } + ActsScalar loc1() const { + return parameters()[eBoundLoc1]; + } /// Access the time parameter of the track at the reference surface /// @return The time parameter - ActsScalar time() const { return parameters()[eBoundTime]; } + ActsScalar time() const { + return parameters()[eBoundTime]; + } /// Access the q/p (curvature) parameter of the track at the reference surface /// @return The q/p parameter - ActsScalar qOverP() const { return parameters()[eBoundQOverP]; } + ActsScalar qOverP() const { + return parameters()[eBoundQOverP]; + } /// Get the absolute momentum of the tack /// @return The absolute track momentum @@ -371,12 +381,16 @@ class TrackProxy { /// Get the global momentum vector /// @return the global momentum vector - Vector3 momentum() const { return absoluteMomentum() * unitDirection(); } + Vector3 momentum() const { + return absoluteMomentum() * unitDirection(); + } /// Get a range over the track states of this track. Return value is /// compatible with range based for loop. Const version /// @return Track state range to iterate over - auto trackStates() const { return m_container->trackStateRange(m_index); } + auto trackStates() const { + return m_container->trackStateRange(m_index); + } /// Get a range over the track states of this track. Return value is /// compatible with range based for loop. Mutable version @@ -481,7 +495,9 @@ class TrackProxy { /// Return the chi squared for the track. Const version /// @return The chi squared - float chi2() const { return component(hashString("chi2")); } + float chi2() const { + return component(hashString("chi2")); + } /// Return a mutable reference to the number of degrees of freedom for the /// track. Mutable version @@ -500,7 +516,9 @@ class TrackProxy { /// Return the index of this track in the track container /// @note This is separate from the tip index /// @return the track index - IndexType index() const { return m_index; } + IndexType index() const { + return m_index; + } /// Return a reference to the track container backend, mutable version. /// @return reference to the track container backend @@ -534,7 +552,9 @@ class TrackProxy { /// Return a reference to the track container backend, const version. /// @return reference to the track container backend - const auto& container() const { return *m_container; } + const auto& container() const { + return *m_container; + } /// Equality operator with another track proxy /// Checks the container identity and the track index diff --git a/Core/include/Acts/EventData/TrackStatePropMask.hpp b/Core/include/Acts/EventData/TrackStatePropMask.hpp index 5ef5be985bb..1523b5ab13b 100644 --- a/Core/include/Acts/EventData/TrackStatePropMask.hpp +++ b/Core/include/Acts/EventData/TrackStatePropMask.hpp @@ -10,6 +10,7 @@ #include "Acts/Utilities/EnumBitwiseOperators.hpp" +#include #include #include #include @@ -24,7 +25,7 @@ namespace Acts { /// a jacobian. /// The enum is used as a strong type wrapper around the bits to prevent /// autoconversion from integer -enum struct TrackStatePropMask : uint8_t { +enum struct TrackStatePropMask : std::uint8_t { None = 0, Predicted = 1 << 0, Filtered = 1 << 1, @@ -33,7 +34,7 @@ enum struct TrackStatePropMask : uint8_t { Calibrated = 1 << 4, - All = std::numeric_limits::max(), // should be all ones + All = std::numeric_limits::max(), // should be all ones }; ACTS_DEFINE_ENUM_BITWISE_OPERATORS(TrackStatePropMask) diff --git a/Core/include/Acts/EventData/VectorMultiTrajectory.hpp b/Core/include/Acts/EventData/VectorMultiTrajectory.hpp index f00058ac085..2956b2f75ff 100644 --- a/Core/include/Acts/EventData/VectorMultiTrajectory.hpp +++ b/Core/include/Acts/EventData/VectorMultiTrajectory.hpp @@ -134,7 +134,7 @@ class VectorMultiTrajectoryBase { double chi2 = 0; double pathLength = 0; - TrackStateType typeFlags; + TrackStateType::raw_type typeFlags{}; IndexType iuncalibrated = kInvalid; IndexType icalibratedsourcelink = kInvalid; @@ -215,16 +215,8 @@ class VectorMultiTrajectoryBase { return &instance.m_index[istate].ifiltered; case "smoothed"_hash: return &instance.m_index[istate].ismoothed; - case "calibrated"_hash: - return &instance.m_measOffset[istate]; - case "calibratedCov"_hash: - return &instance.m_measCovOffset[istate]; - case "jacobian"_hash: - return &instance.m_index[istate].ijacobian; case "projector"_hash: return &instance.m_projectors[instance.m_index[istate].iprojector]; - case "referenceSurface"_hash: - return &instance.m_referenceSurfaces[istate]; case "measdim"_hash: return &instance.m_index[istate].measdim; case "chi2"_hash: @@ -270,6 +262,9 @@ class VectorMultiTrajectoryBase { } } + // END INTERFACE HELPER + + public: IndexType calibratedSize_impl(IndexType istate) const { return m_index[istate].measdim; } @@ -278,8 +273,11 @@ class VectorMultiTrajectoryBase { return m_sourceLinks[m_index[istate].iuncalibrated].value(); } - // END INTERFACE HELPER + const Surface* referenceSurface_impl(IndexType istate) const { + return m_referenceSurfaces[istate].get(); + } + protected: /// index to map track states to the corresponding std::vector m_index; std::vector m_previous; @@ -331,7 +329,6 @@ class VectorMultiTrajectory final return detail_vmt::VectorMultiTrajectoryBase::statistics(*this); } - private: // BEGIN INTERFACE TrackStateProxy::Parameters parameters_impl(IndexType parIdx) { return TrackStateProxy::Parameters{m_params[parIdx].data()}; @@ -349,34 +346,40 @@ class VectorMultiTrajectory final return ConstTrackStateProxy::Covariance{m_cov[parIdx].data()}; } - TrackStateProxy::Covariance jacobian_impl(IndexType parIdx) { - return TrackStateProxy::Covariance{m_jac[parIdx].data()}; + TrackStateProxy::Covariance jacobian_impl(IndexType istate) { + IndexType jacIdx = m_index[istate].ijacobian; + return TrackStateProxy::Covariance{m_jac[jacIdx].data()}; } - ConstTrackStateProxy::Covariance jacobian_impl(IndexType parIdx) const { - return ConstTrackStateProxy::Covariance{m_jac[parIdx].data()}; + ConstTrackStateProxy::Covariance jacobian_impl(IndexType istate) const { + IndexType jacIdx = m_index[istate].ijacobian; + return ConstTrackStateProxy::Covariance{m_jac[jacIdx].data()}; } template - TrackStateProxy::Measurement measurement_impl(IndexType offset) { + TrackStateProxy::Measurement measurement_impl(IndexType istate) { + IndexType offset = m_measOffset[istate]; return TrackStateProxy::Measurement{&m_meas[offset]}; } template ConstTrackStateProxy::Measurement measurement_impl( - IndexType offset) const { + IndexType istate) const { + IndexType offset = m_measOffset[istate]; return ConstTrackStateProxy::Measurement{&m_meas[offset]}; } template TrackStateProxy::MeasurementCovariance measurementCovariance_impl( - IndexType offset) { + IndexType istate) { + IndexType offset = m_measCovOffset[istate]; return TrackStateProxy::MeasurementCovariance{&m_measCov[offset]}; } template ConstTrackStateProxy::MeasurementCovariance - measurementCovariance_impl(IndexType offset) const { + measurementCovariance_impl(IndexType istate) const { + IndexType offset = m_measCovOffset[istate]; return ConstTrackStateProxy::MeasurementCovariance{ &m_measCov[offset]}; } @@ -395,7 +398,9 @@ class VectorMultiTrajectory final return detail_vmt::VectorMultiTrajectoryBase::has_impl(*this, key, istate); } - IndexType size_impl() const { return m_index.size(); } + IndexType size_impl() const { + return m_index.size(); + } void clear_impl(); @@ -439,6 +444,11 @@ class VectorMultiTrajectory final m_sourceLinks[m_index[istate].iuncalibrated] = std::move(sourceLink); } + void setReferenceSurface_impl(IndexType istate, + std::shared_ptr surface) { + m_referenceSurfaces[istate] = std::move(surface); + } + // END INTERFACE }; @@ -472,7 +482,6 @@ class ConstVectorMultiTrajectory final return detail_vmt::VectorMultiTrajectoryBase::statistics(*this); } - private: // BEGIN INTERFACE ConstTrackStateProxy::Parameters parameters_impl(IndexType parIdx) const { @@ -483,19 +492,22 @@ class ConstVectorMultiTrajectory final return ConstTrackStateProxy::Covariance{m_cov[parIdx].data()}; } - ConstTrackStateProxy::Covariance jacobian_impl(IndexType parIdx) const { - return ConstTrackStateProxy::Covariance{m_jac[parIdx].data()}; + ConstTrackStateProxy::Covariance jacobian_impl(IndexType istate) const { + IndexType jacIdx = m_index[istate].ijacobian; + return ConstTrackStateProxy::Covariance{m_jac[jacIdx].data()}; } template ConstTrackStateProxy::Measurement measurement_impl( - IndexType offset) const { + IndexType istate) const { + IndexType offset = m_measOffset[istate]; return ConstTrackStateProxy::Measurement{&m_meas[offset]}; } template ConstTrackStateProxy::MeasurementCovariance - measurementCovariance_impl(IndexType offset) const { + measurementCovariance_impl(IndexType istate) const { + IndexType offset = m_measCovOffset[istate]; return ConstTrackStateProxy::MeasurementCovariance{ &m_measCov[offset]}; } @@ -504,7 +516,9 @@ class ConstVectorMultiTrajectory final return detail_vmt::VectorMultiTrajectoryBase::has_impl(*this, key, istate); } - IndexType size_impl() const { return m_index.size(); } + IndexType size_impl() const { + return m_index.size(); + } std::any component_impl(HashedString key, IndexType istate) const { return detail_vmt::VectorMultiTrajectoryBase::component_impl( diff --git a/Core/include/Acts/EventData/VectorTrackContainer.hpp b/Core/include/Acts/EventData/VectorTrackContainer.hpp index 6cc37602c73..f2e8de048d1 100644 --- a/Core/include/Acts/EventData/VectorTrackContainer.hpp +++ b/Core/include/Acts/EventData/VectorTrackContainer.hpp @@ -61,8 +61,6 @@ class VectorTrackContainerBase { return &instance.m_params[itrack]; case "cov"_hash: return &instance.m_cov[itrack]; - case "referenceSurface"_hash: - return &instance.m_referenceSurfaces[itrack]; case "nMeasurements"_hash: return &instance.m_nMeasurements[itrack]; case "nHoles"_hash: @@ -131,6 +129,10 @@ class VectorTrackContainerBase { } } + const Surface* referenceSurface_impl(IndexType itrack) const { + return m_referenceSurfaces[itrack].get(); + } + std::size_t size_impl() const { assert(checkConsistency()); return m_tipIndex.size(); @@ -217,6 +219,11 @@ class VectorTrackContainer final : public detail_vtc::VectorTrackContainerBase { void reserve(IndexType size); + void setReferenceSurface_impl(IndexType itrack, + std::shared_ptr surface) { + m_referenceSurfaces[itrack] = std::move(surface); + } + // END INTERFACE }; diff --git a/Core/include/Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp b/Core/include/Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp index 599d802245a..d2a779b7635 100644 --- a/Core/include/Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp +++ b/Core/include/Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/EventData/detail/TransformationFreeToBound.hpp" #include "Acts/Utilities/Logger.hpp" @@ -90,7 +90,7 @@ struct CorrectedFreeToBoundTransformer { std::optional> operator()( const FreeVector& freeParams, const FreeSymMatrix& freeCovariance, const Surface& surface, const GeometryContext& geoContext, - NavigationDirection navDir = NavigationDirection::Forward, + Direction navDir = Direction::Forward, const Logger& logger = getDummyLogger()) const; private: diff --git a/Core/include/Acts/EventData/detail/TransformationFreeToBound.hpp b/Core/include/Acts/EventData/detail/TransformationFreeToBound.hpp index fbce148a01c..c7c73dfd140 100644 --- a/Core/include/Acts/EventData/detail/TransformationFreeToBound.hpp +++ b/Core/include/Acts/EventData/detail/TransformationFreeToBound.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Tolerance.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/Result.hpp" @@ -24,10 +25,12 @@ namespace detail { /// @param freeParams Free track parameters vector /// @param surface Surface onto which the parameters are bound /// @param geoCtx Geometry context for the global-to-local transformation +/// @param tolerance Tolerance used for globalToLocal +/// /// @return Bound track parameters vector on the given surface Result transformFreeToBoundParameters( const FreeVector& freeParams, const Surface& surface, - const GeometryContext& geoCtx); + const GeometryContext& geoCtx, ActsScalar tolerance = s_onSurfaceTolerance); /// Convert position and direction to bound track parameters. /// @@ -37,10 +40,13 @@ Result transformFreeToBoundParameters( /// @param qOverP Charge-over-momentum-like parameter /// @param surface Surface onto which the parameters are bound /// @param geoCtx Geometry context for the global-to-local transformation +/// @param tolerance Tolerance used for globalToLocal +/// /// @return Equivalent bound parameters vector on the given surface Result transformFreeToBoundParameters( const Vector3& position, ActsScalar time, const Vector3& direction, - ActsScalar qOverP, const Surface& surface, const GeometryContext& geoCtx); + ActsScalar qOverP, const Surface& surface, const GeometryContext& geoCtx, + ActsScalar tolerance = s_onSurfaceTolerance); /// Convert direction to curvilinear track parameters. /// diff --git a/Core/include/Acts/Geometry/BoundarySurfaceT.hpp b/Core/include/Acts/Geometry/BoundarySurfaceT.hpp index 1fec7a59275..25854431d0b 100644 --- a/Core/include/Acts/Geometry/BoundarySurfaceT.hpp +++ b/Core/include/Acts/Geometry/BoundarySurfaceT.hpp @@ -7,7 +7,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #pragma once + #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/BoundarySurfaceFace.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/Volume.hpp" @@ -102,12 +104,12 @@ class BoundarySurfaceT { /// @param gctx The current geometry context object, e.g. alignment /// @param pos The global position on surface /// @param mom The direction on the surface - /// @param navDir is an aditional direction corrective + /// @param dir is an aditional direction corrective /// /// @return The attached volume at that position virtual const volume_t* attachedVolume(const GeometryContext& gctx, const Vector3& pos, const Vector3& mom, - NavigationDirection navDir) const; + Direction dir) const; /// templated onBoundary method /// @@ -127,16 +129,16 @@ class BoundarySurfaceT { /// this is done during the geometry construction. /// /// @param volume The volume to be attached - /// @param navDir The navigation direction for attaching - void attachVolume(const volume_t* volume, NavigationDirection navDir); + /// @param dir The direction for attaching + void attachVolume(const volume_t* volume, Direction dir); /// Helper method: attach a Volume to this BoundarySurfaceT /// this is done during the geometry construction. /// /// @param volumes The volume array to be attached - /// @param navDir The navigation direction for attaching + /// @param dir The direction for attaching void attachVolumeArray(std::shared_ptr volumes, - NavigationDirection navDir); + Direction dir); protected: /// the represented surface by this @@ -159,8 +161,8 @@ inline const Surface& BoundarySurfaceT::surfaceRepresentation() template void BoundarySurfaceT::attachVolume(const volume_t* volume, - NavigationDirection navDir) { - if (navDir == NavigationDirection::Backward) { + Direction dir) { + if (dir == Direction::Backward) { m_oppositeVolume = volume; } else { m_alongVolume = volume; @@ -169,9 +171,8 @@ void BoundarySurfaceT::attachVolume(const volume_t* volume, template void BoundarySurfaceT::attachVolumeArray( - const std::shared_ptr volumes, - NavigationDirection navDir) { - if (navDir == NavigationDirection::Backward) { + const std::shared_ptr volumes, Direction dir) { + if (dir == Direction::Backward) { m_oppositeVolumeArray = volumes; } else { m_alongVolumeArray = volumes; @@ -181,10 +182,10 @@ void BoundarySurfaceT::attachVolumeArray( template const volume_t* BoundarySurfaceT::attachedVolume( const GeometryContext& gctx, const Vector3& pos, const Vector3& mom, - NavigationDirection navDir) const { + Direction dir) const { const volume_t* attVolume = nullptr; // dot product with normal vector to distinguish inside/outside - if ((surfaceRepresentation().normal(gctx, pos)).dot(navDir * mom) > 0.) { + if ((surfaceRepresentation().normal(gctx, pos)).dot(dir * mom) > 0.) { attVolume = m_alongVolumeArray ? m_alongVolumeArray->object(pos).get() : m_alongVolume; } else { @@ -193,4 +194,5 @@ const volume_t* BoundarySurfaceT::attachedVolume( } return attVolume; } + } // namespace Acts diff --git a/Core/include/Acts/Geometry/VolumeBounds.hpp b/Core/include/Acts/Geometry/VolumeBounds.hpp index 8d01cbc536c..aa7fc723550 100644 --- a/Core/include/Acts/Geometry/VolumeBounds.hpp +++ b/Core/include/Acts/Geometry/VolumeBounds.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Utilities/BinningType.hpp" @@ -25,8 +26,7 @@ class Surface; class VolumeBounds; using VolumeBoundsPtr = std::shared_ptr; -using OrientedSurface = - std::pair, NavigationDirection>; +using OrientedSurface = std::pair, Direction>; using OrientedSurfaces = std::vector; // Planar definitions to help construct the boundary surfaces diff --git a/Core/include/Acts/Geometry/detail/DefaultDetectorElementBase.hpp b/Core/include/Acts/Geometry/detail/DefaultDetectorElementBase.hpp index 27e4bf5a541..14d2e0a11fb 100644 --- a/Core/include/Acts/Geometry/detail/DefaultDetectorElementBase.hpp +++ b/Core/include/Acts/Geometry/detail/DefaultDetectorElementBase.hpp @@ -35,9 +35,12 @@ class DetectorElementBase { /// @param gctx The current geometry context object, e.g. alignment virtual const Transform3& transform(const GeometryContext& gctx) const = 0; - /// Return surface representation + /// Return surface representation - const return pattern virtual const Surface& surface() const = 0; + /// Non-const return pattern + virtual Surface& surface() = 0; + /// Returns the thickness of the module /// @return double that indicates the thickness of the module virtual double thickness() const = 0; diff --git a/Core/include/Acts/MagneticField/detail/SmallObjectCache.hpp b/Core/include/Acts/MagneticField/detail/SmallObjectCache.hpp index 413d945ddec..b26e55e730e 100644 --- a/Core/include/Acts/MagneticField/detail/SmallObjectCache.hpp +++ b/Core/include/Acts/MagneticField/detail/SmallObjectCache.hpp @@ -32,7 +32,7 @@ class SmallObjectCache { "Type needs to be move assignable and move constructible"); /*T* ptr =*/new (cache.m_data.data()) T(std::forward(args)...); - static Handler static_handler{}; + static const Handler static_handler{}; cache.m_handler = &static_handler; return cache; @@ -109,7 +109,7 @@ class SmallObjectCache { }; alignas(std::max_align_t) std::array m_data{}; - HandlerBase* m_handler{nullptr}; + const HandlerBase* m_handler{nullptr}; }; } // namespace detail diff --git a/Core/include/Acts/Material/ISurfaceMaterial.hpp b/Core/include/Acts/Material/ISurfaceMaterial.hpp index 7a93983a643..7109813ba6f 100644 --- a/Core/include/Acts/Material/ISurfaceMaterial.hpp +++ b/Core/include/Acts/Material/ISurfaceMaterial.hpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Material/MaterialSlab.hpp" @@ -77,9 +78,9 @@ class ISurfaceMaterial { /// Update pre factor /// - /// @param pDir is the navigation direction through the surface + /// @param pDir is the positive direction through the surface /// @param mStage is the material update directive (onapproach, full, onleave) - double factor(NavigationDirection pDir, MaterialUpdateStage mStage) const; + double factor(Direction pDir, MaterialUpdateStage mStage) const; /// Return the type of surface material mapping /// @@ -89,22 +90,22 @@ class ISurfaceMaterial { /// - from local coordinate on the surface /// /// @param lp is the local position used for the (eventual) lookup - /// @param pDir is the navigation direction through the surface + /// @param pDir is the positive direction through the surface /// @param mStage is the material update directive (onapproach, full, onleave) /// /// @return MaterialSlab - MaterialSlab materialSlab(const Vector2& lp, NavigationDirection pDir, + MaterialSlab materialSlab(const Vector2& lp, Direction pDir, MaterialUpdateStage mStage) const; /// Return method for full material description of the Surface /// - from the global coordinates /// /// @param gp is the global position used for the (eventual) lookup - /// @param pDir is the navigation direction through the surface + /// @param pDir is the positive direction through the surface /// @param mStage is the material update directive (onapproach, full, onleave) /// /// @return MaterialSlab - MaterialSlab materialSlab(const Vector3& gp, NavigationDirection pDir, + MaterialSlab materialSlab(const Vector3& gp, Direction pDir, MaterialUpdateStage mStage) const; /// @brief output stream operator @@ -128,22 +129,19 @@ class ISurfaceMaterial { Acts::MappingType::Default}; //!< Use the default mapping type by default }; -inline double ISurfaceMaterial::factor(NavigationDirection pDir, +inline double ISurfaceMaterial::factor(Direction pDir, MaterialUpdateStage mStage) const { if (mStage == Acts::MaterialUpdateStage::FullUpdate) { return 1.; } else if (mStage == Acts::MaterialUpdateStage::PreUpdate) { - return pDir == NavigationDirection::Backward ? m_splitFactor - : 1 - m_splitFactor; + return pDir == Direction::Negative ? m_splitFactor : 1 - m_splitFactor; } else /*if (mStage == Acts::MaterialUpdateStage::PostUpdate)*/ { - return pDir == NavigationDirection::Forward ? m_splitFactor - : 1 - m_splitFactor; + return pDir == Direction::Positive ? m_splitFactor : 1 - m_splitFactor; } } inline MaterialSlab ISurfaceMaterial::materialSlab( - const Vector2& lp, NavigationDirection pDir, - MaterialUpdateStage mStage) const { + const Vector2& lp, Direction pDir, MaterialUpdateStage mStage) const { // The plain material properties associated to this bin MaterialSlab plainMatProp = materialSlab(lp); // Scale if you have material to scale @@ -158,8 +156,7 @@ inline MaterialSlab ISurfaceMaterial::materialSlab( } inline MaterialSlab ISurfaceMaterial::materialSlab( - const Vector3& gp, NavigationDirection pDir, - MaterialUpdateStage mStage) const { + const Vector3& gp, Direction pDir, MaterialUpdateStage mStage) const { // The plain material properties associated to this bin MaterialSlab plainMatProp = materialSlab(gp); // Scale if you have material to scale diff --git a/Core/include/Acts/Navigation/NextNavigator.hpp b/Core/include/Acts/Navigation/NextNavigator.hpp index 9469655f913..1844b595625 100644 --- a/Core/include/Acts/Navigation/NextNavigator.hpp +++ b/Core/include/Acts/Navigation/NextNavigator.hpp @@ -45,9 +45,6 @@ class NextNavigator { bool resolveMaterial = true; /// stop at every surface regardless what it is bool resolvePassive = false; - - /// The tolerance used to defined "reached" - double tolerance = s_onSurfaceTolerance; }; /// Nested State struct @@ -87,7 +84,7 @@ class NextNavigator { void resetState(State& state, const GeometryContext& /*geoContext*/, const Vector3& /*pos*/, const Vector3& /*dir*/, - NavigationDirection /*navDir*/, const Surface* /*ssurface*/, + Direction /*navDir*/, const Surface* /*ssurface*/, const Surface* /*tsurface*/) const { // Reset everything first state = State(); diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp index 259dc4a00b0..0ca0b68d2bb 100644 --- a/Core/include/Acts/Propagator/AtlasStepper.hpp +++ b/Core/include/Acts/Propagator/AtlasStepper.hpp @@ -58,7 +58,7 @@ class AtlasStepper { template State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, const Parameters& pars, - NavigationDirection ndir = NavigationDirection::Forward, + Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) : navDir(ndir), @@ -233,7 +233,7 @@ class AtlasStepper { // optimisation that init is not called twice bool state_ready = false; // configuration - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; bool useJacobian = false; double step = 0; double maxPathLength = 0; @@ -298,10 +298,11 @@ class AtlasStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const SingleBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { - return State{gctx, m_bField->makeCache(mctx), par, ndir, ssize, stolerance}; + return State{gctx, m_bField->makeCache(mctx), par, navDir, ssize, + stolerance}; } /// @brief Resets the state @@ -314,8 +315,7 @@ class AtlasStepper { /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, - const NavigationDirection navDir = NavigationDirection::Forward, + const Surface& surface, const Direction navDir = Direction::Forward, const double stepSize = std::numeric_limits::max()) const { // Update the stepping state update(state, @@ -739,7 +739,7 @@ class AtlasStepper { P[45] *= p; P[46] *= p; - double An = sqrt(P[4] * P[4] + P[5] * P[5]); + double An = std::hypot(P[4], P[5]); double Ax[3]; if (An != 0.) { Ax[0] = -P[5] / An; diff --git a/Core/include/Acts/Propagator/ConstrainedStep.hpp b/Core/include/Acts/Propagator/ConstrainedStep.hpp index bea4b82657a..da888df496e 100644 --- a/Core/include/Acts/Propagator/ConstrainedStep.hpp +++ b/Core/include/Acts/Propagator/ConstrainedStep.hpp @@ -9,13 +9,14 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Direction.hpp" #include #include #include #include #include +#include #include namespace Acts { @@ -59,7 +60,7 @@ class ConstrainedStep { /// @param value is the user given initial value constexpr explicit ConstrainedStep(Scalar value) { m_values[user] = std::abs(value); - m_direction = Acts::directionFromStepSize(value); + m_direction = Direction::fromScalar(value); } /// set accuracy by one Scalar @@ -157,7 +158,7 @@ class ConstrainedStep { std::array m_values = {kNotSet, kNotSet, kNotSet, kNotSet}; /// the navigation direction /// the direction is invariant after initialization - NavigationDirection m_direction = NavigationDirection::Forward; + Direction m_direction = Direction::Forward; }; inline std::ostream& operator<<(std::ostream& os, const ConstrainedStep& step) { diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp index 6aeaca1a2e3..0d2a4ff69c5 100644 --- a/Core/include/Acts/Propagator/DirectNavigator.hpp +++ b/Core/include/Acts/Propagator/DirectNavigator.hpp @@ -40,9 +40,6 @@ class DirectNavigator { getDefaultLogger("DirectNavigator", Logging::INFO)) : m_logger{std::move(_logger)} {} - /// The tolerance used to define "surface reached" - double tolerance = s_onSurfaceTolerance; - /// Nested Actor struct, called Initializer /// /// This is needed for the initialization of the @@ -133,7 +130,7 @@ class DirectNavigator { /// @param tsurface is the target surface void resetState(State& state, const GeometryContext& /*geoContext*/, const Vector3& /*pos*/, const Vector3& /*dir*/, - NavigationDirection /*navDir*/, const Surface* ssurface, + Direction /*navDir*/, const Surface* ssurface, const Surface* tsurface) const { // Reset everything except the navSurfaces auto navSurfaces = state.navSurfaces; diff --git a/Core/include/Acts/Propagator/EigenStepper.hpp b/Core/include/Acts/Propagator/EigenStepper.hpp index 837b5114440..7ff54e9fb90 100644 --- a/Core/include/Acts/Propagator/EigenStepper.hpp +++ b/Core/include/Acts/Propagator/EigenStepper.hpp @@ -75,7 +75,7 @@ class EigenStepper { explicit State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, const SingleBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) : q(par.charge()), @@ -112,7 +112,7 @@ class EigenStepper { Covariance cov = Covariance::Zero(); /// Navigation direction, this is needed for searching - NavigationDirection navDir; + Direction navDir; /// The full jacobian of the transport entire transport Jacobian jacobian = Jacobian::Identity(); @@ -171,7 +171,7 @@ class EigenStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const SingleBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const; @@ -185,8 +185,7 @@ class EigenStepper { /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, - const NavigationDirection navDir = NavigationDirection::Forward, + const Surface& surface, const Direction navDir = Direction::Forward, const double stepSize = std::numeric_limits::max()) const; /// Get the field for the stepping, it checks first if the access is still diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index 0b3365b1008..793ecd24dea 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -19,9 +19,9 @@ template auto Acts::EigenStepper::makeState( std::reference_wrapper gctx, std::reference_wrapper mctx, - const SingleBoundTrackParameters& par, NavigationDirection ndir, + const SingleBoundTrackParameters& par, Direction navDir, double ssize, double stolerance) const -> State { - return State{gctx, m_bField->makeCache(mctx), par, ndir, ssize, stolerance}; + return State{gctx, m_bField->makeCache(mctx), par, navDir, ssize, stolerance}; } template @@ -29,7 +29,7 @@ void Acts::EigenStepper::resetState(State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, const Surface& surface, - const NavigationDirection navDir, + const Direction navDir, const double stepSize) const { // Update the stepping state update(state, diff --git a/Core/include/Acts/Propagator/MaterialInteractor.hpp b/Core/include/Acts/Propagator/MaterialInteractor.hpp index 2f03a7fd21e..ebcc2579c56 100644 --- a/Core/include/Acts/Propagator/MaterialInteractor.hpp +++ b/Core/include/Acts/Propagator/MaterialInteractor.hpp @@ -106,9 +106,9 @@ struct MaterialInteractor { stepper.transportCovarianceToCurvilinear(state.stepping); } // Change the noise updater depending on the navigation direction - NoiseUpdateMode mode = - (state.stepping.navDir == NavigationDirection::Forward) ? addNoise - : removeNoise; + NoiseUpdateMode mode = (state.stepping.navDir == Direction::Forward) + ? addNoise + : removeNoise; // Apply the material interactions d.updateState(state, stepper, mode); // Record the result diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp index 2f34ede4aa7..369510c1f71 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp @@ -20,9 +20,9 @@ #include "Acts/Propagator/EigenStepper.hpp" #include "Acts/Propagator/EigenStepperError.hpp" #include "Acts/Propagator/Propagator.hpp" +#include "Acts/Utilities/GaussianMixtureReduction.hpp" #include "Acts/Utilities/Intersection.hpp" #include "Acts/Utilities/Result.hpp" -#include "Acts/Utilities/detail/gaussian_mixture_helpers.hpp" #include #include @@ -159,12 +159,6 @@ struct MaxMomentumReducerLoop { } }; -/// @enum FinalReductionMethod -/// -/// Available reduction methods for the reduction in the boundState and -/// curvilinearState member functions of the MultiEigenStepperLoop. -enum class FinalReductionMethod { eMean, eMaxWeight }; - /// @brief Stepper based on the EigenStepper, but handles Multi-Component Tracks /// (e.g., for the GSF). Internally, this only manages a vector of /// EigenStepper::States. This simplifies implementation, but has several @@ -189,7 +183,7 @@ class MultiEigenStepperLoop /// How to extract a single component state when calling .boundState() or /// .curvilinearState() - FinalReductionMethod m_finalReductionMethod = FinalReductionMethod::eMean; + MixtureReductionMethod m_finalReductionMethod = MixtureReductionMethod::eMean; /// The logger (used if no logger is provided by caller of methods) std::unique_ptr m_logger; @@ -219,11 +213,11 @@ class MultiEigenStepperLoop static constexpr int maxComponents = std::numeric_limits::max(); /// Constructor from a magnetic field and a optionally provided Logger - MultiEigenStepperLoop( - std::shared_ptr bField, - FinalReductionMethod finalReductionMethod = FinalReductionMethod::eMean, - std::unique_ptr logger = getDefaultLogger("GSF", - Logging::INFO)) + MultiEigenStepperLoop(std::shared_ptr bField, + MixtureReductionMethod finalReductionMethod = + MixtureReductionMethod::eMean, + std::unique_ptr logger = + getDefaultLogger("GSF", Logging::INFO)) : EigenStepper(std::move(bField)), m_finalReductionMethod(finalReductionMethod), m_logger(std::move(logger)) {} @@ -240,7 +234,7 @@ class MultiEigenStepperLoop SmallVector components; bool covTransport = false; - NavigationDirection navDir; + Direction navDir; double pathAccumulated = 0.; std::size_t steps = 0; @@ -275,7 +269,7 @@ class MultiEigenStepperLoop const GeometryContext& gctx, const MagneticFieldContext& mctx, const std::shared_ptr& bfield, const MultiComponentBoundTrackParameters& multipars, - NavigationDirection ndir = NavigationDirection::Forward, + Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) : navDir(ndir), geoContext(gctx), magContext(mctx) { @@ -306,10 +300,10 @@ class MultiEigenStepperLoop State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const MultiComponentBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { - return State(gctx, mctx, SingleStepper::m_bField, par, ndir, ssize, + return State(gctx, mctx, SingleStepper::m_bField, par, navDir, ssize, stolerance); } @@ -323,8 +317,7 @@ class MultiEigenStepperLoop /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, - const NavigationDirection navDir = NavigationDirection::Forward, + const Surface& surface, const Direction navDir = Direction::Forward, const double stepSize = std::numeric_limits::max()) const { for (auto& component : state.components) { SingleStepper::resetState(component.state, boundParams, cov, surface, diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp index e0b4594f7fb..76fe8955c69 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp @@ -26,8 +26,23 @@ auto MultiEigenStepperLoop::boundState( double accumulatedPathLength = 0.0; for (auto i = 0ul; i < numberComponents(state); ++i) { - auto bs = SingleStepper::boundState(state.components[i].state, surface, - transportCov, freeToBoundCorrection); + auto& cmpState = state.components[i].state; + + // Force the component to be on the surface + // This needs to be done because of the `averageOnSurface`-option of the + // `MultiStepperSurfaceReached`-Aborter, which can be configured to end the + // propagation when the mean of all components reached the destination + // surface. Thus, it is not garantueed that all states are actually + // onSurface. + cmpState.pars.template segment<3>(eFreePos0) = + surface + .intersect(state.geoContext, + cmpState.pars.template segment<3>(eFreePos0), + cmpState.pars.template segment<3>(eFreeDir0), false) + .intersection.position; + + auto bs = SingleStepper::boundState(cmpState, surface, transportCov, + freeToBoundCorrection); if (bs.ok()) { const auto& btp = std::get(*bs); @@ -43,19 +58,8 @@ auto MultiEigenStepperLoop::boundState( return MultiStepperError::AllComponentsConversionToBoundFailed; } - const auto [mean, cov] = - detail::angleDescriptionSwitch(surface, [&](const auto& desc) { - return detail::combineGaussianMixture(states, Acts::Identity{}, desc); - }); - - const auto finalPars = - (m_finalReductionMethod == FinalReductionMethod::eMaxWeight) - ? std::get(*std::max_element( - states.begin(), states.end(), - [](const auto& a, const auto& b) { - return std::get(a) < std::get(b); - })) - : mean; + const auto [finalPars, cov] = + Acts::reduceGaussianMixture(states, surface, m_finalReductionMethod); std::optional finalCov = std::nullopt; if (cov != BoundSymMatrix::Zero()) { @@ -76,7 +80,7 @@ auto MultiEigenStepperLoop::curvilinearState(State& state, if (numberComponents(state) == 1) { return SingleStepper::curvilinearState(state.components.front().state, transportCov); - } else if (m_finalReductionMethod == FinalReductionMethod::eMaxWeight) { + } else if (m_finalReductionMethod == MixtureReductionMethod::eMaxWeight) { auto cmpIt = std::max_element( state.components.begin(), state.components.end(), [](const auto& a, const auto& b) { return a.weight < b.weight; }); diff --git a/Core/include/Acts/Propagator/Navigator.hpp b/Core/include/Acts/Propagator/Navigator.hpp index c77f1dab3a4..a8e615e3da9 100644 --- a/Core/include/Acts/Propagator/Navigator.hpp +++ b/Core/include/Acts/Propagator/Navigator.hpp @@ -35,7 +35,7 @@ namespace Acts { template struct NavigationOptions { /// The navigation direction - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; /// The boundary check directive BoundaryCheck boundaryCheck = true; @@ -78,9 +78,9 @@ struct NavigationOptions { /// @param resolvep Boolean whether to resolve passives /// @param sobject Start object to check against /// @param eobject End object to check against - NavigationOptions(NavigationDirection ndir, BoundaryCheck bcheck, - bool resolves = true, bool resolvem = true, - bool resolvep = false, const object_t* sobject = nullptr, + NavigationOptions(Direction ndir, BoundaryCheck bcheck, bool resolves = true, + bool resolvem = true, bool resolvep = false, + const object_t* sobject = nullptr, const object_t* eobject = nullptr) : navDir(ndir), boundaryCheck(std::move(bcheck)), @@ -150,9 +150,6 @@ class Navigator { /// stop at every surface regardless what it is bool resolvePassive = false; - /// The tolerance used to defined "reached" - double tolerance = s_onSurfaceTolerance; - /// Wether to perform boundary checks for layer resolving (improves /// navigation for bended tracks) BoundaryCheck boundaryCheckLayerResolving = true; @@ -251,9 +248,8 @@ class Navigator { /// @param ssurface is the new starting surface /// @param tsurface is the target surface void resetState(State& state, const GeometryContext& geoContext, - const Vector3& pos, const Vector3& dir, - NavigationDirection navDir, const Surface* ssurface, - const Surface* tsurface) const { + const Vector3& pos, const Vector3& dir, Direction navDir, + const Surface* ssurface, const Surface* tsurface) const { // Reset everything first state = State(); @@ -820,7 +816,7 @@ class Navigator { // ~ non-zero field double ir = (dir.cross(B).norm()) * q / mom; double s; - if (state.stepping.navDir == NavigationDirection::Forward) { + if (state.stepping.navDir == Direction::Forward) { s = state.stepping.stepSize.max(); } else { s = state.stepping.stepSize.min(); @@ -1119,7 +1115,8 @@ class Navigator { // target volume and layer search through global search auto targetIntersection = state.navigation.targetSurface->intersect( state.geoContext, stepper.position(state.stepping), - state.stepping.navDir * stepper.direction(state.stepping), false); + state.stepping.navDir * stepper.direction(state.stepping), false, + state.options.targetTolerance); if (targetIntersection) { ACTS_VERBOSE(volInfo(state) << "Target estimate position (" diff --git a/Core/include/Acts/Propagator/Propagator.hpp b/Core/include/Acts/Propagator/Propagator.hpp index 25cab9c6ffd..cc48aa919e6 100644 --- a/Core/include/Acts/Propagator/Propagator.hpp +++ b/Core/include/Acts/Propagator/Propagator.hpp @@ -23,6 +23,7 @@ #include "Acts/Propagator/StepperConcept.hpp" #include "Acts/Propagator/detail/VoidPropagatorComponents.hpp" #include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/PdgParticle.hpp" #include "Acts/Utilities/Result.hpp" #include @@ -56,12 +57,14 @@ struct PropagatorResult : private detail::Extendable { /// struct PropagatorPlainOptions { /// Propagation direction - NavigationDirection direction = NavigationDirection::Forward; + Direction direction = Direction::Forward; - /// The |pdg| code for (eventual) material integration - pion default - int absPdgCode = 211; + /// The |pdg| code for (eventual) material integration - + /// pion default + int absPdgCode = PdgParticle::ePionPlus; - /// The mass for the particle for (eventual) material integration + /// The mass for the particle for (eventual) material integration - + /// pion default double mass = 139.57018 * UnitConstants::MeV; /// Maximum number of steps for one propagate call diff --git a/Core/include/Acts/Propagator/StandardAborters.hpp b/Core/include/Acts/Propagator/StandardAborters.hpp index 4f86f87e711..bcad6b21ba9 100644 --- a/Core/include/Acts/Propagator/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/StandardAborters.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -24,7 +25,7 @@ namespace Acts { /// @brief TargetOptions struct for geometry interface struct TargetOptions { /// Navigation direction - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; /// Target Boundary check directive - always false here BoundaryCheck boundaryCheck = false; @@ -36,7 +37,7 @@ struct TargetOptions { double pathLimit = std::numeric_limits::max(); /// create target options - TargetOptions(NavigationDirection ndir) : navDir(ndir) {} + TargetOptions(Direction ndir) : navDir(ndir) {} }; /// This is the condition that the pathLimit has been reached @@ -139,7 +140,8 @@ struct SurfaceReached { const double tolerance = state.options.targetTolerance; const auto sIntersection = targetSurface.intersect( state.geoContext, stepper.position(state.stepping), - state.stepping.navDir * stepper.direction(state.stepping), true); + state.stepping.navDir * stepper.direction(state.stepping), true, + tolerance); // The target is reached bool targetReached = (sIntersection.intersection.status == diff --git a/Core/include/Acts/Propagator/StepperConcept.hpp b/Core/include/Acts/Propagator/StepperConcept.hpp index dc379f1c46f..bbb225eaeaa 100644 --- a/Core/include/Acts/Propagator/StepperConcept.hpp +++ b/Core/include/Acts/Propagator/StepperConcept.hpp @@ -73,7 +73,7 @@ using step_size_t = decltype(std::declval().stepSize); constexpr bool StepperStateConcept = require, has_member, - has_member, + has_member, has_member//, // has_member >; @@ -83,7 +83,7 @@ using step_size_t = decltype(std::declval().stepSize); template constexpr bool MultiStepperStateConcept= require< has_member, - has_member, + has_member, has_member >; // clang-format on @@ -101,7 +101,7 @@ constexpr bool MultiStepperStateConcept= require< static_assert(bound_state_exists, "BoundState type not found"); constexpr static bool curvilinear_state_exists = exists; static_assert(curvilinear_state_exists, "CurvilinearState type not found"); - constexpr static bool reset_state_exists = has_method; + constexpr static bool reset_state_exists = has_method; static_assert(reset_state_exists, "resetState method not found"); constexpr static bool position_exists = has_method; static_assert(position_exists, "position method not found"); diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp index 00dc699865f..c30f5c3bca8 100644 --- a/Core/include/Acts/Propagator/StraightLineStepper.hpp +++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp @@ -63,7 +63,7 @@ class StraightLineStepper { explicit State(const GeometryContext& gctx, const MagneticFieldContext& mctx, const SingleBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) : q(par.charge()), @@ -109,7 +109,7 @@ class StraightLineStepper { Covariance cov = Covariance::Zero(); /// Navigation direction, this is needed for searching - NavigationDirection navDir; + Direction navDir; /// accummulated path length state double pathAccumulated = 0.; @@ -137,10 +137,10 @@ class StraightLineStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const SingleBoundTrackParameters& par, - NavigationDirection ndir = NavigationDirection::Forward, + Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { - return State{gctx, mctx, par, ndir, ssize, stolerance}; + return State{gctx, mctx, par, navDir, ssize, stolerance}; } /// @brief Resets the state @@ -153,8 +153,7 @@ class StraightLineStepper { /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, - const NavigationDirection navDir = NavigationDirection::Forward, + const Surface& surface, const Direction navDir = Direction::Forward, const double stepSize = std::numeric_limits::max()) const; /// Get the field for the stepping, this gives back a zero field diff --git a/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp b/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp index 48c9b92acbb..2075017bd66 100644 --- a/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp +++ b/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp @@ -18,6 +18,7 @@ #include "Acts/Utilities/Helpers.hpp" #include +#include #include namespace Acts { @@ -132,7 +133,6 @@ struct GenericDenseEnvironmentExtension { -qop[0] * qop[0] * qop[0] * g * energy[0] / (stepper.charge(state.stepping) * stepper.charge(state.stepping)); //~ tKi[0] = std::hypot(1, state.options.mass / initialMomentum); - using std::hypot; tKi[0] = hypot(1, state.options.mass * qop[0]); kQoP[0] = Lambdappi[0]; } else { @@ -150,7 +150,6 @@ struct GenericDenseEnvironmentExtension { -qopNew * qopNew * qopNew * g * energy[i] / (stepper.charge(state.stepping) * stepper.charge(state.stepping) * UnitConstants::C * UnitConstants::C); - using std::hypot; tKi[i] = hypot(1, state.options.mass * qopNew); kQoP[i] = Lambdappi[i]; } @@ -186,17 +185,13 @@ struct GenericDenseEnvironmentExtension { } // Add derivative dlambda/ds = Lambda'' - using std::sqrt; - state.stepping.derivative(7) = - -sqrt(state.options.mass * state.options.mass + - newMomentum * newMomentum) * - g / (newMomentum * newMomentum * newMomentum); + state.stepping.derivative(7) = -hypot(state.options.mass, newMomentum) * g / + (newMomentum * newMomentum * newMomentum); // Update momentum state.stepping.pars[eFreeQOverP] = stepper.charge(state.stepping) / newMomentum; // Add derivative dt/ds = 1/(beta * c) = sqrt(m^2 * p^{-2} + c^{-2}) - using std::hypot; state.stepping.derivative(3) = hypot(1, state.options.mass / newMomentum); // Update time state.stepping.pars[eFreeTime] += @@ -394,7 +389,6 @@ struct GenericDenseEnvironmentExtension { /// @param [in] state Deliverer of configurations template void initializeEnergyLoss(const propagator_state_t& state) { - using std::hypot; energy[0] = hypot(initialMomentum, state.options.mass); // use unit length as thickness to compute the energy loss per unit length Acts::MaterialSlab slab(material, 1); @@ -451,8 +445,7 @@ struct GenericDenseEnvironmentExtension { const stepper_t& stepper, const int i) { // Update parameters related to a changed momentum currentMomentum = initialMomentum + h * dPds[i - 1]; - using std::sqrt; - energy[i] = sqrt(currentMomentum * currentMomentum + mass * mass); + energy[i] = hypot(currentMomentum, mass); dPds[i] = g * energy[i] / currentMomentum; qop[i] = stepper.charge(state.stepping) / currentMomentum; // Calculate term for later error propagation diff --git a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp index 0118dcd81a7..992f9fecf97 100644 --- a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp @@ -39,7 +39,7 @@ struct PointwiseMaterialInteraction { /// The covariance transport decision at the interaction const bool performCovarianceTransport; /// The navigation direction - const NavigationDirection nav; + const Direction navDir; /// The effective, passed material properties including the path correction. MaterialSlab slab; @@ -78,7 +78,7 @@ struct PointwiseMaterialInteraction { mass(state.options.mass), pdg(state.options.absPdgCode), performCovarianceTransport(state.stepping.covTransport), - nav(state.stepping.navDir) {} + navDir(state.stepping.navDir) {} /// @brief This function evaluates the material properties to interact with /// @@ -105,7 +105,7 @@ struct PointwiseMaterialInteraction { // Retrieve the material properties slab = navigator.currentSurface(state.navigation) ->surfaceMaterial() - ->materialSlab(pos, nav, updateStage); + ->materialSlab(pos, navDir, updateStage); // Correct the material properties for non-zero incidence pathCorrection = surface->pathCorrection(state.geoContext, pos, dir); @@ -136,11 +136,7 @@ struct PointwiseMaterialInteraction { NoiseUpdateMode updateMode = addNoise) { // in forward(backward) propagation, energy decreases(increases) and // variances increase(decrease) - const auto nextE = - std::sqrt(mass * mass + momentum * momentum) - - std::copysign( - Eloss, - static_cast>(nav)); + const auto nextE = std::hypot(mass, momentum) - Eloss * navDir; // put particle at rest if energy loss is too large nextP = (mass < nextE) ? std::sqrt(nextE * nextE - mass * mass) : 0; // minimum momentum below which we will not push particles via material diff --git a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp index 046ac9b8833..d2d7c15b4c8 100644 --- a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp @@ -39,7 +39,7 @@ struct VolumeMaterialInteraction { /// The covariance transport decision at the interaction const bool performCovarianceTransport; /// The navigation direction - const NavigationDirection nav; + const Direction navDir; /// Data evaluated within this struct MaterialSlab slab; @@ -68,7 +68,7 @@ struct VolumeMaterialInteraction { mass(state.options.mass), pdg(state.options.absPdgCode), performCovarianceTransport(state.stepping.covTransport), - nav(state.stepping.navDir) {} + navDir(state.stepping.navDir) {} /// @brief This function evaluates the material properties to interact with /// diff --git a/Core/include/Acts/Seeding/BinnedSPGroup.hpp b/Core/include/Acts/Seeding/BinnedSPGroup.hpp index 7cc902cc1ba..ab91576c491 100644 --- a/Core/include/Acts/Seeding/BinnedSPGroup.hpp +++ b/Core/include/Acts/Seeding/BinnedSPGroup.hpp @@ -109,7 +109,9 @@ class BinnedSPGroup { BinnedSPGroupIterator begin(); BinnedSPGroupIterator end(); - Acts::SpacePointGrid& grid() { return *m_grid.get(); } + Acts::SpacePointGrid& grid() { + return *m_grid.get(); + } private: // grid with ownership of all InternalSpacePoint diff --git a/Core/include/Acts/Seeding/InternalSpacePoint.hpp b/Core/include/Acts/Seeding/InternalSpacePoint.hpp index 8d202337d20..7e82318ae86 100644 --- a/Core/include/Acts/Seeding/InternalSpacePoint.hpp +++ b/Core/include/Acts/Seeding/InternalSpacePoint.hpp @@ -68,7 +68,7 @@ inline InternalSpacePoint::InternalSpacePoint( m_x(globalPos.x() - offsetXY.x()), m_y(globalPos.y() - offsetXY.y()), m_z(globalPos.z()), - m_r(std::sqrt(m_x * m_x + m_y * m_y)), + m_r(std::hypot(m_x, m_y)), m_varianceR(variance.x()), m_varianceZ(variance.y()), m_sp(sp) {} diff --git a/Core/include/Acts/Seeding/SeedFilter.ipp b/Core/include/Acts/Seeding/SeedFilter.ipp index 717c97831c1..6679df0e316 100644 --- a/Core/include/Acts/Seeding/SeedFilter.ipp +++ b/Core/include/Acts/Seeding/SeedFilter.ipp @@ -80,6 +80,7 @@ void SeedFilter::filterSeeds_2SpFixed( // seeds span 5 layers // -> weaker requirement for a good seed std::vector compatibleSeedR; + compatibleSeedR.reserve(m_cfg.compatSeedLimit); float invHelixDiameter = invHelixDiameterVec[topSPIndex]; float lowerLimitCurv = invHelixDiameter - m_cfg.deltaInvHelixDiameter; diff --git a/Core/include/Acts/Seeding/SeedFinder.ipp b/Core/include/Acts/Seeding/SeedFinder.ipp index e8558160bf9..c5fed888b74 100644 --- a/Core/include/Acts/Seeding/SeedFinder.ipp +++ b/Core/include/Acts/Seeding/SeedFinder.ipp @@ -236,8 +236,8 @@ SeedFinder::getCompatibleDoublets( continue; } - /// we make a copy of the iterator here since we need it to remain - /// the same in the Neighbour object + // we make a copy of the iterator here since we need it to remain + // the same in the Neighbour object auto min_itr = otherSPCol.itr; bool found = false; @@ -269,9 +269,9 @@ SeedFinder::getCompatibleDoublets( } } - /// We update the iterator in the Neighbout object - /// that mean that we have changed the middle space point - /// and the lower bound has moved accordingly + // We update the iterator in the Neighbout object + // that mean that we have changed the middle space point + // and the lower bound has moved accordingly if (not found) { found = true; otherSPCol.itr = min_itr; @@ -283,60 +283,102 @@ SeedFinder::getCompatibleDoublets( deltaZ = (otherSP->z() - zM); } - // ratio Z/R (forward angle) of space point duplet - float cotTheta = deltaZ / deltaR; - if (cotTheta > m_config.cotThetaMax or cotTheta < -m_config.cotThetaMax) { + // the longitudinal impact parameter zOrigin is defined as (zM - rM * + // cotTheta) where cotTheta is the ratio Z/R (forward angle) of space + // point duplet but instead we calculate (zOrigin * deltaR) and multiply + // collisionRegion by deltaR to avoid divisions + const float zOriginTimesDeltaR = (zM * deltaR - rM * deltaZ); + // check if duplet origin on z axis within collision region + if (zOriginTimesDeltaR < m_config.collisionRegionMin * deltaR or + zOriginTimesDeltaR > m_config.collisionRegionMax * deltaR) { continue; } - // check if duplet origin on z axis within collision region - float zOrigin = zM - rM * cotTheta; - if (zOrigin < m_config.collisionRegionMin or - zOrigin > m_config.collisionRegionMax) { + // if interactionPointCut is false we apply z cuts before coordinate + // transformation to avoid unnecessary calculations. If + // interactionPointCut is true we apply the curvature cut first because it + // is more frequent but requires the coordinate transformation + if (not m_config.interactionPointCut) { + // check if duplet cotTheta is within the region of interest + // cotTheta is defined as (deltaZ / deltaR) but instead we multiply + // cotThetaMax by deltaR to avoid division + if (deltaZ > m_config.cotThetaMax * deltaR or + deltaZ < -m_config.cotThetaMax * deltaR) { + continue; + } + // if z-distance between SPs is within max and min values + if (deltaZ > m_config.deltaZMax or deltaZ < -m_config.deltaZMax) { + continue; + } + + // transform SP cordinates to the u-v reference frame + const float deltaX = otherSP->x() - xM; + const float deltaY = otherSP->y() - yM; + + const float xNewFrame = deltaX * cosPhiM + deltaY * sinPhiM; + const float yNewFrame = deltaY * cosPhiM - deltaX * sinPhiM; + + const float deltaR2 = (deltaX * deltaX + deltaY * deltaY); + const float iDeltaR2 = 1. / deltaR2; + + const float uT = xNewFrame * iDeltaR2; + const float vT = yNewFrame * iDeltaR2; + + const float iDeltaR = std::sqrt(iDeltaR2); + const float cotTheta = deltaZ * iDeltaR; + + const float Er = + ((varianceZM + otherSP->varianceZ()) + + (cotTheta * cotTheta) * (varianceRM + otherSP->varianceR())) * + iDeltaR2; + + // fill output vectors + linCircleVec.emplace_back(cotTheta, iDeltaR, Er, uT, vT, xNewFrame, + yNewFrame); + spacePointData.setDeltaR(otherSP->index(), + std::sqrt(deltaR2 + (deltaZ * deltaZ))); + outVec.push_back(otherSP.get()); continue; } + // transform SP cordinates to the u-v reference frame const float deltaX = otherSP->x() - xM; const float deltaY = otherSP->y() - yM; - // calculate projection fraction of spM->sp vector pointing in same - // direction as - // vector origin->spM (x) and projection fraction of spM->sp vector - // pointing orthogonal to origin->spM (y) const float xNewFrame = deltaX * cosPhiM + deltaY * sinPhiM; const float yNewFrame = deltaY * cosPhiM - deltaX * sinPhiM; const float deltaR2 = (deltaX * deltaX + deltaY * deltaY); const float iDeltaR2 = 1. / deltaR2; - // conformal transformation u=x/(x²+y²) v=y/(x²+y²) transform the - // circle into straight lines in the u/v plane the line equation can - // be described in terms of aCoef and bCoef, where v = aCoef * u + - // bCoef const float uT = xNewFrame * iDeltaR2; const float vT = yNewFrame * iDeltaR2; - const float iDeltaR = std::sqrt(iDeltaR2); - cotTheta = deltaZ * iDeltaR; - - // error term for sp-pair without correlation of middle space point - const float Er = - ((varianceZM + otherSP->varianceZ()) + - (cotTheta * cotTheta) * (varianceRM + otherSP->varianceR())) * - iDeltaR2; - - if (not m_config.interactionPointCut or - std::abs(rM * yNewFrame) <= impactMax * xNewFrame) { - if (deltaZ > m_config.deltaZMax or deltaZ < -m_config.deltaZMax) { + // interactionPointCut == true we apply this cut first cuts before + // coordinate transformation to avoid unnecessary calculations + if (std::abs(rM * yNewFrame) <= impactMax * xNewFrame) { + // check if duplet cotTheta is within the region of interest + // cotTheta is defined as (deltaZ / deltaR) but instead we multiply + // cotThetaMax by deltaR to avoid division + if (deltaZ > m_config.cotThetaMax * deltaR or + deltaZ < -m_config.cotThetaMax * deltaR) { continue; } + const float iDeltaR = std::sqrt(iDeltaR2); + const float cotTheta = deltaZ * iDeltaR; + + const float Er = + ((varianceZM + otherSP->varianceZ()) + + (cotTheta * cotTheta) * (varianceRM + otherSP->varianceR())) * + iDeltaR2; + // fill output vectors linCircleVec.emplace_back(cotTheta, iDeltaR, Er, uT, vT, xNewFrame, yNewFrame); spacePointData.setDeltaR(otherSP->index(), std::sqrt(deltaR2 + (deltaZ * deltaZ))); - outVec.push_back(otherSP.get()); + outVec.emplace_back(otherSP.get()); continue; } @@ -356,12 +398,28 @@ SeedFinder::getCompatibleDoublets( continue; } + // check if duplet cotTheta is within the region of interest + // cotTheta is defined as (deltaZ / deltaR) but instead we multiply + // cotThetaMax by deltaR to avoid division + if (deltaZ > m_config.cotThetaMax * deltaR or + deltaZ < -m_config.cotThetaMax * deltaR) { + continue; + } + + const float iDeltaR = std::sqrt(iDeltaR2); + const float cotTheta = deltaZ * iDeltaR; + + const float Er = + ((varianceZM + otherSP->varianceZ()) + + (cotTheta * cotTheta) * (varianceRM + otherSP->varianceR())) * + iDeltaR2; + // fill output vectors linCircleVec.emplace_back(cotTheta, iDeltaR, Er, uT, vT, xNewFrame, yNewFrame); spacePointData.setDeltaR(otherSP->index(), std::sqrt(deltaR2 + (deltaZ * deltaZ))); - outVec.push_back(otherSP.get()); + outVec.emplace_back(otherSP.get()); } } } @@ -546,7 +604,7 @@ inline void SeedFinder::filterCandidates( cotThetaB = -zB * std::sqrt(iDeltaRB2); cotThetaT = zT * std::sqrt(iDeltaRT2); - rMxy = std::sqrt(rMTransf[0] * rMTransf[0] + rMTransf[1] * rMTransf[1]); + rMxy = std::hypot(rMTransf[0], rMTransf[1]); float Ax = rMTransf[0] / rMxy; float Ay = rMTransf[1] / rMxy; @@ -645,12 +703,21 @@ inline void SeedFinder::filterCandidates( if (!std::isinf(m_config.maxPtScattering)) { // if pT > maxPtScattering, calculate allowed scattering angle using // maxPtScattering instead of pt. - float pT = options.pTPerHelixRadius * std::sqrt(S2 / B2) / 2.; - if (pT > m_config.maxPtScattering) { + // To avoid 0-divison the pT check is skipped in case of B2==0, and + // p2scatterSigma is calculated directly from maxPtScattering + if (B2 == 0) { float pTscatterSigma = (m_config.highland / m_config.maxPtScattering) * m_config.sigmaScattering; p2scatterSigma = pTscatterSigma * pTscatterSigma * iSinTheta2; + } else { + float pT = options.pTPerHelixRadius * std::sqrt(S2 / B2) / 2.; + if (pT > m_config.maxPtScattering) { + float pTscatterSigma = + (m_config.highland / m_config.maxPtScattering) * + m_config.sigmaScattering; + p2scatterSigma = pTscatterSigma * pTscatterSigma * iSinTheta2; + } } } diff --git a/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp b/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp index 0053975ae6f..395fa8a3d9b 100644 --- a/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp +++ b/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp @@ -158,16 +158,8 @@ bool SeedFinderOrthogonal::validTuple( float deltaR = rH - rL; - /* - * Cut: Ensure that the forward angle (z / r) lies within reasonable bounds, - * which is to say the absolute value must be smaller than the max cot(θ). - */ float deltaZ = (zH - zL); float cotTheta = deltaZ / deltaR; - if (std::fabs(cotTheta) > m_config.cotThetaMax) { - return false; - } - /* * Cut: Ensure that the origin of the dublet (the intersection of the line * between them with the z axis) lies within the collision region. @@ -177,9 +169,6 @@ bool SeedFinderOrthogonal::validTuple( zOrigin > m_config.collisionRegionMax) { return false; } - if (std::abs(deltaZ) > m_config.deltaZMax) { - return false; - } // cut on the max curvature calculated from dublets // first transform the space point coordinates into a frame such that the @@ -223,6 +212,20 @@ bool SeedFinderOrthogonal::validTuple( } } + /* + * Cut: Ensure that the forward angle (z / r) lies within reasonable bounds, + * which is to say the absolute value must be smaller than the max cot(θ). + */ + if (std::fabs(cotTheta) > m_config.cotThetaMax) { + return false; + } + /* + * Cut: Ensure that z-distance between SPs is within max and min values. + */ + if (std::abs(deltaZ) > m_config.deltaZMax) { + return false; + } + return true; } diff --git a/Core/include/Acts/Surfaces/ConeSurface.hpp b/Core/include/Acts/Surfaces/ConeSurface.hpp index 91fa3f81102..7ef381b1798 100644 --- a/Core/include/Acts/Surfaces/ConeSurface.hpp +++ b/Core/include/Acts/Surfaces/ConeSurface.hpp @@ -167,14 +167,15 @@ class ConeSurface : public Surface { /// @param position The position to start from /// @param direction The direction at start /// @param bcheck the Boundary Check + /// @param tolerance the tolerance used for the intersection /// /// If possible returns both solutions for the cylinder /// /// @return SurfaceIntersection object (contains intersection & surface) - SurfaceIntersection intersect(const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck) const final; + SurfaceIntersection intersect( + const GeometryContext& gctx, const Vector3& position, + const Vector3& direction, const BoundaryCheck& bcheck = false, + double tolerance = s_onSurfaceTolerance) const final; /// The pathCorrection for derived classes with thickness /// diff --git a/Core/include/Acts/Surfaces/CylinderSurface.hpp b/Core/include/Acts/Surfaces/CylinderSurface.hpp index 5ca4b826f2b..5962af2ee0b 100644 --- a/Core/include/Acts/Surfaces/CylinderSurface.hpp +++ b/Core/include/Acts/Surfaces/CylinderSurface.hpp @@ -177,14 +177,15 @@ class CylinderSurface : public Surface { /// @param position The position to start from /// @param direction The direction at start /// @param bcheck the Boundary Check + /// @param tolerance the tolerance used for the intersection /// /// If possible returns both solutions for the cylinder /// /// @return SurfaceIntersection object (contains intersection & surface) - SurfaceIntersection intersect(const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck) const final; + SurfaceIntersection intersect( + const GeometryContext& gctx, const Vector3& position, + const Vector3& direction, const BoundaryCheck& bcheck = false, + ActsScalar tolerance = s_onSurfaceTolerance) const final; /// Path correction due to incident of the track /// diff --git a/Core/include/Acts/Surfaces/DiscSurface.hpp b/Core/include/Acts/Surfaces/DiscSurface.hpp index 1879a29978a..8b66c079d4c 100644 --- a/Core/include/Acts/Surfaces/DiscSurface.hpp +++ b/Core/include/Acts/Surfaces/DiscSurface.hpp @@ -243,6 +243,7 @@ class DiscSurface : public Surface { /// @param direction The global direction at the starting point /// @note expected to be normalized (no checking) /// @param bcheck The boundary check prescription + /// @param tolerance the tolerance used for the intersection /// /// mathematical motivation: /// @@ -263,8 +264,8 @@ class DiscSurface : public Surface { /// @return The SurfaceIntersection object SurfaceIntersection intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck = false) const final; + const Vector3& direction, const BoundaryCheck& bcheck = false, + ActsScalar tolerance = s_onSurfaceTolerance) const final; /// Implement the binningValue /// diff --git a/Core/include/Acts/Surfaces/LineSurface.hpp b/Core/include/Acts/Surfaces/LineSurface.hpp index f0a80593b59..8f6033e02a5 100644 --- a/Core/include/Acts/Surfaces/LineSurface.hpp +++ b/Core/include/Acts/Surfaces/LineSurface.hpp @@ -197,6 +197,7 @@ class LineSurface : public Surface { /// @param direction The global direction at the starting point /// @note exptected to be normalized /// @param bcheck The boundary check directive for the estimate + /// @param tolerance the tolerance used for the intersection /// /// mathematical motivation: /// Given two lines in parameteric form:
@@ -228,8 +229,8 @@ class LineSurface : public Surface { /// @return is the intersection object SurfaceIntersection intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck = false) const final; + const Vector3& direction, const BoundaryCheck& bcheck = false, + ActsScalar tolerance = s_onSurfaceTolerance) const final; /// the pathCorrection for derived classes with thickness /// is by definition 1 for LineSurfaces diff --git a/Core/include/Acts/Surfaces/PlaneSurface.hpp b/Core/include/Acts/Surfaces/PlaneSurface.hpp index 5716f52b0f0..340c69bc52f 100644 --- a/Core/include/Acts/Surfaces/PlaneSurface.hpp +++ b/Core/include/Acts/Surfaces/PlaneSurface.hpp @@ -156,6 +156,7 @@ class PlaneSurface : public Surface { /// @param direction The direction of the interesection attempt, /// (@note expected to be normalized) /// @param bcheck The boundary check directive + /// @param tolerance the tolerance used for the intersection /// /// mathematical motivation: /// @@ -177,8 +178,8 @@ class PlaneSurface : public Surface { /// @return the SurfaceIntersection object SurfaceIntersection intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck = false) const final; + const Vector3& direction, const BoundaryCheck& bcheck = false, + ActsScalar tolerance = s_onSurfaceTolerance) const final; /// Return a Polyhedron for the surfaces /// diff --git a/Core/include/Acts/Surfaces/RadialBounds.hpp b/Core/include/Acts/Surfaces/RadialBounds.hpp index e6f71877ef9..578bb1764cb 100644 --- a/Core/include/Acts/Surfaces/RadialBounds.hpp +++ b/Core/include/Acts/Surfaces/RadialBounds.hpp @@ -163,10 +163,10 @@ inline void RadialBounds::checkConsistency() noexcept(false) { throw std::invalid_argument("RadialBounds: invalid radial setup"); } if (get(eHalfPhiSector) < 0. or get(eHalfPhiSector) > M_PI) { - throw std::invalid_argument("CylinderBounds: invalid phi sector setup."); + throw std::invalid_argument("RadialBounds: invalid phi sector setup."); } if (get(eAveragePhi) != detail::radian_sym(get(eAveragePhi))) { - throw std::invalid_argument("CylinderBounds: invalid phi positioning."); + throw std::invalid_argument("RadialBounds: invalid phi positioning."); } } diff --git a/Core/include/Acts/Surfaces/Surface.hpp b/Core/include/Acts/Surfaces/Surface.hpp index 05021440f3f..29e0610dc24 100644 --- a/Core/include/Acts/Surfaces/Surface.hpp +++ b/Core/include/Acts/Surfaces/Surface.hpp @@ -69,6 +69,9 @@ class Surface : public virtual GeometryObject, Other = 7 }; + /// Helper strings for screen output + static std::array s_surfaceTypeNames; + protected: /// Constructor with Transform3 as a shared object /// @@ -395,12 +398,13 @@ class Surface : public virtual GeometryObject, /// @param position The position to start from /// @param direction The direction at start /// @param bcheck the Boundary Check + /// @param tolerance the tolerance used for the intersection /// /// @return SurfaceIntersection object (contains intersection & surface) - virtual SurfaceIntersection intersect(const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction, - const BoundaryCheck& bcheck) const = 0; + virtual SurfaceIntersection intersect( + const GeometryContext& gctx, const Vector3& position, + const Vector3& direction, const BoundaryCheck& bcheck = false, + ActsScalar tolerance = s_onSurfaceTolerance) const = 0; /// Output Method for std::ostream, to be overloaded by child classes /// diff --git a/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp b/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp index c9c961335a7..eb8034b90a4 100644 --- a/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp @@ -25,7 +25,8 @@ namespace PlanarHelper { /// @return The intersection inline Intersection3D intersect(const Transform3& transform, const Vector3& position, - const Vector3& direction) { + const Vector3& direction, + ActsScalar tolerance) { // Get the matrix from the transform (faster access) const auto& tMatrix = transform.matrix(); const Vector3 pnormal = tMatrix.block<3, 1>(0, 2).transpose(); @@ -36,10 +37,9 @@ inline Intersection3D intersect(const Transform3& transform, // Translate that into a path ActsScalar path = (pnormal.dot((pcenter - position))) / (denom); // Is valid hence either on surface or reachable - Intersection3D::Status status = - std::abs(path) < std::abs(s_onSurfaceTolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; + Intersection3D::Status status = std::abs(path) < std::abs(tolerance) + ? Intersection3D::Status::onSurface + : Intersection3D::Status::reachable; // Return the intersection return Intersection3D{(position + path * direction), path, status}; } diff --git a/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp b/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp index 5164ad93709..e76a4517d3e 100644 --- a/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/VerticesHelper.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Tolerance.hpp" #include #include diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index a273c5ed7b6..04a3f133855 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -487,7 +487,7 @@ class CombinatorialKalmanFilter { // --> Call the smoothing // --> Set a stop condition when all track states have been // handled - auto res = finalize(state, stepper, result); + auto res = finalize(state, stepper, navigator, result); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); result.result = res.error(); @@ -524,10 +524,7 @@ class CombinatorialKalmanFilter { result.iSmoothed++; // Reverse navigation direction to start targeting for the rest // tracks - state.stepping.navDir = - (state.stepping.navDir == NavigationDirection::Backward) - ? NavigationDirection::Forward - : NavigationDirection::Backward; + state.stepping.navDir = state.stepping.navDir.invert(); // To avoid meaningless navigation target call state.stepping.stepSize = ConstrainedStep(state.stepping.navDir * @@ -923,7 +920,7 @@ class CombinatorialKalmanFilter { trackState.allocateCalibrated(candidateTrackState.calibratedSize()); trackState.copyFrom(candidateTrackState, mask, false); - auto& typeFlags = trackState.typeFlags(); + auto typeFlags = trackState.typeFlags(); if (trackState.referenceSurface().surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); } @@ -953,7 +950,7 @@ class CombinatorialKalmanFilter { } else { // Kalman update auto updateRes = m_extensions.updater( - gctx, trackState, NavigationDirection::Forward, getDummyLogger()); + gctx, trackState, Direction::Forward, getDummyLogger()); if (!updateRes.ok()) { ACTS_ERROR("Update step failed: " << updateRes.error()); return updateRes.error(); @@ -1016,7 +1013,7 @@ class CombinatorialKalmanFilter { // parameter // Set the track state flags - auto& typeFlags = trackStateProxy.typeFlags(); + auto typeFlags = trackStateProxy.typeFlags(); if (trackStateProxy.referenceSurface().surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); } @@ -1093,12 +1090,16 @@ class CombinatorialKalmanFilter { /// /// @tparam propagator_state_t is the type of Propagagor state /// @tparam stepper_t Type of the stepper + /// @tparam navigator_t Type of the navigator /// /// @param state is the mutable propagator state object /// @param stepper The stepper in use + /// @param navigator The navigator in use /// @param result is the mutable result state object - template + template Result finalize(propagator_state_t& state, const stepper_t& stepper, + const navigator_t& navigator, result_type& result) const { // The measurement tip of the track being smoothed const auto& lastMeasurementIndex = @@ -1203,10 +1204,7 @@ class CombinatorialKalmanFilter { ACTS_VERBOSE( "Reverse navigation direction after smoothing for reaching the " "target surface"); - state.stepping.navDir = - (state.stepping.navDir == NavigationDirection::Forward) - ? NavigationDirection::Backward - : NavigationDirection::Forward; + state.stepping.navDir = state.stepping.navDir.invert(); } // Reinitialize the stepping jacobian state.stepping.jacobian = BoundMatrix::Identity(); @@ -1218,6 +1216,11 @@ class CombinatorialKalmanFilter { // Set accumulatd path to zero before targeting surface state.stepping.pathAccumulated = 0.; + // Reset the navigation state to enable propagation towards the target + // surface + navigator.targetReached(state.navigation, false); + navigator.currentSurface(state.navigation, nullptr); + return Result::success(); } diff --git a/Core/include/Acts/TrackFitting/Chi2Fitter.hpp b/Core/include/Acts/TrackFitting/Chi2Fitter.hpp index 0cef1892379..b50fab88ba1 100644 --- a/Core/include/Acts/TrackFitting/Chi2Fitter.hpp +++ b/Core/include/Acts/TrackFitting/Chi2Fitter.hpp @@ -241,9 +241,7 @@ class Chi2Fitter { using result_type = Chi2FitterResult; /// Allows retrieving measurements for a surface - const std::map>* - inputMeasurements = nullptr; + const std::map* inputMeasurements = nullptr; /// Whether to consider multiple scattering. bool multipleScattering = false; // TODO: add later @@ -456,7 +454,7 @@ class Chi2Fitter { //==================================== // Get and set the type flags - auto& typeFlags = trackStateProxy.typeFlags(); + auto typeFlags = trackStateProxy.typeFlags(); typeFlags.set(TrackStateFlag::ParameterFlag); if (surface->surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); @@ -501,7 +499,7 @@ class Chi2Fitter { trackStateProxy.setReferenceSurface(surface->getSharedPtr()); // Set the track state flags - auto& typeFlags = trackStateProxy.typeFlags(); + auto typeFlags = trackStateProxy.typeFlags(); typeFlags.set(TrackStateFlag::ParameterFlag); if (surface->surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); @@ -651,12 +649,11 @@ class Chi2Fitter { // We need to copy input SourceLinks anyways, so the map can own them. ACTS_VERBOSE("preparing " << std::distance(it, end) << " input measurements"); - std::map> - inputMeasurements; - + std::map inputMeasurements; for (; it != end; ++it) { - const SourceLink& sl = *it; - inputMeasurements.emplace(sl.geometryId(), sl); + SourceLink sl = *it; + auto geoId = sl.geometryId(); + inputMeasurements.emplace(geoId, std::move(sl)); } // TODO: for now, we use STL objects to collect the information during diff --git a/Core/include/Acts/TrackFitting/GainMatrixUpdater.hpp b/Core/include/Acts/TrackFitting/GainMatrixUpdater.hpp index ca365f3710a..5035ac82c54 100644 --- a/Core/include/Acts/TrackFitting/GainMatrixUpdater.hpp +++ b/Core/include/Acts/TrackFitting/GainMatrixUpdater.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Definitions/Direction.hpp" #include "Acts/EventData/Measurement.hpp" #include "Acts/EventData/MeasurementHelpers.hpp" #include "Acts/EventData/MultiTrajectory.hpp" @@ -49,7 +50,7 @@ class GainMatrixUpdater { Result operator()( const GeometryContext& gctx, typename MultiTrajectory::TrackStateProxy trackState, - NavigationDirection direction = NavigationDirection::Forward, + Direction direction = Direction::Forward, const Logger& logger = getDummyLogger()) const { (void)gctx; ACTS_VERBOSE("Invoked GainMatrixUpdater"); @@ -103,7 +104,7 @@ class GainMatrixUpdater { private: std::tuple visitMeasurement( - InternalTrackState trackState, NavigationDirection direction, + InternalTrackState trackState, Direction direction, const Logger& logger) const; }; diff --git a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp index ff820fcc1f7..ac7a21a871d 100644 --- a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp +++ b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp @@ -214,7 +214,7 @@ struct GaussianSumFitter { // Define directions based on input propagation direction. This way we can // refer to 'forward' and 'backward' regardless of the actual direction. const auto gsfForward = options.propagatorPlainOptions.direction; - const auto gsfBackward = static_cast(-1 * gsfForward); + const auto gsfBackward = gsfForward.invert(); // Check if the start parameters are on the start surface auto intersectionStatusStartSurface = @@ -234,17 +234,19 @@ struct GaussianSumFitter { // We need to copy input SourceLinks anyways, so the map can own them. ACTS_VERBOSE("Preparing " << std::distance(begin, end) << " input measurements"); - std::map> - inputMeasurements; + std::map inputMeasurements; for (auto it = begin; it != end; ++it) { - const SourceLink& sl = *it; - inputMeasurements.emplace(sl.geometryId(), sl); + SourceLink sl = *it; + auto geoId = sl.geometryId(); + inputMeasurements.emplace(geoId, std::move(sl)); } ACTS_VERBOSE( "Gsf: Final measuerement map size: " << inputMeasurements.size()); - throw_assert(sParameters.covariance() != std::nullopt, - "we need a covariance here..."); + + if (sParameters.covariance() == std::nullopt) { + return GsfError::StartParametersHaveNoCovariance; + } ///////////////// // Forward pass @@ -259,7 +261,7 @@ struct GaussianSumFitter { // Catch the actor and set the measurements auto& actor = fwdPropOptions.actionList.template get(); actor.setOptions(options); - actor.m_cfg.inputMeasurements = inputMeasurements; + actor.m_cfg.inputMeasurements = &inputMeasurements; actor.m_cfg.numberMeasurements = inputMeasurements.size(); actor.m_cfg.inReversePass = false; actor.m_cfg.logger = m_actorLogger.get(); @@ -326,7 +328,7 @@ struct GaussianSumFitter { auto& actor = bwdPropOptions.actionList.template get(); actor.setOptions(options); - actor.m_cfg.inputMeasurements = inputMeasurements; + actor.m_cfg.inputMeasurements = &inputMeasurements; actor.m_cfg.inReversePass = true; actor.m_cfg.logger = m_actorLogger.get(); actor.setOptions(options); @@ -364,8 +366,8 @@ struct GaussianSumFitter { auto proxy = r.fittedStates->getTrackState(fwdGsfResult.lastMeasurementTip); - proxy.filtered() = proxy.predicted(); - proxy.filteredCovariance() = proxy.predictedCovariance(); + proxy.shareFrom(TrackStatePropMask::Filtered, + TrackStatePropMask::Smoothed); r.currentTip = fwdGsfResult.lastMeasurementTip; r.visitedSurfaces.push_back(&proxy.referenceSurface()); @@ -424,6 +426,7 @@ struct GaussianSumFitter { if (not found && state.typeFlags().test(MeasurementFlag)) { state.typeFlags().set(OutlierFlag); state.typeFlags().reset(MeasurementFlag); + state.unset(TrackStatePropMask::Smoothed); } measurementStatesFinal += diff --git a/Core/include/Acts/TrackFitting/GsfError.hpp b/Core/include/Acts/TrackFitting/GsfError.hpp index 8ba89455717..f1b41fb7fb6 100644 --- a/Core/include/Acts/TrackFitting/GsfError.hpp +++ b/Core/include/Acts/TrackFitting/GsfError.hpp @@ -14,10 +14,11 @@ namespace Acts { namespace Experimental { enum class GsfError { - NoMeasurementStatesCreatedForward = 1, + StartParametersNotOnStartSurface = 1, + StartParametersHaveNoCovariance, + NoMeasurementStatesCreatedForward, NoMeasurementStatesCreatedBackward, NoMeasurementStatesCreatedFinal, - StartParametersNotOnStartSurface }; std::error_code make_error_code(GsfError e); diff --git a/Core/include/Acts/TrackFitting/GsfOptions.hpp b/Core/include/Acts/TrackFitting/GsfOptions.hpp index 04eacaed920..121a2dce358 100644 --- a/Core/include/Acts/TrackFitting/GsfOptions.hpp +++ b/Core/include/Acts/TrackFitting/GsfOptions.hpp @@ -11,6 +11,7 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" +#include "Acts/Propagator/MultiEigenStepperLoop.hpp" #include "Acts/Propagator/Propagator.hpp" #include "Acts/TrackFitting/detail/VoidKalmanComponents.hpp" #include "Acts/Utilities/CalibrationContext.hpp" @@ -38,7 +39,7 @@ struct GsfExtensions { using Calibrator = Delegate; using Updater = Delegate(const GeometryContext&, TrackStateProxy, - NavigationDirection, const Logger&)>; + Direction, const Logger&)>; using OutlierFinder = Delegate; @@ -84,6 +85,9 @@ struct GsfOptions { std::string_view finalMultiComponentStateColumn = ""; + MixtureReductionMethod stateReductionMethod = + MixtureReductionMethod::eMaxWeight; + #if __cplusplus < 202002L GsfOptions() = delete; #endif diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index eb4445adfdc..b69fb764b4c 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -59,7 +59,7 @@ struct KalmanFitterExtensions { const GeometryContext&, MultiTrajectory&, size_t, const Logger&)>; using Updater = Delegate(const GeometryContext&, TrackStateProxy, - NavigationDirection, const Logger&)>; + Direction, const Logger&)>; using OutlierFinder = Delegate; @@ -362,9 +362,7 @@ class KalmanFitter { // Update: // - Waiting for a current surface auto surface = navigator.currentSurface(state.navigation); - std::string direction = - (state.stepping.navDir == NavigationDirection::Forward) ? "forward" - : "backward"; + std::string direction = state.stepping.navDir.toString(); if (surface != nullptr) { // Check if the surface is in the measurement map // -> Get the measurement / calibrate @@ -514,10 +512,7 @@ class KalmanFitter { result.reversed = true; // Reverse navigation direction - state.stepping.navDir = - (state.stepping.navDir == NavigationDirection::Forward) - ? NavigationDirection::Backward - : NavigationDirection::Forward; + state.stepping.navDir = state.stepping.navDir.invert(); // Reset propagator options state.options.maxStepSize = @@ -979,10 +974,7 @@ class KalmanFitter { ACTS_VERBOSE( "Reverse navigation direction after smoothing for reaching the " "target surface"); - state.stepping.navDir = - (state.stepping.navDir == NavigationDirection::Forward) - ? NavigationDirection::Backward - : NavigationDirection::Forward; + state.stepping.navDir = state.stepping.navDir.invert(); } // Reset the step size state.stepping.stepSize = ConstrainedStep( @@ -1051,7 +1043,8 @@ class KalmanFitter { // for (const auto& sl : sourcelinks) { for (; it != end; ++it) { SourceLink sl = *it; - inputMeasurements.emplace(sl.geometryId(), std::move(sl)); + auto geoId = sl.geometryId(); + inputMeasurements.emplace(geoId, std::move(sl)); } // Create the ActionList and AbortList diff --git a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp index c77c0133f0c..ebff95fcc69 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp @@ -75,8 +75,7 @@ struct GsfActor { std::size_t maxComponents = 16; /// Input measurements - std::map> - inputMeasurements; + const std::map* inputMeasurements = nullptr; /// Bethe Heitler Approximator pointer. The fitter holds the approximator /// instance TODO if we somehow could initialize a reference here... @@ -108,6 +107,9 @@ struct GsfActor { /// be started backwards in the first pass bool inReversePass = false; + /// How to reduce the states that are stored in the multi trajectory + MixtureReductionMethod reductionMethod = MixtureReductionMethod::eMaxWeight; + const Logger* logger{nullptr}; } m_cfg; @@ -219,12 +221,12 @@ struct GsfActor { // Check what we have on this surface const auto found_source_link = - m_cfg.inputMeasurements.find(surface.geometryId()); + m_cfg.inputMeasurements->find(surface.geometryId()); const bool haveMaterial = navigator.currentSurface(state.navigation)->surfaceMaterial() && !m_cfg.disableAllMaterialHandling; const bool haveMeasurement = - found_source_link != m_cfg.inputMeasurements.end(); + found_source_link != m_cfg.inputMeasurements->end(); ACTS_VERBOSE(std::boolalpha << "haveMaterial " << haveMaterial << ", haveMeasurement: " << haveMeasurement); @@ -405,24 +407,21 @@ struct GsfActor { auto new_pars = old_bound.parameters(); const auto delta_p = [&]() { - if (state.stepping.navDir == NavigationDirection::Forward) { + if (state.stepping.navDir == Direction::Forward) { return p_prev * (gaussian.mean - 1.); } else { return p_prev * (1. / gaussian.mean - 1.); } }(); - throw_assert(p_prev + delta_p > 0., - "new momentum after bethe-heitler must be > 0, p_prev= " - << p_prev << ", delta_p=" << delta_p - << ", gaussian mean: " << gaussian.mean); + assert(p_prev + delta_p > 0. && "new momentum must be > 0"); new_pars[eBoundQOverP] = old_bound.charge() / (p_prev + delta_p); // compute inverse variance of p from mixture and update covariance auto new_cov = old_bound.covariance().value(); const auto varInvP = [&]() { - if (state.stepping.navDir == NavigationDirection::Forward) { + if (state.stepping.navDir == Direction::Forward) { const auto f = 1. / (p_prev * gaussian.mean); return f * f * gaussian.var; } else { @@ -431,10 +430,8 @@ struct GsfActor { }(); new_cov(eBoundQOverP, eBoundQOverP) += varInvP; - throw_assert(std::isfinite(new_cov(eBoundQOverP, eBoundQOverP)), - "cov not finite, varInvP=" - << varInvP << ", p_prev=" << p_prev << ", gaussian.mean=" - << gaussian.mean << ", gaussian.var=" << gaussian.var); + assert(std::isfinite(new_cov(eBoundQOverP, eBoundQOverP)) && + "new cov not finite"); // Set the remaining things and push to vector componentCaches.push_back( @@ -718,64 +715,75 @@ struct GsfActor { // Update the state and stepper with material effects interaction.updateState(singleState, singleStepper, addNoise); - throw_assert(singleState.stepping.cov.array().isFinite().all(), - "covariance not finite after update"); + assert(singleState.stepping.cov.array().isFinite().all() && + "covariance not finite after multi scattering"); } } } void addCombinedState(result_type& result, const TemporaryStates& tmpStates, const Surface& surface) const { - using PredProjector = + using PrtProjector = MultiTrajectoryProjector; - using FiltProjector = + using FltProjector = MultiTrajectoryProjector; - // We do not need smoothed and jacobian for now - const auto mask = TrackStatePropMask::Calibrated | - TrackStatePropMask::Predicted | - TrackStatePropMask::Filtered; - if (not m_cfg.inReversePass) { - // The predicted state is the forward pass - const auto [filtMean, filtCov] = - angleDescriptionSwitch(surface, [&](const auto& desc) { - return combineGaussianMixture( - tmpStates.tips, - FiltProjector{tmpStates.traj, tmpStates.weights}, desc); - }); + const auto firstCmpProxy = + tmpStates.traj.getTrackState(tmpStates.tips.front()); + const auto isMeasurement = + firstCmpProxy.typeFlags().test(MeasurementFlag); + + const auto mask = + isMeasurement + ? TrackStatePropMask::Calibrated | TrackStatePropMask::Predicted | + TrackStatePropMask::Filtered | TrackStatePropMask::Smoothed + : TrackStatePropMask::Calibrated | TrackStatePropMask::Predicted; result.currentTip = result.fittedStates->addTrackState(mask, result.currentTip); auto proxy = result.fittedStates->getTrackState(result.currentTip); - auto firstCmpProxy = tmpStates.traj.getTrackState(tmpStates.tips.front()); proxy.setReferenceSurface(surface.getSharedPtr()); proxy.copyFrom(firstCmpProxy, mask); - // We set predicted & filtered the same so that the fields are not - // uninitialized when not finding this state in the reverse pass. - proxy.predicted() = filtMean; - proxy.predictedCovariance() = filtCov; - proxy.filtered() = filtMean; - proxy.filteredCovariance() = filtCov; + auto [prtMean, prtCov] = reduceGaussianMixture( + tmpStates.tips, surface, m_cfg.reductionMethod, + PrtProjector{tmpStates.traj, tmpStates.weights}); + proxy.predicted() = prtMean; + proxy.predictedCovariance() = prtCov; + + if (isMeasurement) { + auto [fltMean, fltCov] = reduceGaussianMixture( + tmpStates.tips, surface, m_cfg.reductionMethod, + FltProjector{tmpStates.traj, tmpStates.weights}); + proxy.filtered() = fltMean; + proxy.filteredCovariance() = fltCov; + proxy.smoothed() = BoundVector::Constant(-2); + proxy.smoothedCovariance() = BoundSymMatrix::Constant(-2); + } else { + proxy.shareFrom(TrackStatePropMask::Predicted, + TrackStatePropMask::Filtered); + } + } else { assert((result.currentTip != MultiTrajectoryTraits::kInvalid && "tip not valid")); + result.fittedStates->applyBackwards( result.currentTip, [&](auto trackState) { auto fSurface = &trackState.referenceSurface(); if (fSurface == &surface) { - const auto [filtMean, filtCov] = - angleDescriptionSwitch(surface, [&](const auto& desc) { - return combineGaussianMixture( - tmpStates.tips, - FiltProjector{tmpStates.traj, tmpStates.weights}, desc); - }); - - trackState.filtered() = filtMean; - trackState.filteredCovariance() = filtCov; result.surfacesVisitedBwdAgain.push_back(&surface); + + if (trackState.hasSmoothed()) { + const auto [smtMean, smtCov] = reduceGaussianMixture( + tmpStates.tips, surface, m_cfg.reductionMethod, + FltProjector{tmpStates.traj, tmpStates.weights}); + + trackState.smoothed() = smtMean; + trackState.smoothedCovariance() = smtCov; + } return false; } return true; @@ -791,6 +799,7 @@ struct GsfActor { m_cfg.abortOnError = options.abortOnError; m_cfg.disableAllMaterialHandling = options.disableAllMaterialHandling; m_cfg.weightCutoff = options.weightCutoff; + m_cfg.reductionMethod = options.stateReductionMethod; } }; diff --git a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp index e142682134c..5d03273c680 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp @@ -124,11 +124,7 @@ class ScopedGsfInfoPrinterAndChecker { << stepper.direction(state.stepping).transpose() << " and momentum " << stepper.momentum(state.stepping) << " and charge " << stepper.charge(state.stepping)); - ACTS_VERBOSE("Propagation is in " - << (state.stepping.navDir == NavigationDirection::Forward - ? "forward" - : "backward") - << " mode"); + ACTS_VERBOSE("Propagation is in " << state.stepping.navDir << " mode"); print_component_stats(); } diff --git a/Core/include/Acts/TrackFitting/detail/KLMixtureReduction.hpp b/Core/include/Acts/TrackFitting/detail/KLMixtureReduction.hpp index a79b5e6d18e..cfade1f353b 100644 --- a/Core/include/Acts/TrackFitting/detail/KLMixtureReduction.hpp +++ b/Core/include/Acts/TrackFitting/detail/KLMixtureReduction.hpp @@ -11,9 +11,8 @@ #include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackParameters.hpp" -#include "Acts/Utilities/detail/gaussian_mixture_helpers.hpp" - -#include "GsfUtils.hpp" +#include "Acts/TrackFitting/detail/GsfUtils.hpp" +#include "Acts/Utilities/GaussianMixtureReduction.hpp" namespace Acts { @@ -29,17 +28,15 @@ auto computeSymmetricKlDivergence(const component_t &a, const component_t &b, const auto covA = proj(a).boundCov(eBoundQOverP, eBoundQOverP); const auto covB = proj(b).boundCov(eBoundQOverP, eBoundQOverP); - throw_assert(covA != 0.0, ""); - throw_assert(std::isfinite(covA), ""); - throw_assert(covB != 0.0, ""); - throw_assert(std::isfinite(covB), ""); + assert(covA != 0.0); + assert(std::isfinite(covA)); + assert(covB != 0.0); + assert(std::isfinite(covB)); const auto kl = covA * (1 / covB) + covB * (1 / covA) + (parsA - parsB) * (1 / covA + 1 / covB) * (parsA - parsB); - throw_assert(kl >= 0.0, "kl-distance should be positive, but is: " - << kl << "(qop_a: " << parsA << "+-" << covA - << ", qop_b: " << parsB << "+-" << covB << ")"); + assert(kl >= 0.0 && "kl-divergence must be non-negative"); return kl; } @@ -49,7 +46,8 @@ template 0.0 && proj(b).weight > 0.0, "weight error"); + assert(proj(a).weight >= 0.0 && proj(b).weight >= 0.0 && + "non-positive weight"); std::array range = {std::ref(proj(a)), std::ref(proj(b))}; const auto refProj = [](auto &c) { @@ -57,7 +55,7 @@ auto mergeComponents(const component_t &a, const component_t &b, }; auto [mergedPars, mergedCov] = - combineGaussianMixture(range, refProj, angle_desc); + gaussianMixtureMeanCov(range, refProj, angle_desc); component_t ret = a; proj(ret).boundPars = mergedPars; @@ -214,9 +212,7 @@ void reduceWithKLDistance(std::vector &cmpCache, [&](const auto &a) { return proj(a).weight == -1.0; }), cmpCache.end()); - throw_assert(cmpCache.size() == maxCmpsAfterMerge, - "size mismatch, should be " << maxCmpsAfterMerge << ", but is " - << cmpCache.size()); + assert(cmpCache.size() == maxCmpsAfterMerge && "size mismatch"); } } // namespace detail diff --git a/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp b/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp index 3a11fddbd5d..2ac21d6ba47 100644 --- a/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp +++ b/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp @@ -76,7 +76,7 @@ auto kalmanHandleMeasurement( extensions.calibrator(state.geoContext, trackStateProxy); // Get and set the type flags - auto &typeFlags = trackStateProxy.typeFlags(); + auto typeFlags = trackStateProxy.typeFlags(); typeFlags.set(TrackStateFlag::ParameterFlag); if (surface.surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); @@ -142,7 +142,7 @@ auto kalmanHandleNoMeasurement( trackStateProxy.setReferenceSurface(surface.getSharedPtr()); // Set the track state flags - auto &typeFlags = trackStateProxy.typeFlags(); + auto typeFlags = trackStateProxy.typeFlags(); typeFlags.set(TrackStateFlag::ParameterFlag); if (surface.surfaceMaterial() != nullptr) { typeFlags.set(TrackStateFlag::MaterialFlag); diff --git a/Core/include/Acts/TrackFitting/detail/VoidKalmanComponents.hpp b/Core/include/Acts/TrackFitting/detail/VoidKalmanComponents.hpp index c03e72abe47..ca62d8166d9 100644 --- a/Core/include/Acts/TrackFitting/detail/VoidKalmanComponents.hpp +++ b/Core/include/Acts/TrackFitting/detail/VoidKalmanComponents.hpp @@ -27,7 +27,7 @@ template Result voidKalmanUpdater( const GeometryContext& /*gctx*/, typename MultiTrajectory::TrackStateProxy trackState, - NavigationDirection /*direction*/, const Logger& /*logger*/) { + Direction /*direction*/, const Logger& /*logger*/) { trackState.filtered() = trackState.predicted(); trackState.filteredCovariance() = trackState.predictedCovariance(); return Result::success(); diff --git a/Core/include/Acts/Utilities/Any.hpp b/Core/include/Acts/Utilities/Any.hpp index f7ae3718295..7cb75bec9bd 100644 --- a/Core/include/Acts/Utilities/Any.hpp +++ b/Core/include/Acts/Utilities/Any.hpp @@ -136,7 +136,9 @@ class AnyBase : public AnyBaseAll { } #if defined(_ACTS_ANY_ENABLE_VERBOSE) - AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); }; + AnyBase() { + _ACTS_ANY_VERBOSE("Default construct this=" << this); + }; #else AnyBase() = default; #endif @@ -173,7 +175,9 @@ class AnyBase : public AnyBaseAll { return *reinterpret_cast(dataPtr()); } - ~AnyBase() { destroy(); } + ~AnyBase() { + destroy(); + } AnyBase(const AnyBase& other) { if (m_handler == nullptr && other.m_handler == nullptr) { @@ -243,7 +247,9 @@ class AnyBase : public AnyBaseAll { return *this; } - operator bool() const { return m_handler != nullptr; } + operator bool() const { + return m_handler != nullptr; + } private: void* dataPtr() { @@ -254,7 +260,9 @@ class AnyBase : public AnyBaseAll { } } - void setDataPtr(void* ptr) { *reinterpret_cast(m_data.data()) = ptr; } + void setDataPtr(void* ptr) { + *reinterpret_cast(m_data.data()) = ptr; + } const void* dataPtr() const { if (m_handler->heapAllocated) { diff --git a/Core/include/Acts/Utilities/detail/gaussian_mixture_helpers.hpp b/Core/include/Acts/Utilities/GaussianMixtureReduction.hpp similarity index 77% rename from Core/include/Acts/Utilities/detail/gaussian_mixture_helpers.hpp rename to Core/include/Acts/Utilities/GaussianMixtureReduction.hpp index a5b30f54d81..202fed7ef03 100644 --- a/Core/include/Acts/Utilities/detail/gaussian_mixture_helpers.hpp +++ b/Core/include/Acts/Utilities/GaussianMixtureReduction.hpp @@ -76,9 +76,10 @@ auto angleDescriptionSwitch(const Surface &surface, Callable &&callable) { template -auto combineCov(const components_t components, const ActsVector &mean, - double sumOfWeights, projector_t &&projector, - const angle_desc_t &angleDesc) { +auto gaussianMixtureCov(const components_t components, + const ActsVector &mean, double sumOfWeights, + projector_t &&projector, + const angle_desc_t &angleDesc) { ActsSymMatrix cov = ActsSymMatrix::Zero(); for (const auto &cmp : components) { @@ -124,7 +125,7 @@ auto combineCov(const components_t components, const ActsVector &mean, /// angles in the bound parameters template ::Desc> -auto combineGaussianMixture(const components_t components, +auto gaussianMixtureMeanCov(const components_t components, projector_t &&projector = projector_t{}, const angle_desc_t &angleDesc = angle_desc_t{}) { // Extract the first component @@ -195,10 +196,48 @@ auto combineGaussianMixture(const components_t components, std::apply([&](auto... dsc) { (wrap(dsc), ...); }, angleDesc); const auto cov = - combineCov(components, mean, sumOfWeights, projector, angleDesc); + gaussianMixtureCov(components, mean, sumOfWeights, projector, angleDesc); return RetType{mean, cov}; } } // namespace detail + +/// @enum MixtureReductionMethod +/// +/// Available reduction methods for the reduction of a Gaussian mixture +enum class MixtureReductionMethod { eMean, eMaxWeight }; + +/// Reduce Gaussian mixture +/// +/// @param mixture The mixture iterable +/// @param surface The surface, on which the bound state is +/// @param method How to reduce the mixture +/// @param projector How to project a element of the iterable to something +/// like a std::tuple< double, BoundVector, BoundMatrix > +/// +/// @return parameters and covariance as std::tuple< BoundVector, BoundMatrix > +template +auto reduceGaussianMixture(const mixture_t &mixture, const Surface &surface, + MixtureReductionMethod method, + projector_t &&projector = projector_t{}) { + using R = std::tuple; + const auto [mean, cov] = + detail::angleDescriptionSwitch(surface, [&](const auto &desc) { + return detail::gaussianMixtureMeanCov(mixture, projector, desc); + }); + + if (method == MixtureReductionMethod::eMean) { + return R{mean, cov}; + } else { + const auto maxWeightIt = std::max_element( + mixture.begin(), mixture.end(), [&](const auto &a, const auto &b) { + return std::get<0>(projector(a)) < std::get<0>(projector(b)); + }); + const BoundVector meanMaxWeight = std::get<1>(projector(*maxWeightIt)); + + return R{meanMaxWeight, cov}; + } +} + } // namespace Acts diff --git a/Core/include/Acts/Utilities/Helpers.hpp b/Core/include/Acts/Utilities/Helpers.hpp index 759286450a9..9caff89804e 100644 --- a/Core/include/Acts/Utilities/Helpers.hpp +++ b/Core/include/Acts/Utilities/Helpers.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -98,7 +99,7 @@ double perp(const Eigen::MatrixBase& v) noexcept { std::abort(); } } - return std::sqrt(v[0] * v[0] + v[1] * v[1]); + return std::hypot(v[0], v[1]); } /// Calculate the theta angle (longitudinal w.r.t. z axis) of a vector @@ -121,7 +122,7 @@ double theta(const Eigen::MatrixBase& v) noexcept { } } - return std::atan2(std::sqrt(v[0] * v[0] + v[1] * v[1]), v[2]); + return std::atan2(perp(v), v[2]); } /// @brief Fast evaluation of trigonomic functions. @@ -136,7 +137,7 @@ static inline const std::array evaluateTrigonomics( const ActsScalar z = direction(2); // == cos(theta) // can be turned into cosine/sine const ActsScalar cosTheta = z; - const ActsScalar sinTheta = std::sqrt(x * x + y * y); + const ActsScalar sinTheta = std::hypot(x, y); const ActsScalar invSinTheta = 1. / sinTheta; const ActsScalar cosPhi = x * invSinTheta; const ActsScalar sinPhi = y * invSinTheta; @@ -598,4 +599,33 @@ T clampValue(U value) { static_cast(std::numeric_limits::max())); } +/// Return min/max from a (optionally) sorted series, obsolete with C++20 +/// (ranges) +/// +/// @tparam T a numeric series +/// +/// @param tseries is the number series +/// +/// @return [ min, max ] in an array of length 2 +template +std::array min_max(const T& tseries) { + return {*std::min_element(tseries.begin(), tseries.end()), + *std::max_element(tseries.begin(), tseries.end())}; +} + +/// Return range and medium of a sorted numeric series +/// +/// @tparam T a numeric series +/// +/// @param tseries is the number series +/// +/// @return [ range, medium ] in an tuple +template +std::tuple range_medium(const T& tseries) { + auto [min, max] = min_max(tseries); + typename T::value_type range = (max - min); + ActsScalar medium = static_cast((max + min) * 0.5); + return std::tie(range, medium); +} + } // namespace Acts diff --git a/Core/include/Acts/Utilities/PdgParticle.hpp b/Core/include/Acts/Utilities/PdgParticle.hpp index 56a60c7f06a..7d5c6eb4056 100644 --- a/Core/include/Acts/Utilities/PdgParticle.hpp +++ b/Core/include/Acts/Utilities/PdgParticle.hpp @@ -30,6 +30,7 @@ enum PdgParticle : int32_t { eAntiNeutron = -eNeutron, eProton = 2212, eAntiProton = -eProton, + eLead = 1000822080 }; /// Convert an anti-particle to its particle and leave particles as-is. diff --git a/Core/include/Acts/Utilities/TypeTraits.hpp b/Core/include/Acts/Utilities/TypeTraits.hpp index e945a32c748..e3ab955e35d 100644 --- a/Core/include/Acts/Utilities/TypeTraits.hpp +++ b/Core/include/Acts/Utilities/TypeTraits.hpp @@ -371,8 +371,8 @@ constexpr bool has_member = identical_to; /* Trait check for the qualifier and the return type of the function */ \ /* This does not check the const qualifier at all */ \ template \ - using qual_ret = decltype( \ - std::declval().method_name(std::declval()...)); \ + using qual_ret = decltype(std::declval().method_name( \ + std::declval()...)); \ \ /* The problem is this: while the above is fine with and without const, \ * and with and without exact argument type match, the assignment to the \ diff --git a/Core/include/Acts/Utilities/detail/grid_helper.hpp b/Core/include/Acts/Utilities/detail/grid_helper.hpp index 18c3ddd1970..59289077184 100644 --- a/Core/include/Acts/Utilities/detail/grid_helper.hpp +++ b/Core/include/Acts/Utilities/detail/grid_helper.hpp @@ -25,9 +25,9 @@ namespace detail { template constexpr T ipow(T num, unsigned int pow) { - return (pow >= sizeof(unsigned int) * 8) - ? 0 - : pow == 0 ? 1 : num * ipow(num, pow - 1); + return (pow >= sizeof(unsigned int) * 8) ? 0 + : pow == 0 ? 1 + : num * ipow(num, pow - 1); } // This object can be iterated to produce the (ordered) set of global indices diff --git a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.hpp b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.hpp index fc0c8015b6b..58d388be9cf 100644 --- a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.hpp +++ b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.hpp @@ -224,7 +224,9 @@ class AdaptiveMultiVertexFinder { std::unique_ptr m_logger; /// Private access to logging instance - const Logger& logger() const { return *m_logger; } + const Logger& logger() const { + return *m_logger; + } /// @brief Calls the seed finder and sets constraints on the found seed /// vertex if desired diff --git a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.ipp b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.ipp index d4ed2d13d0d..502c8ffb64b 100644 --- a/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.ipp +++ b/Core/include/Acts/Vertexing/AdaptiveMultiVertexFitter.ipp @@ -61,7 +61,7 @@ Acts::AdaptiveMultiVertexFitter::fitImpl( currentVtxInfo.oldPosition = currentVtx->fullPosition(); Vector4 dist = currentVtxInfo.oldPosition - currentVtxInfo.linPoint; - double perpDist = std::sqrt(dist[0] * dist[0] + dist[1] * dist[1]); + double perpDist = std::hypot(dist[0], dist[1]); // Determine if relinearization is needed if (perpDist > m_cfg.maxDistToLinPoint) { // Relinearization needed, distance too big diff --git a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp index 3057677bdae..fb7d3de4525 100644 --- a/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp +++ b/Core/include/Acts/Vertexing/HelicalTrackLinearizer.ipp @@ -24,9 +24,10 @@ Acts::Result Acts:: // Create propagator options propagator_options_t pOptions(gctx, mctx); - pOptions.direction = intersection.intersection.pathLength >= 0 - ? NavigationDirection::Forward - : NavigationDirection::Backward; + // Handling zero path length as forward here but we could actually skip the + // whole propagation in this case + pOptions.direction = + Direction::fromScalarZeroAsPositive(intersection.intersection.pathLength); // Do the propagation to linPointPos auto result = m_cfg.propagator->propagate(params, *perigeeSurface, pOptions); @@ -47,16 +48,6 @@ Acts::Result Acts:: } BoundSymMatrix parCovarianceAtPCA = endParams.covariance().value(); - if (parCovarianceAtPCA.determinant() <= 0) { - // Use the original parameters - paramsAtPCA = params.parameters(); - auto pos = endParams.position(gctx); - positionAtPCA[ePos0] = pos[ePos0]; - positionAtPCA[ePos1] = pos[ePos1]; - positionAtPCA[ePos2] = pos[ePos2]; - parCovarianceAtPCA = params.covariance().value(); - } - // phiV and functions double phiV = paramsAtPCA(BoundIndices::eBoundPhi); double sinPhiV = std::sin(phiV); @@ -119,7 +110,7 @@ Acts::Result Acts:: predParamsAtPCA[2] = phiAtPCA; predParamsAtPCA[3] = th; predParamsAtPCA[4] = qOvP; - predParamsAtPCA[5] = 0.; + predParamsAtPCA[5] = positionAtPCA[eTime]; // Fill position jacobian (D_k matrix), Eq. 5.36 in Ref(1) ActsMatrix positionJacobian; diff --git a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp index acacd9e29d5..094ed8433f8 100644 --- a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp +++ b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp @@ -73,7 +73,7 @@ Acts::ImpactPointEstimator:: // Create propagator options propagator_options_t pOptions(gctx, mctx); - pOptions.direction = NavigationDirection::Backward; + pOptions.direction = Direction::Backward; // Do the propagation to linPointPos auto result = m_cfg.propagator->propagate(trkParams, *planeSurface, pOptions); @@ -267,7 +267,7 @@ Acts::ImpactPointEstimator:: // Create propagator options propagator_options_t pOptions(gctx, mctx); - pOptions.direction = NavigationDirection::Backward; + pOptions.direction = Direction::Backward; // Do the propagation to linPoint auto result = m_cfg.propagator->propagate(track, *perigeeSurface, pOptions); @@ -370,7 +370,7 @@ Acts::ImpactPointEstimator:: // Create propagator options propagator_options_t pOptions(gctx, mctx); - pOptions.direction = NavigationDirection::Backward; + pOptions.direction = Direction::Backward; // Do the propagation to the perigeee auto result = m_cfg.propagator->propagate(track, *perigeeSurface, pOptions); @@ -414,7 +414,7 @@ Acts::ImpactPointEstimator:: // Create propagator options propagator_options_t pOptions(gctx, mctx); - pOptions.direction = NavigationDirection::Backward; + pOptions.direction = Direction::Backward; // Do the propagation to the perigeee auto result = m_cfg.propagator->propagate(track, *perigeeSurface, pOptions); diff --git a/Core/src/Definitions/CMakeLists.txt b/Core/src/Definitions/CMakeLists.txt index 88343570f85..24154e0ef3f 100644 --- a/Core/src/Definitions/CMakeLists.txt +++ b/Core/src/Definitions/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources( ActsCore PRIVATE Common.cpp + Direction.cpp ) diff --git a/Core/src/Definitions/Common.cpp b/Core/src/Definitions/Common.cpp index 58ef225ac12..b01889889a7 100644 --- a/Core/src/Definitions/Common.cpp +++ b/Core/src/Definitions/Common.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2022 CERN for the benefit of the Acts project +// Copyright (C) 2022-2023 CERN for the benefit of the Acts project // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,26 +8,11 @@ #include "Acts/Definitions/Common.hpp" +#include #include -namespace Acts { - -std::ostream& operator<<(std::ostream& os, NavigationDirection navDir) { - switch (navDir) { - case NavigationDirection::Forward: - os << "forward"; - break; - case NavigationDirection::Backward: - os << "backward"; - break; - default: - assert(false && "Invalid direction (shouldn't happen)"); - std::abort(); - } - return os; -} - -std::ostream& operator<<(std::ostream& os, MaterialUpdateStage matUpdate) { +std::ostream& Acts::operator<<(std::ostream& os, + MaterialUpdateStage matUpdate) { switch (matUpdate) { case MaterialUpdateStage::PreUpdate: os << "PreUpdate (-1)"; @@ -44,5 +29,3 @@ std::ostream& operator<<(std::ostream& os, MaterialUpdateStage matUpdate) { } return os; } - -} // namespace Acts diff --git a/Core/src/Definitions/Direction.cpp b/Core/src/Definitions/Direction.cpp new file mode 100644 index 00000000000..544543ff3b8 --- /dev/null +++ b/Core/src/Definitions/Direction.cpp @@ -0,0 +1,28 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Definitions/Direction.hpp" + +#include + +std::string Acts::Direction::toString() const { + switch (m_value) { + case Value::Positive: + return "forward"; + case Value::Negative: + return "backward"; + default: + assert(false && "Invalid direction (shouldn't happen)"); + std::abort(); + } +} + +std::ostream& Acts::operator<<(std::ostream& os, Direction dir) { + os << dir.toString(); + return os; +} diff --git a/Core/src/Detector/CMakeLists.txt b/Core/src/Detector/CMakeLists.txt index 7a59d2606ee..a7c35ed63f2 100644 --- a/Core/src/Detector/CMakeLists.txt +++ b/Core/src/Detector/CMakeLists.txt @@ -1,10 +1,15 @@ target_sources( ActsCore PRIVATE + detail/CylindricalDetectorHelper.cpp + detail/PortalHelper.cpp + detail/SupportHelper.cpp + CylindricalContainerBuilder.cpp Detector.cpp DetectorVolume.cpp + DetectorVolumeBuilder.cpp + LayerStructureBuilder.cpp Portal.cpp PortalGenerators.cpp - PortalHelper.cpp ProtoDetector.cpp ) diff --git a/Core/src/Detector/CylindricalContainerBuilder.cpp b/Core/src/Detector/CylindricalContainerBuilder.cpp new file mode 100644 index 00000000000..5228c47439c --- /dev/null +++ b/Core/src/Detector/CylindricalContainerBuilder.cpp @@ -0,0 +1,149 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/CylindricalContainerBuilder.hpp" + +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/detail/CylindricalDetectorHelper.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include + +namespace { + +/// @brief Helper method to connect/wrap volumes or containers +/// +/// @tparam object_collection either a vector of volumes or containers +/// @param gctx The geometry context of the this call +/// @param objects The object vector +/// @param binnning the chosen binning +/// @param logLevel the logging output level +/// +/// @note no checking on consistency is done as the caller container builder +/// is already checked at construction +/// +/// @return a newly built container +template +Acts::Experimental::DetectorComponent::PortalContainer connect( + const Acts::GeometryContext& gctx, object_collection& objects, + const std::vector& binning, + Acts::Logging::Level logLevel) { + // Return container object + Acts::Experimental::DetectorComponent::PortalContainer rContainer; + if (binning.size() == 1u) { + Acts::BinningValue bv = binning.front(); + // 1-dimensional binning options + switch (bv) { + case Acts::binR: { + rContainer = + Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( + gctx, objects, {}, logLevel); + } break; + case Acts::binZ: { + rContainer = + Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( + gctx, objects, {}, logLevel); + } break; + case Acts::binPhi: { + rContainer = + Acts::Experimental::detail::CylindricalDetectorHelper::connectInPhi( + gctx, objects, {}, logLevel); + } break; + default: + break; + } + } else if (binning == + std::vector{Acts::binZ, Acts::binR} and + objects.size() == 2u) { + rContainer = + Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( + gctx, objects, logLevel); + } + return rContainer; +} +} // namespace + +Acts::Experimental::CylindricalContainerBuilder::CylindricalContainerBuilder( + const Acts::Experimental::CylindricalContainerBuilder::Config& cfg, + std::unique_ptr logger) + : IDetectorComponentBuilder(), m_cfg(cfg), m_logger(std::move(logger)) { + // Check if builders are present + if (m_cfg.builders.empty()) { + throw std::invalid_argument( + "CylindricalContainerBuilder: no sub builders provided."); + } + // Check if binning value is correctly chosen + if (m_cfg.binning.size() == 1u) { + // 1-dimensional case + auto b = m_cfg.binning.front(); + if (b != Acts::binR and b != Acts::binZ and b != Acts::binPhi) { + throw std::invalid_argument( + "CylindricalContainerBuilder: 1D binning only supported in z, r, or " + "phi"); + } + } else if (m_cfg.binning.size() == 2u) { + // 2-dimensional case, this is for wrapping + if (m_cfg.binning != + std::vector{Acts::binZ, Acts::binR}) { + throw std::invalid_argument( + "CylindricalContainerBuilder: 2D binning only supports wrapping in " + "z-r."); + } else if (m_cfg.builders.size() != 2u) { + // Wrapping needs exacly one inner (volume or container) and one outer + // volume + throw std::invalid_argument( + "CylindricalContainerBuilder: 2D wrapping in z-r requires exaclty " + "two builders."); + } + } +} + +Acts::Experimental::DetectorComponent +Acts::Experimental::CylindricalContainerBuilder::construct( + RootDetectorVolumes& roots, const GeometryContext& gctx) const { + // Return container object + DetectorComponent::PortalContainer rContainer; + + // Create the indivudal components, collect for both outcomes + std::vector components; + ACTS_DEBUG("Building container from " << m_cfg.builders.size() + << " components."); + // Check through the component volumes - if every builder only + // built exactly one volume, you are at pure navigation level + bool atNavigationLevel = true; + components.reserve(m_cfg.builders.size()); + std::for_each( + m_cfg.builders.begin(), m_cfg.builders.end(), [&](const auto& builder) { + auto cmp = builder->construct(roots, gctx); + atNavigationLevel = (atNavigationLevel and cmp.volumes.size() == 1u); + components.push_back(cmp); + }); + // Navigation level detected, connect volumes (cleaner and faster than + // connect containers) + if (atNavigationLevel) { + ACTS_VERBOSE( + "Component volumes are at navigation level: connecting volumes."); + std::vector> volumes; + volumes.reserve(components.size()); + std::for_each(components.begin(), components.end(), [&](const auto& cmp) { + volumes.push_back(cmp.volumes.front()); + }); + // Connect volumes + rContainer = connect(gctx, volumes, m_cfg.binning, logger().level()); + } else { + ACTS_VERBOSE("Components contain sub containers: connect containers."); + std::vector containers; + containers.reserve(components.size()); + std::for_each(components.begin(), components.end(), + [&](const auto& cmp) { containers.push_back(cmp.portals); }); + // Connect containers + rContainer = connect(gctx, containers, m_cfg.binning, logger().level()); + } + // Return the container + return Acts::Experimental::DetectorComponent{{}, rContainer}; +} diff --git a/Core/src/Detector/DetectorVolume.cpp b/Core/src/Detector/DetectorVolume.cpp index bff8c5c148f..e5d10c9f04d 100644 --- a/Core/src/Detector/DetectorVolume.cpp +++ b/Core/src/Detector/DetectorVolume.cpp @@ -21,7 +21,7 @@ Acts::Experimental::DetectorVolume::DetectorVolume( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, DetectorVolumeUpdator&& detectorVolumeUpdator, @@ -53,7 +53,7 @@ Acts::Experimental::DetectorVolume::DetectorVolume( Acts::Experimental::DetectorVolume::DetectorVolume( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, SurfaceCandidatesUpdator&& surfaceCandidateUpdator) : DetectorVolume(gctx, name, transform, std::move(bounds), {}, {}, tryNoVolumes(), std::move(surfaceCandidateUpdator)) {} @@ -61,7 +61,7 @@ Acts::Experimental::DetectorVolume::DetectorVolume( std::shared_ptr Acts::Experimental::DetectorVolume::makeShared( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, DetectorVolumeUpdator&& detectorVolumeUpdator, @@ -75,7 +75,7 @@ Acts::Experimental::DetectorVolume::makeShared( std::shared_ptr Acts::Experimental::DetectorVolume::makeShared( const GeometryContext& gctx, const std::string& name, - const Transform3& transform, std::unique_ptr bounds, + const Transform3& transform, std::shared_ptr bounds, SurfaceCandidatesUpdator&& surfaceCandidateUpdator) { return std::shared_ptr( new DetectorVolume(gctx, name, transform, std::move(bounds), @@ -277,7 +277,7 @@ void Acts::Experimental::DetectorVolume::closePortals() { // Create a null link for (auto [ivu, vu] : enumerate(p->detectorVolumeUpdators())) { if (not vu.connected()) { - auto eowDir = directionFromIndex(ivu); + auto eowDir = Direction::fromIndex(ivu); auto eow = std::make_unique(); Acts::Experimental::DetectorVolumeUpdator eowLink; eowLink.connect<&EndOfWorldImpl::update>(std::move(eow)); diff --git a/Core/src/Detector/DetectorVolumeBuilder.cpp b/Core/src/Detector/DetectorVolumeBuilder.cpp new file mode 100644 index 00000000000..c6fddae9b65 --- /dev/null +++ b/Core/src/Detector/DetectorVolumeBuilder.cpp @@ -0,0 +1,79 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/DetectorVolumeBuilder.hpp" + +#include "Acts/Detector/DetectorVolume.hpp" + +#include + +Acts::Experimental::DetectorVolumeBuilder::DetectorVolumeBuilder( + const Acts::Experimental::DetectorVolumeBuilder::Config& cfg, + std::unique_ptr logger) + : IDetectorComponentBuilder(), m_cfg(cfg), m_logger(std::move(logger)) { + if (m_cfg.externalsBuilder == nullptr) { + throw std::invalid_argument( + "DetectorVolumeBuilder: no external structure builder defined."); + } +} + +Acts::Experimental::DetectorComponent +Acts::Experimental::DetectorVolumeBuilder::construct( + RootDetectorVolumes& roots, const GeometryContext& gctx) const { + // Screen printout of the auxilliary information + if (not m_cfg.auxilliary.empty()) { + ACTS_DEBUG(m_cfg.auxilliary); + } + ACTS_DEBUG("Building a volume with name " << m_cfg.name); + + // Get transform and bounds from the volume + auto [transform, bounds, portalGenerator] = + m_cfg.externalsBuilder->construct(gctx); + + // Although only a single volume, describe it as a + // container shell for further processing + DetectorComponent::PortalContainer shell; + // The detector volume to be constructed + std::shared_ptr dVolume = nullptr; + // If there are no internals, the volume is fully defined + if (m_cfg.internalsBuilder == nullptr) { + ACTS_VERBOSE("No internal structure present.") + // Contruct the DetectorVolume + dVolume = DetectorVolumeFactory::construct( + portalGenerator, gctx, m_cfg.name, transform, std::move(bounds), + tryAllPortals()); + } else { + // Internal structure is present + ACTS_VERBOSE("Internal structure is being built.") + auto [surfaces, volumes, surfacesUpdator, volumeUpdator] = + m_cfg.internalsBuilder->construct(gctx); + + // Add the internally created volumes as root volumes + if (m_cfg.addInternalsToRoot) { + for (const auto& v : volumes) { + roots.volumes.push_back(v); + } + } + // Contruct the DetectorVolume + dVolume = DetectorVolumeFactory::construct( + portalGenerator, gctx, m_cfg.name, transform, std::move(bounds), + surfaces, volumes, std::move(surfacesUpdator), + std::move(volumeUpdator)); + } + // All portals are defined and build the current shell + for (auto [ip, p] : enumerate(dVolume->portalPtrs())) { + shell[ip] = p; + } + // Add to the root volume collection if configured + if (m_cfg.addToRoot) { + ACTS_VERBOSE("DetectorVolume is being attached to the root volumes."); + roots.volumes.push_back(dVolume); + } + // The newly built volume is the only produced volume + return Acts::Experimental::DetectorComponent{{dVolume}, shell}; +} diff --git a/Core/src/Detector/LayerStructureBuilder.cpp b/Core/src/Detector/LayerStructureBuilder.cpp new file mode 100644 index 00000000000..b6f2b1374b4 --- /dev/null +++ b/Core/src/Detector/LayerStructureBuilder.cpp @@ -0,0 +1,236 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/LayerStructureBuilder.hpp" + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp" +#include "Acts/Detector/detail/ReferenceGenerators.hpp" +#include "Acts/Detector/detail/SupportHelper.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/Helpers.hpp" + +#include + +namespace { + +/// Helper for 1-dimensional generators +/// +/// @tparam aType is the axis boundary type: closed or bound +/// +/// @param gctx the geometry context +/// @param lSurfaces the surfaces of the layer +/// @param assignToAll the indices assigned to all +/// @param binning the binning struct +/// +/// @return a configured surface candidate updators +template +Acts::Experimental::SurfaceCandidatesUpdator createUpdator( + const Acts::GeometryContext& gctx, + const std::vector>& lSurfaces, + const std::vector& assignToAll, + const Acts::Experimental::LayerStructureBuilder::Binning& binning) { + // The surface candidate updator & a generator for polyhedrons + Acts::Experimental::SurfaceCandidatesUpdator sfCandidates; + Acts::Experimental::detail::PolyhedronReferenceGenerator rGenerator; + // Indexed Surface generator for this case + Acts::Experimental::detail::IndexedSurfacesGenerator isg{ + lSurfaces, assignToAll, {binning.data.binvalue}, {binning.expansion}}; + if (binning.data.type == Acts::equidistant) { + // Equidistant + Acts::Experimental::detail::GridAxisGenerators::Eq aGenerator{ + {-M_PI, M_PI}, binning.data.bins()}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } else { + std::vector edges = {binning.data.boundaries().begin(), + binning.data.boundaries().end()}; + // Variable + Acts::Experimental::detail::GridAxisGenerators::Var aGenerator{ + edges}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } + return sfCandidates; +} + +/// Helper for 2-dimensional generators +/// +/// @tparam aType is the axis boundary type, axis a: closed or bound +/// @tparam bType is the axis boundary type, axis b: closed or bound +/// +/// @param gctx the geometry context +/// @param lSurfaces the surfaces of the layer +/// @param assignToAll the indices assigned to all +/// @param aBinning the binning struct of axis a +/// @param bBinning the binning struct of axis b +/// +/// @return a configured surface candidate updators +template +Acts::Experimental::SurfaceCandidatesUpdator createUpdator( + const Acts::GeometryContext& gctx, + const std::vector>& lSurfaces, + const std::vector& assignToAll, + const Acts::Experimental::LayerStructureBuilder::Binning& aBinning, + const Acts::Experimental::LayerStructureBuilder::Binning& bBinning) { + // The surface candidate updator & a generator for polyhedrons + Acts::Experimental::SurfaceCandidatesUpdator sfCandidates; + Acts::Experimental::detail::PolyhedronReferenceGenerator rGenerator; + // Indexed Surface generator for this case + Acts::Experimental::detail::IndexedSurfacesGenerator isg{ + lSurfaces, + assignToAll, + {aBinning.data.binvalue, bBinning.data.binvalue}, + {aBinning.expansion, bBinning.expansion}}; + // Run through the cases + if (aBinning.data.type == Acts::equidistant and + bBinning.data.type == Acts::equidistant) { + // Equidistant-Equidistant + Acts::Experimental::detail::GridAxisGenerators::EqEq + aGenerator{{aBinning.data.min, aBinning.data.max}, + aBinning.data.bins(), + {bBinning.data.min, bBinning.data.max}, + bBinning.data.bins()}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } else if (bBinning.data.type == Acts::equidistant) { + // Variable-Equidistant + std::vector edges0 = {bBinning.data.boundaries().begin(), + bBinning.data.boundaries().end()}; + Acts::Experimental::detail::GridAxisGenerators::VarEq + aGenerator{edges0, + {bBinning.data.min, bBinning.data.max}, + bBinning.data.bins()}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } else if (aBinning.data.type == Acts::equidistant) { + // Equidistant-Variable + std::vector edges1 = {bBinning.data.boundaries().begin(), + bBinning.data.boundaries().end()}; + Acts::Experimental::detail::GridAxisGenerators::EqVar + aGenerator{{aBinning.data.min, aBinning.data.max}, + aBinning.data.bins(), + edges1}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } else { + // Variable-Variable + std::vector edges0 = {aBinning.data.boundaries().begin(), + aBinning.data.boundaries().end()}; + std::vector edges1 = {bBinning.data.boundaries().begin(), + bBinning.data.boundaries().end()}; + Acts::Experimental::detail::GridAxisGenerators::VarVar + aGenerator{edges0, edges1}; + sfCandidates = isg(gctx, aGenerator, rGenerator); + } + // return the candidates + return sfCandidates; +} + +} // namespace + +Acts::Experimental::LayerStructureBuilder::LayerStructureBuilder( + const Acts::Experimental::LayerStructureBuilder::Config& cfg, + std::unique_ptr logger) + : IInternalStructureBuilder(), m_cfg(cfg), m_logger(std::move(logger)) {} + +Acts::Experimental::InternalStructure +Acts::Experimental::LayerStructureBuilder::construct( + const Acts::GeometryContext& gctx) const { + // Trivialities first: internal volumes + std::vector> internalVolumes = {}; + DetectorVolumeUpdator internalVolumeUpdator = tryNoVolumes(); + + if (not m_cfg.auxilliary.empty()) { + ACTS_DEBUG(m_cfg.auxilliary); + } + + // Retrieve the layer surfaces + SurfaceCandidatesUpdator internalCandidatesUpdator; + auto internalSurfaces = m_cfg.surfaces(); + ACTS_DEBUG("Building internal layer structure from " + << internalSurfaces.size() << " provided surfaces."); + + // Check whether support structure is scheduled to be built, and if so + // collect those that should be assigned to all bins + std::vector assignToAll = {}; + if (not m_cfg.supports.empty()) { + ACTS_DEBUG("Adding " << m_cfg.supports.size() << " support structures.") + // The surface candidate updator + for (const auto& support : m_cfg.supports) { + // Throw an excpetion is misconfigured + if (support.type == Surface::SurfaceType::Other) { + throw std::invalid_argument( + "LayerStructureBuilder: support surface type not specified."); + } + ACTS_VERBOSE("- Build support of type '" + << Acts::Surface::s_surfaceTypeNames[support.type] << "'."); + if (support.splits > 1u) { + ACTS_VERBOSE(" Support surface is modelled with " << support.splits + << " planes."); + } + // To correctly attach the support structures, estimate the extent + Extent internalExtent; + for (const auto& s : internalSurfaces) { + auto sPolyhedron = s->polyhedronRepresentation(gctx, m_cfg.nSegments); + internalExtent.extend(sPolyhedron.extent(), support.constraints); + } + // Use the support bulder helper to add support surfaces + detail::SupportHelper::addSupport( + internalSurfaces, assignToAll, internalExtent, support.type, + support.values, support.transform, support.splits); + } + } + + // Create the indexed surface grids + if (m_cfg.binnings.size() == 1u) { + ACTS_DEBUG("- 1-dimensional surface binning detected."); + // Capture the binning + auto binning = m_cfg.binnings[0u]; + if (binning.data.option == closed) { + internalCandidatesUpdator = + createUpdator( + gctx, internalSurfaces, assignToAll, binning); + } else { + internalCandidatesUpdator = + createUpdator( + gctx, internalSurfaces, assignToAll, binning); + } + } else if (m_cfg.binnings.size() == 2u) { + ACTS_DEBUG("- 2-dimensional surface binning detected."); + // Capture the binnings + const auto& binning0 = m_cfg.binnings[0u]; + const auto& binning1 = m_cfg.binnings[1u]; + + if (binning0.data.option == closed) { + internalCandidatesUpdator = + createUpdator( + gctx, internalSurfaces, assignToAll, binning0, binning1); + } else if (binning1.data.option == closed) { + internalCandidatesUpdator = + createUpdator( + gctx, internalSurfaces, assignToAll, binning0, binning1); + } else { + internalCandidatesUpdator = + createUpdator( + gctx, internalSurfaces, assignToAll, binning0, binning1); + } + } + // Check if everything went ok + if (not internalCandidatesUpdator.connected()) { + throw std::runtime_error( + "LayerStructureBuilder: could not connect surface candidate updator."); + } + + // Return the internal structure + return InternalStructure{internalSurfaces, internalVolumes, + std::move(internalCandidatesUpdator), + std::move(internalVolumeUpdator)}; +} diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index 8c5996b5064..06cd88d9322 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -52,25 +52,25 @@ void Acts::Experimental::Portal::assignGeometryId( } void Acts::Experimental::Portal::fuse(std::shared_ptr& other) { + Direction bDir = Direction::Backward; + // Determine this directioon - NavigationDirection tDir = - (not m_volumeUpdators[indexFromDirection(NavigationDirection::Backward)] - .connected()) - ? NavigationDirection::Forward - : NavigationDirection::Backward; + Direction tDir = (not m_volumeUpdators[bDir.index()].connected()) + ? Direction::Forward + : Direction::Backward; - if (not m_volumeUpdators[indexFromDirection(tDir)].connected()) { + if (not m_volumeUpdators[tDir.index()].connected()) { throw std::invalid_argument( "Portal: trying to fuse portal (keep) with no links."); } // And now check other direction - NavigationDirection oDir = invertDirection(tDir); - if (not other->m_volumeUpdators[indexFromDirection(oDir)].connected()) { + Direction oDir = tDir.invert(); + if (not other->m_volumeUpdators[oDir.index()].connected()) { throw std::runtime_error( "Portal: trying to fuse portal (waste) with no links."); } - auto odx = indexFromDirection(oDir); + auto odx = oDir.index(); m_volumeUpdators[odx] = std::move(other->m_volumeUpdators[odx]); m_attachedVolumes[odx] = other->m_attachedVolumes[odx]; // And finally overwrite the original portal @@ -78,9 +78,9 @@ void Acts::Experimental::Portal::fuse(std::shared_ptr& other) { } void Acts::Experimental::Portal::assignDetectorVolumeUpdator( - NavigationDirection dir, DetectorVolumeUpdator&& dVolumeUpdator, + Direction dir, DetectorVolumeUpdator&& dVolumeUpdator, const std::vector>& attachedVolumes) { - auto idx = indexFromDirection(dir); + auto idx = dir.index(); m_volumeUpdators[idx] = std::move(dVolumeUpdator); m_attachedVolumes[idx] = attachedVolumes; } @@ -106,8 +106,8 @@ void Acts::Experimental::Portal::updateDetectorVolume( const auto& position = nState.position; const auto& direction = nState.direction; const Vector3 normal = surface().normal(gctx, position); - NavigationDirection dir = directionFromStepSize(normal.dot(direction)); - const auto& vUpdator = m_volumeUpdators[indexFromDirection(dir)]; + Direction dir = Direction::fromScalar(normal.dot(direction)); + const auto& vUpdator = m_volumeUpdators[dir.index()]; if (vUpdator.connected()) { vUpdator(gctx, nState); } else { diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp new file mode 100644 index 00000000000..e86dfe4c29b --- /dev/null +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -0,0 +1,1239 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/detail/CylindricalDetectorHelper.hpp" + +#include "Acts/Detector/Detector.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/detail/PortalHelper.hpp" +#include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/PlanarBounds.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/detail/Axis.hpp" +#include "Acts/Utilities/detail/Grid.hpp" + +#include + +// Indexing of the portals follows the generation order of portals in the +// CylinderVolumeBounds and BevelledCylinderVolumeBounds (latter for wrapping) +// +// In short: +// +// 0: index of the low-z disc bound +// 1: index of the high-z disc bound +// 2: index of the outer cylinder bound +// +// If the volume doesn't extend inward to inner radius=0: +// 3: index of the inner cylinder bound +// +// Cylindrical sectors have up to 6 portals, enumerated as follows: +// 0: index of the low-z disc sector bound +// 1: index of the high-z disc sector bound +// 2: index of the outer cylinder sector bound +// +// If no inner cylinder sector bound is present: +// 3: index of the low-phi sector bound +// 4: index of the high-phi sector bound +// If an inner cylinder sector bound exists, it takes index 3 and the phi sector +// bounds are shifted by one index: 3: index of the inner cylinder sector bound +// 4: index of the low-phi sector bound +// 5: index of the high-phi sector bound +// + +namespace { + +/// @brief Helper function to create a disc portal replacement +/// +/// @param transform The transform of the newly created portal +/// @param rBoundaries the vector of binning boundaries in r +/// @param phiBoundaries an eventual phi sector value +/// @param dir the direction to be set +/// +/// @return a new portal replacement object +Acts::Experimental::PortalReplacement createDiscReplacement( + const Acts::Transform3& transform, + const std::vector& rBoundaries, + const std::vector& phiBoundaries, unsigned int index, + Acts::Direction dir) { + // Autodetector stitch value + Acts::BinningValue stitchValue = + phiBoundaries.size() == 2u ? Acts::binR : Acts::binPhi; + // Estimate ranges + auto [minR, maxR] = Acts::min_max(rBoundaries); + auto [sectorPhi, avgPhi] = Acts::range_medium(phiBoundaries); + + // Transform and bounds + auto bounds = + std::make_unique(minR, maxR, 0.5 * sectorPhi, avgPhi); + // A new surface on the negative side over the full range + auto surface = Acts::Surface::makeShared( + transform, std::move(bounds)); + // Make a portal and indicate the new link direction + const auto& stitchBoundaries = + (stitchValue == Acts::binR) ? rBoundaries : phiBoundaries; + return Acts::Experimental::PortalReplacement( + Acts::Experimental::Portal::makeShared(surface), index, dir, + stitchBoundaries, stitchValue); +} + +/// @brief Helper function to create a cylinder portal replacement +/// +/// @param transform The transform of the newly created portal +/// @param r is the radius of the cylinder +/// @param zBoundaries the vector of binning boundaries +/// @param phiBoundaries the vector of binning boundaries +/// @param index the index of this portal to be set +/// @param dir the navigation direction to be set +/// +/// @return a new portal replacement object +Acts::Experimental::PortalReplacement createCylinderReplacement( + const Acts::Transform3& transform, Acts::ActsScalar r, + const std::vector& zBoundaries, + const std::vector& phiBoundaries, unsigned int index, + Acts::Direction dir) { + // Autodetector stitch value + Acts::BinningValue stitchValue = + phiBoundaries.size() == 2u ? Acts::binZ : Acts::binPhi; + auto [lengthZ, medZ] = Acts::range_medium(zBoundaries); + auto [sectorPhi, avgPhi] = Acts::range_medium(phiBoundaries); + + // New bounds, with current length and sector values + auto bounds = std::make_unique(r, 0.5 * lengthZ, + 0.5 * sectorPhi, avgPhi); + // A new surface on the negative side over the full range + auto surface = Acts::Surface::makeShared( + transform, std::move(bounds)); + + // A make a portal and indicate the new link direction + const auto& stitchBoundaries = + (stitchValue == Acts::binZ) ? zBoundaries : phiBoundaries; + return Acts::Experimental::PortalReplacement( + Acts::Experimental::Portal::makeShared(surface), index, dir, + stitchBoundaries, stitchValue); +} + +/// @brief Helper function to create a disc portal replacement +/// @param gcxt the geometry context of this call +/// @param volumeCenter a reference center of the volume (combined) +/// @param refSurface the reference surface (old portal) +/// @param boundaries the vector of binning boundaries in r +/// @param binning the binning of the sector (inR, inZ) +/// @param index the index of this portal to be set +/// @param dir the navigation direction to be set +/// +/// @return a new portal replacement object +Acts::Experimental::PortalReplacement createSectorReplacement( + const Acts::GeometryContext& gctx, const Acts::Vector3& volumeCenter, + const Acts::Surface& refSurface, + const std::vector& boundaries, Acts::BinningValue binning, + unsigned int index, Acts::Direction dir) { + // Get a reference transform + const auto& refTransform = refSurface.transform(gctx); + auto refRotation = refTransform.rotation(); + // Bounds handling + const auto& boundValues = refSurface.bounds().values(); + std::unique_ptr bounds = nullptr; + + // Create a new transform + Acts::Transform3 transform = Acts::Transform3::Identity(); + if (binning == Acts::binR) { + // Range and center-r calculation + auto [range, medium] = Acts::range_medium(boundaries); + // New joint center: + // - you start from the center of the volume, and then head the distence + // of medium-r along the y-axis of the former (and new) portal + Acts::Vector3 pCenter = volumeCenter + medium * refRotation.col(1u); + transform.pretranslate(pCenter); + // Create the halflength + Acts::ActsScalar halfX = + 0.5 * (boundValues[Acts::RectangleBounds::BoundValues::eMaxX] - + boundValues[Acts::RectangleBounds::BoundValues::eMinX]); + // New joint bounds + bounds = std::make_unique(halfX, 0.5 * range); + } else if (binning == Acts::binZ) { + // Range and medium z alculation + auto [range, medium] = Acts::range_medium(boundaries); + // Center R calculation, using projection onto vector + const auto& surfaceCenter = refSurface.center(gctx); + Acts::Vector3 centerDiffs = (surfaceCenter - volumeCenter); + Acts::ActsScalar centerR = centerDiffs.dot(refRotation.col(2)); + // New joint center + Acts::Vector3 pCenter = volumeCenter + centerR * refRotation.col(2); + transform.pretranslate(pCenter); + // New joint bounds + Acts::ActsScalar halfY = + 0.5 * (boundValues[Acts::RectangleBounds::BoundValues::eMaxY] - + boundValues[Acts::RectangleBounds::BoundValues::eMinY]); + bounds = std::make_unique(0.5 * range, halfY); + } + // Set the rotation + transform.prerotate(refRotation); + // A new surface on the negative side over the full range + auto surface = Acts::Surface::makeShared( + transform, std::move(bounds)); + // A make a portal and indicate the new link direction + Acts::Experimental::PortalReplacement pRep = { + Acts::Experimental::Portal::makeShared(surface), index, dir, boundaries, + binning}; + return pRep; +} + +/// @brief Helper method to strip side volumes from containers +/// +/// @param containers the list of container +/// @param sides the sides +/// @param selectedOnly the selection restriction +/// +/// @return a map of stripped out container +std::map>> +stripSideVolumes( + const std::vector& + containers, + const std::vector& sides, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("::stripSideVolumes", logLevel)); + + // Thes are the stripped off outside volumes + std::map>> + sideVolumes; + + // Principle sides and selected sides, make an intersection + std::vector selectedSides; + if (not selectedOnly.empty()) { + std::set_intersection(sides.begin(), sides.end(), selectedOnly.begin(), + selectedOnly.end(), + std::back_inserter(selectedSides)); + } else { + selectedSides = sides; + } + + // Loop through the containers + for (const auto& pc : containers) { + // Loop through the selected sides and check if they are contained + for (const auto& s : selectedSides) { + auto cSide = pc.find(s); + if (cSide != pc.end()) { + auto p = cSide->second; + auto& sVolumes = sideVolumes[s]; + auto aVolumes = + Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( + *p); + sVolumes.insert(sVolumes.end(), aVolumes.begin(), aVolumes.end()); + } + } + } + // return them + return sideVolumes; +} + +/// @brief Helper method to check alignment of the volumes, this method checks +/// if the z-axes are aligned and throws an exception if not, it assumes that +/// the detector volume content has been checked already +/// +/// @param gctx the geometry context +/// @param volumes the input volumes to be checked +/// +/// @note this is a strict matching that requires the rotation to be identical +/// +/// @note throws exception if any of checks fails +void checkAlignment( + const Acts::GeometryContext& gctx, + const std::vector>& + volumes) { + // Take first transform as reference transform + auto refRotation = volumes[0u]->transform(gctx).rotation(); + // Loop over rest and recursively test + for (auto [iv, v] : Acts::enumerate(volumes)) { + if (iv > 0) { + auto curRotation = v->transform(gctx).rotation(); + if (not curRotation.isApprox(refRotation)) { + std::string message = "CylindricalDetectorHelper: rotation of volume "; + message += std::to_string(iv); + message += std::string(" is not aligned with previous volume"); + throw std::invalid_argument(message.c_str()); + } + } + } +} + +/// @brief Helper method to check the volumes in general and throw and excpetion if failes +/// +/// @param gctx the geometry context +/// @param volumes the input volumes to be checked +/// +/// @note throws exception if any of checks fails +void checkVolumes( + const Acts::GeometryContext& gctx, + const std::vector>& + volumes) { + // A minimal set of checks - exceptions are thrown + // - not enough volumes given + std::string message = "CylindricalDetectorHelper: "; + if (volumes.size() < 2u) { + message += std::string("not enough volume given (") + + std::to_string(volumes.size()); + message += std::string(" ), when required >=2."); + throw std::invalid_argument(message.c_str()); + } + // - null pointer detected or non-cylindrical volume detected + for (auto [iv, v] : Acts::enumerate(volumes)) { + // Check for nullptr + if (v == nullptr) { + message += std::string("nullptr detector instead of volume " + + std::to_string(iv)); + throw std::invalid_argument(message.c_str()); + } + // Check for cylindrical volume type + if (v->volumeBounds().type() != Acts::VolumeBounds::BoundsType::eCylinder) { + message += + std::string("non-cylindrical volume bounds detected for volume " + + std::to_string(iv)); + throw std::invalid_argument(message.c_str()); + } + } + // Check the alignment of the volumes + checkAlignment(gctx, volumes); +} + +/// @brief Helper method to check the volume bounds +/// +/// @param gctx the geometry context +/// @param volumes the input volumes to be checked +/// @param refCur reference versus current check +/// +/// @note throws exception if any of checks fails +void checkBounds( + [[maybe_unused]] const Acts::GeometryContext& gctx, + const std::vector>& + volumes, + const std::vector>& refCur) { + // Reference values + auto refValues = volumes[0u]->volumeBounds().values(); + for (auto [iv, v] : Acts::enumerate(volumes)) { + if (iv > 0u) { + auto curValues = v->volumeBounds().values(); + for (auto [r, c] : refCur) { + if (std::abs(refValues[r] - curValues[c]) > + Acts::s_onSurfaceTolerance) { + std::string message = "CylindricalDetectorHelper: '"; + message += volumes[iv - 1]->name(); + if (r != c) { + message += "' does not attach to '"; + } else { + message += "' does not match with '"; + } + message += volumes[iv]->name(); + message += "\n"; + message += " - at bound values "; + message += std::to_string(refValues[r]); + message += " / " + std::to_string(curValues[c]); + throw std::runtime_error(message.c_str()); + } + } + refValues = curValues; + } + } +} + +} // namespace + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( + const GeometryContext& gctx, + std::vector>& volumes, + const std::vector& selectedOnly, + Acts::Logging::Level logLevel) { + // Basic checks for eligability of the volumes + checkVolumes(gctx, volumes); + // Check for bounds values of volumes (i) and (i+1) succesively for + // compatibility: + // - check outer (1u) of volume (i) vs inner radius (0u) of volume (i+1) + // - phi sector (3u) and average phi (4u) between volumes (i), (i+1) + std::vector> checks = { + {1u, 0u}, {3u, 3u}, {4u, 4u}}; + // - And we check the half length if it is not a selective attachment + if (selectedOnly.empty()) { + checks.push_back({2u, 2u}); + } + checkBounds(gctx, volumes, checks); + + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << volumes.size() << " detector volumes in R."); + + // Return proto container + DetectorComponent::PortalContainer dShell; + + // Innermost volume boundaries + std::vector rBoundaries = {}; + auto refValues = volumes[0u]->volumeBounds().values(); + + // Reference boundary values + rBoundaries.push_back(refValues[CylinderVolumeBounds::BoundValues::eMinR]); + rBoundaries.push_back(refValues[CylinderVolumeBounds::BoundValues::eMaxR]); + + // Connect in R ? (2u is the index of the outer cylinder) + bool connectR = selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), 2u) != + selectedOnly.end(); + + // Get phi sector and average phi + ActsScalar phiSector = + refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; + ActsScalar avgPhi = refValues[CylinderVolumeBounds::BoundValues::eAveragePhi]; + + // Fuse the cylinders - portals can be reused for this operation + for (unsigned int iv = 1; iv < volumes.size(); ++iv) { + refValues = volumes[iv]->volumeBounds().values(); + // Keep on collecting the outside maximum r for the overal r boundaries + rBoundaries.push_back(refValues[CylinderVolumeBounds::BoundValues::eMaxR]); + // Only connect if configured to do so + if (connectR) { + ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " + << volumes[iv]->name() << "'."); + + // When fusing volumes at a cylinder boundary, we *keep* one + // portal and tranfer the portal link information from the other + // + // In this case the outer cylinder portal of the inner volume is kept, + // the inner cylinder of the outer portal goes to waste + auto& keepCylinder = volumes[iv - 1]->portalPtrs()[2u]; + auto& wasteCylinder = volumes[iv]->portalPtrs()[3u]; + keepCylinder->fuse(wasteCylinder); + volumes[iv]->updatePortal(keepCylinder, 3u); + } + } + + // Proto container setting + if (connectR) { + // The number of portals indicate again if the inner is present or not, + if (volumes[0u]->portals().size() == 4u or + volumes[0u]->portals().size() == 6u) { + dShell[3u] = volumes[0u]->portalPtrs()[3u]; + } + dShell[2u] = volumes[volumes.size() - 1u]->portalPtrs()[2u]; + } + + // Check if sectors are present by the number of portals, check is done on the + // outermost volume as this is required to have an inner cylinder, and hence + // no offset needs to be respected + bool sectorsPresent = volumes[volumes.size() - 1u]->portals().size() > 4u; + + // A portal replacement, it comprises of the portal, the index, the + // direction, the binning and bins + std::vector pReplacements = {}; + + // Disc assignments are forwad for negative disc, backward for positive + std::vector discDirs = {Acts::Direction::Forward, + Acts::Direction::Backward}; + for (const auto [iu, idir] : enumerate(discDirs)) { + if (selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), iu) != + selectedOnly.end()) { + const Surface& refSurface = volumes[0u]->portals()[iu]->surface(); + const Transform3& refTransform = refSurface.transform(gctx); + pReplacements.push_back(createDiscReplacement( + refTransform, rBoundaries, {avgPhi - phiSector, avgPhi + phiSector}, + iu, idir)); + } + } + + // If volume sectors are present, these have to be respected + if (sectorsPresent) { + ACTS_VERBOSE("Sector planes are present, they need replacement."); + // Sector assignments are forward backward + std::vector sectorDirs = {Acts::Direction::Forward, + Acts::Direction::Backward}; + Acts::Vector3 vCenter = volumes[0u]->transform(gctx).translation(); + for (const auto [iu, idir] : enumerate(sectorDirs)) { + // (iu + 4u) corresponds to the indices of the phi-low and phi-high sector + // planes. + if (selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), iu + 4u) != + selectedOnly.end()) { + // As it is r-wrapping, the inner tube is guaranteed + const Surface& refSurface = + volumes[volumes.size() - 1u]->portals()[iu + 4u]->surface(); + pReplacements.push_back(createSectorReplacement( + gctx, vCenter, refSurface, rBoundaries, Acts::binR, iu + 4u, idir)); + } + } + } else { + ACTS_VERBOSE( + "No sector planes present, full 2 * M_PI cylindrical geometry."); + } + + // Attach the new volume multi links + PortalHelper::attachDetectorVolumeUpdators(gctx, volumes, pReplacements); + + // Exchange the portals of the volumes + ACTS_VERBOSE("Portals of " << volumes.size() << " volumes need updating."); + // Exchange the portals of the volumes + for (auto& iv : volumes) { + ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); + for (auto& [p, i, dir, boundaries, binning] : pReplacements) { + // Fill the map + dShell[i] = p; + + // Potential offset for tube vs/ cylinder + // - if the volume doesn't have an inner portal, indices need to + // be shifted by -1 to update the correct index, that's the case for + // size 3 and 5 for portals + size_t nPortals = iv->portals().size(); + bool innerPresent = (nPortals == 3u or nPortals == 5u); + int iOffset = (innerPresent and i > 2u) ? -1 : 0; + ACTS_VERBOSE("-- update portal with index " + << i + iOffset << " (including offset " << iOffset << ")"); + iv->updatePortal(p, static_cast(i + iOffset)); + } + } + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( + const Acts::GeometryContext& gctx, + std::vector>& volumes, + const std::vector& selectedOnly, + Acts::Logging::Level logLevel) { + // Basic checks for eligability of the volumes + checkVolumes(gctx, volumes); + // Check for bounds compatibility + // We check phi sector (3u) and average phi (4u) + std::vector> checks = {{3u, 3u}, {4u, 4u}}; + // And we check the inner radius [0u], outer radius[1u] if its' not a + // selective attachment + if (selectedOnly.empty()) { + checks.push_back({0u, 0u}); + checks.push_back({1u, 1u}); + } + checkBounds(gctx, volumes, checks); + + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << volumes.size() << " detector volumes in Z."); + + // Return proto container + DetectorComponent::PortalContainer dShell; + + // Connect in Z ? + // - 1u corresponds to the index of the high-z disc portal for the reference + // volume. + bool connectZ = selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), 1u) != + selectedOnly.end(); + // Reference z axis + const auto rotation = volumes[0u]->transform(gctx).rotation(); + + std::vector zBoundaries3D = {}; + + /// @brief Add the z boundary + /// @param gctx the geometry context + /// @param volume the volume + /// @param side side value + auto addZboundary3D = [&](const Experimental::DetectorVolume& volume, + int side) -> void { + const auto boundValues = volume.volumeBounds().values(); + ActsScalar halflengthZ = + boundValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ]; + zBoundaries3D.push_back(volume.transform(gctx).translation() + + side * halflengthZ * rotation.col(2)); + }; + + // Fuse the discs - portals can be reused + addZboundary3D(*volumes[0u].get(), -1); + addZboundary3D(*volumes[0u].get(), 1); + for (unsigned int iv = 1; iv < volumes.size(); ++iv) { + // Add the z boundary + addZboundary3D(*volumes[iv].get(), 1u); + // Do the connection + if (connectZ) { + ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " + << volumes[iv]->name() << "'."); + // When fusing, one portal survives (keep) and gets the + // portal linking from the waste tranfered + // + // In this case we keep the disc at positive z of the volume + // at lower relative z, and trash the disc at negative z of the + // following volume + auto& keepDisc = volumes[iv - 1]->portalPtrs()[1u]; + auto& wasteDisc = volumes[iv]->portalPtrs()[0u]; + // Throw an exception if the discs are not at the same position + Vector3 keepPosition = keepDisc->surface().center(gctx); + Vector3 wastePosition = wasteDisc->surface().center(gctx); + if (not keepPosition.isApprox(wastePosition)) { + std::string message = "CylindricalDetectorHelper: '"; + message += volumes[iv - 1]->name(); + message += "' does not attach to '"; + message += volumes[iv]->name(); + message += "\n"; + message += " - along z with values "; + message += Acts::toString(keepPosition); + message += " / " + Acts::toString(wastePosition); + throw std::runtime_error(message.c_str()); + } + keepDisc->fuse(wasteDisc); + volumes[iv]->updatePortal(keepDisc, 0u); + } + } + + // Register what we have from the container + if (connectZ) { + dShell[0u] = volumes[0u]->portalPtrs()[0u]; + dShell[1u] = volumes[volumes.size() - 1u]->portalPtrs()[1u]; + } + + // Centre of gravity + Vector3 combinedCenter = + 0.5 * (zBoundaries3D[zBoundaries3D.size() - 1u] + zBoundaries3D[0u]); + + ACTS_VERBOSE("New combined center calculated at " + << toString(combinedCenter)); + + // Evaluate the series of z boundaries + std::vector zBoundaries = {}; + for (const auto& zb3D : zBoundaries3D) { + auto proj3D = (zb3D - combinedCenter).dot(rotation.col(2)); + ActsScalar zBoundary = + std::copysign((zb3D - combinedCenter).norm(), proj3D); + zBoundaries.push_back(zBoundary); + } + + Transform3 combinedTransform = Transform3::Identity(); + combinedTransform.pretranslate(combinedCenter); + combinedTransform.rotate(rotation); + + // Get phi sector and average phi + const auto& refVolume = volumes[0u]; + const auto refValues = refVolume->volumeBounds().values(); + + // Get phi sector and average phi + ActsScalar minR = refValues[CylinderVolumeBounds::BoundValues::eMinR]; + ActsScalar maxR = refValues[CylinderVolumeBounds::BoundValues::eMaxR]; + ActsScalar phiSector = + refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; + ActsScalar avgPhi = refValues[CylinderVolumeBounds::BoundValues::eAveragePhi]; + + // Check if inner cylinder and sectors are present by the number of portals + size_t nPortals = volumes[volumes.size() - 1u]->portals().size(); + bool innerPresent = (nPortals != 3u and nPortals != 5u); + bool sectorsPresent = nPortals > 4u; + + // A portal replacement, it comprises of the portal, the index, the + // direction, the binning and bins + std::vector pReplacements = {}; + + // Disc assignments are forwad for negative disc, backward for positive + std::vector cylinderDirs = {Acts::Direction::Backward}; + // Cylinder radii + std::vector cylinderR = {maxR}; + if (innerPresent) { + ACTS_VERBOSE("Inner surface present, tube geometry detected."); + cylinderDirs.push_back(Direction::Forward); + cylinderR.push_back(minR); + } else { + ACTS_VERBOSE("No inner surface present, solid cylinder geometry detected."); + } + // Tube/cylinder offset + unsigned int iSecOffset = innerPresent ? 4u : 3u; + // Prepare the cylinder replacements + for (const auto [iu, idir] : enumerate(cylinderDirs)) { + if (selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), iu + 2u) != + selectedOnly.end()) { + pReplacements.push_back(createCylinderReplacement( + combinedTransform, cylinderR[iu], zBoundaries, + {avgPhi - phiSector, avgPhi + phiSector}, iu + 2u, idir)); + } + } + + // Prepare the sector side replacements + if (sectorsPresent) { + ACTS_VERBOSE("Sector planes are present, they need replacement."); + // Sector assignmenta are forward backward + std::vector sectorDirs = {Acts::Direction::Forward, + Acts::Direction::Backward}; + for (const auto [iu, idir] : enumerate(sectorDirs)) { + // Access with 3u or 4u but always write 4u (to be caught later) + if (selectedOnly.empty() or + std::find(selectedOnly.begin(), selectedOnly.end(), iu + 4u) != + selectedOnly.end()) { + const Surface& refSurface = + volumes[0u]->portals()[iu + iSecOffset]->surface(); + pReplacements.push_back( + createSectorReplacement(gctx, combinedCenter, refSurface, + zBoundaries, Acts::binZ, iu + 4u, idir)); + } + } + } else { + ACTS_VERBOSE( + "No sector planes present, full 2 * M_PI cylindrical geometry."); + } + + // Attach the new volume multi links + PortalHelper::attachDetectorVolumeUpdators(gctx, volumes, pReplacements); + + // Exchange the portals of the volumes + ACTS_VERBOSE("Portals of " << volumes.size() << " volumes need updating."); + for (auto& iv : volumes) { + ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); + for (auto& [p, i, dir, boundaries, binning] : pReplacements) { + // Potential offset for tube vs/ cylinder + // if the volume doesn't have an inner portal, indices need to be shifted + // by -1 to update the correct index. + int iOffset = (i > 2u and not innerPresent) ? -1 : 0; + ACTS_VERBOSE("-- update portal with index " << i); + iv->updatePortal(p, static_cast(i + iOffset)); + // Fill the map + dShell[i] = p; + } + } + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInPhi( + const Acts::GeometryContext& gctx, + std::vector>& volumes, + const std::vector& /*selectedOnly*/, + Acts::Logging::Level logLevel) { + // Basic checks for eligability of the volumes + checkVolumes(gctx, volumes); + + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << volumes.size() << " detector volumes in phi."); + + // Return proto container + DetectorComponent::PortalContainer dShell; + + // Check if inner cylinder and sectors are present by the number of portals + size_t nPortals = volumes[volumes.size() - 1u]->portals().size(); + bool innerPresent = (nPortals != 3u and nPortals != 5u); + + Transform3 refTransform = volumes[0u]->transform(gctx); + + // Sector offset + unsigned int iSecOffset = innerPresent ? 4u : 3u; + std::vector phiBoundaries = {}; + auto refValues = volumes[0u]->volumeBounds().values(); + phiBoundaries.push_back( + refValues[CylinderVolumeBounds::BoundValues::eAveragePhi] - + refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]); + phiBoundaries.push_back( + refValues[CylinderVolumeBounds::BoundValues::eAveragePhi] + + refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]); + // Fuse the sectors - portals can be reused for this operation + for (unsigned int iv = 1; iv < volumes.size(); ++iv) { + ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " + << volumes[iv]->name() << "'."); + + // Fuse and swap + auto& keepSector = volumes[iv - 1]->portalPtrs()[iSecOffset + 1u]; + auto& wasteSector = volumes[iv]->portalPtrs()[iSecOffset]; + keepSector->fuse(wasteSector); + volumes[iv]->updatePortal(keepSector, iSecOffset); + // The current values + auto curValues = volumes[iv]->volumeBounds().values(); + // Bail out if they do not match + ActsScalar lowPhi = + curValues[CylinderVolumeBounds::BoundValues::eAveragePhi] - + curValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; + ActsScalar highPhi = + curValues[CylinderVolumeBounds::BoundValues::eAveragePhi] + + curValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; + // Check phi attachment + if (std::abs(phiBoundaries[phiBoundaries.size() - 1u] - lowPhi) > + Acts::s_onSurfaceTolerance) { + std::string message = "CylindricalDetectorHelper: '"; + message += volumes[iv - 1]->name(); + message += "' does not attach to '"; + message += volumes[iv]->name(); + message += "\n"; + message += " - within phi sectors "; + message += std::to_string(lowPhi); + message += + " / " + std::to_string(phiBoundaries[phiBoundaries.size() - 1u]); + throw std::runtime_error(message.c_str()); + } + // Check radial and longitudinal compatibility - @TODO + phiBoundaries.push_back(highPhi); + // Recursive setting of the values + refValues = curValues; + } + + // A portal replacement, it comprises of the portal, the index, the + // direction, the binning and bins + std::vector pReplacements = {}; + // Negative disc + pReplacements.push_back(createDiscReplacement( + refTransform, + {refValues[CylinderVolumeBounds::BoundValues::eMinR], + refValues[CylinderVolumeBounds::BoundValues::eMaxR]}, + phiBoundaries, 0u, Acts::Direction::Forward)); + + // Positive disc + pReplacements.push_back(createDiscReplacement( + refTransform, + {refValues[CylinderVolumeBounds::BoundValues::eMinR], + refValues[CylinderVolumeBounds::BoundValues::eMaxR]}, + phiBoundaries, 1u, Acts::Direction::Backward)); + + // Outside cylinder + pReplacements.push_back(createCylinderReplacement( + refTransform, refValues[CylinderVolumeBounds::BoundValues::eMaxR], + {-refValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ], + refValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ]}, + phiBoundaries, 2u, Acts::Direction::Backward)); + + // If the volume has a different inner radius than 0, it MUST have + // an inner cylinder + if (refValues[CylinderVolumeBounds::BoundValues::eMinR] > 0.) { + // Inner cylinder + pReplacements.push_back(createCylinderReplacement( + refTransform, refValues[CylinderVolumeBounds::BoundValues::eMinR], + {-refValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ], + refValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ]}, + phiBoundaries, 3u, Acts::Direction::Forward)); + } + + // Attach the new volume multi links + PortalHelper::attachDetectorVolumeUpdators(gctx, volumes, pReplacements); + // Exchange the portals of the volumes + ACTS_VERBOSE("Portals of " << volumes.size() << " volumes need updating."); + for (auto& iv : volumes) { + ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); + for (auto& [p, i, dir, boundaries, binning] : pReplacements) { + ACTS_VERBOSE("-- update portal with index " << i); + iv->updatePortal(p, static_cast(i)); + // Fill the map + dShell[i] = p; + } + } + + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( + const Acts::GeometryContext& gctx, + std::vector>& volumes, + Acts::Logging::Level logLevel) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Wrapping volumes in Z-R."); + + // Minimal set of checks + if (volumes.size() != 2u) { + throw std::invalid_argument( + "Wrapping the detector volume requires exactly 2 volumes."); + } + + // Return the new container + DetectorComponent::PortalContainer dShell; + + // Keep the outer shells + dShell[0u] = volumes[1u]->portalPtrs()[0u]; + dShell[1u] = volumes[1u]->portalPtrs()[1u]; + dShell[2u] = volumes[1u]->portalPtrs()[2u]; + + // Fuse outer cover of first with inner cylinder of wrapping volume + auto& keepCover = volumes[0u]->portalPtrs()[2u]; + auto& wasteCover = volumes[1u]->portalPtrs()[3u]; + keepCover->fuse(wasteCover); + volumes[1u]->updatePortal(keepCover, 3u); + + // Stitch sides - negative + auto& keepDiscN = volumes[1u]->portalPtrs()[4u]; + auto& wasteDiscN = volumes[0u]->portalPtrs()[0u]; + keepDiscN->fuse(wasteDiscN); + volumes[0u]->updatePortal(keepDiscN, 0u); + + // Stich sides - positive + auto& keepDiscP = volumes[0u]->portalPtrs()[1u]; + auto& wasteDiscP = volumes[1u]->portalPtrs()[5u]; + keepDiscP->fuse(wasteDiscP); + volumes[1u]->updatePortal(keepDiscP, 5u); + + // If needed, insert new cylinder + if (volumes[0u]->portalPtrs().size() == 4u and + volumes[1u]->portalPtrs().size() == 8u) { + // We need a new cylinder spanning over the entire inner tube + ActsScalar hlZ = + volumes[0u] + ->volumeBounds() + .values()[Acts::CylinderVolumeBounds::BoundValues::eHalfLengthZ]; + ActsScalar HlZ = + volumes[1u]->volumeBounds().values() + [Acts::CutoutCylinderVolumeBounds::BoundValues::eHalfLengthZ]; + ActsScalar innerR = + volumes[0u] + ->volumeBounds() + .values()[Acts::CylinderVolumeBounds::BoundValues::eMinR]; + // Create the inner replacement + std::vector pReplacements; + pReplacements.push_back(createCylinderReplacement( + volumes[0u]->transform(gctx), innerR, {-HlZ, -hlZ, hlZ, HlZ}, + {-M_PI, M_PI}, 3u, Direction::Forward)); + std::vector> zVolumes = { + volumes[1u], volumes[0u], volumes[1u]}; + // Attach the new volume multi links + PortalHelper::attachDetectorVolumeUpdators(gctx, zVolumes, pReplacements); + auto& [p, i, dir, boundaries, binning] = pReplacements[0u]; + // Update the portals + volumes[1u]->updatePortal(p, 6u); + volumes[0u]->updatePortal(p, 3u); + volumes[1u]->updatePortal(p, 7u); + // Inner skin + dShell[3u] = p; + } + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( + const GeometryContext& gctx, + const std::vector& containers, + const std::vector& selectedOnly, + Acts::Logging::Level logLevel) noexcept(false) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << containers.size() << " proto containers in R."); + + // Return the new container + DetectorComponent::PortalContainer dShell; + + // Fuse the cylinders - portals can be reused for this operation + for (unsigned int ic = 1; ic < containers.size(); ++ic) { + auto& formerContainer = containers[ic - 1]; + auto& currentContainer = containers[ic]; + // Check and throw exception + if (formerContainer.find(2u) == formerContainer.end()) { + throw std::invalid_argument( + "CylindricalDetectorHelper: proto container has no outer cover, " + "can " + "not be connected in R"); + } + if (currentContainer.find(3u) == currentContainer.end()) { + throw std::invalid_argument( + "CylindricalDetectorHelper: proto container has no inner cover, " + "can " + "not be connected in R"); + } + + // Fuse and swap + std::shared_ptr keepCylinder = containers[ic - 1].find(2u)->second; + std::shared_ptr wasteCylinder = containers[ic].find(3u)->second; + keepCylinder->fuse(wasteCylinder); + for (auto& av : wasteCylinder->attachedDetectorVolumes()[1u]) { + av->updatePortal(keepCylinder, 3u); + } + } + + // Proto container refurbishment + if (containers[0u].find(3u) != containers[0u].end()) { + dShell[3u] = containers[0u].find(3u)->second; + } + dShell[2u] = containers[containers.size() - 1u].find(2u)->second; + + auto sideVolumes = + stripSideVolumes(containers, {0u, 1u, 4u, 5u}, selectedOnly, logLevel); + + for (auto [s, volumes] : sideVolumes) { + auto pR = connectInR(gctx, volumes, {s}); + if (pR.find(s) != pR.end()) { + dShell[s] = pR.find(s)->second; + } + } + + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( + const GeometryContext& gctx, + const std::vector& containers, + const std::vector& selectedOnly, + Acts::Logging::Level logLevel) noexcept(false) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << containers.size() << " proto containers in Z."); + + // Return the new container + DetectorComponent::PortalContainer dShell; + + for (unsigned int ic = 1; ic < containers.size(); ++ic) { + auto& formerContainer = containers[ic - 1]; + auto& currentContainer = containers[ic]; + // Check and throw exception + if (formerContainer.find(1u) == formerContainer.end()) { + throw std::invalid_argument( + "CylindricalDetectorHelper: proto container has no negative disc, " + "can not be connected in Z"); + } + if (currentContainer.find(0u) == currentContainer.end()) { + throw std::invalid_argument( + "CylindricalDetectorHelper: proto container has no positive disc, " + "can not be connected in Z"); + } + std::shared_ptr keepDisc = formerContainer.find(1u)->second; + std::shared_ptr wasteDisc = currentContainer.find(0u)->second; + keepDisc->fuse(wasteDisc); + for (auto& av : wasteDisc->attachedDetectorVolumes()[1u]) { + ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'."); + av->updatePortal(keepDisc, 0u); + } + } + + // Proto container refurbishment + dShell[0u] = containers[0u].find(0u)->second; + dShell[1u] = containers[containers.size() - 1u].find(1u)->second; + + // Check if this is a tube or a cylinder container (check done on 1st) + std::vector nominalSides = {2u, 4u, 5u}; + if (containers[0u].find(3u) != containers[0u].end()) { + nominalSides.push_back(3u); + } + + // Strip the side volumes + auto sideVolumes = + stripSideVolumes(containers, nominalSides, selectedOnly, logLevel); + + ACTS_VERBOSE("There remain " << sideVolumes.size() + << " side volume packs to be connected"); + for (auto [s, volumes] : sideVolumes) { + ACTS_VERBOSE(" - connect " << volumes.size() << " at selected side " << s); + auto pR = connectInZ(gctx, volumes, {s}, logLevel); + if (pR.find(s) != pR.end()) { + dShell[s] = pR.find(s)->second; + } + } + + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::connectInPhi( + [[maybe_unused]] const GeometryContext& gctx, + [[maybe_unused]] const std::vector& + containers, + [[maybe_unused]] const std::vector& selectedOnly, + [[maybe_unused]] Acts::Logging::Level logLevel) noexcept(false) { + throw std::invalid_argument( + "CylindricalDetectorHelper: container connection in phi not implemented " + "yet."); + DetectorComponent::PortalContainer dShell; + // Done. + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( + [[maybe_unused]] const GeometryContext& gctx, + const std::vector& containers, + Acts::Logging::Level logLevel) { + if (containers.size() != 2u) { + throw std::invalid_argument( + "CylindricalDetectorHelper: wrapping must take exaclty two " + "containers."); + } + + // The inner one is a container + auto innerContainer = containers.front(); + // The outer one is a single volume represented as a container + auto outerContainer = containers.back(); + std::shared_ptr wrappingVolume = nullptr; + for (auto [key, value] : outerContainer) { + auto attachedVolumes = value->attachedDetectorVolumes(); + for (const auto& ava : attachedVolumes) { + for (const auto& av : ava) { + if (wrappingVolume == nullptr and av != nullptr) { + wrappingVolume = av; + } else if (wrappingVolume != nullptr and av != wrappingVolume) { + throw std::invalid_argument( + "CylindricalDetectorHelper: wrapping container must represent a " + "single volume."); + } + } + } + } + if (wrappingVolume == nullptr) { + throw std::invalid_argument( + "CylindricalDetectorHelper: wrapping volume could not be " + "determined."); + } + + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Wrapping a container with volume `" << wrappingVolume->name() + << "'."); + // Return the new container + DetectorComponent::PortalContainer dShell; + + // Keep the outer shells of the proto container + dShell[0u] = wrappingVolume->portalPtrs()[0u]; + dShell[1u] = wrappingVolume->portalPtrs()[1u]; + dShell[2u] = wrappingVolume->portalPtrs()[2u]; + + // Fuse outer cover of first with inner cylinder of wrapping volume + auto& keepCover = innerContainer[2u]; + auto& wasteCover = wrappingVolume->portalPtrs()[3u]; + keepCover->fuse(wasteCover); + wrappingVolume->updatePortal(keepCover, 3u); + + // Stitch sides - negative + auto& keepDiscN = innerContainer[0u]; + auto& wasteDiscN = wrappingVolume->portalPtrs()[4u]; + keepDiscN->fuse(wasteDiscN); + wrappingVolume->updatePortal(keepDiscN, 4u); + + // Stich sides - positive + auto& keepDiscP = innerContainer[1u]; + auto& wasteDiscP = wrappingVolume->portalPtrs()[5u]; + keepDiscP->fuse(wasteDiscP); + wrappingVolume->updatePortal(keepDiscP, 5u); + + // If inner stitching is necessary + if (innerContainer.size() == 4u and + wrappingVolume->portalPtrs().size() == 8u) { + // Inner Container portal + auto& centralSegment = innerContainer[3u]; + auto centralValues = centralSegment->surface().bounds().values(); + ActsScalar centralHalfLengthZ = + centralValues[CylinderBounds::BoundValues::eHalfLengthZ]; + // The two segments + auto& nSegment = wrappingVolume->portalPtrs()[6u]; + auto nValues = nSegment->surface().bounds().values(); + ActsScalar nHalfLengthZ = + nValues[CylinderBounds::BoundValues::eHalfLengthZ]; + auto& pSegment = wrappingVolume->portalPtrs()[7u]; + auto pValues = pSegment->surface().bounds().values(); + ActsScalar pHalfLengthZ = + pValues[CylinderBounds::BoundValues::eHalfLengthZ]; + + auto sideVolumes = stripSideVolumes({innerContainer}, {3u}, {3u}, logLevel); + + // First the left volume sector + std::vector> innerVolumes = { + wrappingVolume->getSharedPtr()}; + + std::vector zBoundaries = { + -centralHalfLengthZ - 2 * nHalfLengthZ, centralHalfLengthZ}; + // Loop over side volume and register the z boundaries + for (auto& svs : sideVolumes) { + for (auto& v : svs.second) { + ActsScalar hlZ = v->volumeBounds().values()[2u]; + zBoundaries.push_back(zBoundaries.back() + 2 * hlZ); + innerVolumes.push_back(v); + } + } + // Last the right volume sector + zBoundaries.push_back(zBoundaries.back() + 2 * pHalfLengthZ); + innerVolumes.push_back(wrappingVolume); + } + + // Done. + return dShell; +} + +std::array, 3u> +Acts::Experimental::detail::CylindricalDetectorHelper::rzphiBoundaries( + const GeometryContext& gctx, + const std::vector& volumes, + Acts::Logging::Level logLevel) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CylindricalDetectorHelper", logLevel)); + + ACTS_DEBUG("Estimate R/z/phi boundaries of " << volumes.size() + << " volumes."); + + // The return boundaries + std::array, 3u> boundaries; + + // The map for collecting + std::array, 3u> valueMaps; + auto& rMap = valueMaps[0u]; + auto& zMap = valueMaps[1u]; + auto& phiMap = valueMaps[2u]; + + auto fillMap = [&](std::map& map, + const std::array& values) { + for (auto v : values) { + if (map.find(v) != map.end()) { + ++map[v]; + } else { + map[v] = 1u; + } + } + }; + + // Loop over the volumes and collect boundaries + for (const auto& v : volumes) { + if (v->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCylinder) { + auto bValues = v->volumeBounds().values(); + // The min/max values + ActsScalar rMin = bValues[CylinderVolumeBounds::BoundValues::eMinR]; + ActsScalar rMax = bValues[CylinderVolumeBounds::BoundValues::eMaxR]; + ActsScalar zCenter = v->transform(gctx).translation().z(); + ActsScalar zHalfLength = + bValues[CylinderVolumeBounds::BoundValues::eHalfLengthZ]; + ActsScalar zMin = zCenter - zHalfLength; + ActsScalar zMax = zCenter + zHalfLength; + ActsScalar phiCenter = + bValues[CylinderVolumeBounds::BoundValues::eAveragePhi]; + ActsScalar phiSector = + bValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; + ActsScalar phiMin = phiCenter - phiSector; + ActsScalar phiMax = phiCenter + phiSector; + // Fill the maps + fillMap(rMap, {rMin, rMax}); + fillMap(zMap, {zMin, zMax}); + fillMap(phiMap, {phiMin, phiMax}); + } + } + + for (auto [im, map] : enumerate(valueMaps)) { + for (auto [key, value] : map) { + boundaries[im].push_back(key); + } + std::sort(boundaries[im].begin(), boundaries[im].end()); + } + + ACTS_VERBOSE("- did yield " << boundaries[0u].size() << " boundaries in R."); + ACTS_VERBOSE("- did yield " << boundaries[1u].size() << " boundaries in z."); + ACTS_VERBOSE("- did yield " << boundaries[2u].size() + << " boundaries in phi."); + + return boundaries; +} diff --git a/Core/src/Detector/PortalHelper.cpp b/Core/src/Detector/detail/PortalHelper.cpp similarity index 88% rename from Core/src/Detector/PortalHelper.cpp rename to Core/src/Detector/detail/PortalHelper.cpp index 28fd9120637..3e99dec5662 100644 --- a/Core/src/Detector/PortalHelper.cpp +++ b/Core/src/Detector/detail/PortalHelper.cpp @@ -6,14 +6,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include "Acts/Detector/PortalHelper.hpp" +#include "Acts/Detector/detail/PortalHelper.hpp" #include "Acts/Detector/Portal.hpp" #include "Acts/Navigation/DetectorVolumeUpdators.hpp" #include -void Acts::Experimental::attachDetectorVolumeUpdators( +void Acts::Experimental::detail::PortalHelper::attachDetectorVolumeUpdators( const GeometryContext& gctx, const std::vector>& volumes, std::vector& pReplacements) { @@ -35,7 +35,8 @@ void Acts::Experimental::attachDetectorVolumeUpdators( } std::vector> -Acts::Experimental::attachedDetectorVolumes(Portal& portal) noexcept(false) { +Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( + Portal& portal) noexcept(false) { auto& attachedVolumes = portal.attachedDetectorVolumes(); if (not attachedVolumes[0u].empty() and not attachedVolumes[1u].empty()) { throw std::invalid_argument( diff --git a/Core/src/Detector/detail/SupportHelper.cpp b/Core/src/Detector/detail/SupportHelper.cpp new file mode 100644 index 00000000000..11cc8d326ac --- /dev/null +++ b/Core/src/Detector/detail/SupportHelper.cpp @@ -0,0 +1,219 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/detail/SupportHelper.hpp" + +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/TrapezoidBounds.hpp" + +#include + +std::vector> +Acts::Experimental::detail::SupportHelper::cylindricalSupport( + const Transform3& transform, const std::array& bounds, + unsigned int splits) { + // Return vector preparation + std::vector> cSupport; + if (splits == 1u) { + // No splitting is done in this case + cSupport.push_back(Surface::makeShared( + transform, std::make_shared(bounds))); + } else { + // Split into n(splits) planar surfaces, prep work: + ActsScalar r = bounds[0u]; + ActsScalar halfZ = bounds[1u]; + ActsScalar minPhi = bounds[3u] - bounds[2u]; + ActsScalar maxPhi = bounds[3u] + bounds[2u]; + ActsScalar dHalfPhi = (maxPhi - minPhi) / (2 * splits); + ActsScalar cosPhiHalf = std::cos(dHalfPhi); + ActsScalar sinPhiHalf = std::sin(dHalfPhi); + ActsScalar planeR = r * cosPhiHalf; + ActsScalar planeHalfX = r * sinPhiHalf; + ActsScalar planeZ = transform.translation().z(); + + auto sRectangle = + std::make_shared(planeHalfX, halfZ); + // Now create the Trapezoids + for (unsigned int iphi = 0; iphi < splits; ++iphi) { + // Get the moduleTransform + ActsScalar phi = -M_PI + (iphi + 0.5) * 2 * dHalfPhi; + ActsScalar cosPhi = std::cos(phi); + ActsScalar sinPhi = std::sin(phi); + ActsScalar planeX = planeR * cosPhi; + ActsScalar planeY = planeR * sinPhi; + + Acts::Vector3 planeCenter(planeX, planeY, planeZ); + Acts::Vector3 planeAxisZ(cosPhi, sinPhi, 0.); + Acts::Vector3 planeAxisY(0., 0., 1.); + Acts::Vector3 planeAxisX = planeAxisY.cross(planeAxisZ); + + RotationMatrix3 planeRotation; + planeRotation.col(0) = planeAxisX; + planeRotation.col(1) = planeAxisY; + planeRotation.col(2) = planeAxisZ; + + Transform3 sTransform{planeRotation}; + sTransform.pretranslate(planeCenter); + // Place it + cSupport.push_back( + Surface::makeShared(sTransform, sRectangle)); + } + } + + return cSupport; +} + +std::vector> +Acts::Experimental::detail::SupportHelper::discSupport( + const Transform3& transform, const std::array& bounds, + unsigned int splits) { + // Return vector + std::vector> dSupport; + if (splits == 1u) { + // No splitting is done in this case + dSupport.push_back(Surface::makeShared( + transform, std::make_shared(bounds))); + } else { + // Split into n(splits) planar surfaces in phi, prep work: + ActsScalar minR = bounds[0u]; + ActsScalar maxR = bounds[1u]; + ActsScalar minPhi = bounds[3u] - bounds[2u]; + ActsScalar maxPhi = bounds[3u] + bounds[2u]; + ActsScalar dHalfPhi = (maxPhi - minPhi) / (2 * splits); + ActsScalar cosPhiHalf = std::cos(dHalfPhi); + ActsScalar sinPhiHalf = std::sin(dHalfPhi); + ActsScalar maxLocY = maxR * cosPhiHalf; + ActsScalar minLocY = minR * cosPhiHalf; + ActsScalar hR = 0.5 * (maxLocY + minLocY); + ActsScalar hY = 0.5 * (maxLocY - minLocY); + ActsScalar hXminY = minR * sinPhiHalf; + ActsScalar hXmaxY = maxR * sinPhiHalf; + // Split trapezoid + auto sTrapezoid = + std::make_shared(hXminY, hXmaxY, hY); + Vector3 zAxis = transform.rotation().col(2); + ActsScalar zPosition = transform.translation().z(); + // Now create the Trapezoids + for (unsigned int iphi = 0; iphi < splits; ++iphi) { + // Create the split module transform + ActsScalar phi = -M_PI + (iphi + 0.5) * 2 * dHalfPhi; + auto sTransform = Transform3( + Translation3(hR * std::cos(phi), hR * std::sin(phi), zPosition) * + AngleAxis3(phi - 0.5 * M_PI, zAxis)); + // Place it + dSupport.push_back( + Surface::makeShared(sTransform, sTrapezoid)); + } + } + return dSupport; +} + +void Acts::Experimental::detail::SupportHelper::addSupport( + std::vector>& layerSurfaces, + std::vector& assignToAll, const Extent& layerExtent, + Surface::SurfaceType layerRepresentation, + const std::array& layerSupportValues, + std::optional layerTransform, unsigned int supportSplits) { + // Cylinder and Disc section + if (layerRepresentation == Surface::SurfaceType::Cylinder or + layerRepresentation == Surface::SurfaceType::Disc) { + // Bail out if you have no measure of R, Z + if (not layerExtent.constrains(binZ) or not layerExtent.constrains(binR)) { + throw std::runtime_error( + "SupportHelper::addSupport(...) - z or phi are not constrained."); + } + /// Overall parameters of support surfaces + ActsScalar minZ = layerExtent.min(binZ); + ActsScalar maxZ = layerExtent.max(binZ); + ActsScalar minR = layerExtent.min(binR); + ActsScalar maxR = layerExtent.max(binR); + ActsScalar minPhi = -M_PI; + ActsScalar maxPhi = M_PI; + bool sectoral = false; + bool concentric = false; + // Check if concentric + if (layerTransform.has_value() and + layerTransform.value().isApprox(Transform3::Identity())) { + concentric = true; + } + // Check if we are dealing with a sectoral setup + if (layerExtent.constrains(binPhi)) { + minPhi = layerExtent.min(binPhi); + maxPhi = layerExtent.max(binPhi); + sectoral = true; + } + + // Get the main support parameters: + // - doff .. offset (in r.z) + // - demin,d emax .. envelop min, max (in z,r) + // - dphimin, dphimin .. envelop min, max (in phi) + auto [doff, demin, demax, dphimin, dphimax] = layerSupportValues; + // phi treatment is common between the cylinders and discs + if (sectoral) { + minPhi -= std::abs(demin); + maxPhi += std::abs(demax); + } + // Average phi and half phi + ActsScalar avgPhi = 0.5 * (maxPhi + minPhi); + ActsScalar halfPhi = 0.5 * (maxPhi - minPhi); + // Now specify into Cylinder or disc + if (layerRepresentation == Surface::SurfaceType::Cylinder) { + ActsScalar layerR = doff < 0 ? minR + doff : maxR + doff; + minZ -= std::abs(demin); + maxZ += std::abs(demax); + ActsScalar midZ = 0.5 * (minZ + maxZ); + ActsScalar halfZ = 0.5 * (maxZ - minZ); + // midZ / halfZ are overwritten if the cylinder + // is chosen to be concentric + Transform3 sTransform = Transform3::Identity(); + if (concentric) { + midZ = 0.; + halfZ = std::max(std::abs(minZ), std::abs(maxZ)); + } else { + sTransform.pretranslate(Vector3(0., 0., midZ)); + } + auto cSupport = SupportHelper::cylindricalSupport( + sTransform, {layerR, halfZ, halfPhi, avgPhi, 0., 0.}, supportSplits); + // Remember the surfaces to be assigned to all bins, once the + // support surfaces are split they enter the standard bin assignment + if (supportSplits == 1u and cSupport.size() == 1u) { + assignToAll.push_back(layerSurfaces.size()); + } + // Add those to the layer surfaces + layerSurfaces.insert(layerSurfaces.end(), cSupport.begin(), + cSupport.end()); + + } else { + // Disc section + ActsScalar layerZ = doff < 0 ? minZ + doff : maxZ + doff; + minR -= std::abs(demin); + maxR += std::abs(demax); + Transform3 sTransform = Transform3::Identity(); + sTransform.pretranslate(Vector3(0., 0., layerZ)); + auto dSupport = SupportHelper::discSupport( + sTransform, {minR, maxR, halfPhi, avgPhi}, supportSplits); + // Remember the surfaces to be assigned to all bins, once the + // support surfaces are split they enter the standard bin assignment + if (supportSplits == 1u and dSupport.size() == 1u) { + assignToAll.push_back(layerSurfaces.size()); + } + // Add those to the layer surfaces + layerSurfaces.insert(layerSurfaces.end(), dSupport.begin(), + dSupport.end()); + } + } else { + throw std::invalid_argument( + "SupportHelper: currently only cylindrical/disc support building " + "possible."); + } +} diff --git a/Core/src/EventData/CorrectedTransformationFreeToBound.cpp b/Core/src/EventData/CorrectedTransformationFreeToBound.cpp index 731d6ab3e86..995d78bee30 100644 --- a/Core/src/EventData/CorrectedTransformationFreeToBound.cpp +++ b/Core/src/EventData/CorrectedTransformationFreeToBound.cpp @@ -42,7 +42,7 @@ std::optional> Acts::detail::CorrectedFreeToBoundTransformer::operator()( const Acts::FreeVector& freeParams, const Acts::FreeSymMatrix& freeCovariance, const Acts::Surface& surface, - const Acts::GeometryContext& geoContext, NavigationDirection navDir, + const Acts::GeometryContext& geoContext, Direction navDir, const Logger& logger) const { // Get the incidence angle Vector3 dir = freeParams.segment<3>(eFreeDir0); diff --git a/Core/src/EventData/TransformationFreeToBound.cpp b/Core/src/EventData/TransformationFreeToBound.cpp index 904a5a8e9a3..4e6917c7b63 100644 --- a/Core/src/EventData/TransformationFreeToBound.cpp +++ b/Core/src/EventData/TransformationFreeToBound.cpp @@ -15,13 +15,13 @@ Acts::Result Acts::detail::transformFreeToBoundParameters( const FreeVector& freeParams, const Surface& surface, - const GeometryContext& geoCtx) { + const GeometryContext& geoCtx, ActsScalar tolerance) { // initialize the bound vector BoundVector bp = BoundVector::Zero(); // convert global to local position on the surface auto position = freeParams.segment<3>(eFreePos0); auto direction = freeParams.segment<3>(eFreeDir0); - auto result = surface.globalToLocal(geoCtx, position, direction); + auto result = surface.globalToLocal(geoCtx, position, direction, tolerance); if (!result.ok()) { return Result::failure(result.error()); } @@ -40,11 +40,12 @@ Acts::Result Acts::detail::transformFreeToBoundParameters( Acts::Result Acts::detail::transformFreeToBoundParameters( const Acts::Vector3& position, ActsScalar time, const Acts::Vector3& direction, ActsScalar qOverP, - const Acts::Surface& surface, const Acts::GeometryContext& geoCtx) { + const Acts::Surface& surface, const Acts::GeometryContext& geoCtx, + ActsScalar tolerance) { // initialize the bound vector BoundVector bp = BoundVector::Zero(); // convert global to local position on the surface - auto result = surface.globalToLocal(geoCtx, position, direction); + auto result = surface.globalToLocal(geoCtx, position, direction, tolerance); if (!result.ok()) { return Result::failure(result.error()); } diff --git a/Core/src/Geometry/AbstractVolume.cpp b/Core/src/Geometry/AbstractVolume.cpp index eb1d04316c5..b281e91162d 100644 --- a/Core/src/Geometry/AbstractVolume.cpp +++ b/Core/src/Geometry/AbstractVolume.cpp @@ -35,7 +35,7 @@ void Acts::AbstractVolume::createBoundarySurfaces() { for (auto& osf : orientedSurfaces) { AbstractVolume* opposite = nullptr; AbstractVolume* along = nullptr; - if (osf.second == NavigationDirection::Backward) { + if (osf.second == Direction::Negative) { opposite = this; } else { along = this; diff --git a/Core/src/Geometry/ConeVolumeBounds.cpp b/Core/src/Geometry/ConeVolumeBounds.cpp index 98a897eaf89..20d08fa9089 100644 --- a/Core/src/Geometry/ConeVolumeBounds.cpp +++ b/Core/src/Geometry/ConeVolumeBounds.cpp @@ -99,13 +99,13 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto innerCone = Surface::makeShared(innerConeTrans, m_innerConeBounds); oSurfaces.push_back( - OrientedSurface(std::move(innerCone), NavigationDirection::Forward)); + OrientedSurface(std::move(innerCone), Direction::Forward)); } else if (m_innerCylinderBounds != nullptr) { // Or alternatively the inner Cylinder auto innerCylinder = Surface::makeShared(transform, m_innerCylinderBounds); - oSurfaces.push_back(OrientedSurface(std::move(innerCylinder), - NavigationDirection::Forward)); + oSurfaces.push_back( + OrientedSurface(std::move(innerCylinder), Direction::Forward)); } // Create an outer Cone @@ -114,13 +114,13 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto outerCone = Surface::makeShared(outerConeTrans, m_outerConeBounds); oSurfaces.push_back( - OrientedSurface(std::move(outerCone), NavigationDirection::Backward)); + OrientedSurface(std::move(outerCone), Direction::Backward)); } else if (m_outerCylinderBounds != nullptr) { // or alternatively an outer Cylinder auto outerCylinder = Surface::makeShared(transform, m_outerCylinderBounds); - oSurfaces.push_back(OrientedSurface(std::move(outerCylinder), - NavigationDirection::Backward)); + oSurfaces.push_back( + OrientedSurface(std::move(outerCylinder), Direction::Backward)); } // Set a disc at Zmin @@ -130,7 +130,7 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto negativeDisc = Surface::makeShared(negativeDiscTrans, m_negativeDiscBounds); oSurfaces.push_back( - OrientedSurface(std::move(negativeDisc), NavigationDirection::Forward)); + OrientedSurface(std::move(negativeDisc), Direction::Forward)); } // Set a disc at Zmax @@ -138,7 +138,7 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto positiveDisc = Surface::makeShared(positiveDiscTrans, m_positiveDiscBounds); oSurfaces.push_back( - OrientedSurface(std::move(positiveDisc), NavigationDirection::Backward)); + OrientedSurface(std::move(positiveDisc), Direction::Backward)); if (m_sectorBounds) { RotationMatrix3 sectorRotation; @@ -152,8 +152,8 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto negSectorAbsTrans = transform * negSectorRelTrans; auto negSectorPlane = Surface::makeShared(negSectorAbsTrans, m_sectorBounds); - oSurfaces.push_back(OrientedSurface(std::move(negSectorPlane), - NavigationDirection::Forward)); + oSurfaces.push_back( + OrientedSurface(std::move(negSectorPlane), Direction::Positive)); Transform3 posSectorRelTrans{sectorRotation}; posSectorRelTrans.prerotate( @@ -162,8 +162,8 @@ Acts::OrientedSurfaces Acts::ConeVolumeBounds::orientedSurfaces( auto posSectorPlane = Surface::makeShared(posSectorAbsTrans, m_sectorBounds); - oSurfaces.push_back(OrientedSurface(std::move(posSectorPlane), - NavigationDirection::Backward)); + oSurfaces.push_back( + OrientedSurface(std::move(posSectorPlane), Direction::Negative)); } return oSurfaces; } diff --git a/Core/src/Geometry/CuboidVolumeBounds.cpp b/Core/src/Geometry/CuboidVolumeBounds.cpp index 0942eac7412..c6106b172f6 100644 --- a/Core/src/Geometry/CuboidVolumeBounds.cpp +++ b/Core/src/Geometry/CuboidVolumeBounds.cpp @@ -48,39 +48,33 @@ Acts::OrientedSurfaces Acts::CuboidVolumeBounds::orientedSurfaces( // (1) - at negative local z auto sf = Surface::makeShared( transform * Translation3(0., 0., -get(eHalfLengthZ)), m_xyBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (2) - at positive local z sf = Surface::makeShared( transform * Translation3(0., 0., get(eHalfLengthZ)), m_xyBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); // Face surfaces yz ------------------------------------- // (3) - at negative local x sf = Surface::makeShared( transform * Translation3(-get(eHalfLengthX), 0., 0.) * s_planeYZ, m_yzBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (4) - at positive local x sf = Surface::makeShared( transform * Translation3(get(eHalfLengthX), 0., 0.) * s_planeYZ, m_yzBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); // Face surfaces zx ------------------------------------- // (5) - at negative local y sf = Surface::makeShared( transform * Translation3(0., -get(eHalfLengthY), 0.) * s_planeZX, m_zxBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (6) - at positive local y sf = Surface::makeShared( transform * Translation3(0., get(eHalfLengthY), 0.) * s_planeZX, m_zxBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); return oSurfaces; } diff --git a/Core/src/Geometry/CutoutCylinderVolumeBounds.cpp b/Core/src/Geometry/CutoutCylinderVolumeBounds.cpp index 05dac98a8fa..a65214c155e 100644 --- a/Core/src/Geometry/CutoutCylinderVolumeBounds.cpp +++ b/Core/src/Geometry/CutoutCylinderVolumeBounds.cpp @@ -60,13 +60,13 @@ Acts::OrientedSurfaces Acts::CutoutCylinderVolumeBounds::orientedSurfaces( auto outer = Surface::makeShared(transform, m_outerCylinderBounds); oSurfaces.at(tubeOuterCover) = - OrientedSurface(std::move(outer), NavigationDirection::Backward); + OrientedSurface(std::move(outer), Direction::Negative); // Inner (cutout) cylinder envelope auto cutoutInner = Surface::makeShared(transform, m_cutoutCylinderBounds); oSurfaces.at(tubeInnerCover) = - OrientedSurface(std::move(cutoutInner), NavigationDirection::Forward); + OrientedSurface(std::move(cutoutInner), Direction::Positive); // z position of the pos and neg choke points double hlChoke = (get(eHalfLengthZ) - get(eHalfLengthZcutout)) * 0.5; @@ -77,13 +77,13 @@ Acts::OrientedSurfaces Acts::CutoutCylinderVolumeBounds::orientedSurfaces( auto posInner = Surface::makeShared(posChokeTrf, m_innerCylinderBounds); oSurfaces.at(index7) = - OrientedSurface(std::move(posInner), NavigationDirection::Forward); + OrientedSurface(std::move(posInner), Direction::Positive); auto negChokeTrf = transform * Translation3(Vector3(0, 0, -zChoke)); auto negInner = Surface::makeShared(negChokeTrf, m_innerCylinderBounds); oSurfaces.at(index6) = - OrientedSurface(std::move(negInner), NavigationDirection::Forward); + OrientedSurface(std::move(negInner), Direction::Positive); } // Two Outer disks @@ -92,14 +92,14 @@ Acts::OrientedSurfaces Acts::CutoutCylinderVolumeBounds::orientedSurfaces( auto posOutDisc = Surface::makeShared(posOutDiscTrf, m_outerDiscBounds); oSurfaces.at(positiveFaceXY) = - OrientedSurface(std::move(posOutDisc), NavigationDirection::Backward); + OrientedSurface(std::move(posOutDisc), Direction::Negative); auto negOutDiscTrf = transform * Translation3(Vector3(0, 0, -get(eHalfLengthZ))); auto negOutDisc = Surface::makeShared(negOutDiscTrf, m_outerDiscBounds); oSurfaces.at(negativeFaceXY) = - OrientedSurface(std::move(negOutDisc), NavigationDirection::Forward); + OrientedSurface(std::move(negOutDisc), Direction::Positive); // Two Inner disks auto posInDiscTrf = @@ -107,14 +107,14 @@ Acts::OrientedSurfaces Acts::CutoutCylinderVolumeBounds::orientedSurfaces( auto posInDisc = Surface::makeShared(posInDiscTrf, m_innerDiscBounds); oSurfaces.at(index5) = - OrientedSurface(std::move(posInDisc), NavigationDirection::Forward); + OrientedSurface(std::move(posInDisc), Direction::Positive); auto negInDiscTrf = transform * Translation3(Vector3(0, 0, -get(eHalfLengthZcutout))); auto negInDisc = Surface::makeShared(negInDiscTrf, m_innerDiscBounds); oSurfaces.at(index4) = - OrientedSurface(std::move(negInDisc), NavigationDirection::Backward); + OrientedSurface(std::move(negInDisc), Direction::Negative); return oSurfaces; } diff --git a/Core/src/Geometry/CylinderVolumeBounds.cpp b/Core/src/Geometry/CylinderVolumeBounds.cpp index f86570ad492..1c3e09e8602 100644 --- a/Core/src/Geometry/CylinderVolumeBounds.cpp +++ b/Core/src/Geometry/CylinderVolumeBounds.cpp @@ -85,24 +85,24 @@ Acts::OrientedSurfaces Acts::CylinderVolumeBounds::orientedSurfaces( // [0] Bottom Disc (negative z) auto dSurface = Surface::makeShared(transMinZ, m_discBounds); oSurfaces.push_back( - OrientedSurface(std::move(dSurface), NavigationDirection::Forward)); + OrientedSurface(std::move(dSurface), Direction::Positive)); // [1] Top Disc (positive z) dSurface = Surface::makeShared(transMaxZ, m_discBounds); oSurfaces.push_back( - OrientedSurface(std::move(dSurface), NavigationDirection::Backward)); + OrientedSurface(std::move(dSurface), Direction::Negative)); // [2] Outer Cylinder auto cSurface = Surface::makeShared(transform, m_outerCylinderBounds); oSurfaces.push_back( - OrientedSurface(std::move(cSurface), NavigationDirection::Backward)); + OrientedSurface(std::move(cSurface), Direction::Negative)); // [3] Inner Cylinder (optional) if (m_innerCylinderBounds != nullptr) { cSurface = Surface::makeShared(transform, m_innerCylinderBounds); oSurfaces.push_back( - OrientedSurface(std::move(cSurface), NavigationDirection::Forward)); + OrientedSurface(std::move(cSurface), Direction::Positive)); } // [4] & [5] - Sectoral planes (optional) @@ -117,7 +117,7 @@ Acts::OrientedSurfaces Acts::CylinderVolumeBounds::orientedSurfaces( auto pSurface = Surface::makeShared(sp1Transform, m_sectorPlaneBounds); oSurfaces.push_back( - OrientedSurface(std::move(pSurface), NavigationDirection::Forward)); + OrientedSurface(std::move(pSurface), Direction::Positive)); // sectorPlane 2 (positive phi) const Transform3 sp2Transform = Transform3(transform * @@ -128,7 +128,7 @@ Acts::OrientedSurfaces Acts::CylinderVolumeBounds::orientedSurfaces( pSurface = Surface::makeShared(sp2Transform, m_sectorPlaneBounds); oSurfaces.push_back( - OrientedSurface(std::move(pSurface), NavigationDirection::Backward)); + OrientedSurface(std::move(pSurface), Direction::Negative)); } return oSurfaces; } diff --git a/Core/src/Geometry/GenericCuboidVolumeBounds.cpp b/Core/src/Geometry/GenericCuboidVolumeBounds.cpp index 20cb8e2c26d..327b1675719 100644 --- a/Core/src/Geometry/GenericCuboidVolumeBounds.cpp +++ b/Core/src/Geometry/GenericCuboidVolumeBounds.cpp @@ -81,9 +81,7 @@ Acts::OrientedSurfaces Acts::GenericCuboidVolumeBounds::orientedSurfaces( const Vector3 ab = b - a, ac = c - a; Vector3 normal = ab.cross(ac).normalized(); - NavigationDirection nDir = ((cog - d).dot(normal) < 0) - ? NavigationDirection::Backward - : NavigationDirection::Forward; + Direction dir = Direction::fromScalar((cog - d).dot(normal)); // build transform from z unit to normal // z is normal in local coordinates @@ -110,7 +108,7 @@ Acts::OrientedSurfaces Acts::GenericCuboidVolumeBounds::orientedSurfaces( auto srfTrf = transform * vol2srf.inverse(); auto srf = Surface::makeShared(srfTrf, polyBounds); - oSurfaces.push_back(OrientedSurface(std::move(srf), nDir)); + oSurfaces.push_back(OrientedSurface(std::move(srf), dir)); }; make_surface(m_vertices[0], m_vertices[1], m_vertices[2], m_vertices[3]); diff --git a/Core/src/Geometry/Layer.cpp b/Core/src/Geometry/Layer.cpp index 02f631c7b53..abbf3800bda 100644 --- a/Core/src/Geometry/Layer.cpp +++ b/Core/src/Geometry/Layer.cpp @@ -245,7 +245,7 @@ Acts::Layer::compatibleSurfaces( sIntersections.resize(std::distance(sIntersections.begin(), it)); // sort according to the path length - if (options.navDir == NavigationDirection::Forward) { + if (options.navDir == Direction::Forward) { std::sort(sIntersections.begin(), sIntersections.end()); } else { std::sort(sIntersections.begin(), sIntersections.end(), std::greater<>()); diff --git a/Core/src/Geometry/TrackingVolume.cpp b/Core/src/Geometry/TrackingVolume.cpp index 90b1ed3b2bc..96361091271 100644 --- a/Core/src/Geometry/TrackingVolume.cpp +++ b/Core/src/Geometry/TrackingVolume.cpp @@ -116,7 +116,7 @@ const Acts::TrackingVolumeBoundaries& Acts::TrackingVolume::boundarySurfaces() void Acts::TrackingVolume::connectDenseBoundarySurfaces( MutableTrackingVolumeVector& confinedDenseVolumes) { if (!confinedDenseVolumes.empty()) { - NavigationDirection navDir = NavigationDirection::Forward; + Direction dir = Direction::Positive; // Walk over each dense volume for (auto& confDenseVol : confinedDenseVolumes) { // Walk over each boundary surface of the volume @@ -135,13 +135,13 @@ void Acts::TrackingVolume::connectDenseBoundarySurfaces( boundSur.at(i)); if (mutableBs->m_oppositeVolume != nullptr && mutableBs->m_alongVolume == nullptr) { - navDir = NavigationDirection::Forward; - mutableBs->attachVolume(this, navDir); + dir = Direction::Positive; + mutableBs->attachVolume(this, dir); } else { if (mutableBs->m_oppositeVolume == nullptr && mutableBs->m_alongVolume != nullptr) { - navDir = NavigationDirection::Backward; - mutableBs->attachVolume(this, navDir); + dir = Direction::Negative; + mutableBs->attachVolume(this, dir); } } @@ -164,7 +164,7 @@ void Acts::TrackingVolume::createBoundarySurfaces() { for (auto& osf : orientedSurfaces) { TrackingVolume* opposite = nullptr; TrackingVolume* along = nullptr; - if (osf.second == NavigationDirection::Backward) { + if (osf.second == Direction::Negative) { opposite = this; } else { along = this; @@ -190,9 +190,7 @@ void Acts::TrackingVolume::glueTrackingVolume(const GeometryContext& gctx, Vector3 nvector = bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition); // estimate the orientation - NavigationDirection navDir = (nvector.dot(distance) > 0.) - ? NavigationDirection::Forward - : NavigationDirection::Backward; + Direction dir = Direction::fromScalar(nvector.dot(distance)); // The easy case : // - no glue volume descriptors on either side if ((m_glueVolumeDescriptor == nullptr) || @@ -200,7 +198,7 @@ void Acts::TrackingVolume::glueTrackingVolume(const GeometryContext& gctx, // the boundary orientation auto mutableBSurfaceMine = std::const_pointer_cast>(bSurfaceMine); - mutableBSurfaceMine->attachVolume(neighbor, navDir); + mutableBSurfaceMine->attachVolume(neighbor, dir); // Make sure you keep the boundary material if there const Surface& neighborSurface = neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation(); @@ -237,9 +235,7 @@ void Acts::TrackingVolume::glueTrackingVolumes( Vector3 nvector = bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition); // estimate the orientation - NavigationDirection navDir = (nvector.dot(distance) > 0.) - ? NavigationDirection::Forward - : NavigationDirection::Backward; + Direction dir = Direction::fromScalar(nvector.dot(distance)); // the easy case : // - no glue volume descriptors on either side if ((m_glueVolumeDescriptor == nullptr) || @@ -247,7 +243,7 @@ void Acts::TrackingVolume::glueTrackingVolumes( // the boundary orientation auto mutableBSurfaceMine = std::const_pointer_cast>(bSurfaceMine); - mutableBSurfaceMine->attachVolumeArray(neighbors, navDir); + mutableBSurfaceMine->attachVolumeArray(neighbors, dir); // now set it to the neighbor volumes - the optised way for (auto& nVolume : neighbors->arrayObjects()) { auto mutableNVolume = std::const_pointer_cast(nVolume); @@ -593,7 +589,7 @@ Acts::TrackingVolume::compatibleBoundaries( }; // Sort them accordingly to the navigation direction - if (options.navDir == NavigationDirection::Forward) { + if (options.navDir == Direction::Forward) { std::sort(bIntersections.begin(), bIntersections.end(), [&](const auto& a, const auto& b) { return comparator(a.intersection.pathLength, @@ -650,7 +646,7 @@ Acts::TrackingVolume::compatibleLayers( : tLayer->nextLayer(gctx, position, options.navDir * direction); } // sort them accordingly to the navigation direction - if (options.navDir == NavigationDirection::Forward) { + if (options.navDir == Direction::Forward) { std::sort(lIntersections.begin(), lIntersections.end()); } else { std::sort(lIntersections.begin(), lIntersections.end(), std::greater<>()); @@ -736,7 +732,7 @@ Acts::TrackingVolume::compatibleSurfacesFromHierarchy( } // Sort according to the path length - if (options.navDir == NavigationDirection::Forward) { + if (options.navDir == Direction::Forward) { std::sort(sIntersections.begin(), sIntersections.end()); } else { std::sort(sIntersections.begin(), sIntersections.end(), std::greater<>()); diff --git a/Core/src/Geometry/TrapezoidVolumeBounds.cpp b/Core/src/Geometry/TrapezoidVolumeBounds.cpp index 36de0606919..c0c6e57864f 100644 --- a/Core/src/Geometry/TrapezoidVolumeBounds.cpp +++ b/Core/src/Geometry/TrapezoidVolumeBounds.cpp @@ -66,13 +66,11 @@ Acts::OrientedSurfaces Acts::TrapezoidVolumeBounds::orientedSurfaces( auto nzTransform = transform * Translation3(0., 0., -get(eHalfLengthZ)); auto sf = Surface::makeShared(nzTransform, m_faceXYTrapezoidBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (2) - At positive local z auto pzTransform = transform * Translation3(0., 0., get(eHalfLengthZ)); sf = Surface::makeShared(pzTransform, m_faceXYTrapezoidBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); double poshOffset = get(eHalfLengthY) / std::tan(get(eAlpha)); double neghOffset = get(eHalfLengthY) / std::tan(get(eBeta)); @@ -86,8 +84,7 @@ Acts::OrientedSurfaces Acts::TrapezoidVolumeBounds::orientedSurfaces( s_planeYZ; sf = Surface::makeShared(fbTransform, m_faceBetaRectangleBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (4) - At point A, attached to alpha opening angle Vector3 faPosition(get(eHalfLengthXnegY) + poshOffset, 0., 0.); @@ -96,8 +93,7 @@ Acts::OrientedSurfaces Acts::TrapezoidVolumeBounds::orientedSurfaces( AngleAxis3(-0.5 * M_PI + get(eAlpha), Vector3(0., 0., 1.)) * s_planeYZ; sf = Surface::makeShared(faTransform, m_faceAlphaRectangleBounds); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); // Face surfaces zx // (5) - At negative local y @@ -105,15 +101,13 @@ Acts::OrientedSurfaces Acts::TrapezoidVolumeBounds::orientedSurfaces( transform * Translation3(0., -get(eHalfLengthY), 0.) * s_planeZX; sf = Surface::makeShared(nxTransform, m_faceZXRectangleBoundsBottom); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Forward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Positive)); // (6) - At positive local y auto pxTransform = transform * Translation3(topShift, get(eHalfLengthY), 0.) * s_planeZX; sf = Surface::makeShared(pxTransform, m_faceZXRectangleBoundsTop); - oSurfaces.push_back( - OrientedSurface(std::move(sf), NavigationDirection::Backward)); + oSurfaces.push_back(OrientedSurface(std::move(sf), Direction::Negative)); return oSurfaces; } diff --git a/Core/src/Material/Interactions.cpp b/Core/src/Material/Interactions.cpp index 2323f57a85a..2e401096775 100644 --- a/Core/src/Material/Interactions.cpp +++ b/Core/src/Material/Interactions.cpp @@ -381,7 +381,7 @@ float Acts::computeEnergyLossRadiative(const MaterialSlab& slab, int pdg, // particle momentum and energy // do not need to care about the sign since it is only used squared const auto momentum = q / qOverP; - const auto energy = std::sqrt(m * m + momentum * momentum); + const auto energy = std::hypot(m, momentum); auto dEdx = computeBremsstrahlungLossMean(m, energy); if (((pdg == PdgParticle::eMuon) or (pdg == PdgParticle::eAntiMuon)) and @@ -406,7 +406,7 @@ float Acts::deriveEnergyLossRadiativeQOverP(const MaterialSlab& slab, int pdg, // particle momentum and energy // do not need to care about the sign since it is only used squared const auto momentum = q / qOverP; - const auto energy = std::sqrt(m * m + momentum * momentum); + const auto energy = std::hypot(m, momentum); // compute derivative w/ respect to energy. auto derE = deriveBremsstrahlungLossMeanE(m); diff --git a/Core/src/Propagator/StraightLineStepper.cpp b/Core/src/Propagator/StraightLineStepper.cpp index 31e1da004c4..efd170b07d0 100644 --- a/Core/src/Propagator/StraightLineStepper.cpp +++ b/Core/src/Propagator/StraightLineStepper.cpp @@ -70,7 +70,7 @@ void StraightLineStepper::resetState(State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, const Surface& surface, - const NavigationDirection navDir, + const Direction navDir, const double stepSize) const { // Update the stepping state update(state, diff --git a/Core/src/Propagator/detail/CovarianceEngine.cpp b/Core/src/Propagator/detail/CovarianceEngine.cpp index f9bbd774484..750ad8e6e02 100644 --- a/Core/src/Propagator/detail/CovarianceEngine.cpp +++ b/Core/src/Propagator/detail/CovarianceEngine.cpp @@ -36,7 +36,7 @@ FreeToBoundMatrix freeToCurvilinearJacobian(const Vector3& direction) { const double z = direction(2); // == cos(theta) // can be turned into cosine/sine const double cosTheta = z; - const double sinTheta = sqrt(x * x + y * y); + const double sinTheta = std::hypot(x, y); const double invSinTheta = 1. / sinTheta; const double cosPhi = x * invSinTheta; const double sinPhi = y * invSinTheta; @@ -52,7 +52,7 @@ FreeToBoundMatrix freeToCurvilinearJacobian(const Vector3& direction) { } else { // Under grazing incidence to z, the above coordinate system definition // becomes numerically unstable, and we need to switch to another one - const double c = sqrt(y * y + z * z); + const double c = std::hypot(y, z); const double invC = 1. / c; jacToCurv(0, 1) = -z * invC; jacToCurv(0, 2) = y * invC; @@ -232,7 +232,7 @@ void reinitializeJacobians(FreeMatrix& freeTransportJacobian, const double z = direction(2); // == cos(theta) // can be turned into cosine/sine const double cosTheta = z; - const double sinTheta = sqrt(x * x + y * y); + const double sinTheta = std::hypot(x, y); const double invSinTheta = 1. / sinTheta; const double cosPhi = x * invSinTheta; const double sinPhi = y * invSinTheta; diff --git a/Core/src/Propagator/detail/JacobianEngine.cpp b/Core/src/Propagator/detail/JacobianEngine.cpp index 81df96acf4a..dc4f838e32f 100644 --- a/Core/src/Propagator/detail/JacobianEngine.cpp +++ b/Core/src/Propagator/detail/JacobianEngine.cpp @@ -35,7 +35,7 @@ FreeToBoundMatrix freeToCurvilinearJacobian(const Vector3& direction) { const ActsScalar x = direction(0); // == cos(phi) * sin(theta) const ActsScalar y = direction(1); // == sin(phi) * sin(theta) const ActsScalar z = direction(2); // == cos(theta) - const ActsScalar c = std::sqrt(y * y + z * z); + const ActsScalar c = std::hypot(y, z); const ActsScalar invC = 1. / c; freeToCurvJacobian(eBoundLoc0, eFreePos1) = -z * invC; freeToCurvJacobian(eBoundLoc0, eFreePos2) = y * invC; diff --git a/Core/src/Surfaces/ConeSurface.cpp b/Core/src/Surfaces/ConeSurface.cpp index 9f549ef45ed..77bbe837c5c 100644 --- a/Core/src/Surfaces/ConeSurface.cpp +++ b/Core/src/Surfaces/ConeSurface.cpp @@ -280,7 +280,8 @@ Acts::detail::RealQuadraticEquation Acts::ConeSurface::intersectionSolver( Acts::SurfaceIntersection Acts::ConeSurface::intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const { + const Vector3& direction, const BoundaryCheck& bcheck, + ActsScalar tolerance) const { // Solve the quadratic equation auto qe = intersectionSolver(gctx, position, direction); @@ -291,10 +292,9 @@ Acts::SurfaceIntersection Acts::ConeSurface::intersect( // Check the validity of the first solution Vector3 solution1 = position + qe.first * direction; - Intersection3D::Status status1 = - std::abs(qe.first) < std::abs(s_onSurfaceTolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; + Intersection3D::Status status1 = std::abs(qe.first) < std::abs(tolerance) + ? Intersection3D::Status::onSurface + : Intersection3D::Status::reachable; if (bcheck and not isOnSurface(gctx, solution1, direction, bcheck)) { status1 = Intersection3D::Status::missed; @@ -302,10 +302,9 @@ Acts::SurfaceIntersection Acts::ConeSurface::intersect( // Check the validity of the second solution Vector3 solution2 = position + qe.first * direction; - Intersection3D::Status status2 = - std::abs(qe.second) < std::abs(s_onSurfaceTolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; + Intersection3D::Status status2 = std::abs(qe.second) < std::abs(tolerance) + ? Intersection3D::Status::onSurface + : Intersection3D::Status::reachable; if (bcheck and not isOnSurface(gctx, solution2, direction, bcheck)) { status2 = Intersection3D::Status::missed; } diff --git a/Core/src/Surfaces/CylinderSurface.cpp b/Core/src/Surfaces/CylinderSurface.cpp index e83ddc8367a..d340b2565d9 100644 --- a/Core/src/Surfaces/CylinderSurface.cpp +++ b/Core/src/Surfaces/CylinderSurface.cpp @@ -210,7 +210,8 @@ Acts::detail::RealQuadraticEquation Acts::CylinderSurface::intersectionSolver( Acts::SurfaceIntersection Acts::CylinderSurface::intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const { + const Vector3& direction, const BoundaryCheck& bcheck, + ActsScalar tolerance) const { const auto& gctxTransform = transform(gctx); // Solve the quadratic equation @@ -223,10 +224,9 @@ Acts::SurfaceIntersection Acts::CylinderSurface::intersect( // Check the validity of the first solution Vector3 solution1 = position + qe.first * direction; - Intersection3D::Status status1 = - std::abs(qe.first) < std::abs(s_onSurfaceTolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; + Intersection3D::Status status1 = std::abs(qe.first) < std::abs(tolerance) + ? Intersection3D::Status::onSurface + : Intersection3D::Status::reachable; // Helper method for boundary check auto boundaryCheck = @@ -245,8 +245,8 @@ Acts::SurfaceIntersection Acts::CylinderSurface::intersect( // Create the reference vector in local const Vector3 vecLocal(solution - tMatrix.block<3, 1>(0, 3)); double cZ = vecLocal.dot(tMatrix.block<3, 1>(0, 2)); - double tolerance = s_onSurfaceTolerance + bcheck.tolerance()[eBoundLoc1]; - double hZ = cBounds.get(CylinderBounds::eHalfLengthZ) + tolerance; + double modifiedTolerance = tolerance + bcheck.tolerance()[eBoundLoc1]; + double hZ = cBounds.get(CylinderBounds::eHalfLengthZ) + modifiedTolerance; return std::abs(cZ) < std::abs(hZ) ? status : Intersection3D::Status::missed; } @@ -264,10 +264,9 @@ Acts::SurfaceIntersection Acts::CylinderSurface::intersect( } // Check the validity of the second solution Vector3 solution2 = position + qe.second * direction; - Intersection3D::Status status2 = - std::abs(qe.second) < std::abs(s_onSurfaceTolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; + Intersection3D::Status status2 = std::abs(qe.second) < std::abs(tolerance) + ? Intersection3D::Status::onSurface + : Intersection3D::Status::reachable; // Check first solution for boundary compatiblity status2 = boundaryCheck(solution2, status2); Intersection3D second(solution2, qe.second, status2); diff --git a/Core/src/Surfaces/DiscSurface.cpp b/Core/src/Surfaces/DiscSurface.cpp index c1e66899ae6..1b3382397e7 100644 --- a/Core/src/Surfaces/DiscSurface.cpp +++ b/Core/src/Surfaces/DiscSurface.cpp @@ -191,9 +191,8 @@ Acts::Vector2 Acts::DiscSurface::localPolarToCartesian( Acts::Vector2 Acts::DiscSurface::localCartesianToPolar( const Vector2& lcart) const { - return Vector2(sqrt(lcart[eBoundLoc0] * lcart[eBoundLoc0] + - lcart[eBoundLoc1] * lcart[eBoundLoc1]), - atan2(lcart[eBoundLoc1], lcart[eBoundLoc0])); + return Vector2(std::hypot(lcart[eBoundLoc0], lcart[eBoundLoc1]), + std::atan2(lcart[eBoundLoc1], lcart[eBoundLoc0])); } Acts::BoundToFreeMatrix Acts::DiscSurface::boundToFreeJacobian( @@ -251,7 +250,7 @@ Acts::FreeToBoundMatrix Acts::DiscSurface::freeToBoundJacobian( const double z = direction(2); // == cos(theta) // can be turned into cosine/sine const double cosTheta = z; - const double sinTheta = sqrt(x * x + y * y); + const double sinTheta = std::hypot(x, y); const double invSinTheta = 1. / sinTheta; const double cosPhi = x * invSinTheta; const double sinPhi = y * invSinTheta; @@ -287,12 +286,13 @@ Acts::FreeToBoundMatrix Acts::DiscSurface::freeToBoundJacobian( Acts::SurfaceIntersection Acts::DiscSurface::intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const { + const Vector3& direction, const BoundaryCheck& bcheck, + ActsScalar tolerance) const { // Get the contextual transform auto gctxTransform = transform(gctx); // Use the intersection helper for planar surfaces auto intersection = - PlanarHelper::intersect(gctxTransform, position, direction); + PlanarHelper::intersect(gctxTransform, position, direction, tolerance); // Evaluate boundary check if requested (and reachable) if (intersection.status != Intersection3D::Status::unreachable and bcheck and m_bounds != nullptr) { @@ -302,9 +302,9 @@ Acts::SurfaceIntersection Acts::DiscSurface::intersect( const Vector2 lcartesian = tMatrix.block<3, 2>(0, 0).transpose() * vecLocal; if (bcheck.type() == BoundaryCheck::Type::eAbsolute and m_bounds->coversFullAzimuth()) { - double tolerance = s_onSurfaceTolerance + bcheck.tolerance()[eBoundLoc0]; + double modifiedTolerance = tolerance + bcheck.tolerance()[eBoundLoc0]; if (not m_bounds->insideRadialBounds(VectorHelpers::perp(lcartesian), - tolerance)) { + modifiedTolerance)) { intersection.status = Intersection3D::Status::missed; } } else if (not insideBounds(localCartesianToPolar(lcartesian), bcheck)) { diff --git a/Core/src/Surfaces/IntersectionHelper2D.cpp b/Core/src/Surfaces/IntersectionHelper2D.cpp index 7d58bf72fc2..a8a4386c6b2 100644 --- a/Core/src/Surfaces/IntersectionHelper2D.cpp +++ b/Core/src/Surfaces/IntersectionHelper2D.cpp @@ -8,6 +8,7 @@ #include "Acts/Surfaces/detail/IntersectionHelper2D.hpp" +#include "Acts/Definitions/Tolerance.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/detail/RealQuadraticEquation.hpp" diff --git a/Core/src/Surfaces/LineSurface.cpp b/Core/src/Surfaces/LineSurface.cpp index 55c80a9a254..daeb7275452 100644 --- a/Core/src/Surfaces/LineSurface.cpp +++ b/Core/src/Surfaces/LineSurface.cpp @@ -8,6 +8,7 @@ #include "Acts/Surfaces/LineSurface.hpp" +#include "Acts/Definitions/Algebra.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" #include "Acts/Utilities/ThrowAssert.hpp" @@ -63,7 +64,7 @@ Acts::Vector3 Acts::LineSurface::localToGlobal(const GeometryContext& gctx, Acts::Result Acts::LineSurface::globalToLocal( const GeometryContext& gctx, const Vector3& position, - const Vector3& momentum, double /*tolerance*/) const { + const Vector3& momentum, double tolerance) const { using VectorHelpers::perp; const auto& sTransform = transform(gctx); const auto& tMatrix = sTransform.matrix(); @@ -77,6 +78,12 @@ Acts::Result Acts::LineSurface::globalToLocal( // assign the right sign double sign = ((lineDirection.cross(momentum)).dot(decVec) < 0.) ? -1. : 1.; lposition[eBoundLoc0] *= sign; + + if ((localToGlobal(gctx, lposition, momentum) - position).norm() > + tolerance) { + return Result::failure(SurfaceError::GlobalPositionNotOnSurface); + } + return Result::success(lposition); } @@ -126,7 +133,8 @@ const Acts::SurfaceBounds& Acts::LineSurface::bounds() const { Acts::SurfaceIntersection Acts::LineSurface::intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const { + const Vector3& direction, const BoundaryCheck& bcheck, + ActsScalar tolerance) const { // following nominclature found in header file and doxygen documentation // line one is the straight track const Vector3& ma = position; @@ -141,10 +149,10 @@ Acts::SurfaceIntersection Acts::LineSurface::intersect( double denom = 1 - eaTeb * eaTeb; // validity parameter Intersection3D::Status status = Intersection3D::Status::unreachable; - if (std::abs(denom) > std::abs(s_onSurfaceTolerance)) { + if (std::abs(denom) > std::abs(tolerance)) { double u = (mab.dot(ea) - mab.dot(eb) * eaTeb) / denom; // Check if we are on the surface already - status = std::abs(u) < std::abs(s_onSurfaceTolerance) + status = std::abs(u) < std::abs(tolerance) ? Intersection3D::Status::onSurface : Intersection3D::Status::reachable; Vector3 result = (ma + u * ea); @@ -154,11 +162,10 @@ Acts::SurfaceIntersection Acts::LineSurface::intersect( // At closest approach: check inside R or and inside Z const Vector3 vecLocal(result - mb); double cZ = vecLocal.dot(eb); - double hZ = - m_bounds->get(LineBounds::eHalfLengthZ) + s_onSurfaceTolerance; + double hZ = m_bounds->get(LineBounds::eHalfLengthZ) + tolerance; if ((std::abs(cZ) > std::abs(hZ)) or ((vecLocal - cZ * eb).norm() > - m_bounds->get(LineBounds::eR) + s_onSurfaceTolerance)) { + m_bounds->get(LineBounds::eR) + tolerance)) { status = Intersection3D::Status::missed; } } diff --git a/Core/src/Surfaces/PlaneSurface.cpp b/Core/src/Surfaces/PlaneSurface.cpp index a6cc404f3f1..286d9d2694b 100644 --- a/Core/src/Surfaces/PlaneSurface.cpp +++ b/Core/src/Surfaces/PlaneSurface.cpp @@ -172,12 +172,13 @@ double Acts::PlaneSurface::pathCorrection(const GeometryContext& gctx, Acts::SurfaceIntersection Acts::PlaneSurface::intersect( const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const { + const Vector3& direction, const BoundaryCheck& bcheck, + ActsScalar tolerance) const { // Get the contextual transform const auto& gctxTransform = transform(gctx); // Use the intersection helper for planar surfaces auto intersection = - PlanarHelper::intersect(gctxTransform, position, direction); + PlanarHelper::intersect(gctxTransform, position, direction, tolerance); // Evaluate boundary check if requested (and reachable) if (intersection.status != Intersection3D::Status::unreachable and bcheck) { // Built-in local to global for speed reasons diff --git a/Core/src/Surfaces/Surface.cpp b/Core/src/Surfaces/Surface.cpp index ed40c4b202a..ca785571c02 100644 --- a/Core/src/Surfaces/Surface.cpp +++ b/Core/src/Surfaces/Surface.cpp @@ -16,6 +16,10 @@ #include #include +std::array + Acts::Surface::s_surfaceTypeNames = { + "Cone", "Cylinder", "Disc", "Perigee", "Plane", "Straw", "Curvilinear"}; + Acts::Surface::Surface(const Transform3& transform) : GeometryObject(), m_transform(transform) {} diff --git a/Core/src/TrackFitting/GainMatrixUpdater.cpp b/Core/src/TrackFitting/GainMatrixUpdater.cpp index ca3111fac08..2a9255b2893 100644 --- a/Core/src/TrackFitting/GainMatrixUpdater.cpp +++ b/Core/src/TrackFitting/GainMatrixUpdater.cpp @@ -19,7 +19,7 @@ namespace Acts { std::tuple GainMatrixUpdater::visitMeasurement( - InternalTrackState trackState, NavigationDirection direction, + InternalTrackState trackState, Direction direction, const Logger& logger) const { // default-constructed error represents success, i.e. an invalid error code std::error_code error; @@ -55,7 +55,7 @@ std::tuple GainMatrixUpdater::visitMeasurement( ACTS_VERBOSE("Gain Matrix K:\n" << K); if (K.hasNaN()) { - error = (direction == NavigationDirection::Forward) + error = (direction == Direction::Forward) ? KalmanFitterError::ForwardUpdateFailed : KalmanFitterError::BackwardUpdateFailed; // set to error return false; // abort execution diff --git a/Core/src/TrackFitting/GsfError.cpp b/Core/src/TrackFitting/GsfError.cpp index 4493f27bde1..a5e98851a84 100644 --- a/Core/src/TrackFitting/GsfError.cpp +++ b/Core/src/TrackFitting/GsfError.cpp @@ -20,6 +20,8 @@ class GsfErrorCategory : public std::error_category { using Acts::Experimental::GsfError; switch (static_cast(c)) { + case GsfError::StartParametersHaveNoCovariance: + return "Start parameters have no Covariance"; case GsfError::StartParametersNotOnStartSurface: return "Start parameters don't lie in the start surface"; case GsfError::NoMeasurementStatesCreatedForward: diff --git a/Examples/Algorithms/Alignment/CMakeLists.txt b/Examples/Algorithms/Alignment/CMakeLists.txt index 4a2d9495454..59c7b1d9a08 100644 --- a/Examples/Algorithms/Alignment/CMakeLists.txt +++ b/Examples/Algorithms/Alignment/CMakeLists.txt @@ -9,7 +9,7 @@ target_link_libraries( ActsExamplesAlignment PUBLIC ActsCore ActsAlignment - ActsExamplesFramework ActsExamplesMagneticField) + ActsExamplesFramework ActsExamplesMagneticField) install( TARGETS ActsExamplesAlignment diff --git a/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp b/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp index 31dc01dea11..48dde02db75 100644 --- a/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp +++ b/Examples/Algorithms/Alignment/src/AlignmentAlgorithm.cpp @@ -11,6 +11,7 @@ #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/EventData/Trajectories.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" @@ -98,8 +99,10 @@ ActsExamples::ProcessCode ActsExamples::AlignmentAlgorithm::execute( Acts::Vector3{0., 0., 0.}); Acts::KalmanFitterExtensions extensions; - MeasurementCalibrator calibrator{measurements}; - extensions.calibrator.connect<&MeasurementCalibrator::calibrate>(&calibrator); + PassThroughCalibrator pcalibrator; + MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); + extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( + &calibrator); Acts::GainMatrixUpdater kfUpdater; Acts::GainMatrixSmoother kfSmoother; extensions.updater.connect< diff --git a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp index 4ae1a95dd3f..62a6132dddd 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp @@ -338,6 +338,15 @@ ActsExamples::DigitizationAlgorithm::localParameters( std::vector(dParameters.indices.size(), -1.); } + if (dParameters.variances[0] == -1) { + size_t ictr = b0min + size0 / 2; + dParameters.variances[0] = std::pow(binningData[0].width(ictr), 2) / 12.0; + } + if (dParameters.variances[1] == -1) { + size_t ictr = b1min + size1 / 2; + dParameters.variances[1] = std::pow(binningData[1].width(ictr), 2) / 12.0; + } + dParameters.cluster.sizeLoc0 = size0; dParameters.cluster.sizeLoc1 = size1; } diff --git a/Examples/Algorithms/Geant4/CMakeLists.txt b/Examples/Algorithms/Geant4/CMakeLists.txt index 74fa53073b4..e5b1acebe5f 100644 --- a/Examples/Algorithms/Geant4/CMakeLists.txt +++ b/Examples/Algorithms/Geant4/CMakeLists.txt @@ -9,7 +9,8 @@ set(ACTS_EXAMPLES_G4SOURCES src/ParticleTrackingAction.cpp src/SensitiveSurfaceMapper.cpp src/SensitiveSteppingAction.cpp - src/SimParticleTranslation.cpp) + src/SimParticleTranslation.cpp + src/ParticleKillAction.cpp) if (ACTS_BUILD_EXAMPLES_DD4HEP) list(APPEND ACTS_EXAMPLES_G4SOURCES src/DDG4DetectorConstruction.cpp) diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ActsSteppingActionList.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ActsSteppingActionList.hpp new file mode 100644 index 00000000000..af9972ac39a --- /dev/null +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ActsSteppingActionList.hpp @@ -0,0 +1,45 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" + +#include +#include +#include + +#include + +namespace ActsExamples { + +/// Geant4 only allows one user action of each type. This simple wrapper +/// dispatches multiple actions to Geant4. +class ActsSteppingActionList : public G4UserSteppingAction { + public: + struct Config { + std::vector actions; + }; + + ActsSteppingActionList(const Config &cfg) : m_cfg(cfg) {} + + void UserSteppingAction(const G4Step *step) override { + for (auto action : m_cfg.actions) { + if (action) { + action->UserSteppingAction(step); + } + } + } + + private: + Config m_cfg; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/EventStoreRegistry.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/EventStoreRegistry.hpp index d90492dc5c5..3d3d89b954d 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/EventStoreRegistry.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/EventStoreRegistry.hpp @@ -14,9 +14,12 @@ #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsFatras/EventData/Barcode.hpp" +#include #include #include +#include "G4Types.hh" + namespace ActsExamples { class WhiteBoard; @@ -34,24 +37,44 @@ class EventStoreRegistry { struct State { /// The current event store WhiteBoard* store = nullptr; + + /// Use a std::set here because it allows for fast insertion and ensures + /// uniqueness. Thus particle collisions are detected early. + using ParticleContainer = + std::set; + /// Initial and final particle collections - SimParticleContainer::sequence_type particlesInitial; - SimParticleContainer::sequence_type particlesFinal; + ParticleContainer particlesInitial; + ParticleContainer particlesFinal; + /// The hits in sensitive detectors SimHitContainer::sequence_type hits; + /// Tracks recorded in material mapping std::unordered_map materialTracks; + /// Particle hit count (for hit indexing) std::unordered_map particleHitCount; - /// Track ID to Barcode mapping - std::unordered_map trackIdMapping; - /// Track ID to root Track ID mapping - std::unordered_map trackIdRootId; - /// Track ID generation counter - std::unordered_map trackIdGenerationCount; + /// Geant4 Track ID to Barcode mapping + std::unordered_map trackIdMapping; + /// Geant4 Track ID subparticle counter (for subparticle indexing) + std::unordered_map trackIdSubparticleCount; /// Data handles to read particles from the whiteboard const ReadDataHandle* inputParticles{nullptr}; + + /// Count particle ID collisions + std::size_t particleIdCollisionsInitial = 0; + std::size_t particleIdCollisionsFinal = 0; + std::size_t parentIdNotFound = 0; + + /// Store subparticle count for {primVertex, secVertex, part, gen} + /// This is done using a pseudo-barcode that contains all fields but not the + /// subparticle counter. This can be used as key in a map to store the + /// subparticle information + using BarcodeWithoutSubparticle = + Acts::MultiIndex; + std::unordered_map subparticleMap; }; EventStoreRegistry() = delete; diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp index 37b9facd01b..dad245698d6 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/Geant4Simulation.hpp @@ -77,17 +77,17 @@ class Geant4Simulation final : public IAlgorithm { /// User Action: Primary generator action of the simulation G4VUserPrimaryGeneratorAction* primaryGeneratorAction = nullptr; - /// User Actions: Run - std::vector runActions = {}; + /// User Action: Run + G4UserRunAction* runAction = nullptr; - /// User Actions: Event - std::vector eventActions = {}; + /// User Action: Event + G4UserEventAction* eventAction = nullptr; - /// User Actions: Tracking - std::vector trackingActions = {}; + /// User Action: Tracking + G4UserTrackingAction* trackingAction = nullptr; - /// User Actions: Stepping - std::vector steppingActions = {}; + /// User Action: Stepping + G4UserSteppingAction* steppingAction = nullptr; /// Detector construction object. G4VUserDetectorConstruction* detectorConstruction = nullptr; diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleKillAction.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleKillAction.hpp new file mode 100644 index 00000000000..b79c9ce2b02 --- /dev/null +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleKillAction.hpp @@ -0,0 +1,54 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/Volume.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +#include + +namespace ActsExamples { + +/// A G4SteppingAction that is called for every step in +/// the simulation process. +/// +/// It checks whether the particle can be killed according when its position +/// exceeds the configured values for |z| or r. +class ParticleKillAction : public G4UserSteppingAction { + public: + /// Configuration of the Stepping action + struct Config { + std::shared_ptr volume; + }; + + /// Construct the stepping action + /// + /// @param cfg the configuration struct + /// @param logger the ACTS logging instance + ParticleKillAction(const Config& cfg, + std::unique_ptr logger = + Acts::getDefaultLogger("ParticleKillAction", + Acts::Logging::INFO)); + ~ParticleKillAction() override = default; + + /// @brief Called every step, conditionally sets the tracking state to `fStopAndKill` + /// @param step is the Geant4 step of the particle + void UserSteppingAction(const G4Step* step) override; + + private: + const Acts::Logger& logger() const { return *m_logger; } + + Config m_cfg; + std::unique_ptr m_logger; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleTrackingAction.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleTrackingAction.hpp index 0cf837c66bd..4c9b2e301ae 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleTrackingAction.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/ParticleTrackingAction.hpp @@ -9,9 +9,11 @@ #pragma once #include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include +#include #include #include @@ -25,7 +27,9 @@ namespace ActsExamples { /// It records the initial and final particle state class ParticleTrackingAction : public G4UserTrackingAction { public: - struct Config {}; + struct Config { + bool keepParticlesWithoutHits = true; + }; /// Construct the stepping action /// @@ -56,7 +60,11 @@ class ParticleTrackingAction : public G4UserTrackingAction { /// Convert a G4Track to a SimParticle /// /// @param aTrack the current Geant4 track - SimParticle convert(const G4Track& aTrack) const; + /// @param particleId the particle ID the particle will have + SimParticle convert(const G4Track& aTrack, SimBarcode particleId) const; + + /// Make the particle id + std::optional makeParticleId(G4int trackId, G4int parentId) const; /// Private access method to the logging instance const Acts::Logger& logger() const { return *m_logger; } diff --git a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSteppingAction.hpp b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSteppingAction.hpp index 879aa28d824..2bbf8fdfb55 100644 --- a/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSteppingAction.hpp +++ b/Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSteppingAction.hpp @@ -30,7 +30,7 @@ class SensitiveSteppingAction : public G4UserSteppingAction { bool charged = true; bool neutral = false; bool primary = true; - bool secondary = false; + bool secondary = true; }; /// Construct the stepping action diff --git a/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp b/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp index d05aaf53153..b6ff6786d27 100644 --- a/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp +++ b/Examples/Algorithms/Geant4/src/Geant4Simulation.cpp @@ -34,20 +34,6 @@ #include #include -namespace { -/// Helper method to add the user actions -/// @tparam manager_t the run manager type -/// @tparam actions_t the actions iterable list -template -void setUserActions(manager_t& manager, actions_t& actions) { - for (const auto& action : actions) { - if (action != nullptr) { - manager.SetUserAction(action); - } - } -} -} // namespace - ActsExamples::Geant4Simulation::Geant4Simulation( const ActsExamples::Geant4Simulation::Config& config, Acts::Logging::Level level) @@ -111,10 +97,10 @@ ActsExamples::Geant4Simulation::Geant4Simulation( m_cfg.runManager->SetUserAction(m_cfg.primaryGeneratorAction); // Set the configured user actions - setUserActions(*m_cfg.runManager, m_cfg.runActions); - setUserActions(*m_cfg.runManager, m_cfg.eventActions); - setUserActions(*m_cfg.runManager, m_cfg.trackingActions); - setUserActions(*m_cfg.runManager, m_cfg.steppingActions); + m_cfg.runManager->SetUserAction(m_cfg.runAction); + m_cfg.runManager->SetUserAction(m_cfg.eventAction); + m_cfg.runManager->SetUserAction(m_cfg.trackingAction); + m_cfg.runManager->SetUserAction(m_cfg.steppingAction); // Initialize the Geant4 run manager m_cfg.runManager->Initialize(); @@ -179,16 +165,27 @@ ActsExamples::ProcessCode ActsExamples::Geant4Simulation::execute( // Start simulation. each track is simulated as a separate Geant4 event. m_cfg.runManager->BeamOn(1); + // Since these are std::set, this ensures that each particle is in both sets + assert(eventData.particlesInitial.size() == eventData.particlesFinal.size()); + + // Print out warnings about possible particle collision if happened + if (eventData.particleIdCollisionsInitial > 0 or + eventData.particleIdCollisionsFinal > 0 or + eventData.parentIdNotFound > 0) { + ACTS_WARNING( + "Particle ID collisions detected, don't trust the particle " + "identification!"); + ACTS_WARNING("- initial states: " << eventData.particleIdCollisionsInitial); + ACTS_WARNING("- final states: " << eventData.particleIdCollisionsFinal); + ACTS_WARNING("- parent ID not found: " << eventData.parentIdNotFound); + } + // Output handling: Initial/Final particles if (not m_cfg.outputParticlesInitial.empty() and not m_cfg.outputParticlesFinal.empty()) { - // Initial state of particles - // Register to the event store m_outputParticlesInitial( ctx, SimParticleContainer(eventData.particlesInitial.begin(), eventData.particlesInitial.end())); - // Final state of particles - // Register to the event store m_outputParticlesFinal( ctx, SimParticleContainer(eventData.particlesFinal.begin(), eventData.particlesFinal.end())); @@ -196,9 +193,16 @@ ActsExamples::ProcessCode ActsExamples::Geant4Simulation::execute( // Output handling: Simulated hits if (not m_cfg.outputSimHits.empty()) { - // Register to the event store +#if BOOST_VERSION < 107800 + SimHitContainer container; + for (const auto& hit : eventData.hits) { + container.insert(hit); + } + m_outputSimHits(ctx, std::move(container)); +#else m_outputSimHits( ctx, SimHitContainer(eventData.hits.begin(), eventData.hits.end())); +#endif } // Output handling: Material tracks diff --git a/Examples/Algorithms/Geant4/src/ParticleKillAction.cpp b/Examples/Algorithms/Geant4/src/ParticleKillAction.cpp new file mode 100644 index 00000000000..b0e0788ac87 --- /dev/null +++ b/Examples/Algorithms/Geant4/src/ParticleKillAction.cpp @@ -0,0 +1,38 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Geant4/ParticleKillAction.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "ActsExamples/Geant4/EventStoreRegistry.hpp" +#include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp" + +#include +#include +#include +#include +#include +#include + +ActsExamples::ParticleKillAction::ParticleKillAction( + const Config& cfg, std::unique_ptr logger) + : G4UserSteppingAction(), m_cfg(cfg), m_logger(std::move(logger)) {} + +void ActsExamples::ParticleKillAction::UserSteppingAction(const G4Step* step) { + constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm; + G4Track* track = step->GetTrack(); + + const auto pos = convertLength * track->GetPosition(); + + if (m_cfg.volume and + not m_cfg.volume->inside(Acts::Vector3{pos.x(), pos.y(), pos.z()})) { + ACTS_DEBUG("Kill track with internal track ID " << track->GetTrackID() + << " at " << pos); + track->SetTrackStatus(G4TrackStatus::fStopAndKill); + } +} diff --git a/Examples/Algorithms/Geant4/src/ParticleTrackingAction.cpp b/Examples/Algorithms/Geant4/src/ParticleTrackingAction.cpp index 663f77204ff..e2297e7a3e3 100644 --- a/Examples/Algorithms/Geant4/src/ParticleTrackingAction.cpp +++ b/Examples/Algorithms/Geant4/src/ParticleTrackingAction.cpp @@ -25,19 +25,66 @@ ActsExamples::ParticleTrackingAction::ParticleTrackingAction( void ActsExamples::ParticleTrackingAction::PreUserTrackingAction( const G4Track* aTrack) { auto& eventData = EventStoreRegistry::eventData(); - eventData.particlesInitial.push_back(convert(*aTrack)); + + auto particleId = makeParticleId(aTrack->GetTrackID(), aTrack->GetParentID()); + + // There is already a warning printed in the makeParticleId function + if (not particleId) { + return; + } + + auto [it, success] = + eventData.particlesInitial.insert(convert(*aTrack, *particleId)); + + // Only register particle at the initial state AND if there is no particle ID + // collision + if (success) { + eventData.trackIdMapping[aTrack->GetTrackID()] = *particleId; + } else { + eventData.particleIdCollisionsInitial++; + ACTS_WARNING("Particle ID collision with " + << *particleId + << " detected for initial particles. Skip particle"); + } } void ActsExamples::ParticleTrackingAction::PostUserTrackingAction( const G4Track* aTrack) { auto& eventData = EventStoreRegistry::eventData(); - eventData.particlesFinal.push_back(convert(*aTrack)); + + // The initial particle maybe was not registered because a particle ID + // collision + if (eventData.trackIdMapping.find(aTrack->GetTrackID()) == + eventData.trackIdMapping.end()) { + return; + } + + const auto barcode = eventData.trackIdMapping.at(aTrack->GetTrackID()); + + auto hasHits = eventData.particleHitCount.find(barcode) != + eventData.particleHitCount.end() and + eventData.particleHitCount.at(barcode) > 0; + + if (not m_cfg.keepParticlesWithoutHits and not hasHits) { + auto n = eventData.particlesInitial.erase( + ActsExamples::SimParticle{barcode, Acts::PdgParticle::eInvalid}); + assert(n == 1); + return; + } + + auto particle = convert(*aTrack, barcode); + auto [it, success] = eventData.particlesFinal.insert(particle); + + if (not success) { + eventData.particleIdCollisionsFinal++; + ACTS_WARNING("Particle ID collision with " + << particle.particleId() + << " detected for final particles. Skip particle"); + } } ActsExamples::SimParticle ActsExamples::ParticleTrackingAction::convert( - const G4Track& aTrack) const { - auto& eventData = EventStoreRegistry::eventData(); - + const G4Track& aTrack, SimBarcode particleId) const { // Unit conversions G4->::ACTS constexpr double convertTime = Acts::UnitConstants::s / CLHEP::s; constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm; @@ -48,33 +95,11 @@ ActsExamples::SimParticle ActsExamples::ParticleTrackingAction::convert( G4int pdg = particleDef->GetPDGEncoding(); G4double charge = particleDef->GetPDGCharge(); G4double mass = particleDef->GetPDGMass(); - G4int id = aTrack.GetTrackID(); - G4int parentId = aTrack.GetParentID(); G4ThreeVector pPosition = convertLength * aTrack.GetPosition(); G4double pTime = convertTime * aTrack.GetGlobalTime(); G4ThreeVector pDirection = aTrack.GetMomentumDirection(); G4double p = convertEnergy * aTrack.GetKineticEnergy(); - if (parentId != 0) { - eventData.trackIdRootId[id] = eventData.trackIdRootId[parentId]; - } else { - eventData.trackIdRootId[id] = id; - } - - SimBarcode particleId; - if (eventData.trackIdMapping.find(id) != eventData.trackIdMapping.end()) { - particleId = eventData.trackIdMapping[id]; - } else { - if (eventData.trackIdRootId.find(id) != eventData.trackIdRootId.end()) { - auto rootId = eventData.trackIdRootId[id]; - particleId = eventData.trackIdMapping[rootId]; - particleId.setGeneration(++eventData.trackIdGenerationCount[rootId]); - eventData.trackIdMapping[id] = particleId; - } else { - ACTS_WARNING("could not find parent " << parentId << " of " << id); - } - } - // Now create the Particle ActsExamples::SimParticle aParticle(particleId, Acts::PdgParticle(pdg), charge, mass); @@ -83,3 +108,33 @@ ActsExamples::SimParticle ActsExamples::ParticleTrackingAction::convert( aParticle.setAbsoluteMomentum(p); return aParticle; } + +std::optional +ActsExamples::ParticleTrackingAction::makeParticleId(G4int trackId, + G4int parentId) const { + auto& ed = EventStoreRegistry::eventData(); + + // We already have this particle registered (it is one of the input particles + // or we are making a final particle state) + if (ed.trackIdMapping.find(trackId) != ed.trackIdMapping.end()) { + return ed.trackIdMapping.at(trackId); + } + + if (ed.trackIdMapping.find(parentId) == ed.trackIdMapping.end()) { + ACTS_DEBUG("Parent particle " << parentId + << " not registered, cannot build barcode"); + ed.parentIdNotFound++; + return std::nullopt; + } + + auto pid = ed.trackIdMapping.at(parentId).makeDescendant(); + + auto key = EventStoreRegistry::State::BarcodeWithoutSubparticle::Zeros(); + key.set(0, pid.vertexPrimary()) + .set(1, pid.vertexSecondary()) + .set(2, pid.particle()) + .set(3, pid.generation()); + pid.setSubParticle(++ed.subparticleMap[key]); + + return pid; +} diff --git a/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp b/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp index a758730686b..69c70124a13 100644 --- a/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp +++ b/Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp @@ -65,57 +65,63 @@ void ActsExamples::SensitiveSteppingAction::UserSteppingAction( std::string mappingPfx(SensitiveSurfaceMapper::mappingPrefix); - if (volumeName.find(mappingPfx) != std::string::npos) { - ACTS_VERBOSE("Step in senstive volume " << volumeName); - - // Get PreStepPoint and PostStepPoint - G4StepPoint* preStepPoint = step->GetPreStepPoint(); - G4StepPoint* postStepPoint = step->GetPostStepPoint(); - - G4ThreeVector preStepPosition = convertLength * preStepPoint->GetPosition(); - G4double preStepTime = convertTime * preStepPoint->GetGlobalTime(); - G4ThreeVector postStepPosition = - convertLength * postStepPoint->GetPosition(); - G4double postStepTime = convertTime * postStepPoint->GetGlobalTime(); - - G4ThreeVector preStepMomentum = convertEnergy * preStepPoint->GetMomentum(); - G4double preStepEnergy = convertEnergy * preStepPoint->GetTotalEnergy(); - G4ThreeVector postStepMomentum = - convertEnergy * postStepPoint->GetMomentum(); - G4double postStepEnergy = convertEnergy * postStepPoint->GetTotalEnergy(); - - Acts::ActsScalar hX = 0.5 * (preStepPosition[0] + postStepPosition[0]); - Acts::ActsScalar hY = 0.5 * (preStepPosition[1] + postStepPosition[1]); - Acts::ActsScalar hZ = 0.5 * (preStepPosition[2] + postStepPosition[2]); - Acts::ActsScalar hT = 0.5 * (preStepTime + postStepTime); - - Acts::ActsScalar mXpre = preStepMomentum[0]; - Acts::ActsScalar mYpre = preStepMomentum[1]; - Acts::ActsScalar mZpre = preStepMomentum[2]; - Acts::ActsScalar mEpre = preStepEnergy; - Acts::ActsScalar mXpost = postStepMomentum[0]; - Acts::ActsScalar mYpost = postStepMomentum[1]; - Acts::ActsScalar mZpost = postStepMomentum[2]; - Acts::ActsScalar mEpost = postStepEnergy; - - // Cast out the GeometryIdentifier - volumeName.erase(0, mappingPfx.size()); - - Acts::GeometryIdentifier::Value sGeoVal = std::stoul(volumeName); - Acts::GeometryIdentifier geoID(sGeoVal); - - auto particleID = eventData.trackIdMapping[track->GetTrackID()]; - - Acts::Vector4 particlePosition(hX, hY, hZ, hT); - Acts::Vector4 beforeMomentum(mXpre, mYpre, mZpre, mEpre); - Acts::Vector4 afterMomentum(mXpost, mYpost, mZpost, mEpost); - - // Increase counter (starts at 1 because of ++) - ++eventData.particleHitCount[particleID]; - - // Fill into the registry (subtract 1 from hit-count to get 0-based index) - eventData.hits.emplace_back(geoID, particleID, particlePosition, - beforeMomentum, afterMomentum, - eventData.particleHitCount[particleID] - 1); + if (volumeName.find(mappingPfx) == std::string::npos) { + return; + } + + ACTS_VERBOSE("Step in senstive volume " << volumeName); + + // Get PreStepPoint and PostStepPoint + G4StepPoint* preStepPoint = step->GetPreStepPoint(); + G4StepPoint* postStepPoint = step->GetPostStepPoint(); + + G4ThreeVector preStepPosition = convertLength * preStepPoint->GetPosition(); + G4double preStepTime = convertTime * preStepPoint->GetGlobalTime(); + G4ThreeVector postStepPosition = convertLength * postStepPoint->GetPosition(); + G4double postStepTime = convertTime * postStepPoint->GetGlobalTime(); + + G4ThreeVector preStepMomentum = convertEnergy * preStepPoint->GetMomentum(); + G4double preStepEnergy = convertEnergy * preStepPoint->GetTotalEnergy(); + G4ThreeVector postStepMomentum = convertEnergy * postStepPoint->GetMomentum(); + G4double postStepEnergy = convertEnergy * postStepPoint->GetTotalEnergy(); + + Acts::ActsScalar hX = 0.5 * (preStepPosition[0] + postStepPosition[0]); + Acts::ActsScalar hY = 0.5 * (preStepPosition[1] + postStepPosition[1]); + Acts::ActsScalar hZ = 0.5 * (preStepPosition[2] + postStepPosition[2]); + Acts::ActsScalar hT = 0.5 * (preStepTime + postStepTime); + + Acts::ActsScalar mXpre = preStepMomentum[0]; + Acts::ActsScalar mYpre = preStepMomentum[1]; + Acts::ActsScalar mZpre = preStepMomentum[2]; + Acts::ActsScalar mEpre = preStepEnergy; + Acts::ActsScalar mXpost = postStepMomentum[0]; + Acts::ActsScalar mYpost = postStepMomentum[1]; + Acts::ActsScalar mZpost = postStepMomentum[2]; + Acts::ActsScalar mEpost = postStepEnergy; + + // Cast out the GeometryIdentifier + volumeName.erase(0, mappingPfx.size()); + + Acts::GeometryIdentifier::Value sGeoVal = std::stoul(volumeName); + Acts::GeometryIdentifier geoID(sGeoVal); + + // This is not the case if we have a particle-ID collision + if (eventData.trackIdMapping.find(track->GetTrackID()) == + eventData.trackIdMapping.end()) { + return; } + + auto particleID = eventData.trackIdMapping.at(track->GetTrackID()); + + Acts::Vector4 particlePosition(hX, hY, hZ, hT); + Acts::Vector4 beforeMomentum(mXpre, mYpre, mZpre, mEpre); + Acts::Vector4 afterMomentum(mXpost, mYpost, mZpost, mEpost); + + // Increase counter (starts at 1 because of ++) + ++eventData.particleHitCount[particleID]; + + // Fill into the registry (subtract 1 from hit-count to get 0-based index) + eventData.hits.emplace_back(geoID, particleID, particlePosition, + beforeMomentum, afterMomentum, + eventData.particleHitCount.at(particleID) - 1); } diff --git a/Examples/Algorithms/Geant4/src/SimParticleTranslation.cpp b/Examples/Algorithms/Geant4/src/SimParticleTranslation.cpp index 5958c96ac1a..e9826864bcf 100644 --- a/Examples/Algorithms/Geant4/src/SimParticleTranslation.cpp +++ b/Examples/Algorithms/Geant4/src/SimParticleTranslation.cpp @@ -55,10 +55,6 @@ void ActsExamples::SimParticleTranslation::GeneratePrimaries(G4Event* anEvent) { // Get the number of input particles const auto inputParticles = (*eventData.inputParticles)(*eventStore); - // Reserve appropriate resources for initial/final particles - eventData.particlesInitial.reserve(inputParticles.size()); - eventData.particlesFinal.reserve(inputParticles.size()); - // Reserve hopefully enough hit space eventData.hits.reserve(inputParticles.size() * m_cfg.reserveHitsPerParticle); @@ -120,7 +116,7 @@ void ActsExamples::SimParticleTranslation::GeneratePrimaries(G4Event* anEvent) { // Skip if tranlation failed if (particleDefinition == nullptr) { - ACTS_VERBOSE( + ACTS_DEBUG( "Could not translate particle with PDG code : " << particlePdgCode); continue; } diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index 0cee68f4ac7..0f45650ad45 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -58,7 +58,8 @@ class TrackFindingAlgorithm final : public IAlgorithm { /// contains shared_ptr anyways. static std::shared_ptr makeTrackFinderFunction( std::shared_ptr trackingGeometry, - std::shared_ptr magneticField); + std::shared_ptr magneticField, + const Acts::Logger& logger); struct Config { /// Input measurements collection. diff --git a/Examples/Algorithms/TrackFinding/src/AmbiguityResolutionAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/AmbiguityResolutionAlgorithm.cpp index d1742807fc3..2ed25a0038e 100644 --- a/Examples/Algorithms/TrackFinding/src/AmbiguityResolutionAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/AmbiguityResolutionAlgorithm.cpp @@ -56,6 +56,7 @@ struct State { State computeInitialState(const ActsExamples::ConstTrackContainer& tracks, std::size_t nMeasurementsMin) { State state; + for (const auto& track : tracks) { auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState( tracks.trackStateContainer(), track.tipIndex()); @@ -120,8 +121,13 @@ void removeTrack(State& state, std::size_t iTrack) { ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionAlgorithm::execute( const AlgorithmContext& ctx) const { const auto& tracks = m_inputTracks(ctx); + + ACTS_VERBOSE("number of input tracks " << tracks.size()); + auto state = computeInitialState(tracks, m_cfg.nMeasurementsMin); + ACTS_VERBOSE("state initialized"); + auto sharedMeasurementsComperator = [&state](std::size_t a, std::size_t b) { return state.sharedMeasurementsPerTrack[a] < state.sharedMeasurementsPerTrack[b]; @@ -139,6 +145,11 @@ ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionAlgorithm::execute( }; for (std::size_t i = 0; i < m_cfg.maximumIterations; ++i) { + if (state.selectedTracks.empty()) { + ACTS_VERBOSE("no tracks left - exit loop"); + break; + } + auto maximumSharedMeasurements = *std::max_element( state.selectedTracks.begin(), state.selectedTracks.end(), sharedMeasurementsComperator); @@ -158,7 +169,7 @@ ActsExamples::ProcessCode ActsExamples::AmbiguityResolutionAlgorithm::execute( } ACTS_INFO("Resolved to " << state.selectedTracks.size() << " tracks from " - << state.trackTips.size()); + << tracks.size()); std::shared_ptr trackStateContainer = tracks.trackStateContainerHolder(); diff --git a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp index 36d17f6737f..5acafdfacfa 100644 --- a/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/SeedingAlgorithm.cpp @@ -70,21 +70,25 @@ ActsExamples::SeedingAlgorithm::SeedingAlgorithm( throw std::invalid_argument("Inconsistent config deltaRMax"); } - static_assert(std::numeric_limits::has_quiet_NaN, - "Value of deltaRMaxTopSP must support NaN values"); - - static_assert(std::numeric_limits::has_quiet_NaN, - "Value of deltaRMinTopSP must support NaN values"); - - static_assert(std::numeric_limits::has_quiet_NaN, - "Value of deltaRMaxBottomSP must support NaN values"); - - static_assert(std::numeric_limits::has_quiet_NaN, - "Value of deltaRMinBottomSP must support NaN values"); + static_assert( + std::numeric_limits< + decltype(m_cfg.seedFinderConfig.deltaRMaxTopSP)>::has_quiet_NaN, + "Value of deltaRMaxTopSP must support NaN values"); + + static_assert( + std::numeric_limits< + decltype(m_cfg.seedFinderConfig.deltaRMinTopSP)>::has_quiet_NaN, + "Value of deltaRMinTopSP must support NaN values"); + + static_assert( + std::numeric_limits< + decltype(m_cfg.seedFinderConfig.deltaRMaxBottomSP)>::has_quiet_NaN, + "Value of deltaRMaxBottomSP must support NaN values"); + + static_assert( + std::numeric_limits< + decltype(m_cfg.seedFinderConfig.deltaRMinBottomSP)>::has_quiet_NaN, + "Value of deltaRMinBottomSP must support NaN values"); if (std::isnan(m_cfg.seedFinderConfig.deltaRMaxTopSP)) { m_cfg.seedFinderConfig.deltaRMaxTopSP = m_cfg.seedFinderConfig.deltaRMax; diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index b60ee65b257..d62df237582 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -16,6 +16,7 @@ #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" @@ -63,14 +64,16 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( Acts::PropagatorPlainOptions pOptions; pOptions.maxSteps = 100000; - MeasurementCalibrator calibrator{measurements}; + PassThroughCalibrator pcalibrator; + MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); Acts::GainMatrixUpdater kfUpdater; Acts::GainMatrixSmoother kfSmoother; Acts::MeasurementSelector measSel{m_cfg.measurementSelectorCfg}; Acts::CombinatorialKalmanFilterExtensions extensions; - extensions.calibrator.connect<&MeasurementCalibrator::calibrate>(&calibrator); + extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( + &calibrator); extensions.updater.connect< &Acts::GainMatrixUpdater::operator()>( &kfUpdater); diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp index b45c94de9cc..e5f1a757ff8 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithmFunction.cpp @@ -54,7 +54,8 @@ struct TrackFinderFunctionImpl std::shared_ptr ActsExamples::TrackFindingAlgorithm::makeTrackFinderFunction( std::shared_ptr trackingGeometry, - std::shared_ptr magneticField) { + std::shared_ptr magneticField, + const Acts::Logger& logger) { Stepper stepper(std::move(magneticField)); Navigator::Config cfg{std::move(trackingGeometry)}; cfg.resolvePassive = false; @@ -62,7 +63,7 @@ ActsExamples::TrackFindingAlgorithm::makeTrackFinderFunction( cfg.resolveSensitive = true; Navigator navigator(cfg); Propagator propagator(std::move(stepper), std::move(navigator)); - CKF trackFinder(std::move(propagator)); + CKF trackFinder(std::move(propagator), logger.cloneWithSuffix("CKF")); // build the track finder functions. owns the track finder object. return std::make_shared(std::move(trackFinder)); diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp index f732acdc32c..1115ae4038d 100644 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp +++ b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp @@ -21,6 +21,7 @@ #include "Acts/TrackFitting/BetheHeitlerApprox.hpp" #include "Acts/Utilities/CalibrationContext.hpp" #include "ActsExamples/EventData/Measurement.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/TrackFitting/RefittingCalibrator.hpp" @@ -46,7 +47,7 @@ class TrackFitterFunction { virtual TrackFitterResult operator()(const std::vector&, const TrackParameters&, const GeneralFitterOptions&, - const MeasurementCalibrator&, + const MeasurementCalibratorAdapter&, TrackContainer&) const = 0; virtual TrackFitterResult operator()(const std::vector&, @@ -87,7 +88,7 @@ std::shared_ptr makeGsfFitterFunction( std::shared_ptr trackingGeometry, std::shared_ptr magneticField, BetheHeitlerApprox betheHeitlerApprox, std::size_t maxComponents, - double weightCutoff, Acts::FinalReductionMethod finalReductionMethod, + double weightCutoff, Acts::MixtureReductionMethod finalReductionMethod, bool abortOnError, bool disableAllMaterialHandling, const Acts::Logger& logger); diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp index db2e3399e18..a6150a6281f 100644 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp @@ -8,6 +8,7 @@ #pragma once +#include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" @@ -33,12 +34,16 @@ class TrackFittingAlgorithm final : public IAlgorithm { std::string inputProtoTracks; /// Input initial track parameter estimates for for each proto track. std::string inputInitialTrackParameters; + /// (optional) Input clusters for each measurement + std::string inputClusters; /// Output fitted tracks collection. std::string outputTracks; /// Type erased fitter function. std::shared_ptr fit; - /// Tracking geometry for surface lookup + /// Pick a single track for debugging (-1 process all tracks) int pickTrack = -1; + // Type erased calibrator for the measurements + std::shared_ptr calibrator; }; /// Constructor of the fitting algorithm @@ -68,6 +73,8 @@ class TrackFittingAlgorithm final : public IAlgorithm { ReadDataHandle m_inputInitialTrackParameters{ this, "InputInitialTrackParameters"}; + ReadDataHandle m_inputClusters{this, "InputClusters"}; + WriteDataHandle m_outputTracks{this, "OutputTracks"}; }; diff --git a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp index b6f004595ba..4cfaa9d6634 100644 --- a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp +++ b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp @@ -54,6 +54,8 @@ struct GsfFitterFunctionImpl final : public ActsExamples::TrackFitterFunction { double weightCutoff = 0; bool abortOnError = false; bool disableAllMaterialHandling = false; + Acts::MixtureReductionMethod reductionMethod = + Acts::MixtureReductionMethod::eMaxWeight; GsfFitterFunctionImpl(Fitter&& f, DirectFitter&& df) : fitter(std::move(f)), directFitter(std::move(df)) {} @@ -87,7 +89,7 @@ struct GsfFitterFunctionImpl final : public ActsExamples::TrackFitterFunction { TrackFitterResult operator()(const std::vector& sourceLinks, const TrackParameters& initialParameters, const GeneralFitterOptions& options, - const MeasurementCalibrator& calibrator, + const MeasurementCalibratorAdapter& calibrator, TrackContainer& tracks) const override { const auto gsfOptions = makeGsfOptions(options, calibrator); @@ -130,7 +132,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( std::shared_ptr trackingGeometry, std::shared_ptr magneticField, BetheHeitlerApprox betheHeitlerApprox, std::size_t maxComponents, - double weightCutoff, Acts::FinalReductionMethod finalReductionMethod, + double weightCutoff, Acts::MixtureReductionMethod finalReductionMethod, bool abortOnError, bool disableAllMaterialHandling, const Acts::Logger& logger) { // Standard fitter @@ -166,6 +168,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( fitterFunction->weightCutoff = weightCutoff; fitterFunction->abortOnError = abortOnError; fitterFunction->disableAllMaterialHandling = disableAllMaterialHandling; + fitterFunction->reductionMethod = finalReductionMethod; return fitterFunction; } diff --git a/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp index b125fe5952e..7553675ce43 100644 --- a/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp +++ b/Examples/Algorithms/TrackFitting/src/KalmanFitterFunction.cpp @@ -94,7 +94,7 @@ struct KalmanFitterFunctionImpl final : public TrackFitterFunction { TrackFitterResult operator()(const std::vector& sourceLinks, const TrackParameters& initialParameters, const GeneralFitterOptions& options, - const MeasurementCalibrator& calibrator, + const MeasurementCalibratorAdapter& calibrator, TrackContainer& tracks) const override { const auto kfOptions = makeKfOptions(options, calibrator); return fitter.fit(sourceLinks.begin(), sourceLinks.end(), initialParameters, diff --git a/Examples/Algorithms/TrackFitting/src/RefittingCalibrator.cpp b/Examples/Algorithms/TrackFitting/src/RefittingCalibrator.cpp index 99a8d99634f..b0ad1148d7b 100644 --- a/Examples/Algorithms/TrackFitting/src/RefittingCalibrator.cpp +++ b/Examples/Algorithms/TrackFitting/src/RefittingCalibrator.cpp @@ -12,7 +12,7 @@ namespace ActsExamples { void RefittingCalibrator::calibrate(const Acts::GeometryContext& /*gctx*/, Proxy trackState) const { - const auto& sl = + const auto sl = trackState.getUncalibratedSourceLink().get(); // Here we construct a measurement by extracting the information available diff --git a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp index 40321542b05..a25e80ec5cb 100644 --- a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp @@ -38,11 +38,18 @@ ActsExamples::TrackFittingAlgorithm::TrackFittingAlgorithm( if (m_cfg.outputTracks.empty()) { throw std::invalid_argument("Missing output tracks collection"); } + if (!m_cfg.calibrator) { + throw std::invalid_argument("Missing calibrator"); + } + if (m_cfg.inputClusters.empty() && m_cfg.calibrator->needsClusters()) { + throw std::invalid_argument("The configured calibrator needs clusters"); + } m_inputMeasurements.initialize(m_cfg.inputMeasurements); m_inputSourceLinks.initialize(m_cfg.inputSourceLinks); m_inputProtoTracks.initialize(m_cfg.inputProtoTracks); m_inputInitialTrackParameters.initialize(m_cfg.inputInitialTrackParameters); + m_inputClusters.maybeInitialize(m_cfg.inputClusters); m_outputTracks.initialize(m_cfg.outputTracks); } @@ -54,6 +61,9 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingAlgorithm::execute( const auto& protoTracks = m_inputProtoTracks(ctx); const auto& initialParameters = m_inputInitialTrackParameters(ctx); + const ClusterContainer* clusters = + m_inputClusters.isInitialized() ? &m_inputClusters(ctx) : nullptr; + // Consistency cross checks if (protoTracks.size() != initialParameters.size()) { ACTS_FATAL("Inconsistent number of proto tracks and parameters " @@ -68,7 +78,8 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingAlgorithm::execute( // Measurement calibrator must be instantiated here, because we need the // measurements to construct it. The other extensions are hold by the // fit-function-object - ActsExamples::MeasurementCalibrator calibrator(measurements); + ActsExamples::MeasurementCalibratorAdapter calibrator(*(m_cfg.calibrator), + measurements, clusters); TrackFitterFunction::GeneralFitterOptions options{ ctx.geoContext, ctx.magFieldContext, ctx.calibContext, pSurface.get(), diff --git a/Examples/Algorithms/TrackFittingChi2/src/TrackFittingChi2Algorithm.cpp b/Examples/Algorithms/TrackFittingChi2/src/TrackFittingChi2Algorithm.cpp index b7ed29876e8..10febed6960 100644 --- a/Examples/Algorithms/TrackFittingChi2/src/TrackFittingChi2Algorithm.cpp +++ b/Examples/Algorithms/TrackFittingChi2/src/TrackFittingChi2Algorithm.cpp @@ -10,6 +10,7 @@ #include "Acts/EventData/TrackContainer.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" +#include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" @@ -64,8 +65,10 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingChi2Algorithm::execute( // Set Chi2 options Acts::Experimental::Chi2FitterExtensions extensions; - MeasurementCalibrator calibrator{measurements}; - extensions.calibrator.connect<&MeasurementCalibrator::calibrate>(&calibrator); + PassThroughCalibrator pcalibrator; + MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); + extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( + &calibrator); Acts::Experimental::Chi2FitterOptions chi2Options( ctx.geoContext, ctx.magFieldContext, ctx.calibContext, extensions, diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp index 74227cf6e6b..12ff2d3d49c 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.cpp @@ -55,6 +55,7 @@ ActsExamples::TruthSeedingAlgorithm::TruthSeedingAlgorithm( m_inputParticles.initialize(m_cfg.inputParticles); m_inputMeasurementParticlesMap.initialize(m_cfg.inputMeasurementParticlesMap); m_outputParticles.initialize(m_cfg.outputParticles); + m_outputProtoTracks.initialize(m_cfg.outputProtoTracks); m_outputSeeds.initialize(m_cfg.outputSeeds); } @@ -184,6 +185,7 @@ ActsExamples::ProcessCode ActsExamples::TruthSeedingAlgorithm::execute( ACTS_VERBOSE("Found " << seeds.size() << " seeds"); m_outputParticles(ctx, std::move(seededParticles)); + m_outputProtoTracks(ctx, std::move(tracks)); m_outputSeeds(ctx, std::move(seeds)); return ActsExamples::ProcessCode::SUCCESS; diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp index caf7aea1c69..56037cf0b84 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TruthSeedingAlgorithm.hpp @@ -82,8 +82,8 @@ class TruthSeedingAlgorithm final : public IAlgorithm { WriteDataHandle m_outputParticles{this, "OutputParticles"}; - WriteDataHandle m_outputFullProtoTracks{ - this, "OutputFullProtoTracks"}; + WriteDataHandle m_outputProtoTracks{this, + "OutputProtoTracks"}; WriteDataHandle m_outputSeeds{this, "OutputSeeds"}; }; diff --git a/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp b/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp index 59423e144ed..4059decfb72 100644 --- a/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp +++ b/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp @@ -61,7 +61,7 @@ ProcessCode TracksToTrajectories::execute(const AlgorithmContext& ctx) const { } if (tips.empty()) { - ACTS_ERROR("Last trajectory is empty"); + ACTS_DEBUG("Last trajectory is empty"); } // last entry: move vectors diff --git a/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp b/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp index 71780a0118c..5f4244222cc 100644 --- a/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp +++ b/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp @@ -100,6 +100,7 @@ ActsExamples::AdaptiveMultiVertexFinderAlgorithm::execute( // We do not want to use a beamspot constraint here finderConfig.useBeamSpotConstraint = false; finderConfig.tracksMaxZinterval = 1. * Acts::UnitConstants::mm; + finderConfig.maxIterations = 200; // Instantiate the finder Finder finder(finderConfig, logger().cloneWithSuffix("AMVFinder")); @@ -109,6 +110,15 @@ ActsExamples::AdaptiveMultiVertexFinderAlgorithm::execute( auto [inputTrackParameters, inputTrackPointers] = makeParameterContainers(ctx, m_inputTrackParameters, m_inputTrajectories); + for (const auto& trk : inputTrackParameters) { + if (trk.covariance() && trk.covariance()->determinant() <= 0) { + // actually we should consider this as an error but I do not want the CI + // to fail + ACTS_WARNING("input track " << trk << " has det(cov) = " + << trk.covariance()->determinant()); + } + } + ////////////////////////////////////////////// /* Full tutorial example code for reference */ ////////////////////////////////////////////// diff --git a/Examples/Algorithms/Vertexing/src/IterativeVertexFinderAlgorithm.cpp b/Examples/Algorithms/Vertexing/src/IterativeVertexFinderAlgorithm.cpp index 39108c4b7f8..d535229ac5f 100644 --- a/Examples/Algorithms/Vertexing/src/IterativeVertexFinderAlgorithm.cpp +++ b/Examples/Algorithms/Vertexing/src/IterativeVertexFinderAlgorithm.cpp @@ -69,6 +69,15 @@ ActsExamples::ProcessCode ActsExamples::IterativeVertexFinderAlgorithm::execute( auto [inputTrackParameters, inputTrackPointers] = makeParameterContainers(ctx, m_inputTrackParameters, m_inputTrajectories); + for (const auto& trk : inputTrackParameters) { + if (trk.covariance() && trk.covariance()->determinant() <= 0) { + // actually we should consider this as an error but I do not want the CI + // to fail + ACTS_WARNING("input track " << trk << " has det(cov) = " + << trk.covariance()->determinant()); + } + } + // Set up EigenStepper Acts::EigenStepper<> stepper(m_cfg.bField); diff --git a/Examples/Detectors/CMakeLists.txt b/Examples/Detectors/CMakeLists.txt index 29872310fdc..da56b59bef2 100644 --- a/Examples/Detectors/CMakeLists.txt +++ b/Examples/Detectors/CMakeLists.txt @@ -6,4 +6,4 @@ add_subdirectory(MagneticField) add_subdirectory(TGeoDetector) add_subdirectory(TelescopeDetector) add_subdirectory_if(MuonSpectrometerMockupDetector ACTS_BUILD_EXAMPLES_GEANT4) - +add_subdirectory(DetectorInspectors) diff --git a/Examples/Detectors/DetectorInspectors/CMakeLists.txt b/Examples/Detectors/DetectorInspectors/CMakeLists.txt new file mode 100644 index 00000000000..38dc9829aa3 --- /dev/null +++ b/Examples/Detectors/DetectorInspectors/CMakeLists.txt @@ -0,0 +1,14 @@ +if (ACTS_BUILD_PLUGIN_ACTSVG AND ACTS_BUILD_PLUGIN_JSON) + add_library(ActsExamplesDetectoInspectors SHARED src/SurfaceIndexing.cpp) + target_include_directories( + ActsExamplesDetectoInspectors + PUBLIC $) + target_link_libraries( + ActsExamplesDetectoInspectors + PUBLIC ActsCore ActsPluginJson ActsPluginActSVG) + install( + TARGETS ActsExamplesDetectoInspectors + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + + \ No newline at end of file diff --git a/Examples/Detectors/DetectorInspectors/include/ActsExamples/DetectorInspectors/SurfaceIndexing.hpp b/Examples/Detectors/DetectorInspectors/include/ActsExamples/DetectorInspectors/SurfaceIndexing.hpp new file mode 100644 index 00000000000..a9a4d380cab --- /dev/null +++ b/Examples/Detectors/DetectorInspectors/include/ActsExamples/DetectorInspectors/SurfaceIndexing.hpp @@ -0,0 +1,253 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/LayerStructureBuilder.hpp" +#include "Acts/Detector/detail/KdtSurfacesProvider.hpp" +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/Helpers.hpp" + +#include +#include +#include +#include + +namespace ActsExamples { + +/// @brief Helper method to read the surfaces from file +/// +/// @param fname The file name +/// +/// @return a vector of created surfaces for the KDT tree creation +std::vector> readSurfacesFromJson( + const std::string& fname); + +/// The surface indexing struct for KDT driven layer structure creation +template +struct SurfaceIndexing { + /// The KDT structure definition + using KDT = Acts::Experimental::detail::KdtSurfaces< + kDIM, 100u, Acts::Experimental::detail::PolyhedronReferenceGenerator>; + + public: + /// Constructor from arguments + /// + /// @param fname the file name of the json file to be read in + /// @param bValues the vector of binning values for the KDT construction + /// + SurfaceIndexing(const std::string& fname, + const std::vector& bValues = { + Acts::binZ, Acts::binR}) { + if (bValues.size() != kDIM) { + throw std::invalid_argument( + "SurfaceIndexing: wrong number of binning values"); + } + // Fill the binning Values + for (auto [ibv, bv] : Acts::enumerate(bValues)) { + m_binningValues[ibv] = bv; + } + // Get the surfaces + auto surfaces = readSurfacesFromJson(fname); + m_surfacesKDT = std::make_shared( + KDT(Acts::GeometryContext(), surfaces, m_binningValues)); + } + + /// pyton: axis binning value, axis option, axis type, nbins, boundaries, + /// expansion + using Binning = std::tuple, unsigned int>; + + /// python: support description, values, type, split + using Support = + std::tuple, std::string, unsigned int>; + + // Translate the binning - ACTS version + using LayerBinning = + typename Acts::Experimental::LayerStructureBuilder::Binning; + + // Translate the support - ACTS class version + using LayerSupport = + typename Acts::Experimental::LayerStructureBuilder::Support; + + /// Inspect the layer - written for an easy python binidng + /// + /// @param name of the layer structure + /// @param qRange is the query range for the Surfaces + /// @param representation of the layer structure + /// @param binnings the surface binning description + /// @param supports the supports to be added + /// @param drawOptions the display/draw options + /// + void createIndexing( + const std::string& name, + const std::array, kDIM> qRange, + const std::vector& binnings, + const std::vector& supports, + Acts::Svg::IndexedSurfacesConverter::Options drawOptions) { + // Translate into an Extent + Acts::Extent lExtent; + for (const auto& [iq, qr] : Acts::enumerate(qRange)) { + lExtent.set(m_binningValues[iq], qr[0u], qr[1u]); + } + + // Translate the binnings + std::vector lBinnings; + for (const auto& bn : binnings) { + // Translate the number of bins + unsigned int nBins = std::get<3u>(bn); + // Translate the boundaries + const std::vector& edges = std::get<4u>(bn); + float eMin = edges.front(); + float eMax = edges.back(); + // Translate the value + Acts::BinningValue bValue = Acts::binPhi; + std::string bstring = std::get<0u>(bn); + if (bstring == "binX" or bstring == "x") { + bValue = Acts::binX; + } else if (bstring == "binY" or bstring == "y") { + bValue = Acts::binY; + } else if (bstring == "binZ" or bstring == "z") { + bValue = Acts::binZ; + } else if (bstring == "binR" or bstring == "r") { + bValue = Acts::binR; + } else if (bstring == "binPhi" or bstring == "phi") { + bValue = Acts::binPhi; + } else { + throw std::invalid_argument( + "SurfaceBinning: binning type not supported."); + } + // Translate the option + Acts::BinningOption bOption = Acts::open; + std::string ostring = std::get<1u>(bn); + if (ostring == "closed") { + bOption = Acts::closed; + } + // Translate the type + std::string tstring = std::get<2u>(bn); + // Expansion + unsigned int expansion = std::get<5u>(bn); + if (tstring == "arbitrary" or tstring == "variable") { + // Set the different type + lBinnings.push_back( + {Acts::BinningData(bOption, bValue, edges), expansion}); + } else { + lBinnings.push_back( + {Acts::BinningData(bOption, bValue, nBins, eMin, eMax), expansion}); + } + } + + // Translate the layer supports + std::vector lSupports; + if (not supports.empty()) { + std::vector sConstraints; + for (const auto& bv : m_binningValues) { + sConstraints.push_back(bv); + } + // Run over the supports + for (const auto& ls : supports) { + std::string type = std::get<1u>(ls); + // Translate the representation + Acts::Surface::SurfaceType sType = Acts::Surface::SurfaceType::Other; + if (type == "cylinder") { + sType = Acts::Surface::SurfaceType::Cylinder; + } else if (type == "disc" or type == "disk") { + sType = Acts::Surface::SurfaceType::Disc; + } else if (type == "plane") { + sType = Acts::Surface::SurfaceType::Plane; + } else { + throw std::invalid_argument( + "Inspectors::SurfaceIndexing: representation type not " + "recognized, " + "please use 'cylinder', 'disc', or 'plane'."); + } + std::array sValues = std::get<0u>(ls); + unsigned int sSplits = std::get<2u>(ls); + lSupports.push_back( + LayerSupport{sValues, sType, sConstraints, sSplits}); + } + } + // Call the ACTS based implementations + createIndexingImpl(name, lExtent, lBinnings, lSupports, drawOptions); + } + + /// Inspect the layer - view implementation using ACTS classes + /// + /// @param name of the layer structure + /// @param lExtent the query range for the surfaces as an extent + /// @param lBinnings the surface binning description + /// @param lSupports the supports to be added + /// @param drawOptions the display/draw options + /// + void createIndexingImpl( + const std::string& name, const Acts::Extent& lExtent, + const std::vector& lBinnings, + const std::vector& lSupports, + Acts::Svg::IndexedSurfacesConverter::Options drawOptions) { + // A test context for this + Acts::GeometryContext tContext; + + Acts::Experimental::detail::KdtSurfacesProvider selectedSurfaces; + selectedSurfaces.kdt = m_surfacesKDT; + selectedSurfaces.region = lExtent; + + // Configure the layer structure builder + Acts::Experimental::LayerStructureBuilder::Config lsConfig; + lsConfig.auxilliary = + std::string("*** Building ") + name + std::string(" ***"); + lsConfig.surfaces = selectedSurfaces; + lsConfig.binnings = lBinnings; + lsConfig.supports = lSupports; + + auto builder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger(name, Acts::Logging::VERBOSE)); + + auto [surfaces, volumes, surfacesUpdator, volumeUpdator] = + builder.construct(tContext); + + // The displaying + auto pIndexedStructure = Acts::Svg::IndexedSurfacesConverter::convert( + tContext, surfaces, surfacesUpdator, drawOptions); + auto pIndexedView = Acts::Svg::View::xy(pIndexedStructure, name); + Acts::Svg::toFile({pIndexedView}, pIndexedView._id + ".svg"); + } + + private: + /// The KDT surface structure to be created + std::shared_ptr m_surfacesKDT = nullptr; + /// Query binning of the KDT surface structure + std::array m_binningValues; +}; + +/// Typify for cylinders +class CylindricalDetectorIndexing : public SurfaceIndexing<2u> { + public: + /// Constructor with @param fname filename + CylindricalDetectorIndexing(const std::string& fname); + + /// Inspect the layer - written for an easy python binidng + /// + /// @param name of the layer structure + /// @param qRange is the query range for the Surfaces + /// @param representation of the layer structure + /// @param binnings the surface binning description + /// @param supports the supports to be added + /// @param drawOptions the display/draw options + /// + void inspect(const std::string& name, + const std::array, 2u> qRange, + const std::vector::Binning>& binnings, + const std::vector::Support>& supports); +}; + +} // namespace ActsExamples diff --git a/Examples/Detectors/DetectorInspectors/src/SurfaceIndexing.cpp b/Examples/Detectors/DetectorInspectors/src/SurfaceIndexing.cpp new file mode 100644 index 00000000000..0acad9e10cc --- /dev/null +++ b/Examples/Detectors/DetectorInspectors/src/SurfaceIndexing.cpp @@ -0,0 +1,88 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DetectorInspectors/SurfaceIndexing.hpp" + +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Plugins/ActSVG/SvgUtils.hpp" +#include "Acts/Plugins/Json/ActsJson.hpp" +#include "Acts/Plugins/Json/SurfaceJsonConverter.hpp" + +#include +#include + +namespace ActsExamples { + +std::vector> readSurfacesFromJson( + const std::string& fname) { + std::ifstream ifile(fname.c_str()); + nlohmann::json jin; + ifile >> jin; + // The json surface container + auto jSurfaces = jin["entries"]; + std::vector> rSurfaces = {}; + rSurfaces.reserve(jSurfaces.size()); + for (const auto& js : jSurfaces) { + rSurfaces.push_back(Acts::surfaceFromJson(js["value"])); + } + return rSurfaces; +} + +namespace { +Acts::Svg::IndexedSurfacesConverter::Options generateDrawOptions() { + // The converter options + Acts::Svg::IndexedSurfacesConverter::Options isOptions; + // Sensitive surface stuyle + Acts::Svg::Style sensitiveStyle; + sensitiveStyle.fillColor = {51, 153, 255}; + sensitiveStyle.fillOpacity = 0.9; + sensitiveStyle.highlightColor = {255, 153, 51}; + sensitiveStyle.highlights = {"onmouseover", "onmouseout"}; + sensitiveStyle.strokeWidth = 0.5; + sensitiveStyle.strokeColor = {0, 0, 0}; + sensitiveStyle.nSegments = 72u; + std::pair allSensitives = { + Acts::GeometryIdentifier(0u), sensitiveStyle}; + + // Hierarchy map of styles + Acts::GeometryHierarchyMap surfaceStyles({allSensitives}); + isOptions.surfaceStyles = surfaceStyles; + + // The grid style + Acts::Svg::GridConverter::Options gridOptions; + Acts::Svg::Style gridStyle; + gridStyle.fillOpacity = 0.; + gridStyle.strokeColor = {0, 0, 255}; + gridStyle.strokeWidth = 1.; + gridStyle.highlightStrokeWidth = 3; + gridStyle.highlightStrokeColor = {255, 0, 0}; + gridOptions.style = gridStyle; + + isOptions.gridOptions = gridOptions; + return isOptions; +}; + +} // namespace + +// Cylindrical inspector +CylindricalDetectorIndexing::CylindricalDetectorIndexing( + const std::string& fname) + : SurfaceIndexing<2u>(fname) {} + +// Run the inspector code +void CylindricalDetectorIndexing::inspect( + const std::string& name, + const std::array, 2u> qRange, + const std::vector::Binning>& binnings, + const std::vector::Support>& supports) { + SurfaceIndexing<2u>::createIndexing(name, qRange, binnings, supports, + generateDrawOptions()); +} + +} // namespace ActsExamples diff --git a/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetectorElement.hpp b/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetectorElement.hpp index 647ad3848ee..3df9fd4f978 100644 --- a/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetectorElement.hpp +++ b/Examples/Detectors/GenericDetector/include/ActsExamples/GenericDetector/GenericDetectorElement.hpp @@ -83,6 +83,9 @@ class GenericDetectorElement : public Acts::IdentifiedDetectorElement { /// Return surface associated with this detector element const Acts::Surface& surface() const override; + /// Non-cost access to surface associated with this detector element + Acts::Surface& surface() override; + /// Set the identifier after construction (sometimes needed) void assignIdentifier(const Identifier& identifier); @@ -100,7 +103,7 @@ class GenericDetectorElement : public Acts::IdentifiedDetectorElement { /// the transform for positioning in 3D space std::shared_ptr m_elementTransform; /// the surface represented by it - std::shared_ptr m_elementSurface; + std::shared_ptr m_elementSurface; /// the element thickness double m_elementThickness; /// store either @@ -132,6 +135,10 @@ ActsExamples::Generic::GenericDetectorElement::surface() const { return *m_elementSurface; } +inline Acts::Surface& ActsExamples::Generic::GenericDetectorElement::surface() { + return *m_elementSurface; +} + inline double ActsExamples::Generic::GenericDetectorElement::thickness() const { return m_elementThickness; } diff --git a/Examples/Detectors/GenericDetector/src/GenericDetectorElement.cpp b/Examples/Detectors/GenericDetector/src/GenericDetectorElement.cpp index 71cb34e6a10..886e76fbe8e 100644 --- a/Examples/Detectors/GenericDetector/src/GenericDetectorElement.cpp +++ b/Examples/Detectors/GenericDetector/src/GenericDetectorElement.cpp @@ -28,9 +28,7 @@ ActsExamples::Generic::GenericDetectorElement::GenericDetectorElement( m_elementPlanarBounds(std::move(pBounds)), m_elementDiscBounds(nullptr), m_digitizationModule(std::move(digitizationModule)) { - auto mutableSurface = - std::const_pointer_cast(m_elementSurface); - mutableSurface->assignSurfaceMaterial(std::move(material)); + m_elementSurface->assignSurfaceMaterial(std::move(material)); } ActsExamples::Generic::GenericDetectorElement::GenericDetectorElement( @@ -48,7 +46,5 @@ ActsExamples::Generic::GenericDetectorElement::GenericDetectorElement( m_elementPlanarBounds(nullptr), m_elementDiscBounds(std::move(dBounds)), m_digitizationModule(std::move(digitizationModule)) { - auto mutableSurface = - std::const_pointer_cast(m_elementSurface); - mutableSurface->assignSurfaceMaterial(std::move(material)); + m_elementSurface->assignSurfaceMaterial(std::move(material)); } diff --git a/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp b/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp index 65e34c6312b..5c6d7fabcef 100644 --- a/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp +++ b/Examples/Detectors/MuonSpectrometerMockupDetector/src/MockupSectorBuilder.cpp @@ -10,7 +10,6 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/PortalGenerators.hpp" -#include "Acts/Detector/PortalHelper.hpp" #include "Acts/Geometry/CuboidVolumeBounds.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -129,7 +128,7 @@ ActsExamples::MockupSectorBuilder::buildChamber( mCfg.toleranceOverlap; auto detectorVolumeBounds = - std::make_unique(hx, hy, hz); + std::make_shared(hx, hy, hz); Acts::Vector3 chamber_position = {(maxValues.x() + minValues.x()) / 2, (maxValues.y() + minValues.y()) / 2, @@ -198,7 +197,7 @@ ActsExamples::MockupSectorBuilder::buildSector( std::vector rmins(detVolumesSize); std::vector rmaxs(detVolumesSize); std::vector halfZ(detVolumesSize); - std::vector> + std::vector> cylinderVolumesBounds(detVolumesSize); for (int i = 0; i < detVolumesSize; i++) { @@ -212,7 +211,7 @@ ActsExamples::MockupSectorBuilder::buildSector( mCfg.toleranceOverlap; halfZ[i] = detVol->volumeBounds().values()[2]; - cylinderVolumesBounds[i] = std::make_unique( + cylinderVolumesBounds[i] = std::make_shared( rmins[i], rmaxs[i], halfZ[i], sectorAngle); } @@ -294,7 +293,7 @@ ActsExamples::MockupSectorBuilder::buildSector( } // end of cylinder volumes auto cylinderVolumesBoundsOfMother = - std::make_unique( + std::make_shared( rmins.front(), rmaxs.back(), *std::max_element(halfZ.begin(), halfZ.end()), sectorAngle); diff --git a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp index ceed47b5304..6f358a653bc 100644 --- a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp +++ b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp @@ -63,6 +63,9 @@ class TelescopeDetectorElement : public Acts::DetectorElementBase { /// Return surface associated with this detector element const Acts::Surface& surface() const final; + /// Non-const access to the surface associated with this detector element + Acts::Surface& surface() final; + /// The maximal thickness of the detector element wrt normal axis double thickness() const final; @@ -97,7 +100,7 @@ class TelescopeDetectorElement : public Acts::DetectorElementBase { // the aligned transforms std::vector> m_alignedTransforms = {}; /// the surface represented by it - std::shared_ptr m_elementSurface = nullptr; + std::shared_ptr m_elementSurface = nullptr; /// the element thickness double m_elementThickness = 0.; /// the planar bounds @@ -110,6 +113,10 @@ inline const Acts::Surface& TelescopeDetectorElement::surface() const { return *m_elementSurface; } +inline Acts::Surface& TelescopeDetectorElement::surface() { + return *m_elementSurface; +} + inline double TelescopeDetectorElement::thickness() const { return m_elementThickness; } diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp index 6a66dfbbfe0..f5f1c02283b 100644 --- a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp @@ -15,7 +15,7 @@ #include "ActsExamples/TelescopeDetector/TelescopeDetectorElement.hpp" auto ActsExamples::Telescope::TelescopeDetector::finalize( - const Config& cfg, const std::shared_ptr & + const Config& cfg, const std::shared_ptr& /*mdecorator*/) -> std::pair { DetectorElement::ContextType nominalContext; diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorElement.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorElement.cpp index f6245f9af1b..f9bfbcfe37f 100644 --- a/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorElement.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetectorElement.cpp @@ -40,7 +40,5 @@ ActsExamples::Telescope::TelescopeDetectorElement::TelescopeDetectorElement( m_elementThickness(thickness), m_elementPlanarBounds(nullptr), m_elementDiscBounds(std::move(dBounds)) { - auto mutableSurface = - std::const_pointer_cast(m_elementSurface); - mutableSurface->assignSurfaceMaterial(std::move(material)); + m_elementSurface->assignSurfaceMaterial(std::move(material)); } diff --git a/Examples/Framework/CMakeLists.txt b/Examples/Framework/CMakeLists.txt index e72d6bfa927..24cae515337 100644 --- a/Examples/Framework/CMakeLists.txt +++ b/Examples/Framework/CMakeLists.txt @@ -2,6 +2,7 @@ include(ActsTargetLinkLibrariesSystem) add_library( ActsExamplesFramework SHARED + src/EventData/MeasurementCalibration.cpp src/Framework/IAlgorithm.cpp src/Framework/SequenceElement.cpp src/Framework/WhiteBoard.cpp @@ -62,4 +63,4 @@ install( install( DIRECTORY include/ActsExamples - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) \ No newline at end of file + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/Examples/Framework/include/ActsExamples/EventData/Index.hpp b/Examples/Framework/include/ActsExamples/EventData/Index.hpp index 26a31ad7cfb..f4330b75325 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Index.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Index.hpp @@ -48,7 +48,13 @@ inline boost::container::flat_multimap invertIndexMultimap( // adopting the unordered sequence will reestablish the correct order InverseMultimap inverse; +#if BOOST_VERSION < 107800 + for (const auto& i : unordered) { + inverse.insert(i); + } +#else inverse.insert(unordered.begin(), unordered.end()); +#endif return inverse; } diff --git a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp index a770d0da78b..df16d773103 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2020 CERN for the benefit of the Acts project +// Copyright (C) 2020-2023 CERN for the benefit of the Acts project // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,19 +8,15 @@ #pragma once -#include "Acts/EventData/Measurement.hpp" -#include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SourceLink.hpp" -#include "Acts/EventData/VectorMultiTrajectory.hpp" -#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include -#include #include namespace ActsExamples { /// Variable measurement type that can contain all possible combinations. using Measurement = ::Acts::BoundVariantMeasurement; + /// Container of measurements. /// /// In contrast to the source links, the measurements themself must not be @@ -28,41 +24,4 @@ using Measurement = ::Acts::BoundVariantMeasurement; /// as opaque here and no ordering is enforced on the stored measurements. using MeasurementContainer = std::vector; -/// Calibrator to convert an index source link to a measurement. -class MeasurementCalibrator { - public: - /// Construct an invalid calibrator. Required to allow copying. - MeasurementCalibrator() = default; - /// Construct using a user-provided container to chose measurements from. - MeasurementCalibrator(const MeasurementContainer& measurements) - : m_measurements(&measurements) {} - - /// Find the measurement corresponding to the source link. - /// - /// @tparam parameters_t Track parameters type - /// @param gctx The geometry context (unused) - /// @param trackState The track state to calibrate - void calibrate( - const Acts::GeometryContext& /*gctx*/, - Acts::MultiTrajectory::TrackStateProxy - trackState) const { - const IndexSourceLink& sourceLink = - trackState.getUncalibratedSourceLink().get(); - assert(m_measurements and - "Undefined measurement container in DigitizedCalibrator"); - assert((sourceLink.index() < m_measurements->size()) and - "Source link index is outside the container bounds"); - std::visit( - [&](const auto& meas) { - trackState.allocateCalibrated(meas.size()); - trackState.setCalibrated(meas); - }, - (*m_measurements)[sourceLink.index()]); - } - - private: - // use pointer so the calibrator is copyable and default constructible. - const MeasurementContainer* m_measurements = nullptr; -}; - } // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp b/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp new file mode 100644 index 00000000000..b4cb62a3843 --- /dev/null +++ b/Examples/Framework/include/ActsExamples/EventData/MeasurementCalibration.hpp @@ -0,0 +1,72 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" +#include + +#include + +namespace ActsExamples { + +/// Abstract base class for measurement-based calibration +class MeasurementCalibrator { + public: + virtual void calibrate( + const MeasurementContainer& measurements, + const ClusterContainer* clusters, const Acts::GeometryContext& gctx, + Acts::MultiTrajectory::TrackStateProxy& + trackState) const = 0; + + virtual ~MeasurementCalibrator() = default; + virtual bool needsClusters() { return false; } +}; + +// Calibrator to convert an index source link to a measurement as-is +class PassThroughCalibrator : public MeasurementCalibrator { + public: + /// Find the measurement corresponding to the source link. + /// + /// @tparam parameters_t Track parameters type + /// @param gctx The geometry context (unused) + /// @param trackState The track state to calibrate + void calibrate( + const MeasurementContainer& measurements, + const ClusterContainer* /*clusters*/, + const Acts::GeometryContext& /*gctx*/, + Acts::MultiTrajectory::TrackStateProxy& + trackState) const override; +}; + +// Adapter class that wraps a MeasurementCalibrator to conform to the +// core ACTS calibration interface +class MeasurementCalibratorAdapter { + public: + MeasurementCalibratorAdapter(const MeasurementCalibrator& calibrator, + const MeasurementContainer& measurements, + const ClusterContainer* clusters = nullptr); + + MeasurementCalibratorAdapter() = delete; + + void calibrate( + const Acts::GeometryContext& gctx, + Acts::MultiTrajectory::TrackStateProxy + trackState) const; + + private: + const MeasurementCalibrator& m_calibrator; + const MeasurementContainer& m_measurements; + const ClusterContainer* m_clusters; +}; + +} // namespace ActsExamples diff --git a/Examples/Framework/src/EventData/MeasurementCalibration.cpp b/Examples/Framework/src/EventData/MeasurementCalibration.cpp new file mode 100644 index 00000000000..541fc060297 --- /dev/null +++ b/Examples/Framework/src/EventData/MeasurementCalibration.cpp @@ -0,0 +1,41 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +void ActsExamples::PassThroughCalibrator::calibrate( + const MeasurementContainer& measurements, + const ClusterContainer* /*clusters*/, const Acts::GeometryContext& /*gctx*/, + Acts::MultiTrajectory::TrackStateProxy& + trackState) const { + const IndexSourceLink& sourceLink = + trackState.getUncalibratedSourceLink().get(); + assert((sourceLink.index() < measurements.size()) and + "Source link index is outside the container bounds"); + + std::visit( + [&trackState](const auto& meas) { + trackState.allocateCalibrated(meas.size()); + trackState.setCalibrated(meas); + }, + (measurements)[sourceLink.index()]); +} + +ActsExamples::MeasurementCalibratorAdapter::MeasurementCalibratorAdapter( + const MeasurementCalibrator& calibrator, + const MeasurementContainer& measurements, const ClusterContainer* clusters) + : m_calibrator{calibrator}, + m_measurements{measurements}, + m_clusters{clusters} {} + +void ActsExamples::MeasurementCalibratorAdapter::calibrate( + const Acts::GeometryContext& gctx, + Acts::MultiTrajectory::TrackStateProxy + trackState) const { + return m_calibrator.calibrate(m_measurements, m_clusters, gctx, trackState); +} diff --git a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp index f41a227e88c..feeee5cd7ae 100644 --- a/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp +++ b/Examples/Io/Csv/include/ActsExamples/Io/Csv/CsvMeasurementReader.hpp @@ -11,6 +11,8 @@ #include "Acts/Geometry/TrackingGeometry.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/EventData/Index.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IReader.hpp" diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp index aad6934c457..692345c4cde 100644 --- a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepMeasurementReader.hpp @@ -9,6 +9,7 @@ #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/Cluster.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IReader.hpp" diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp b/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp index 92a683dbc3a..a9f6267b945 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/SeedingPerformanceWriter.cpp @@ -69,6 +69,7 @@ ActsExamples::ProcessCode ActsExamples::SeedingPerformanceWriter::finalize() { float aveNDuplicatedSeeds = float(m_nTotalMatchedSeeds - m_nTotalMatchedParticles) / m_nTotalMatchedParticles; + float totalSeedPurity = float(m_nTotalMatchedSeeds) / m_nTotalSeeds; ACTS_DEBUG("nTotalSeeds = " << m_nTotalSeeds); ACTS_DEBUG("nTotalMatchedSeeds = " << m_nTotalMatchedSeeds); ACTS_DEBUG("nTotalParticles = " << m_nTotalParticles); @@ -77,6 +78,8 @@ ActsExamples::ProcessCode ActsExamples::SeedingPerformanceWriter::finalize() { ACTS_INFO("Efficiency (nMatchedParticles / nAllParticles) = " << eff); ACTS_INFO("Fake rate (nUnMatchedSeeds / nAllSeeds) = " << fakeRate); + ACTS_INFO("Total seed purity (nTotalMatchedSeeds / m_nTotalSeeds) = " + << totalSeedPurity); ACTS_INFO( "Duplication rate (nDuplicatedMatchedParticles / nMatchedParticles) = " << duplicationRate); diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp index 0ece80af5c9..08260479ee6 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp @@ -85,9 +85,13 @@ ActsExamples::VertexPerformanceWriter::VertexPerformanceWriter( throw std::bad_alloc(); } else { // I/O parameters - m_outputTree->Branch("diffx", &m_diffx); - m_outputTree->Branch("diffy", &m_diffy); - m_outputTree->Branch("diffz", &m_diffz); + m_outputTree->Branch("resX", &m_resX); + m_outputTree->Branch("resY", &m_resY); + m_outputTree->Branch("resZ", &m_resZ); + + m_outputTree->Branch("pullX", &m_pullX); + m_outputTree->Branch("pullY", &m_pullY); + m_outputTree->Branch("pullZ", &m_pullZ); m_outputTree->Branch("recoX", &m_recoX); m_outputTree->Branch("recoY", &m_recoY); @@ -99,8 +103,11 @@ ActsExamples::VertexPerformanceWriter::VertexPerformanceWriter( m_outputTree->Branch("covXX", &m_covXX); m_outputTree->Branch("covYY", &m_covYY); + m_outputTree->Branch("covZZ", &m_covZZ); m_outputTree->Branch("covXY", &m_covXY); - m_outputTree->Branch("covYX", &m_covYX); + m_outputTree->Branch("covXZ", &m_covXZ); + m_outputTree->Branch("covYZ", &m_covYZ); + m_outputTree->Branch("trkVtxMatch", &m_trackVtxMatchFraction); m_outputTree->Branch("nRecoVtx", &m_nrecoVtx); m_outputTree->Branch("nTrueVtx", &m_ntrueVtx); @@ -359,9 +366,27 @@ ActsExamples::ProcessCode ActsExamples::VertexPerformanceWriter::writeT( // Vertex found, fill varibles const auto& truePos = particle.position(); - m_diffx.push_back(vtx.position()[0] - truePos[0]); - m_diffy.push_back(vtx.position()[1] - truePos[1]); - m_diffz.push_back(vtx.position()[2] - truePos[2]); + m_resX.push_back(vtx.position()[0] - truePos[0]); + m_resY.push_back(vtx.position()[1] - truePos[1]); + m_resZ.push_back(vtx.position()[2] - truePos[2]); + + auto pull = [&](int i) { + double var = vtx.covariance()(i, i); + if (var < 0) { + ACTS_WARNING("var(" << i << ") = " << var << " < 0"); + return std::numeric_limits::quiet_NaN(); + } + double std = std::sqrt(var); + if (std == 0) { + ACTS_WARNING("std(" << i << ") = 0"); + return std::numeric_limits::quiet_NaN(); + } + return (vtx.position()[i] - truePos[i]) / std; + }; + + m_pullX.push_back(pull(0)); + m_pullY.push_back(pull(1)); + m_pullZ.push_back(pull(2)); m_truthX.push_back(truePos[0]); m_truthY.push_back(truePos[1]); @@ -373,8 +398,11 @@ ActsExamples::ProcessCode ActsExamples::VertexPerformanceWriter::writeT( m_covXX.push_back(vtx.covariance()(0, 0)); m_covYY.push_back(vtx.covariance()(1, 1)); + m_covZZ.push_back(vtx.covariance()(2, 2)); m_covXY.push_back(vtx.covariance()(0, 1)); - m_covYX.push_back(vtx.covariance()(1, 0)); + m_covXZ.push_back(vtx.covariance()(0, 2)); + m_covYZ.push_back(vtx.covariance()(1, 2)); + m_trackVtxMatchFraction.push_back(trackVtxMatchFraction); // Next vertex now break; @@ -394,9 +422,12 @@ ActsExamples::ProcessCode ActsExamples::VertexPerformanceWriter::writeT( // fill the variables m_outputTree->Fill(); - m_diffx.clear(); - m_diffy.clear(); - m_diffz.clear(); + m_resX.clear(); + m_resY.clear(); + m_resZ.clear(); + m_pullX.clear(); + m_pullY.clear(); + m_pullZ.clear(); m_truthX.clear(); m_truthY.clear(); m_truthZ.clear(); @@ -405,8 +436,10 @@ ActsExamples::ProcessCode ActsExamples::VertexPerformanceWriter::writeT( m_recoZ.clear(); m_covXX.clear(); m_covYY.clear(); + m_covZZ.clear(); m_covXY.clear(); - m_covYX.clear(); + m_covXZ.clear(); + m_covYZ.clear(); m_trackVtxMatchFraction.clear(); return ProcessCode::SUCCESS; diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp index bc778f848fe..e91b755e6a7 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.hpp @@ -95,12 +95,14 @@ class VertexPerformanceWriter final TFile* m_outputFile{nullptr}; ///< The output file TTree* m_outputTree{nullptr}; ///< The output tree - std::vector - m_diffx; ///< Difference in x positon between reco and true vtx - std::vector - m_diffy; ///< Difference in y positon between reco and true vtx - std::vector - m_diffz; ///< Difference in z positon between reco and true vtx + /// Difference in x positon between reco and true vtx + std::vector m_resX; + std::vector m_resY; + std::vector m_resZ; + + std::vector m_pullX; + std::vector m_pullY; + std::vector m_pullZ; std::vector m_truthX; std::vector m_truthY; @@ -112,8 +114,11 @@ class VertexPerformanceWriter final std::vector m_covXX; std::vector m_covYY; + std::vector m_covZZ; std::vector m_covXY; - std::vector m_covYX; + std::vector m_covXZ; + std::vector m_covYZ; + std::vector m_trackVtxMatchFraction; int m_nrecoVtx = -1; ///< Number of reconstructed vertices diff --git a/Examples/Io/Root/src/RootParticleReader.cpp b/Examples/Io/Root/src/RootParticleReader.cpp index ad3699a1a78..3b4914e987a 100644 --- a/Examples/Io/Root/src/RootParticleReader.cpp +++ b/Examples/Io/Root/src/RootParticleReader.cpp @@ -10,9 +10,11 @@ #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Utilities/PdgParticle.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/WhiteBoard.hpp" #include "ActsExamples/Utilities/Paths.hpp" +#include "ActsFatras/EventData/ProcessType.hpp" #include @@ -143,11 +145,14 @@ ActsExamples::ProcessCode ActsExamples::RootParticleReader::read( for (unsigned int i = 0; i < nParticles; i++) { SimParticle p; + p.setProcess(static_cast((*m_process)[i])); + p.setPdg(static_cast((*m_particleType)[i])); + p.setCharge((*m_q)[i]); + p.setMass((*m_m)[i]); p.setParticleId((*m_particleId)[i]); p.setPosition4((*m_vx)[i], (*m_vy)[i], (*m_vz)[i], (*m_vt)[i]); p.setDirection((*m_px)[i], (*m_py)[i], (*m_pz)[i]); p.setAbsoluteMomentum((*m_p)[i]); - p.setCharge((*m_q)[i]); particleContainer.insert(particleContainer.end(), p); priVtxCollection.push_back((*m_vertexPrimary)[i]); diff --git a/Examples/Io/Root/src/RootTrackParameterWriter.cpp b/Examples/Io/Root/src/RootTrackParameterWriter.cpp index 56ffb2796d7..7769a24cc99 100644 --- a/Examples/Io/Root/src/RootTrackParameterWriter.cpp +++ b/Examples/Io/Root/src/RootTrackParameterWriter.cpp @@ -192,9 +192,8 @@ ActsExamples::ProcessCode ActsExamples::RootTrackParameterWriter::writeT( m_t_charge = static_cast(particle.charge()); m_t_qop = m_t_charge / p; } else { - ACTS_WARNING("Truth particle with barcode " << particleId << "=" - << particleId.value() - << " not found!"); + ACTS_DEBUG("Truth particle with barcode " + << particleId << "=" << particleId.value() << " not found!"); } } diff --git a/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp b/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp index dd29978bdd6..4ae7e40c1c5 100644 --- a/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp +++ b/Examples/Io/Root/src/RootTrajectoryStatesWriter.cpp @@ -303,8 +303,8 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectoryStatesWriter::writeT( // Get the truth particle charge truthQ = static_cast(particle.charge()); } else { - ACTS_WARNING("Truth particle with barcode " - << barcode << "=" << barcode.value() << " not found!"); + ACTS_DEBUG("Truth particle with barcode " + << barcode << "=" << barcode.value() << " not found!"); } } @@ -424,12 +424,13 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectoryStatesWriter::writeT( // // local hit residual info auto H = state.effectiveProjector(); - auto resCov = state.effectiveCalibratedCovariance() + - H * covariance * H.transpose(); + auto hitCov = state.effectiveCalibratedCovariance(); + auto resCov = hitCov + H * covariance * H.transpose(); auto res = state.effectiveCalibrated() - H * parameters; + m_res_x_hit.push_back(res[Acts::eBoundLoc0]); m_err_x_hit.push_back( - sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0))); + sqrt(hitCov(Acts::eBoundLoc0, Acts::eBoundLoc0))); m_pull_x_hit.push_back( res[Acts::eBoundLoc0] / sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0))); @@ -440,7 +441,7 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectoryStatesWriter::writeT( sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1))); m_res_y_hit.push_back(res[Acts::eBoundLoc1]); m_err_y_hit.push_back( - sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1))); + sqrt(hitCov(Acts::eBoundLoc1, Acts::eBoundLoc1))); } else { float nan = std::numeric_limits::quiet_NaN(); m_pull_y_hit.push_back(nan); diff --git a/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp b/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp index e21e8ffb30e..ede3d881045 100644 --- a/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp +++ b/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp @@ -274,9 +274,14 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectorySummaryWriter::writeT( t_pT = t_p * perp(particle.unitDirection()); if (pSurface != nullptr) { + auto intersection = + pSurface->intersect(ctx.geoContext, particle.position(), + particle.unitDirection(), false); + auto position = intersection.intersection.position; + // get the truth perigee parameter - auto lpResult = pSurface->globalToLocal( - ctx.geoContext, particle.position(), particle.unitDirection()); + auto lpResult = pSurface->globalToLocal(ctx.geoContext, position, + particle.unitDirection()); if (lpResult.ok()) { t_d0 = lpResult.value()[Acts::BoundIndices::eBoundLoc0]; t_z0 = lpResult.value()[Acts::BoundIndices::eBoundLoc1]; @@ -285,15 +290,14 @@ ActsExamples::ProcessCode ActsExamples::RootTrajectorySummaryWriter::writeT( } } } else { - ACTS_WARNING("Truth particle with barcode " - << majorityParticleId << "=" - << majorityParticleId.value() - << " not found in the input collection!"); + ACTS_DEBUG("Truth particle with barcode " + << majorityParticleId << "=" << majorityParticleId.value() + << " not found in the input collection!"); } } if (not foundMajorityParticle) { - ACTS_WARNING("Truth particle for mj " << itraj << " subtraj " - << isubtraj << " not found!"); + ACTS_DEBUG("Truth particle for mj " << itraj << " subtraj " << isubtraj + << " not found!"); } // Push the corresponding truth particle info for the track. diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index 9afced301aa..def5f574117 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -78,6 +78,13 @@ else() target_sources(ActsPythonBindings PRIVATE src/SvgStub.cpp) endif() +if(ACTS_BUILD_PLUGIN_JSON AND ACTS_BUILD_PLUGIN_ACTSVG) + target_link_libraries(ActsPythonBindings PUBLIC ActsExamplesDetectoInspectors) + target_sources(ActsPythonBindings PRIVATE src/DetectorInspectors.cpp) +else() + target_sources(ActsPythonBindings PRIVATE src/DetectorInspectorsStub.cpp) +endif() + if(ACTS_BUILD_PLUGIN_DD4HEP AND ACTS_BUILD_EXAMPLES_DD4HEP) pybind11_add_module(ActsPythonBindingsDD4hep src/DD4hepComponent.cpp) target_link_libraries(ActsPythonBindingsDD4hep PUBLIC diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index 2f1396c093f..5d46ec3228d 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -819,6 +819,7 @@ def addKalmanTracks( inputProtoTracks: str = "truth_particle_tracks", multipleScattering: bool = True, energyLoss: bool = True, + clusters: str = None, logLevel: Optional[acts.logging.Level] = None, ) -> None: customLogLevel = acts.examples.defaultLogging(s, logLevel) @@ -848,13 +849,16 @@ def addKalmanTracks( inputSourceLinks="sourcelinks", inputProtoTracks=inputProtoTracks, inputInitialTrackParameters="estimatedparameters", + inputClusters=clusters if clusters is not None else "", outputTracks="kfTracks", pickTrack=-1, fit=acts.examples.makeKalmanFitterFunction( trackingGeometry, field, **kalmanOptions ), + calibrator=acts.examples.makePassThroughCalibrator(), ) s.addAlgorithm(fitAlg) + s.addWhiteboardAlias("tracks", fitAlg.config.outputTracks) trackConverter = acts.examples.TracksToTrajectories( level=customLogLevel(), @@ -862,7 +866,6 @@ def addKalmanTracks( outputTrajectories="kfTrajectories", ) s.addAlgorithm(trackConverter) - s.addWhiteboardAlias("trajectories", trackConverter.config.outputTrajectories) return s @@ -896,6 +899,7 @@ def addTruthTrackingGsf( outputTracks="gsf_tracks", pickTrack=-1, fit=acts.examples.makeGsfFitterFunction(trackingGeometry, field, **gsfOptions), + calibrator=acts.examples.makePassThroughCalibrator(), ) s.addAlgorithm(gsfAlg) @@ -961,10 +965,11 @@ def addCKFTracks( inputInitialTrackParameters="estimatedparameters", outputTracks="ckfTracks", findTracks=acts.examples.TrackFindingAlgorithm.makeTrackFinderFunction( - trackingGeometry, field + trackingGeometry, field, customLogLevel() ), ) s.addAlgorithm(trackFinder) + s.addWhiteboardAlias("tracks", trackFinder.config.outputTracks) trackConverter = acts.examples.TracksToTrajectories( level=customLogLevel(), @@ -982,6 +987,7 @@ def addCKFTracks( outputTracks="selectedTracks", logLevel=customLogLevel(), ) + s.addWhiteboardAlias("tracks", trackSelector.config.outputTracks) selectedTrackConverter = acts.examples.TracksToTrajectories( level=customLogLevel(), @@ -1273,7 +1279,7 @@ def addAmbiguityResolution( alg = AmbiguityResolutionAlgorithm( level=customLogLevel(), - inputTracks="selectedTracks", + inputTracks="tracks", outputTracks="filteredTrajectories", **acts.examples.defaultKWArgs( maximumSharedHits=config.maximumSharedHits, @@ -1329,7 +1335,7 @@ def addAmbiguityResolutionML( alg = AmbiguityResolutionMLAlgorithm( level=customLogLevel(), - inputTracks="selectedTracks", + inputTracks="tracks", inputDuplicateNN=onnxModelFile, outputTracks="filteredTrajectoriesML", **acts.examples.defaultKWArgs( @@ -1384,7 +1390,7 @@ def addAmbiguityResolutionMLDBScan( alg = AmbiguityResolutionMLDBScanAlgorithm( level=customLogLevel(), - inputTracks="selectedTracks", + inputTracks="tracks", inputDuplicateNN=onnxModelFile, outputTracks="filteredTrajectoriesMLDBScan", **acts.examples.defaultKWArgs( @@ -1427,12 +1433,15 @@ def addAmbiguityResolutionMLDBScan( def addVertexFitting( s, field, - outputDirRoot: Optional[Union[Path, str]] = None, trajectories: Optional[str] = "trajectories", trackParameters: Optional[str] = None, associatedParticles: Optional[str] = None, + outputProtoVertices: str = "protovertices", + outputVertices: str = "fittedVertices", + outputTime: str = "outputTime", vertexFinder: VertexFinder = VertexFinder.Truth, trackSelectorRanges: Optional[TrackSelectorRanges] = None, + outputDirRoot: Optional[Union[Path, str]] = None, logLevel: Optional[acts.logging.Level] = None, ) -> None: """This function steers the vertex fitting @@ -1483,14 +1492,14 @@ def addVertexFitting( inputParticles = "particles_input" selectedParticles = "particles_selected" - outputVertices = "fittedVertices" + if vertexFinder != VertexFinder.AMVF: + outputTime = "" - outputTime = "" if vertexFinder == VertexFinder.Truth: findVertices = TruthVertexFinder( level=customLogLevel(), inputParticles=selectedParticles, - outputProtoVertices="protovertices", + outputProtoVertices=outputProtoVertices, excludeSecondaries=True, ) s.addAlgorithm(findVertices) @@ -1509,18 +1518,17 @@ def addVertexFitting( bField=field, inputTrajectories=trajectories, inputTrackParameters=trackParameters, - outputProtoVertices="protovertices", + outputProtoVertices=outputProtoVertices, outputVertices=outputVertices, ) s.addAlgorithm(findVertices) elif vertexFinder == VertexFinder.AMVF: - outputTime = "outputTime" findVertices = AdaptiveMultiVertexFinderAlgorithm( level=customLogLevel(), bField=field, inputTrajectories=trajectories, inputTrackParameters=trackParameters, - outputProtoVertices="protovertices", + outputProtoVertices=outputProtoVertices, outputVertices=outputVertices, outputTime=outputTime, ) @@ -1547,8 +1555,8 @@ def addVertexFitting( inputTrackParameters=trackParameters, inputAssociatedTruthParticles=associatedParticles, inputVertices=outputVertices, - minTrackVtxMatchFraction=0.5 if associatedParticles else 0.0, inputTime=outputTime, + minTrackVtxMatchFraction=0.5 if associatedParticles else 0.0, treeName="vertexing", filePath=str(outputDirRoot / "performance_vertexing.root"), ) diff --git a/Examples/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index db9f3b079ef..8fa5c6832a2 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -571,9 +571,12 @@ def addGeant4( inputParticles: str = "particles_input", preSelectParticles: Optional[ParticleSelectorConfig] = ParticleSelectorConfig(), postSelectParticles: Optional[ParticleSelectorConfig] = None, + recordHitsOfSecondaries=True, + keepParticlesWithoutHits=True, outputDirCsv: Optional[Union[Path, str]] = None, outputDirRoot: Optional[Union[Path, str]] = None, logLevel: Optional[acts.logging.Level] = None, + killVolume: Optional[acts.Volume] = None, ) -> None: """This function steers the detector simulation using Geant4 @@ -595,6 +598,8 @@ def addGeant4( the output folder for the Csv output, None triggers no output outputDirRoot : Path|str, path, None the output folder for the Root output, None triggers no output + killVolume: acts.Volume, None + if given, particles are killed when going outside of this volume. """ from acts.examples.geant4 import Geant4Simulation, makeGeant4SimulationConfig @@ -627,6 +632,9 @@ def addGeant4( magneticField=field, volumeMappings=volumeMappings, materialMappings=materialMappings, + killVolume=killVolume, + recordHitsOfSecondaries=recordHitsOfSecondaries, + keepParticlesWithoutHits=keepParticlesWithoutHits, ) g4conf.outputSimHits = "simhits" g4conf.outputParticlesInitial = "particles_initial" diff --git a/Examples/Python/src/Base.cpp b/Examples/Python/src/Base.cpp index 83c7420bb8e..00d2a273da9 100644 --- a/Examples/Python/src/Base.cpp +++ b/Examples/Python/src/Base.cpp @@ -226,7 +226,8 @@ void addPdgParticle(Acts::Python::Context& ctx) { .value("eNeutron", Acts::PdgParticle::eNeutron) .value("eAntiNeutron", Acts::PdgParticle::eAntiNeutron) .value("eProton", Acts::PdgParticle::eProton) - .value("eAntiProton", Acts::PdgParticle::eAntiProton); + .value("eAntiProton", Acts::PdgParticle::eAntiProton) + .value("eLead", Acts::PdgParticle::eLead); } void addAlgebra(Acts::Python::Context& ctx) { diff --git a/Examples/Python/src/DetectorInspectors.cpp b/Examples/Python/src/DetectorInspectors.cpp new file mode 100644 index 00000000000..d0d90634bbe --- /dev/null +++ b/Examples/Python/src/DetectorInspectors.cpp @@ -0,0 +1,32 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Plugins/Python/Utilities.hpp" +#include "ActsExamples/DetectorInspectors/SurfaceIndexing.hpp" + +#include + +#include +#include + +namespace py = pybind11; +using namespace ActsExamples; + +namespace Acts::Python { +void addDetectorInspectors(Context& ctx) { + auto [m, mex] = ctx.get("main", "examples"); + { + auto ci = py::class_>( + mex, "CylindricalDetectorIndexing") + .def(py::init()) + .def("inspect", &CylindricalDetectorIndexing::inspect); + } +} +} // namespace Acts::Python diff --git a/Examples/Python/src/DetectorInspectorsStub.cpp b/Examples/Python/src/DetectorInspectorsStub.cpp new file mode 100644 index 00000000000..67f79b48018 --- /dev/null +++ b/Examples/Python/src/DetectorInspectorsStub.cpp @@ -0,0 +1,13 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Python/Utilities.hpp" + +namespace Acts::Python { +void addDetectorInspectors(Context& /*ctx*/) {} +} // namespace Acts::Python diff --git a/Examples/Python/src/Geant4Component.cpp b/Examples/Python/src/Geant4Component.cpp index aa5132c7788..9d0b824c55c 100644 --- a/Examples/Python/src/Geant4Component.cpp +++ b/Examples/Python/src/Geant4Component.cpp @@ -15,11 +15,13 @@ #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Framework/IContextDecorator.hpp" +#include "ActsExamples/Geant4/ActsSteppingActionList.hpp" #include "ActsExamples/Geant4/GdmlDetectorConstruction.hpp" #include "ActsExamples/Geant4/Geant4Simulation.hpp" #include "ActsExamples/Geant4/MagneticFieldWrapper.hpp" #include "ActsExamples/Geant4/MaterialPhysicsList.hpp" #include "ActsExamples/Geant4/MaterialSteppingAction.hpp" +#include "ActsExamples/Geant4/ParticleKillAction.hpp" #include "ActsExamples/Geant4/ParticleTrackingAction.hpp" #include "ActsExamples/Geant4/SensitiveSteppingAction.hpp" #include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp" @@ -79,12 +81,12 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { ACTS_PYTHON_DECLARE_ALGORITHM( Geant4Simulation, mod, "Geant4Simulation", outputSimHits, outputParticlesInitial, outputParticlesFinal, outputMaterialTracks, - randomNumbers, runManager, primaryGeneratorAction, runActions, - eventActions, trackingActions, steppingActions, detectorConstruction, - magneticField, sensitiveSurfaceMapper); + randomNumbers, runManager, primaryGeneratorAction, runAction, eventAction, + trackingAction, steppingAction, detectorConstruction, magneticField, + sensitiveSurfaceMapper); auto makeGeant4Config = - [](Acts::Logging::Level& level, + [](const Acts::Logger& logger, std::shared_ptr randomNumbers, G4VUserDetectorConstruction* detector, G4VUserPhysicsList* physicsList, const SimParticleTranslation::Config& prCfg) @@ -99,7 +101,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { // Set the primarty generator g4Cfg.primaryGeneratorAction = new SimParticleTranslation( - prCfg, Acts::getDefaultLogger("SimParticleTranslation", level)); + prCfg, logger.cloneWithSuffix("SimParticleTranslation")); g4Cfg.detectorConstruction = detector; return g4Cfg; @@ -112,8 +114,9 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { std::shared_ptr randomNumbers, const std::string& inputParticles, const std::string& outputMaterialTracks) { + auto logger = Acts::getDefaultLogger("Geant4", level); auto physicsList = new MaterialPhysicsList( - Acts::getDefaultLogger("MaterialPhysicsList", level)); + logger->cloneWithSuffix("MaterialPhysicsList")); // Read the particle from the generator SimParticleTranslation::Config g4PrCfg; @@ -121,15 +124,15 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { g4PrCfg.forcedCharge = 0.; g4PrCfg.forcedMass = 0.; - auto g4Cfg = makeGeant4Config(level, std::move(randomNumbers), detector, - physicsList, g4PrCfg); + auto g4Cfg = makeGeant4Config(*logger, std::move(randomNumbers), + detector, physicsList, g4PrCfg); g4Cfg.inputParticles = inputParticles; MaterialSteppingAction::Config mStepCfg; mStepCfg.excludeMaterials = {"Air", "Vacuum"}; auto steppingAction = new MaterialSteppingAction( - mStepCfg, Acts::getDefaultLogger("MaterialSteppingAction", level)); - g4Cfg.steppingActions = {steppingAction}; + mStepCfg, logger->cloneWithSuffix("MaterialSteppingAction")); + g4Cfg.steppingAction = steppingAction; // Set the material tracks at output g4Cfg.outputMaterialTracks = outputMaterialTracks; @@ -142,34 +145,46 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { mod.def( "makeGeant4SimulationConfig", [makeGeant4Config]( - Acts::Logging::Level& level, G4VUserDetectorConstruction* detector, + Acts::Logging::Level level, G4VUserDetectorConstruction* detector, std::shared_ptr randomNumbers, const std::string& inputParticles, const std::shared_ptr& trackingGeometry, const std::shared_ptr& magneticField, const std::vector& volumeMappings, - const std::vector& materialMappings) { - auto physicsList = new FTFP_BERT(); - - // Read the particle from the generator - SimParticleTranslation::Config g4PrCfg; + const std::vector& materialMappings, + std::shared_ptr killVolume, + bool recordHitsOfSecondaries, bool keepParticlesWithoutHits) { + auto logger = Acts::getDefaultLogger("Geant4", level); - auto g4Cfg = makeGeant4Config(level, std::move(randomNumbers), detector, - physicsList, g4PrCfg); + auto physicsList = new FTFP_BERT(); + auto g4Cfg = + makeGeant4Config(*logger, std::move(randomNumbers), detector, + physicsList, SimParticleTranslation::Config{}); g4Cfg.inputParticles = inputParticles; - ParticleTrackingAction::Config g4TrackCfg; - ParticleTrackingAction* particleAction = new ParticleTrackingAction( - g4TrackCfg, - Acts::getDefaultLogger("ParticleTrackingAction", level)); - g4Cfg.trackingActions.push_back(particleAction); + // Particle action + ParticleTrackingAction::Config trackingCfg; + trackingCfg.keepParticlesWithoutHits = keepParticlesWithoutHits; + g4Cfg.trackingAction = new ParticleTrackingAction( + trackingCfg, logger->cloneWithSuffix("ParticleTracking")); + + // Stepping actions + ActsSteppingActionList::Config steppingCfg; SensitiveSteppingAction::Config g4StepCfg; - G4UserSteppingAction* steppingAction = new SensitiveSteppingAction( - g4StepCfg, - Acts::getDefaultLogger("SensitiveSteppingAction", level)); - g4Cfg.steppingActions.push_back(steppingAction); + g4StepCfg.charged = true; + g4StepCfg.neutral = false; + g4StepCfg.primary = true; + g4StepCfg.secondary = recordHitsOfSecondaries; + steppingCfg.actions.push_back(new SensitiveSteppingAction( + g4StepCfg, logger->cloneWithSuffix("SensitiveStepping"))); + + steppingCfg.actions.push_back( + new ParticleKillAction(ParticleKillAction::Config{killVolume}, + logger->cloneWithSuffix("Killer"))); + + g4Cfg.steppingAction = new ActsSteppingActionList(steppingCfg); // An ACTS Magnetic field is provided if (magneticField) { @@ -194,8 +209,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { g4Cfg.sensitiveSurfaceMapper = std::make_shared( - ssmCfg, - Acts::getDefaultLogger("SensitiveSurfaceMapper", level)); + ssmCfg, logger->cloneWithSuffix("SensitiveSurfaceMapper")); } return g4Cfg; @@ -203,7 +217,10 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) { "level"_a, "detector"_a, "randomNumbers"_a, "inputParticles"_a, py::arg("trackingGeometry") = nullptr, py::arg("magneticField") = nullptr, py::arg("volumeMappings") = std::vector{}, - py::arg("materialMappings") = std::vector{}); + py::arg("materialMappings") = std::vector{}, + py::arg("killVolume") = nullptr, + py::arg("recordHitsOfSecondaries") = true, + py::arg("keepParticlesWithoutHits") = true); { using Detector = ActsExamples::Telescope::TelescopeDetector; diff --git a/Examples/Python/src/Geometry.cpp b/Examples/Python/src/Geometry.cpp index a2a0c5e36ca..31ce9dc3982 100644 --- a/Examples/Python/src/Geometry.cpp +++ b/Examples/Python/src/Geometry.cpp @@ -9,8 +9,10 @@ #include "Acts/Detector/Detector.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/ProtoDetector.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Geometry/Volume.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -71,6 +73,19 @@ void addGeometry(Context& ctx) { }); } + { + py::class_>(m, "Volume") + .def_static( + "makeCylinderVolume", + [](double r, double halfZ) { + auto bounds = + std::make_shared(0, r, halfZ); + return std::make_shared(Transform3::Identity(), + bounds); + }, + "r"_a, "halfZ"_a); + } + { py::class_>( diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index 08b4dd39fdc..327dc61eca6 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -91,6 +91,7 @@ void addMagneticField(Context& ctx); void addMaterial(Context& ctx); void addOutput(Context& ctx); void addDetector(Context& ctx); +void addDetectorInspectors(Context& ctx); void addExampleAlgorithms(Context& ctx); void addInput(Context& ctx); void addGenerators(Context& ctx); @@ -263,6 +264,7 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addMaterial(ctx); addOutput(ctx); addDetector(ctx); + addDetectorInspectors(ctx); addExampleAlgorithms(ctx); addInput(ctx); addGenerators(ctx); diff --git a/Examples/Python/src/TrackFinding.cpp b/Examples/Python/src/TrackFinding.cpp index ab772d4d40b..08fa34db2a0 100644 --- a/Examples/Python/src/TrackFinding.cpp +++ b/Examples/Python/src/TrackFinding.cpp @@ -245,13 +245,22 @@ void addTrackFinding(Context& ctx) { using Alg = ActsExamples::TrackFindingAlgorithm; using Config = Alg::Config; - auto alg = py::class_>( - mex, "TrackFindingAlgorithm") - .def(py::init(), - py::arg("config"), py::arg("level")) - .def_property_readonly("config", &Alg::config) - .def_static("makeTrackFinderFunction", - &Alg::makeTrackFinderFunction); + auto alg = + py::class_>( + mex, "TrackFindingAlgorithm") + .def(py::init(), + py::arg("config"), py::arg("level")) + .def_property_readonly("config", &Alg::config) + .def_static("makeTrackFinderFunction", + [](std::shared_ptr + trackingGeometry, + std::shared_ptr + magneticField, + Logging::Level level) { + return Alg::makeTrackFinderFunction( + trackingGeometry, magneticField, + *Acts::getDefaultLogger("TrackFinding", level)); + }); py::class_>( diff --git a/Examples/Python/src/TrackFitting.cpp b/Examples/Python/src/TrackFitting.cpp index 98cefc1ccf9..77d404b8a30 100644 --- a/Examples/Python/src/TrackFitting.cpp +++ b/Examples/Python/src/TrackFitting.cpp @@ -35,10 +35,11 @@ void addTrackFitting(Context& ctx) { inputSimHits, inputMeasurementSimHitsMap, outputProtoTracks); - ACTS_PYTHON_DECLARE_ALGORITHM( - ActsExamples::TrackFittingAlgorithm, mex, "TrackFittingAlgorithm", - inputMeasurements, inputSourceLinks, inputProtoTracks, - inputInitialTrackParameters, outputTracks, fit, pickTrack); + ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::TrackFittingAlgorithm, mex, + "TrackFittingAlgorithm", inputMeasurements, + inputSourceLinks, inputProtoTracks, + inputInitialTrackParameters, inputClusters, + outputTracks, fit, pickTrack, calibrator); ACTS_PYTHON_DECLARE_ALGORITHM(ActsExamples::RefittingAlgorithm, mex, "RefittingAlgorithm", inputTracks, outputTracks, @@ -66,9 +67,17 @@ void addTrackFitting(Context& ctx) { py::arg("reverseFilteringMomThreshold"), py::arg("freeToBoundCorrection"), py::arg("level")); - py::enum_(mex, "FinalReductionMethod") - .value("mean", Acts::FinalReductionMethod::eMean) - .value("maxWeight", Acts::FinalReductionMethod::eMaxWeight); + py::class_>( + mex, "MeasurementCalibrator"); + + mex.def("makePassThroughCalibrator", + []() -> std::shared_ptr { + return std::make_shared(); + }); + + py::enum_(mex, "FinalReductionMethod") + .value("mean", Acts::MixtureReductionMethod::eMean) + .value("maxWeight", Acts::MixtureReductionMethod::eMaxWeight); py::class_(mex, "AtlasBetheHeitlerApprox") .def_static("loadFromFiles", @@ -83,9 +92,9 @@ void addTrackFitting(Context& ctx) { [](std::shared_ptr trackingGeometry, std::shared_ptr magneticField, BetheHeitlerApprox betheHeitlerApprox, std::size_t maxComponents, - double weightCutoff, Acts::FinalReductionMethod finalReductionMethod, - bool abortOnError, bool disableAllMaterialHandling, - Logging::Level level) { + double weightCutoff, + Acts::MixtureReductionMethod finalReductionMethod, bool abortOnError, + bool disableAllMaterialHandling, Logging::Level level) { return ActsExamples::makeGsfFitterFunction( trackingGeometry, magneticField, betheHeitlerApprox, maxComponents, weightCutoff, finalReductionMethod, abortOnError, diff --git a/Examples/Python/tests/requirements.in b/Examples/Python/tests/requirements.in index 06a308ae561..d0d90a00a84 100644 --- a/Examples/Python/tests/requirements.in +++ b/Examples/Python/tests/requirements.in @@ -3,3 +3,4 @@ pytest-check uproot awkward pytest-rerunfailures +pytest-xdist diff --git a/Examples/Python/tests/requirements.txt b/Examples/Python/tests/requirements.txt index c9b74234224..e7b0105df1b 100644 --- a/Examples/Python/tests/requirements.txt +++ b/Examples/Python/tests/requirements.txt @@ -1,13 +1,15 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: # -# pip-compile requirements.in +# pip-compile Examples/Python/tests/requirements.in # attrs==21.4.0 # via pytest awkward==1.7.0 - # via -r requirements.in + # via -r Examples/Python/tests/requirements.in +execnet==1.9.0 + # via pytest-xdist iniconfig==1.1.1 # via pytest numpy==1.22.2 @@ -24,17 +26,20 @@ pyparsing==3.0.7 # via packaging pytest==7.0.1 # via - # -r requirements.in + # -r Examples/Python/tests/requirements.in # pytest-check # pytest-rerunfailures + # pytest-xdist pytest-check==1.0.4 - # via -r requirements.in + # via -r Examples/Python/tests/requirements.in pytest-rerunfailures==10.2 - # via -r requirements.in + # via -r Examples/Python/tests/requirements.in +pytest-xdist==3.2.1 + # via -r Examples/Python/tests/requirements.in tomli==2.0.1 # via pytest uproot==4.1.9 - # via -r requirements.in + # via -r Examples/Python/tests/requirements.in # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index 66ff019fcb0..a55073660bc 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -2,9 +2,9 @@ test_pythia8__pythia8_particles.root: bf2ddabd7ad7aee83e18c3fb83627fed6541c776ab test_fatras__particles_final.root: 0bfe4bbac90b112fd960b39cd5edd7d19fb9b8e308f8315db0871bfae061b550 test_fatras__particles_initial.root: 0e2d9974fdd4aa5549c5c05817ec54fe6c7d9cbe7969647f227956167e31110d test_fatras__hits.root: d46b760e1647ab94c49d6976a78ee5a6178d41c45f2238f86f3987938a7901fb -test_geant4__particles_final.root: ba00c21e6f0696316000f5ff7f0fb27df72d191157043bfa815844d8897056b7 -test_geant4__particles_initial.root: 9eea306cd4ece5f509d73c0cc8de1bd321b2b40646bfcd5cc148a7ccf77980d8 -test_geant4__hits.root: 6ab07fe4fe596c6948d7c4b821e51f6fae4ba69ac5cc62c07798c7de1cedfccb +test_geant4__particles_final.root: 94069908d2c77d5dd54193fbaa0d22d401b98df17c148fff6b48dbfa9e0b8a55 +test_geant4__particles_initial.root: 0c70278caa0adebcae8ce64e8f262b99daab0c905bc4aefb9600bcb22106e7c6 +test_geant4__hits.root: 0ec94c05c6d657fbf56ac9407a66c6a19514c3cd421cd47de905952824902575 test_seeding__estimatedparams.root: fe5ce98fb6b6886e05f5e6c17e037c898017a48201534b07bc56ccbf672c7a33 test_seeding__performance_seeding.root: 992f9c611d30dde0d3f3ab676bab19ada61ab6a4442828e27b65ec5e5b7a2880 test_seeding__particles.root: 74e08ee12bdaf9f7d273369c71c742df345475c8b73bb7ce223619d0dd1f87ee @@ -22,22 +22,22 @@ test_itk_seeding__particles_final.root: e7699af6835ca90f730ad5186989c611d782e8b9 test_itk_seeding__particles_initial.root: 88315e93ed4cb5d40a8721502048a9d1fc100e0a7d504e25fd4502c8302f1578 test_propagation__propagation_steps.root: 174301b25784dbb881196b658f2d7f99c0a2ea688a0129e6110fc19aa5cf8e54 test_material_recording__geant4_material_tracks.root: e411152d370775463c22b19a351dfc7bfe40b51985e10a7c1a010aebde80715d -test_truth_tracking_kalman[generic-0.0]__trackstates_fitter.root: c1d53be0a0c909c6b4733a927794f66e9fe413dd6007a0e28eca4f5b9aa83c37 -test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: 7b419d783c08e3ac7145ba64d67a08baff698c5d888fb35fef280714b727827a +test_truth_tracking_kalman[generic-0.0]__trackstates_fitter.root: 3f99b6c0e1a6bdfb339e3d162e034b72a034c9ee2d56f0579322f0915b360d8d +test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: a6794a05025e86fd480d42c510d38e5de551de351b06e9f63684bed27e3113a5 test_truth_tracking_kalman[generic-0.0]__performance_track_finder.root: 7fc6f717723c9eddcbf44820b384b373cee6f04b72f79902f938f35e3ff9b470 -test_truth_tracking_kalman[generic-1000.0]__trackstates_fitter.root: f7032af2046d504342760403b5bab63c357774b14dca361e292b30b0dfe768e4 -test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: a15a3f361d0bbf6a1f7a43e738fd9d754bc04553ffe1e11d37a97193cb077e35 +test_truth_tracking_kalman[generic-1000.0]__trackstates_fitter.root: 8c7e4f81a2e089fb692fe262eb0100317b41f9e076fd2a03dd0df73bb74656a6 +test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: b533d624fd0dfe69d737c79272f157de7a8bf8bf7ad6e41485b94bffa4bb30a0 test_truth_tracking_kalman[generic-1000.0]__performance_track_finder.root: 7fc6f717723c9eddcbf44820b384b373cee6f04b72f79902f938f35e3ff9b470 -test_truth_tracking_kalman[odd-0.0]__trackstates_fitter.root: f41fd2e4e50dde14280834cf5713d3db3b6650797132ab87a351479589a3fd30 -test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: b638239caa71e0636cc1f4669544ec5a217aeeda63c69d16e90052a70db83562 +test_truth_tracking_kalman[odd-0.0]__trackstates_fitter.root: e25f23687eb5ea893687f806ba202c4c4f88e003ec4e707617f6b3b17aee1143 +test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: 88162a46905043ee3bbf7a8387e4006ee4b81a61c1788ba106c94eb99db96541 test_truth_tracking_kalman[odd-0.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 -test_truth_tracking_kalman[odd-1000.0]__trackstates_fitter.root: 8aaf7ba26ef36f590f2aa95b50a2fbdbb44112071076f10b46dbe03d7d118e94 -test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: 11f66720a86d762e1a66a104df1e39eab9b8f4e35ad968771c26bffada10bd40 +test_truth_tracking_kalman[odd-1000.0]__trackstates_fitter.root: 25dd2cd396133da19b8f320d14d673a9cc0f6c1eff81696529e7ff404cec9e39 +test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: 6efe3eae003bc1bd435b4407995a2f26c9eae4f918440ffa7aa535e25ab631dd test_truth_tracking_kalman[odd-1000.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 -test_truth_tracking_gsf[generic]__trackstates_gsf.root: 39af9f19e7d5bdb691ab74afcae0722a437dc020d23ab2e0d7c5f8275ef24365 -test_truth_tracking_gsf[generic]__tracksummary_gsf.root: a34a60d07c61bef149603fbfd475cc30202a8eb7a7dc7a116b48285151649cb6 -test_truth_tracking_gsf[odd]__trackstates_gsf.root: 155ad00f54d5cf6b3bf022c5d6c897b1b5e630754ba8ccd4ecc740c363771f4f -test_truth_tracking_gsf[odd]__tracksummary_gsf.root: f382fa66c01f4183e8d2763c89cda075d671c1e4d39116544c026007e60a0bd1 +test_truth_tracking_gsf[generic]__trackstates_gsf.root: e158422929fefae3977c73169113eb91a28f0855a4cd9bb7f8300e0a920dbc24 +test_truth_tracking_gsf[generic]__tracksummary_gsf.root: 93a2f447402fe1fb0e8db9508c061b5a63dd5160eea566b8525ee1b0be467dce +test_truth_tracking_gsf[odd]__trackstates_gsf.root: 19efc9a17c2c24132e90e32f071bc1dfade2a6af3cb718033a0041a3db825d28 +test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 0c2934cbeebc7a21bcec6acfc35aefa0eaa2bc79dfa2b731e8f6d037422040bd test_particle_gun__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec test_material_mapping__material-map_tracks.root: 4e1c866038f0c06b099aa74fd01c3d875f07b89f54898f90debd9b558d8e4025 test_material_mapping__propagation-material.root: 646b8e2bbacec40d0bc4132236f9ab3f03b088e656e6e9b80c47ae03eaf6eab5 @@ -45,23 +45,23 @@ test_volume_material_mapping__material-map-volume_tracks.root: b95561a6247df9e35 test_volume_material_mapping__propagation-volume-material.root: b7597dada372d1b4aaec2c4fc3c0db830ce147ecf515c367ac6ba8ffc2708302 test_digitization_example__measurements.root: 96c75125b172d206f3e8c0458ace8f7e7687a5ed651dc00200d08682b5275e9d test_digitization_example_input__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec -test_digitization_example_input__measurements.root: 6c156d872cd1b160e6a573752dab164081469da49b7a1fb4c595a043da69c19b -test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: 6caf89b0590b8c1cc833e1f13bd1110185c2eaa064dd06b9f47a6b1693bbd941 -test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 2b24372052a4166c2dca2df3deb30c85491699b09b2c373da1cbcab1a7ca4db3 +test_digitization_example_input__measurements.root: 97d695ea55114aa3cb6c967c43e820472ceb8129afbb4f1f22bf1b3eca55ced9 +test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: 5fd202a141ca98db3c3dd9c567eecfa94aecd38099cfa8cf5d88236097824498 +test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 329378d774d511a91adadd3f524f8dfc218b42a62e2072daff315951f9c62c97 test_ckf_tracks_example[generic-full_seeding]__performance_seeding_trees.root: 0e0676ffafdb27112fbda50d1cf627859fa745760f98073261dcf6db3f2f991e -test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: b60179a4d3e7ca0f7c5e9a533ece0293052ea5a97a730303cce09813485258ca -test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: d2b6780ecd666e8c55ba2ec3bfc2404ac4ffcdf6e18d1d787ecdb4f5f4af7e3a +test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: 96c04ba1772465f791f8582e10b1741d3d5c8deb95bab8de36656642e5f83a45 +test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 273432afb39a1f9b93ce4efbf1f038c9b2b1a96aae7c858447405f923132f687 test_ckf_tracks_example[generic-truth_estimated]__performance_seeding.root: 1facb05c066221f6361b61f015cdf0918e94d9f3fce2269ec7b6a4dffeb2bc7e -test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: c8c77e313355f946caa39e102340d36654d8faa02460c1449c9b23441026b590 -test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 972fe113560071f0ae8fc9ee80c8e0ea1ad04469869738c9c5ca4ab305c41a56 -test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: a7c6e6834b990c83ceefe093ccbf39e7e2b6e1a59e2446dc6da1ba025033f15b -test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 7ee63de00941cfb690a5ac1cae4fff835c389896749e8aebe78cf877b401088c +test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 5dd89134280d06beb6866242c8091f2494bcc93bb37cc19bdd3573afe4fc622c +test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 74c86d8b88874d936250dad90aa7a650cb6f4085c12583381923d31baca7c8a2 +test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: f425ecd386cb32bcd7fe72f64a6e05414398cd2f07e709953c2d80cb4b0785c4 +test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 4a258db4545e50b5a6d4cbe7705c38aafbbc11d803d9f6cf19b31aff03350bf1 test_ckf_tracks_example[odd-full_seeding]__performance_seeding_trees.root: 43c58577aafe07645e5660c4f43904efadf91d8cda45c5c04c248bbe0f59814f -test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: f9ac9ed856147587d98a5d86d4a5936ac5954071fa31967b440516b2a88698aa -test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: cd4c8340933835afe42056a5f0b723fea60727706dd6c96cb4e874a3ee3318f7 +test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 3e3bc5bcdde16f0ca74b35b20dcae3760d57e28eff95fa617d9e37f6dfb4bc52 +test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 0e423f659bc2fb9ead9565e883dce31c8004a107384cd524f320ad50bf8ea053 test_ckf_tracks_example[odd-truth_estimated]__performance_seeding.root: 1a36b7017e59f1c08602ef3c2cb0483c51df248f112e3780c66594110719c575 -test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 67319e2c37469c604fff999a53aa91be4a79bcad447229601c6fb16c80fe08f6 -test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: 48b7bb655204ff89c7c0c474366edb7cea8e1a9ed40f2203b6f05efedc7cf3c7 +test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 2ec0661cd604c5e3b867a857c9aa2005e682ed95296d852e5f52a353c200b0cc +test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: 84ca3fab8275f35ae9d500e161b16a0ce9b3681fce00c3d63e24fca712d965ec test_vertex_fitting_reading[Truth-False-100]__performance_vertexing.root: 76ef6084d758dfdfc0151ddec2170e12d73394424e3dac4ffe46f0f339ec8293 test_vertex_fitting_reading[Iterative-False-100]__performance_vertexing.root: 60372210c830a04f95ceb78c6c68a9b0de217746ff59e8e73053750c837b57eb test_vertex_fitting_reading[Iterative-True-100]__performance_vertexing.root: e34f217d524a5051dbb04a811d3407df3ebe2cc4bb7f54f6bda0847dbd7b52c3 diff --git a/Examples/Python/tests/test_base.py b/Examples/Python/tests/test_base.py index ba0f3815df9..f14e04df7e8 100644 --- a/Examples/Python/tests/test_base.py +++ b/Examples/Python/tests/test_base.py @@ -12,7 +12,7 @@ def test_logging(): def test_pgd_particle(): - assert len(acts.PdgParticle.__members__) == 16 + assert len(acts.PdgParticle.__members__) == 17 def test_algebra(): diff --git a/Examples/Python/tests/test_examples.py b/Examples/Python/tests/test_examples.py index fec29eac43b..927588853e6 100644 --- a/Examples/Python/tests/test_examples.py +++ b/Examples/Python/tests/test_examples.py @@ -6,6 +6,8 @@ import urllib.request import subprocess import sys +import re +import collections import pytest @@ -181,12 +183,16 @@ def test_geant4(tmp_path, assert_root_hash): assert script.exists() env = os.environ.copy() env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script)], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + try: + subprocess.check_call( + [sys.executable, str(script)], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise assert_csv_output(csv, "particles_final") assert_csv_output(csv, "particles_initial") @@ -509,12 +515,16 @@ def test_event_recording(tmp_path): env = os.environ.copy() env["NEVENTS"] = "1" env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script)], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + try: + subprocess.check_call( + [sys.executable, str(script)], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise from acts.examples.hepmc3 import HepMC3AsciiReader @@ -1082,13 +1092,17 @@ def test_full_chain_odd_example(tmp_path): ) assert script.exists() env = os.environ.copy() - env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script), "-n1"], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + env["ACTS_LOG_FAILURE_THRESHOLD"] = "ERROR" + try: + subprocess.check_call( + [sys.executable, str(script), "-n1"], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise @pytest.mark.skipif( @@ -1110,13 +1124,27 @@ def test_full_chain_odd_example_pythia_geant4(tmp_path): ) assert script.exists() env = os.environ.copy() - env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script), "-n1", "--geant4", "--ttbar"], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + env["ACTS_LOG_FAILURE_THRESHOLD"] = "ERROR" + try: + stdout = subprocess.check_output( + [sys.executable, str(script), "-n1", "--geant4", "--ttbar"], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + stdout = stdout.decode("utf-8") + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise + + # collect and compare known errors + errors = [] + error_regex = re.compile(r"^\d\d:\d\d:\d\d\s+(\w+)\s+ERROR\s+", re.MULTILINE) + for match in error_regex.finditer(stdout): + (algo,) = match.groups() + errors.append(algo) + errors = collections.Counter(errors) + assert dict(errors) == {}, stdout @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep not set up") @@ -1140,13 +1168,17 @@ def test_ML_Ambiguity_Solver(tmp_path, assert_root_hash): ) assert script.exists() env = os.environ.copy() - env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script), "-n5", "--MLSolver"], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + env["ACTS_LOG_FAILURE_THRESHOLD"] = "ERROR" + try: + subprocess.check_call( + [sys.executable, str(script), "-n5", "--MLSolver"], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise rfp = tmp_path / output_dir / root_file assert rfp.exists() @@ -1201,12 +1233,16 @@ def test_exatrkx(tmp_path, trk_geo, field, assert_root_hash, backend): env = os.environ.copy() env["ACTS_LOG_FAILURE_THRESHOLD"] = "WARNING" - subprocess.check_call( - [sys.executable, str(script), backend], - cwd=tmp_path, - env=env, - stderr=subprocess.STDOUT, - ) + try: + subprocess.check_call( + [sys.executable, str(script), backend], + cwd=tmp_path, + env=env, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + raise rfp = tmp_path / root_file assert rfp.exists() diff --git a/Examples/Run/Geant4/Common/src/Geant4Common.cpp b/Examples/Run/Geant4/Common/src/Geant4Common.cpp index 62d79d73bc5..5bb00dcbb62 100644 --- a/Examples/Run/Geant4/Common/src/Geant4Common.cpp +++ b/Examples/Run/Geant4/Common/src/Geant4Common.cpp @@ -61,6 +61,11 @@ void setupGeant4Simulation( auto g4loglevel = Acts::Logging::Level(vars["g4-loglevel"].as()); + if (runActions.size() > 1 or eventActions.size() > 1 or + trackingActions.size() > 1 or steppingActions.size() > 1) { + throw std::invalid_argument("Only one user action per category allowed"); + } + size_t seed = vars["g4-seed"].as(); // Set the main Geant4 algorithm, primary generation, detector construction @@ -89,10 +94,12 @@ void setupGeant4Simulation( g4Cfg.detectorConstruction = detector.release(); // Set the user actions - g4Cfg.runActions = std::move(runActions); - g4Cfg.eventActions = std::move(eventActions); - g4Cfg.trackingActions = std::move(trackingActions); - g4Cfg.steppingActions = std::move(steppingActions); + g4Cfg.runAction = runActions.empty() ? nullptr : runActions.front(); + g4Cfg.eventAction = eventActions.empty() ? nullptr : eventActions.front(); + g4Cfg.trackingAction = + trackingActions.empty() ? nullptr : trackingActions.front(); + g4Cfg.steppingAction = + steppingActions.empty() ? nullptr : steppingActions.front(); // An ACTS Magnetic field is provided if (magneticField) { diff --git a/Examples/Run/Geant4/TestMockupBuilder.cpp b/Examples/Run/Geant4/TestMockupBuilder.cpp index 919b0fb8343..e22785012b6 100644 --- a/Examples/Run/Geant4/TestMockupBuilder.cpp +++ b/Examples/Run/Geant4/TestMockupBuilder.cpp @@ -9,7 +9,6 @@ #include "Acts/Detector/Detector.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/Portal.hpp" -#include "Acts/Detector/PortalHelper.hpp" #include "Acts/MagneticField/ConstantBField.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Navigation/DetectorVolumeFinders.hpp" @@ -76,11 +75,9 @@ int main() { mockup_builder.buildChamber(mockup_chamberConfig_outer); std::vector> - detector_volumes = {}; - - detector_volumes.push_back(detectorVolume_inner_chamber); - detector_volumes.push_back(detectorVolume_middle_chamber); - detector_volumes.push_back(detectorVolume_outer_chamber); + detector_volumes = {detectorVolume_inner_chamber, + detectorVolume_middle_chamber, + detectorVolume_outer_chamber}; auto detectorVolume_sector = mockup_builder.buildSector(detector_volumes); diff --git a/Examples/Run/Misc/TabulateEnergyLoss.cpp b/Examples/Run/Misc/TabulateEnergyLoss.cpp index ad9a88f3fb2..3750c870037 100644 --- a/Examples/Run/Misc/TabulateEnergyLoss.cpp +++ b/Examples/Run/Misc/TabulateEnergyLoss.cpp @@ -51,7 +51,7 @@ static void printHeader(std::ostream& os, const Acts::MaterialSlab& slab, static void printLine(std::ostream& os, float mass, float momentum, float delta, float deltaIon, float deltaRad, float sigma) { - const auto energy = std::sqrt(mass * mass + momentum * momentum); + const auto energy = std::hypot(mass, momentum); const auto beta = momentum / energy; const auto betaGamma = momentum / mass; os << std::right << std::fixed << std::setprecision(precision); diff --git a/Examples/Run/Reconstruction/Common/RecCKFTracks.cpp b/Examples/Run/Reconstruction/Common/RecCKFTracks.cpp index 8d792b0a6fe..e4fef2e9eb0 100644 --- a/Examples/Run/Reconstruction/Common/RecCKFTracks.cpp +++ b/Examples/Run/Reconstruction/Common/RecCKFTracks.cpp @@ -6,6 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Detector/IBaseDetector.hpp" #ifdef ACTS_PLUGIN_ONNX #include "Acts/Plugins/Onnx/MLTrackClassifier.hpp" @@ -269,7 +270,8 @@ int runRecCKFTracks( trackFindingCfg.outputTracks = "tracks"; trackFindingCfg.computeSharedHits = true; trackFindingCfg.findTracks = TrackFindingAlgorithm::makeTrackFinderFunction( - trackingGeometry, magneticField); + trackingGeometry, magneticField, + *Acts::getDefaultLogger("TrackFinder", logLevel)); sequencer.addAlgorithm( std::make_shared(trackFindingCfg, logLevel)); diff --git a/Examples/Scripts/MaterialMapping/materialComposition.C b/Examples/Scripts/MaterialMapping/materialComposition.C index 56eefd79737..8eb431205d0 100644 --- a/Examples/Scripts/MaterialMapping/materialComposition.C +++ b/Examples/Scripts/MaterialMapping/materialComposition.C @@ -6,6 +6,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include #include #include #include @@ -183,7 +184,7 @@ void materialComposition(const std::string& inFile, const std::string& treeName, float x = stepX->at(is); float y = stepY->at(is); float z = stepZ->at(is); - float r = sqrt(x * x + y * y); + float r = std::hypot(x, y); if (minR > r or minZ > z or maxR < r or maxZ < z) { continue; diff --git a/Examples/Scripts/Python/full_chain_odd.py b/Examples/Scripts/Python/full_chain_odd.py index c8a27d8fce9..c14724e1e5e 100755 --- a/Examples/Scripts/Python/full_chain_odd.py +++ b/Examples/Scripts/Python/full_chain_odd.py @@ -46,7 +46,7 @@ args = vars(parser.parse_args()) -ttbar_pu200 = args["ttbar"] +ttbar = args["ttbar"] g4_simulation = args["geant4"] ambiguity_MLSolver = args["MLSolver"] u = acts.UnitConstants @@ -73,7 +73,7 @@ outputDir=str(outputDir), ) - if not ttbar_pu200: + if not ttbar: addParticleGun( s, MomentumConfig(1.0 * u.GeV, 10.0 * u.GeV, transverse=True), @@ -92,7 +92,7 @@ addPythia8( s, hardProcess=["Top:qqbar2ttbar=on"], - npileup=200, + npileup=50, vtxGen=acts.examples.GaussianVertexGenerator( stddev=acts.Vector4( 0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns @@ -125,6 +125,7 @@ outputDirRoot=outputDir, # outputDirCsv=outputDir, rnd=rnd, + killVolume=acts.Volume.makeCylinderVolume(r=1100, halfZ=3000), ) else: addFatras( @@ -136,7 +137,7 @@ pt=(150 * u.MeV, None), removeNeutral=True, ) - if ttbar_pu200 + if ttbar else ParticleSelectorConfig(), outputDirRoot=outputDir, # outputDirCsv=outputDir, @@ -158,7 +159,7 @@ trackingGeometry, field, TruthSeedRanges(pt=(1.0 * u.GeV, None), eta=(-3.0, 3.0), nHits=(9, None)) - if ttbar_pu200 + if ttbar else TruthSeedRanges(), geoSelectionConfigFile=oddSeedingSel, outputDirRoot=outputDir, @@ -169,7 +170,7 @@ trackingGeometry, field, CKFPerformanceConfig( - ptMin=1.0 * u.GeV if ttbar_pu200 else 0.0, + ptMin=1.0 * u.GeV if ttbar else 0.0, nMeasurementsMin=7, ), TrackSelectorRanges( @@ -186,7 +187,7 @@ s, AmbiguityResolutionMLConfig(nMeasurementsMin=7), CKFPerformanceConfig( - ptMin=1.0 * u.GeV if ttbar_pu200 else 0.0, nMeasurementsMin=7 + ptMin=1.0 * u.GeV if ttbar else 0.0, nMeasurementsMin=7 ), outputDirRoot=outputDir, # outputDirCsv=outputDir, @@ -200,7 +201,7 @@ maximumSharedHits=3, maximumIterations=10000, nMeasurementsMin=7 ), CKFPerformanceConfig( - ptMin=1.0 * u.GeV if ttbar_pu200 else 0.0, + ptMin=1.0 * u.GeV if ttbar else 0.0, nMeasurementsMin=7, ), outputDirRoot=outputDir, diff --git a/Examples/Scripts/TrackingPerformance/boundParamResolution.C b/Examples/Scripts/TrackingPerformance/boundParamResolution.C index 68007d11095..5b7a3febc2e 100644 --- a/Examples/Scripts/TrackingPerformance/boundParamResolution.C +++ b/Examples/Scripts/TrackingPerformance/boundParamResolution.C @@ -414,7 +414,7 @@ int boundParamResolution(const std::string& inFile, const std::string& treeName, if (predicted and tsReader.predicted->at(i)) { auto x_prt = tsReader.g_x_prt->at(i); auto y_prt = tsReader.g_y_prt->at(i); - auto r_prt = std::sqrt(x_prt * x_prt + y_prt * y_prt); + auto r_prt = std::hypot(x_prt, y_prt); auto z_prt = tsReader.g_z_prt->at(i); p2d_res_zr_prt[0]->Fill(z_prt, r_prt, tsReader.res_LOC0_prt->at(i)); p2d_res_zr_prt[1]->Fill(z_prt, r_prt, tsReader.res_LOC1_prt->at(i)); @@ -432,7 +432,7 @@ int boundParamResolution(const std::string& inFile, const std::string& treeName, if (filtered and tsReader.filtered->at(i)) { auto x_flt = tsReader.g_x_flt->at(i); auto y_flt = tsReader.g_y_flt->at(i); - auto r_flt = std::sqrt(x_flt * x_flt + y_flt * y_flt); + auto r_flt = std::hypot(x_flt, y_flt); auto z_flt = tsReader.g_z_flt->at(i); p2d_res_zr_flt[0]->Fill(z_flt, r_flt, tsReader.res_LOC0_flt->at(i)); p2d_res_zr_flt[1]->Fill(z_flt, r_flt, tsReader.res_LOC1_flt->at(i)); @@ -450,7 +450,7 @@ int boundParamResolution(const std::string& inFile, const std::string& treeName, if (smoothed and tsReader.smoothed->at(i)) { auto x_smt = tsReader.g_x_smt->at(i); auto y_smt = tsReader.g_y_smt->at(i); - auto r_smt = std::sqrt(x_smt * x_smt + y_smt * y_smt); + auto r_smt = std::hypot(x_smt, y_smt); auto z_smt = tsReader.g_z_smt->at(i); p2d_res_zr_smt[0]->Fill(z_smt, r_smt, tsReader.res_LOC0_smt->at(i)); p2d_res_zr_smt[1]->Fill(z_smt, r_smt, tsReader.res_LOC1_smt->at(i)); diff --git a/Examples/Scripts/generic_plotter.py b/Examples/Scripts/generic_plotter.py index 38f9c16cf52..169466b5c6f 100755 --- a/Examples/Scripts/generic_plotter.py +++ b/Examples/Scripts/generic_plotter.py @@ -3,6 +3,7 @@ from typing import Optional, Dict, List import re import enum +import sys import uproot import typer @@ -34,6 +35,7 @@ class Extra(HistConfig): class Config(Model): histograms: Dict[str, HistConfig] = pydantic.Field(default_factory=dict) extra_histograms: List[Extra] = pydantic.Field(default_factory=list) + exclude: List[str] = pydantic.Field(default_factory=list) class Mode(str, enum.Enum): @@ -71,6 +73,7 @@ def main( silent: bool = typer.Option( False, "--silent", "-s", help="Do not print any output" ), + dump_yml: bool = typer.Option(False, help="Print axis ranges as yml"), ): """ Script to plot all branches in a TTree from a ROOT file, with optional configurable binning and ranges. @@ -91,10 +94,12 @@ def main( histograms = {} if not silent: - print(config.extra_histograms) + print(config.extra_histograms, file=sys.stderr) for df in tree.iterate(library="ak", how=dict): for col in df.keys(): + if any([re.match(ex, col) for ex in config.exclude]): + continue h = histograms.get(col) values = awkward.flatten(df[col], axis=None) @@ -104,6 +109,15 @@ def main( for ex, data in config.histograms.items(): if re.match(ex, col): found = data.copy() + print( + "Found HistConfig", + ex, + "for", + col, + ":", + found, + file=sys.stderr, + ) if found is None: found = HistConfig() @@ -159,16 +173,29 @@ def main( for k, h in histograms.items(): if not silent: - print(k, h.axes[0]) + if dump_yml: + ax = h.axes[0] + s = """ +{k}: + nbins: {b} + min: {min} + max: {max} + """.format( + k=k, b=len(ax.edges) - 1, min=ax.edges[0], max=ax.edges[-1] + ) + print(s) + else: + print(k, h.axes[0]) outfile[k] = h if plots is not None: fig, ax = matplotlib.pyplot.subplots() - h.plot(ax=ax) + h.plot(ax=ax, flow=None) fig.tight_layout() fig.savefig(str(plots / f"{k}.{plot_format}")) + matplotlib.pyplot.close() if __name__ == "__main__": diff --git a/Examples/Scripts/momentumDistributions.C b/Examples/Scripts/momentumDistributions.C index 53865820999..cd19fb51980 100644 --- a/Examples/Scripts/momentumDistributions.C +++ b/Examples/Scripts/momentumDistributions.C @@ -18,25 +18,16 @@ // To plot two momentum distributions of this kind in one canvas the root // script "compareDistributions.C" can be used. -void -momentumDistributions(std::string inFile, - std::string treeName, - std::string outFile, - int nBins, - float r, - float zMin, - float zMax, - float etaMin, - float etaMax, - float thetaMin = 0., - float thetaMax = M_PI) -{ +void momentumDistributions(std::string inFile, std::string treeName, + std::string outFile, int nBins, float r, float zMin, + float zMax, float etaMin, float etaMax, + float thetaMin = 0., float thetaMax = M_PI) { std::cout << "Opening file: " << inFile << std::endl; TFile inputFile(inFile.c_str()); std::cout << "Reading tree: " << treeName << std::endl; TTree* tree = (TTree*)inputFile.Get(treeName.c_str()); - int nHits; + int nHits; float eta; std::vector* x = new std::vector; @@ -57,16 +48,16 @@ momentumDistributions(std::string inFile, TFile outputFile(outFile.c_str(), "recreate"); // distributions of the number of hits versus momentum coordinates - TProfile* nHits_eta = new TProfile( - "nHits_eta", "Hits in sensitive Material", nBins, etaMin, etaMax); + TProfile* nHits_eta = new TProfile("nHits_eta", "Hits in sensitive Material", + nBins, etaMin, etaMax); nHits_eta->GetXaxis()->SetTitle("#eta"); nHits_eta->GetYaxis()->SetTitle("#hits"); TProfile* nHits_theta = new TProfile( "nHits_theta", "Hits in sensitive Material", nBins, thetaMin, thetaMax); nHits_theta->GetXaxis()->SetTitle("#theta [rad]"); nHits_theta->GetYaxis()->SetTitle("#hits"); - TProfile* nHits_z = new TProfile( - "nHits_z", "Hits in sensitive Material", nBins, zMin, zMax); + TProfile* nHits_z = + new TProfile("nHits_z", "Hits in sensitive Material", nBins, zMin, zMax); nHits_z->GetXaxis()->SetTitle("z coordinate of momentum [mm]"); nHits_z->GetYaxis()->SetTitle("#hits"); @@ -77,33 +68,33 @@ momentumDistributions(std::string inFile, // distributions of the momentum coordinates calculated from eta - since in // the extrapolation test eta is flat randomly generated and theta and z are // calculated from eta. - TH1F* Theta - = new TH1F("theta", "Distribution of #theta", nBins, thetaMin, thetaMax); + TH1F* Theta = + new TH1F("theta", "Distribution of #theta", nBins, thetaMin, thetaMax); Theta->GetXaxis()->SetTitle("#theta [rad]"); Theta->GetYaxis()->SetTitle("#events"); - TH1F* Z = new TH1F( - "z", "Distribution of z coordinate of the momentum", nBins, zMin, zMax); + TH1F* Z = new TH1F("z", "Distribution of z coordinate of the momentum", nBins, + zMin, zMax); Z->GetXaxis()->SetTitle("z coordinate of momentum [mm]"); Z->GetYaxis()->SetTitle("#events"); // hit distributions - TH1F* hitsEta = new TH1F( - "hitsEta", "Sensitive Hit Distribution", nBins, etaMin, etaMax); + TH1F* hitsEta = + new TH1F("hitsEta", "Sensitive Hit Distribution", nBins, etaMin, etaMax); hitsEta->GetXaxis()->SetTitle("#eta"); hitsEta->GetYaxis()->SetTitle("#hits"); - TH1F* hitsTheta = new TH1F( - "hitsTheta", "Sensitive Hit Distribution", nBins, thetaMin, thetaMax); + TH1F* hitsTheta = new TH1F("hitsTheta", "Sensitive Hit Distribution", nBins, + thetaMin, thetaMax); hitsTheta->GetXaxis()->SetTitle("#theta"); hitsTheta->GetYaxis()->SetTitle("#hits"); - TH1F* hitsZ - = new TH1F("hitsZ", "Sensitive Hit Distribution", nBins, zMin, zMax); + TH1F* hitsZ = + new TH1F("hitsZ", "Sensitive Hit Distribution", nBins, zMin, zMax); hitsZ->GetXaxis()->SetTitle("z [mm]"); hitsZ->GetYaxis()->SetTitle("#hits"); for (int i = 0; i < entries; i++) { tree->GetEvent(i); double theta = 2. * atan(exp(-eta)); - double zDir = r / tan(theta); + double zDir = r / tan(theta); nHits_eta->Fill(eta, nHits); nHits_theta->Fill(theta, nHits); @@ -114,8 +105,7 @@ momentumDistributions(std::string inFile, Z->Fill(zDir, 1); for (int j = 0; j < x->size(); j++) { - float hitTheta - = atan2(sqrt(x->at(j) * x->at(j) + y->at(j) * y->at(j)), z->at(j)); + float hitTheta = std::atan2(std::hypot(x->at(j), y->at(j)), z->at(j)); hitsEta->Fill(-log(tan(hitTheta * 0.5))); hitsTheta->Fill(hitTheta); hitsZ->Fill(z->at(j)); diff --git a/Examples/Scripts/printBField.C b/Examples/Scripts/printBField.C index fab8ff068ff..c9e0605d032 100644 --- a/Examples/Scripts/printBField.C +++ b/Examples/Scripts/printBField.C @@ -115,10 +115,10 @@ printBField(std::string inFile, for (int i = 0; i < entries; i++) { tree->GetEvent(i); if (cylinderCoordinates) { - float bFieldValue = sqrt(Br * Br + Bz * Bz); + float bFieldValue = std::hypot(Br, Bz); bField_rz->Fill(z / 1000., r / 1000., bFieldValue); } else { - float bFieldValue = sqrt(Bx * Bx + By * By + Bz * Bz); + float bFieldValue = std::hypot(Bx, By, Bz); bField_xy->Fill(x / 1000., y / 1000., bFieldValue); bField_yz->Fill(z / 1000., y / 1000., bFieldValue); diff --git a/Examples/Scripts/printHits.C b/Examples/Scripts/printHits.C index 2aa66fa841c..f35bc0f3715 100644 --- a/Examples/Scripts/printHits.C +++ b/Examples/Scripts/printHits.C @@ -139,8 +139,8 @@ printHits(std::string inFile, } Sens_zx->Fill(z->at(j), x->at(j), sens->at(j)); Sens_zy->Fill(z->at(j), y->at(j), sens->at(j)); - Sens_zr->Fill(z->at(j), sqrt(x->at(j) * x->at(j) + y->at(j) * y->at(j))); - Full_zr->Fill(z->at(j), sqrt(x->at(j) * x->at(j) + y->at(j) * y->at(j))); + Sens_zr->Fill(z->at(j), std::hypot(x->at(j), y->at(j))); + Full_zr->Fill(z->at(j), std::hypot(x->at(j), y->at(j))); // boundaries if (tree->FindBranch("boundary")) { @@ -149,7 +149,7 @@ printHits(std::string inFile, Bounds_zx->Fill(z->at(j), x->at(j), bounds->at(j)); Bounds_zy->Fill(z->at(j), y->at(j), bounds->at(j)); Bounds_zr->Fill(z->at(j), - sqrt(x->at(j) * x->at(j) + y->at(j) * y->at(j)), + std::hypot(x->at(j), y->at(j)), bounds->at(j)); } // material @@ -159,7 +159,7 @@ printHits(std::string inFile, Mat_zx->Fill(z->at(j), x->at(j), mat->at(j)); Mat_zy->Fill(z->at(j), y->at(j), mat->at(j)); Mat_zr->Fill(z->at(j), - sqrt(x->at(j) * x->at(j) + y->at(j) * y->at(j)), + std::hypot(x->at(j), y->at(j)), mat->at(j)); } } diff --git a/Examples/Scripts/requirements.txt b/Examples/Scripts/requirements.txt index ca50b6e05ae..f66cc89ce17 100644 --- a/Examples/Scripts/requirements.txt +++ b/Examples/Scripts/requirements.txt @@ -4,36 +4,44 @@ # # pip-compile Examples/Scripts/requirements.in # -awkward==1.8.0 - # via -r Examples/Scripts/requirements.in -boost-histogram==1.3.1 +awkward==2.1.3 + # via + # -r Examples/Scripts/requirements.in + # uproot +awkward-cpp==14 + # via awkward +boost-histogram==1.3.2 # via hist click==8.1.3 # via # histoprint # typer +contourpy==1.0.7 + # via matplotlib cycler==0.11.0 # via matplotlib -fonttools==4.34.4 +fonttools==4.39.3 # via matplotlib -hist==2.6.1 +hist==2.6.3 # via -r Examples/Scripts/requirements.in histoprint==2.4.0 # via hist kiwisolver==1.4.4 # via matplotlib -matplotlib==3.5.2 +matplotlib==3.7.1 # via # -r Examples/Scripts/requirements.in # mplhep -mplhep==0.3.26 +mplhep==0.3.27 # via -r Examples/Scripts/requirements.in mplhep-data==0.0.3 # via mplhep -numpy==1.23.1 +numpy==1.24.3 # via # awkward + # awkward-cpp # boost-histogram + # contourpy # hist # histoprint # matplotlib @@ -42,42 +50,43 @@ numpy==1.23.1 # scipy # uhi # uproot -packaging==21.3 +packaging==23.1 # via + # awkward # matplotlib # mplhep -pandas==1.4.3 + # uproot +pandas==2.0.1 # via -r Examples/Scripts/requirements.in -pillow==9.2.0 +pillow==9.5.0 # via matplotlib -pydantic==1.9.1 +pydantic==1.10.7 # via -r Examples/Scripts/requirements.in pyparsing==3.0.9 - # via - # matplotlib - # packaging + # via matplotlib python-dateutil==2.8.2 # via # matplotlib # pandas -pytz==2022.1 +pytz==2023.3 # via pandas pyyaml==6.0 # via -r Examples/Scripts/requirements.in -scipy==1.9.0 +scipy==1.10.1 # via -r Examples/Scripts/requirements.in six==1.16.0 # via python-dateutil -typer==0.6.1 +typer==0.7.0 # via -r Examples/Scripts/requirements.in -typing-extensions==4.3.0 - # via pydantic -uhi==0.3.1 +typing-extensions==4.5.0 + # via + # awkward + # pydantic +tzdata==2023.3 + # via pandas +uhi==0.3.3 # via # histoprint # mplhep -uproot==4.3.4 +uproot==5.0.7 # via -r Examples/Scripts/requirements.in - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/Fatras/include/ActsFatras/EventData/Particle.hpp b/Fatras/include/ActsFatras/EventData/Particle.hpp index 88d25363fcd..aea7209d5cc 100644 --- a/Fatras/include/ActsFatras/EventData/Particle.hpp +++ b/Fatras/include/ActsFatras/EventData/Particle.hpp @@ -67,10 +67,29 @@ class Particle { } /// Set the process type that generated this particle. - Particle &setProcess(ProcessType proc) { return m_process = proc, *this; } + Particle &setProcess(ProcessType proc) { + m_process = proc; + return *this; + } + /// Set the pdg. + Particle setPdg(Acts::PdgParticle pdg) { + m_pdg = pdg; + return *this; + } + /// Set the charge. + Particle setCharge(Scalar charge) { + m_charge = charge; + return *this; + } + /// Set the mass. + Particle setMass(Scalar mass) { + m_mass = mass; + return *this; + } /// Set the particle ID. Particle &setParticleId(Barcode barcode) { - return m_particleId = barcode, *this; + m_particleId = barcode; + return *this; } /// Set the space-time position four-vector. Particle &setPosition4(const Vector4 &pos4) { @@ -111,9 +130,6 @@ class Particle { return *this; } - /// Set the particle charge. - Particle &setCharge(Scalar charge) { return m_charge = charge, *this; } - /// Change the energy by the given amount. /// /// Energy loss corresponds to a negative change. If the updated energy diff --git a/Fatras/include/ActsFatras/Selectors/detail/combine_selectors.hpp b/Fatras/include/ActsFatras/Selectors/detail/combine_selectors.hpp index f07c5d0472f..6b9c2a36822 100644 --- a/Fatras/include/ActsFatras/Selectors/detail/combine_selectors.hpp +++ b/Fatras/include/ActsFatras/Selectors/detail/combine_selectors.hpp @@ -29,7 +29,7 @@ class CombineSelectors { /// /// @tparam Ts the types of the selected inputs template - bool operator()(const Ts &... things) const { + bool operator()(const Ts &...things) const { static_assert( (true && ... && std::is_same_v), "Not all selectors conform to the expected interface (bool)(const " @@ -52,8 +52,7 @@ class CombineSelectors { std::tuple m_selectors; template - bool impl(std::index_sequence /*indices*/, - const Ts &... things) const { + bool impl(std::index_sequence /*indices*/, const Ts &...things) const { Combine combine; // compute status for all selectors bool status[] = {std::get(m_selectors)(things...)...}; diff --git a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp index 2e51da30b01..3ceaaf84615 100644 --- a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp +++ b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp @@ -8,8 +8,8 @@ #pragma once -#include "Acts/Detector/GridAxisGenerators.hpp" -#include "Acts/Detector/IndexedSurfacesGenerator.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryHierarchyMap.hpp" @@ -30,7 +30,7 @@ namespace Acts { namespace Experimental { -using namespace GridAxisGenerators; +using namespace detail::GridAxisGenerators; // Generate the possible axes in this case static auto s_possibleAxes = diff --git a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp index 4d0c8982d70..2c2a3893d09 100644 --- a/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp +++ b/Plugins/ActSVG/include/Acts/Plugins/ActSVG/SvgUtils.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Algebra.hpp" #include "actsvg/meta.hpp" #include @@ -18,7 +18,6 @@ #include namespace Acts { - namespace Svg { struct Style { @@ -180,5 +179,4 @@ inline static void toFile(const std::vector& objects, } } // namespace Svg - -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Plugins/EDM4hep/src/EDM4hepUtil.cpp b/Plugins/EDM4hep/src/EDM4hepUtil.cpp index 148aed1a67b..eb8fd6a7513 100644 --- a/Plugins/EDM4hep/src/EDM4hepUtil.cpp +++ b/Plugins/EDM4hep/src/EDM4hepUtil.cpp @@ -85,7 +85,7 @@ ActsSymMatrix<6> jacobianFromEdm4hep(double tanLambda, double omega, J(3, 3) = -1 / (tanLambda * tanLambda + 1); J(4, 3) = -1 * omega * tanLambda / (Bz * std::pow(tanLambda * tanLambda + 1, 3. / 2.)); - J(4, 4) = 1 / (Bz * std::sqrt(tanLambda * tanLambda + 1)); + J(4, 4) = 1 / (Bz * std::hypot(tanLambda, 1)); return J; } diff --git a/Plugins/ExaTrkX/src/ExaTrkXTrackFindingTorch.cpp b/Plugins/ExaTrkX/src/ExaTrkXTrackFindingTorch.cpp index fa901e616c0..2eef6980629 100644 --- a/Plugins/ExaTrkX/src/ExaTrkXTrackFindingTorch.cpp +++ b/Plugins/ExaTrkX/src/ExaTrkXTrackFindingTorch.cpp @@ -136,8 +136,10 @@ std::optional ExaTrkXTrackFindingTorch::getTracks( timer.start(); // At this point, buildEdgesBruteForce could be used instead - std::optional edgeList = buildEdges( - *eOutput, numSpacepoints, m_cfg.embeddingDim, m_cfg.rVal, m_cfg.knnVal); + std::optional edgeList = + buildEdges(*eOutput, numSpacepoints, m_cfg.embeddingDim, m_cfg.rVal, + m_cfg.knnVal) + .to(device); eOutput.reset(); ACTS_VERBOSE("Shape of built edges: (" << edgeList->size(0) << ", " diff --git a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4Converters.hpp b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4Converters.hpp index 5f992f19e4b..6550f021c97 100644 --- a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4Converters.hpp +++ b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4Converters.hpp @@ -33,14 +33,14 @@ struct Geant4AlgebraConverter { // A potential scalar between Geant4 and ACTS ActsScalar scale = 1.; - /// @brief Translate a geometry transform: translation only + /// @brief Translate a geometry transform: translation only /// /// @param g4Trans the translation of the Geant4 object /// /// @return a Acts transform Transform3 transform(const G4ThreeVector& g4Trans); - /// @brief Translate a geometry transform + /// @brief Translate a geometry transform /// /// @param g4Rot the rotation of the Geant4 object /// @param g4Trans the translation of the Geant4 object @@ -49,12 +49,19 @@ struct Geant4AlgebraConverter { Transform3 transform(const G4RotationMatrix& g4Rot, const G4ThreeVector& g4Trans); - /// @brief Translate a geometry transform + /// @brief Translate a geometry transform /// - /// @param g4Trf theGeant4 transform object + /// @param g4Trf the Geant4 transform object /// /// @return a Acts transform Transform3 transform(const G4Transform3D& g4Trf); + + /// @brief Translate a geometry transform from a G4 physical volume + /// + /// @param g4PhysVol the Geant4 physical volume + /// + /// @return a Acts transform + Transform3 transform(const G4VPhysicalVolume& g4PhysVol); }; class AnnulusBounds; @@ -146,9 +153,13 @@ struct Geant4PhysicalVolumeConverter { ActsScalar compressed = 0.); }; +class Material; class HomogeneousSurfaceMaterial; +class HomogeneousVolumeMaterial; struct Geant4MaterialConverter { + Material material(const G4Material& g4Material, ActsScalar compression = 1); + /// @brief Convert a Geant4 material to a surface material description /// /// @param g4Material the geant4 material descrition @@ -159,4 +170,15 @@ struct Geant4MaterialConverter { const G4Material& g4Material, ActsScalar original, ActsScalar compressed); }; +class CylinderVolumeBounds; + +struct Geant4VolumeConverter { + /// @brief Convert to cylinder bounds + /// + /// @param g4Tubs a Geant4 tube shape + /// + /// @return an Acts Cylinder bounds object + std::shared_ptr cylinderBounds(const G4Tubs& g4Tubs); +}; + } // namespace Acts diff --git a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorElement.hpp b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorElement.hpp index 133b8ba7bb4..4778750fe74 100644 --- a/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorElement.hpp +++ b/Plugins/Geant4/include/Acts/Plugins/Geant4/Geant4DetectorElement.hpp @@ -43,6 +43,9 @@ class Geant4DetectorElement : public DetectorElementBase { /// Return surface associated with this detector element const Surface& surface() const override; + /// Non-const access to surface associated with this detector element + Surface& surface() override; + /// Return the thickness of this detector element ActsScalar thickness() const override; diff --git a/Plugins/Geant4/src/Geant4Converters.cpp b/Plugins/Geant4/src/Geant4Converters.cpp index 5b557aeb96e..b995df8099a 100644 --- a/Plugins/Geant4/src/Geant4Converters.cpp +++ b/Plugins/Geant4/src/Geant4Converters.cpp @@ -9,7 +9,9 @@ #include "Acts/Plugins/Geant4/Geant4Converters.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" +#include "Acts/Material/HomogeneousVolumeMaterial.hpp" #include "Acts/Material/Material.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" @@ -65,33 +67,52 @@ Acts::Transform3 Acts::Geant4AlgebraConverter::transform( return transform(g4Rot, g4Trans); } -std::tuple, Acts::ActsScalar> -Acts::Geant4ShapeConverter::cylinderBounds(const G4Tubs& g4Tubs) { - std::array tArray = {}; - tArray[0u] = static_cast(g4Tubs.GetInnerRadius() + - g4Tubs.GetOuterRadius()) * - 0.5; - tArray[1u] = static_cast(g4Tubs.GetZHalfLength()); - tArray[2u] = 0.5 * static_cast(g4Tubs.GetDeltaPhiAngle()); - tArray[3u] = static_cast(g4Tubs.GetStartPhiAngle()); +Acts::Transform3 Acts::Geant4AlgebraConverter::transform( + const G4VPhysicalVolume& g4PhysVol) { + // Get Rotation and translation + auto g4Translation = g4PhysVol.GetTranslation(); + auto g4Rotation = g4PhysVol.GetRotation(); - ActsScalar thickness = (g4Tubs.GetOuterRadius() - g4Tubs.GetInnerRadius()); + G4Transform3D g4Transform = + (g4Rotation == nullptr) + ? G4Transform3D(CLHEP::HepRotation(), g4Translation) + : G4Transform3D(*g4Rotation, g4Translation); + return transform(g4Transform); +} + +std::tuple, Acts::ActsScalar> +Acts::Geant4ShapeConverter::cylinderBounds(const G4Tubs& g4Tubs) { + using B = Acts::CylinderBounds; + + std::array tArray = {}; + tArray[B::eR] = static_cast(g4Tubs.GetInnerRadius() + + g4Tubs.GetOuterRadius()) * + 0.5; + tArray[B::eHalfLengthZ] = static_cast(g4Tubs.GetZHalfLength()); + tArray[B::eHalfPhiSector] = + 0.5 * static_cast(g4Tubs.GetDeltaPhiAngle()); + tArray[B::eAveragePhi] = static_cast(g4Tubs.GetStartPhiAngle()); + + ActsScalar thickness = g4Tubs.GetOuterRadius() - g4Tubs.GetInnerRadius(); auto cBounds = std::make_shared(tArray); - return std::tie(cBounds, thickness); + return std::make_tuple(std::move(cBounds), thickness); } std::tuple, Acts::ActsScalar> Acts::Geant4ShapeConverter::radialBounds(const G4Tubs& g4Tubs) { - std::array tArray = {}; - tArray[0u] = static_cast(g4Tubs.GetInnerRadius()); - tArray[1u] = static_cast(g4Tubs.GetOuterRadius()); - tArray[2u] = 0.5 * static_cast(g4Tubs.GetDeltaPhiAngle()); - tArray[3u] = static_cast(g4Tubs.GetStartPhiAngle()); + using B = Acts::RadialBounds; + + std::array tArray = {}; + tArray[B::eMinR] = static_cast(g4Tubs.GetInnerRadius()); + tArray[B::eMaxR] = static_cast(g4Tubs.GetOuterRadius()); + tArray[B::eHalfPhiSector] = + 0.5 * static_cast(g4Tubs.GetDeltaPhiAngle()); + tArray[B::eAveragePhi] = static_cast(g4Tubs.GetStartPhiAngle()); ActsScalar thickness = g4Tubs.GetZHalfLength() * 2; auto rBounds = std::make_shared(tArray); - return std::tie(rBounds, thickness); + return std::make_tuple(std::move(rBounds), thickness); } std::shared_ptr Acts::Geant4ShapeConverter::lineBounds( @@ -131,7 +152,7 @@ Acts::Geant4ShapeConverter::rectangleBounds(const G4Box& g4Box) { } auto rBounds = std::make_shared(hG4XYZ[std::abs(rAxes[0u])], hG4XYZ[std::abs(rAxes[1u])]); - return std::tie(rBounds, rAxes, thickness); + return std::make_tuple(std::move(rBounds), rAxes, thickness); } std::tuple, std::array, @@ -186,7 +207,7 @@ Acts::Geant4ShapeConverter::trapezoidBounds(const G4Trd& g4Trd) { auto tBounds = std::make_shared( halfLengthXminY, halfLengthXmaxY, halfLengthY); - return std::tie(tBounds, rAxes, thickness); + return std::make_tuple(std::move(tBounds), rAxes, thickness); } std::tuple, std::array, @@ -195,19 +216,19 @@ Acts::Geant4ShapeConverter::planarBounds(const G4VSolid& g4Solid) { const G4Box* box = dynamic_cast(&g4Solid); if (box != nullptr) { auto [rBounds, axes, thickness] = rectangleBounds(*box); - return std::tie(rBounds, axes, thickness); + return std::make_tuple(std::move(rBounds), axes, thickness); } const G4Trd* trd = dynamic_cast(&g4Solid); if (trd != nullptr) { auto [tBounds, axes, thickness] = trapezoidBounds(*trd); - return std::tie(tBounds, axes, thickness); + return std::make_tuple(std::move(tBounds), axes, thickness); } std::shared_ptr pBounds = nullptr; std::array rAxes = {}; ActsScalar rThickness = 0.; - return std::tie(pBounds, rAxes, rThickness); + return std::make_tuple(std::move(pBounds), rAxes, rThickness); } namespace { @@ -246,7 +267,7 @@ std::shared_ptr Acts::Geant4PhysicalVolumeConverter::surface( } auto surfaceMaterial = Geant4MaterialConverter{}.surfaceMaterial( *g4Material, moriginal, mcompressed); - sf.assignSurfaceMaterial(surfaceMaterial); + sf.assignSurfaceMaterial(std::move(surfaceMaterial)); } }; @@ -321,12 +342,8 @@ std::shared_ptr Acts::Geant4PhysicalVolumeConverter::surface( return nullptr; } -std::shared_ptr -Acts::Geant4MaterialConverter::surfaceMaterial(const G4Material& g4Material, - ActsScalar original, - ActsScalar compressed) { - ActsScalar compression = original / compressed; - +Acts::Material Acts::Geant4MaterialConverter::material( + const G4Material& g4Material, ActsScalar compression) { auto X0 = g4Material.GetRadlen(); auto L0 = g4Material.GetNuclearInterLength(); auto Rho = g4Material.GetDensity(); @@ -348,9 +365,30 @@ Acts::Geant4MaterialConverter::surfaceMaterial(const G4Material& g4Material, } } - Material mat = Material::fromMassDensity(X0 / compression, L0 / compression, - Ar, Z, compression * Rho); + return Material::fromMassDensity(X0 / compression, L0 / compression, Ar, Z, + compression * Rho); +} +std::shared_ptr +Acts::Geant4MaterialConverter::surfaceMaterial(const G4Material& g4Material, + ActsScalar original, + ActsScalar compressed) { + ActsScalar compression = original / compressed; return std::make_shared( - MaterialSlab(mat, compressed)); + MaterialSlab(material(g4Material, compression), compressed)); +} + +std::shared_ptr +Acts::Geant4VolumeConverter::cylinderBounds(const G4Tubs& g4Tubs) { + using C = Acts::CylinderVolumeBounds; + + std::array tArray = {}; + tArray[C::eMinR] = static_cast(g4Tubs.GetInnerRadius()); + tArray[C::eMaxR] = static_cast(g4Tubs.GetOuterRadius()); + tArray[C::eHalfLengthZ] = static_cast(g4Tubs.GetZHalfLength()); + tArray[C::eHalfPhiSector] = + 0.5 * static_cast(g4Tubs.GetDeltaPhiAngle()); + tArray[C::eAveragePhi] = static_cast(g4Tubs.GetStartPhiAngle()); + + return std::make_shared(tArray); } diff --git a/Plugins/Geant4/src/Geant4DetectorElement.cpp b/Plugins/Geant4/src/Geant4DetectorElement.cpp index fb0ee3ccb72..b38330fdabe 100644 --- a/Plugins/Geant4/src/Geant4DetectorElement.cpp +++ b/Plugins/Geant4/src/Geant4DetectorElement.cpp @@ -25,6 +25,10 @@ const Acts::Surface& Acts::Geant4DetectorElement::surface() const { return *m_surface; } +Acts::Surface& Acts::Geant4DetectorElement::surface() { + return *m_surface; +} + Acts::ActsScalar Acts::Geant4DetectorElement::thickness() const { return m_thickness; } diff --git a/Plugins/Json/src/SurfaceJsonConverter.cpp b/Plugins/Json/src/SurfaceJsonConverter.cpp index a57513837b6..655e691afdd 100644 --- a/Plugins/Json/src/SurfaceJsonConverter.cpp +++ b/Plugins/Json/src/SurfaceJsonConverter.cpp @@ -101,7 +101,6 @@ std::shared_ptr Acts::surfaceFromJson(const nlohmann::json& j) { } else if (sType == "PerigeeSurface") { Transform3 pTransform; nlohmann::json trfj = j["transform"]; - ; from_json(trfj, pTransform); mutableSf = Surface::makeShared(pTransform); } diff --git a/Plugins/Legacy/include/Acts/Seeding/AtlasSeedFinder.ipp b/Plugins/Legacy/include/Acts/Seeding/AtlasSeedFinder.ipp index e0d32c603d8..1bd5d7a023a 100644 --- a/Plugins/Legacy/include/Acts/Seeding/AtlasSeedFinder.ipp +++ b/Plugins/Legacy/include/Acts/Seeding/AtlasSeedFinder.ipp @@ -470,18 +470,19 @@ void Acts::Legacy::AtlasSeedFinder::fillLists() { // assign z-bin a value between 0 and 10 identifying the z-slice of a // space-point if (Z > 0.) { - Z < 250. ? z = 5 - : Z < 450. ? z = 6 - : Z < 925. ? z = 7 - : Z < 1400. ? z = 8 - : Z < 2500. ? z = 9 : z = 10; + Z < 250. ? z = 5 + : Z < 450. ? z = 6 + : Z < 925. ? z = 7 + : Z < 1400. ? z = 8 + : Z < 2500. ? z = 9 + : z = 10; } else { - Z > -250. - ? z = 5 - : Z > -450. - ? z = 4 - : Z > -925. ? z = 3 - : Z > -1400. ? z = 2 : Z > -2500. ? z = 1 : z = 0; + Z > -250. ? z = 5 + : Z > -450. ? z = 4 + : Z > -925. ? z = 3 + : Z > -1400. ? z = 2 + : Z > -2500. ? z = 1 + : z = 0; } // calculate bin nr "n" for self made r-phi-z sorted 3D array "rfz_Sorted" // record number of sp in m_nsaz diff --git a/Plugins/Legacy/include/Acts/Seeding/SPForSeed.hpp b/Plugins/Legacy/include/Acts/Seeding/SPForSeed.hpp index 9522a20b21c..4c889ad6746 100644 --- a/Plugins/Legacy/include/Acts/Seeding/SPForSeed.hpp +++ b/Plugins/Legacy/include/Acts/Seeding/SPForSeed.hpp @@ -148,7 +148,7 @@ inline void SPForSeed::set(SpacePoint* const& sp, const float* r) { m_x = r[0]; m_y = r[1]; m_z = r[2]; - m_r = sqrt(m_x * m_x + m_y * m_y); + m_r = std::hypot(m_x, m_y); m_surface = sp->surface; m_q = 100000.; @@ -188,7 +188,7 @@ inline void SPForSeed::set(SpacePoint* const& sp, const float* r, m_x = r[0]; m_y = r[1]; m_z = r[2]; - m_r = sqrt(m_x * m_x + m_y * m_y); + m_r = std::hypot(m_x, m_y); m_q = 100000.; if (!sp->clusterList().second) { m_covr = sp->covr * 9. * sc[0]; diff --git a/Plugins/TGeo/include/Acts/Plugins/TGeo/TGeoDetectorElement.hpp b/Plugins/TGeo/include/Acts/Plugins/TGeo/TGeoDetectorElement.hpp index a901c292805..4f84794f407 100644 --- a/Plugins/TGeo/include/Acts/Plugins/TGeo/TGeoDetectorElement.hpp +++ b/Plugins/TGeo/include/Acts/Plugins/TGeo/TGeoDetectorElement.hpp @@ -114,6 +114,11 @@ class TGeoDetectorElement : public IdentifiedDetectorElement { /// Return surface associated with this detector element const Surface& surface() const override; + /// Return surface associated with this detector element + /// + /// @note this is the non-const access + Surface& surface() override; + /// Retrieve the DigitizationModule const std::shared_ptr digitizationModule() const final { @@ -154,6 +159,10 @@ inline const Surface& TGeoDetectorElement::surface() const { return (*m_surface); } +inline Surface& TGeoDetectorElement::surface() { + return (*m_surface); +} + inline double TGeoDetectorElement::thickness() const { return m_thickness; } diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/DetectorElementStub.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/DetectorElementStub.hpp index fa37b262e89..7ba66c4c458 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/DetectorElementStub.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/DetectorElementStub.hpp @@ -52,9 +52,8 @@ class DetectorElementStub : public DetectorElementBase { : DetectorElementBase(), m_elementTransform(transform), m_elementThickness(thickness) { - auto mutableSurface = Surface::makeShared(pBounds, *this); - mutableSurface->assignSurfaceMaterial(std::move(material)); - m_elementSurface = mutableSurface; + m_elementSurface = Surface::makeShared(pBounds, *this); + m_elementSurface->assignSurfaceMaterial(std::move(material)); } /// Constructor for single sided detector element @@ -71,9 +70,8 @@ class DetectorElementStub : public DetectorElementBase { : DetectorElementBase(), m_elementTransform(transform), m_elementThickness(thickness) { - auto mutableSurface = Surface::makeShared(lBounds, *this); - mutableSurface->assignSurfaceMaterial(std::move(material)); - m_elementSurface = mutableSurface; + m_elementSurface = Surface::makeShared(lBounds, *this); + m_elementSurface->assignSurfaceMaterial(std::move(material)); } /// Destructor @@ -89,6 +87,9 @@ class DetectorElementStub : public DetectorElementBase { /// Return surface associated with this detector element const Surface& surface() const override; + /// Non-const access to surface associated with this detector element + Surface& surface() override; + /// The maximal thickness of the detector element wrt normal axis double thickness() const override; @@ -96,7 +97,7 @@ class DetectorElementStub : public DetectorElementBase { /// the transform for positioning in 3D space Transform3 m_elementTransform; /// the surface represented by it - std::shared_ptr m_elementSurface{nullptr}; + std::shared_ptr m_elementSurface{nullptr}; /// the element thickness double m_elementThickness{0.}; }; @@ -110,6 +111,10 @@ inline const Surface& DetectorElementStub::surface() const { return *m_elementSurface; } +inline Surface& DetectorElementStub::surface() { + return *m_elementSurface; +} + inline double DetectorElementStub::thickness() const { return m_elementThickness; } diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/MultiTrajectoryTestsCommon.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/MultiTrajectoryTestsCommon.hpp new file mode 100644 index 00000000000..98c1e4dc109 --- /dev/null +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/MultiTrajectoryTestsCommon.hpp @@ -0,0 +1,1057 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/TrackStatePropMask.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Tests/CommonHelpers/TestTrackState.hpp" +#include "Acts/Utilities/HashedString.hpp" + +#include + +namespace Acts::Test { + +template +class MultiTrajectoryTestsCommon { + using ParametersVector = BoundTrackParameters::ParametersVector; + using CovarianceMatrix = BoundTrackParameters::CovarianceMatrix; + using Jacobian = BoundMatrix; + + using trajectory_t = typename factory_t::trajectory_t; + using const_trajectory_t = typename factory_t::const_trajectory_t; + + private: + factory_t m_factory; + + public: + void testBuild() { + constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; + + // construct trajectory w/ multiple components + trajectory_t t = m_factory.create(); + + auto i0 = t.addTrackState(kMask); + // trajectory bifurcates here into multiple hypotheses + auto i1a = t.addTrackState(kMask, i0); + auto i1b = t.addTrackState(kMask, i0); + auto i2a = t.addTrackState(kMask, i1a); + auto i2b = t.addTrackState(kMask, i1b); + + // print each trajectory component + std::vector act; + auto collect = [&](auto p) { + act.push_back(p.index()); + BOOST_CHECK(!p.hasCalibrated()); + BOOST_CHECK(!p.hasFiltered()); + BOOST_CHECK(!p.hasSmoothed()); + BOOST_CHECK(!p.hasJacobian()); + BOOST_CHECK(!p.hasProjector()); + }; + + std::vector exp = {i2a, i1a, i0}; + t.visitBackwards(i2a, collect); + BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), + exp.end()); + + act.clear(); + for (const auto& p : t.trackStateRange(i2a)) { + act.push_back(p.index()); + } + BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), + exp.end()); + + act.clear(); + exp = {i2b, i1b, i0}; + t.visitBackwards(i2b, collect); + BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), + exp.end()); + + act.clear(); + for (const auto& p : t.trackStateRange(i2b)) { + act.push_back(p.index()); + } + BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), + exp.end()); + + act.clear(); + t.applyBackwards(i2b, collect); + BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), + exp.end()); + + auto r = t.trackStateRange(i2b); + BOOST_CHECK_EQUAL(std::distance(r.begin(), r.end()), 3); + + // check const-correctness + const auto& ct = t; + std::vector predicteds; + // mutation in this loop works! + for (auto p : t.trackStateRange(i2b)) { + predicteds.push_back(BoundVector::Random()); + p.predicted() = predicteds.back(); + } + std::vector predictedsAct; + for (const auto& p : ct.trackStateRange(i2b)) { + predictedsAct.push_back(p.predicted()); + // mutation in this loop doesn't work: does not compile + // p.predicted() = BoundVector::Random(); + } + BOOST_CHECK_EQUAL_COLLECTIONS(predictedsAct.begin(), predictedsAct.end(), + predicteds.begin(), predicteds.end()); + } + + void testClear() { + constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; + trajectory_t t = m_factory.create(); + BOOST_CHECK_EQUAL(t.size(), 0); + + auto i0 = t.addTrackState(kMask); + // trajectory bifurcates here into multiple hypotheses + auto i1a = t.addTrackState(kMask, i0); + auto i1b = t.addTrackState(kMask, i0); + t.addTrackState(kMask, i1a); + t.addTrackState(kMask, i1b); + + BOOST_CHECK_EQUAL(t.size(), 5); + t.clear(); + BOOST_CHECK_EQUAL(t.size(), 0); + } + + void testApplyWithAbort() { + constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; + + // construct trajectory with three components + trajectory_t t = m_factory.create(); + auto i0 = t.addTrackState(kMask); + auto i1 = t.addTrackState(kMask, i0); + auto i2 = t.addTrackState(kMask, i1); + + size_t n = 0; + t.applyBackwards(i2, [&](const auto&) { + n++; + return false; + }); + BOOST_CHECK_EQUAL(n, 1u); + + n = 0; + t.applyBackwards(i2, [&](const auto& ts) { + n++; + if (ts.index() == i1) { + return false; + } + return true; + }); + BOOST_CHECK_EQUAL(n, 2u); + + n = 0; + t.applyBackwards(i2, [&](const auto&) { + n++; + return true; + }); + BOOST_CHECK_EQUAL(n, 3u); + } + + void testAddTrackStateWithBitMask() { + using PM = TrackStatePropMask; + using namespace Acts::HashedStringLiteral; + + trajectory_t t = m_factory.create(); + + auto alwaysPresent = [](auto& ts) { + BOOST_CHECK(ts.template has<"referenceSurface"_hash>()); + BOOST_CHECK(ts.template has<"measdim"_hash>()); + BOOST_CHECK(ts.template has<"chi2"_hash>()); + BOOST_CHECK(ts.template has<"pathLength"_hash>()); + BOOST_CHECK(ts.template has<"typeFlags"_hash>()); + }; + + auto ts = t.getTrackState(t.addTrackState(PM::All)); + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK(ts.hasFiltered()); + BOOST_CHECK(ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(ts.hasProjector()); + BOOST_CHECK(ts.hasJacobian()); + alwaysPresent(ts); + ts.allocateCalibrated(5); + BOOST_CHECK(ts.hasCalibrated()); + + ts = t.getTrackState(t.addTrackState(PM::None)); + BOOST_CHECK(!ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(!ts.hasProjector()); + BOOST_CHECK(!ts.hasJacobian()); + alwaysPresent(ts); + + ts = t.getTrackState(t.addTrackState(PM::Predicted)); + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(!ts.hasProjector()); + BOOST_CHECK(!ts.hasJacobian()); + alwaysPresent(ts); + + ts = t.getTrackState(t.addTrackState(PM::Filtered)); + BOOST_CHECK(!ts.hasPredicted()); + BOOST_CHECK(ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(!ts.hasProjector()); + BOOST_CHECK(!ts.hasJacobian()); + alwaysPresent(ts); + + ts = t.getTrackState(t.addTrackState(PM::Smoothed)); + BOOST_CHECK(!ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(!ts.hasProjector()); + BOOST_CHECK(!ts.hasJacobian()); + alwaysPresent(ts); + + ts = t.getTrackState(t.addTrackState(PM::Calibrated)); + BOOST_CHECK(!ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(ts.hasProjector()); + BOOST_CHECK(!ts.hasJacobian()); + ts.allocateCalibrated(5); + BOOST_CHECK(ts.hasCalibrated()); + + ts = t.getTrackState(t.addTrackState(PM::Jacobian)); + BOOST_CHECK(!ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK(!ts.hasCalibrated()); + BOOST_CHECK(!ts.hasProjector()); + BOOST_CHECK(ts.hasJacobian()); + alwaysPresent(ts); + } + + void testTrackStateProxyCrossTalk(std::default_random_engine& rng) { + TestTrackState pc(rng, 2u); + + // multi trajectory w/ a single, fully set, track state + trajectory_t traj = m_factory.create(); + size_t index = traj.addTrackState(); + { + auto ts = traj.getTrackState(index); + fillTrackState(pc, TrackStatePropMask::All, ts); + } + // get two TrackStateProxies that reference the same data + auto tsa = traj.getTrackState(index); + auto tsb = traj.getTrackState(index); + // then modify one and check that the other was modified as well + { + auto [par, cov] = generateBoundParametersCovariance(rng); + tsb.predicted() = par; + tsb.predictedCovariance() = cov; + BOOST_CHECK_EQUAL(tsa.predicted(), par); + BOOST_CHECK_EQUAL(tsa.predictedCovariance(), cov); + BOOST_CHECK_EQUAL(tsb.predicted(), par); + BOOST_CHECK_EQUAL(tsb.predictedCovariance(), cov); + } + { + auto [par, cov] = generateBoundParametersCovariance(rng); + tsb.filtered() = par; + tsb.filteredCovariance() = cov; + BOOST_CHECK_EQUAL(tsa.filtered(), par); + BOOST_CHECK_EQUAL(tsa.filteredCovariance(), cov); + BOOST_CHECK_EQUAL(tsb.filtered(), par); + BOOST_CHECK_EQUAL(tsb.filteredCovariance(), cov); + } + { + auto [par, cov] = generateBoundParametersCovariance(rng); + tsb.smoothed() = par; + tsb.smoothedCovariance() = cov; + BOOST_CHECK_EQUAL(tsa.smoothed(), par); + BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), cov); + BOOST_CHECK_EQUAL(tsb.smoothed(), par); + BOOST_CHECK_EQUAL(tsb.smoothedCovariance(), cov); + } + { + // create a new (invalid) source link + TestSourceLink invalid; + invalid.sourceId = -1; + BOOST_CHECK_NE( + tsa.getUncalibratedSourceLink().template get(), + invalid); + BOOST_CHECK_NE( + tsb.getUncalibratedSourceLink().template get(), + invalid); + tsb.setUncalibratedSourceLink(SourceLink{invalid}); + BOOST_CHECK_EQUAL( + tsa.getUncalibratedSourceLink().template get(), + invalid); + BOOST_CHECK_EQUAL( + tsb.getUncalibratedSourceLink().template get(), + invalid); + } + { + // reset measurements w/ full parameters + auto [measPar, measCov] = generateBoundParametersCovariance(rng); + tsb.allocateCalibrated(eBoundSize); + tsb.template calibrated() = measPar; + tsb.template calibratedCovariance() = measCov; + BOOST_CHECK_EQUAL(tsa.template calibrated(), measPar); + BOOST_CHECK_EQUAL(tsa.template calibratedCovariance(), + measCov); + BOOST_CHECK_EQUAL(tsb.template calibrated(), measPar); + BOOST_CHECK_EQUAL(tsb.template calibratedCovariance(), + measCov); + } + { + // reset only the effective measurements + auto [measPar, measCov] = generateBoundParametersCovariance(rng); + size_t nMeasurements = tsb.effectiveCalibrated().rows(); + auto effPar = measPar.head(nMeasurements); + auto effCov = measCov.topLeftCorner(nMeasurements, nMeasurements); + tsb.allocateCalibrated(eBoundSize); + tsb.effectiveCalibrated() = effPar; + tsb.effectiveCalibratedCovariance() = effCov; + BOOST_CHECK_EQUAL(tsa.effectiveCalibrated(), effPar); + BOOST_CHECK_EQUAL(tsa.effectiveCalibratedCovariance(), effCov); + BOOST_CHECK_EQUAL(tsb.effectiveCalibrated(), effPar); + BOOST_CHECK_EQUAL(tsb.effectiveCalibratedCovariance(), effCov); + } + { + Jacobian jac = Jacobian::Identity(); + BOOST_CHECK_NE(tsa.jacobian(), jac); + BOOST_CHECK_NE(tsb.jacobian(), jac); + tsb.jacobian() = jac; + BOOST_CHECK_EQUAL(tsa.jacobian(), jac); + BOOST_CHECK_EQUAL(tsb.jacobian(), jac); + } + { + tsb.chi2() = 98.0; + BOOST_CHECK_EQUAL(tsa.chi2(), 98.0); + BOOST_CHECK_EQUAL(tsb.chi2(), 98.0); + } + { + tsb.pathLength() = 66.0; + BOOST_CHECK_EQUAL(tsa.pathLength(), 66.0); + BOOST_CHECK_EQUAL(tsb.pathLength(), 66.0); + } + } + + void testTrackStateReassignment(std::default_random_engine& rng) { + TestTrackState pc(rng, 1u); + + trajectory_t t = m_factory.create(); + size_t index = t.addTrackState(); + auto ts = t.getTrackState(index); + fillTrackState(pc, TrackStatePropMask::All, ts); + + // assert contents of original measurement (just to be safe) + BOOST_CHECK_EQUAL(ts.calibratedSize(), 1u); + BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), + (pc.sourceLink.parameters.head<1>())); + BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(), + (pc.sourceLink.covariance.topLeftCorner<1, 1>())); + + // use temporary measurement to reset calibrated data + TestTrackState ttsb(rng, 2u); + ts.setUncalibratedSourceLink(SourceLink{ttsb.sourceLink}); + Acts::GeometryContext gctx; + auto meas = testSourceLinkCalibratorReturn(gctx, ts); + auto m2 = std::get>(meas); + + BOOST_CHECK_EQUAL(ts.calibratedSize(), 2); + BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), m2.parameters()); + BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(), m2.covariance()); + BOOST_CHECK_EQUAL(ts.effectiveProjector(), m2.projector()); + } + + void testTrackStateProxyStorage(std::default_random_engine& rng, + size_t nMeasurements) { + TestTrackState pc(rng, nMeasurements); + + // create trajectory with a single fully-filled random track state + trajectory_t t = m_factory.create(); + size_t index = t.addTrackState(); + auto ts = t.getTrackState(index); + fillTrackState(pc, TrackStatePropMask::All, ts); + + // check that the surface is correctly set + BOOST_CHECK_EQUAL(&ts.referenceSurface(), pc.surface.get()); + BOOST_CHECK_EQUAL(ts.referenceSurface().geometryId(), + pc.sourceLink.geometryId()); + + // check that the track parameters are set + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK_EQUAL(ts.predicted(), pc.predicted.parameters()); + BOOST_CHECK(pc.predicted.covariance().has_value()); + BOOST_CHECK_EQUAL(ts.predictedCovariance(), *pc.predicted.covariance()); + BOOST_CHECK(ts.hasFiltered()); + BOOST_CHECK_EQUAL(ts.filtered(), pc.filtered.parameters()); + BOOST_CHECK(pc.filtered.covariance().has_value()); + BOOST_CHECK_EQUAL(ts.filteredCovariance(), *pc.filtered.covariance()); + BOOST_CHECK(ts.hasSmoothed()); + BOOST_CHECK_EQUAL(ts.smoothed(), pc.smoothed.parameters()); + BOOST_CHECK(pc.smoothed.covariance().has_value()); + BOOST_CHECK_EQUAL(ts.smoothedCovariance(), *pc.smoothed.covariance()); + + // check that the jacobian is set + BOOST_CHECK(ts.hasJacobian()); + BOOST_CHECK_EQUAL(ts.jacobian(), pc.jacobian); + BOOST_CHECK_EQUAL(ts.pathLength(), pc.pathLength); + // check that chi2 is set + BOOST_CHECK_EQUAL(ts.chi2(), pc.chi2); + + // check that the uncalibratedSourceLink source link is set + BOOST_CHECK_EQUAL( + ts.getUncalibratedSourceLink().template get(), + pc.sourceLink); + + // check that the calibrated measurement is set + BOOST_CHECK(ts.hasCalibrated()); + BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), + pc.sourceLink.parameters.head(nMeasurements)); + BOOST_CHECK_EQUAL( + ts.effectiveCalibratedCovariance(), + pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements)); + { + ParametersVector mParFull = ParametersVector::Zero(); + CovarianceMatrix mCovFull = CovarianceMatrix::Zero(); + mParFull.head(nMeasurements) = + pc.sourceLink.parameters.head(nMeasurements); + mCovFull.topLeftCorner(nMeasurements, nMeasurements) = + pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements); + + auto expMeas = pc.sourceLink.parameters.head(nMeasurements); + auto expCov = + pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements); + + visit_measurement(ts.calibratedSize(), [&](auto N) { + constexpr size_t measdim = decltype(N)::value; + BOOST_CHECK_EQUAL(ts.template calibrated(), expMeas); + BOOST_CHECK_EQUAL(ts.template calibratedCovariance(), expCov); + }); + } + + BOOST_CHECK(ts.hasProjector()); + ActsMatrix fullProj; + fullProj.setZero(); + { + Acts::GeometryContext gctx; + // create a temporary measurement to extract the projector matrix + auto meas = testSourceLinkCalibratorReturn(gctx, ts); + std::visit( + [&](const auto& m) { + fullProj.topLeftCorner(nMeasurements, eBoundSize) = m.projector(); + }, + meas); + } + BOOST_CHECK_EQUAL(ts.effectiveProjector(), + fullProj.topLeftCorner(nMeasurements, eBoundSize)); + BOOST_CHECK_EQUAL(ts.projector(), fullProj); + } + + void testTrackStateProxyAllocations(std::default_random_engine& rng) { + using namespace Acts::HashedStringLiteral; + + TestTrackState pc(rng, 2u); + + // this should allocate for all components in the trackstate, plus filtered + trajectory_t t = m_factory.create(); + size_t i = t.addTrackState(TrackStatePropMask::Predicted | + TrackStatePropMask::Filtered | + TrackStatePropMask::Jacobian); + auto tso = t.getTrackState(i); + fillTrackState(pc, TrackStatePropMask::Predicted, tso); + fillTrackState(pc, TrackStatePropMask::Filtered, tso); + fillTrackState(pc, TrackStatePropMask::Jacobian, tso); + + BOOST_CHECK(tso.hasPredicted()); + BOOST_CHECK(tso.hasFiltered()); + BOOST_CHECK(!tso.hasSmoothed()); + BOOST_CHECK(!tso.hasCalibrated()); + BOOST_CHECK(tso.hasJacobian()); + + auto tsnone = t.getTrackState(t.addTrackState(TrackStatePropMask::None)); + BOOST_CHECK(!tsnone.template has<"predicted"_hash>()); + BOOST_CHECK(!tsnone.template has<"filtered"_hash>()); + BOOST_CHECK(!tsnone.template has<"smoothed"_hash>()); + BOOST_CHECK(!tsnone.template has<"jacobian"_hash>()); + BOOST_CHECK(!tsnone.template has<"calibrated"_hash>()); + BOOST_CHECK(!tsnone.template has<"projector"_hash>()); + BOOST_CHECK( + !tsnone.template has<"uncalibratedSourceLink"_hash>()); // separate + // optional + // mechanism + BOOST_CHECK(tsnone.template has<"referenceSurface"_hash>()); + BOOST_CHECK(tsnone.template has<"measdim"_hash>()); + BOOST_CHECK(tsnone.template has<"chi2"_hash>()); + BOOST_CHECK(tsnone.template has<"pathLength"_hash>()); + BOOST_CHECK(tsnone.template has<"typeFlags"_hash>()); + + auto tsall = t.getTrackState(t.addTrackState(TrackStatePropMask::All)); + BOOST_CHECK(tsall.template has<"predicted"_hash>()); + BOOST_CHECK(tsall.template has<"filtered"_hash>()); + BOOST_CHECK(tsall.template has<"smoothed"_hash>()); + BOOST_CHECK(tsall.template has<"jacobian"_hash>()); + BOOST_CHECK(!tsall.template has<"calibrated"_hash>()); + tsall.allocateCalibrated(5); + BOOST_CHECK(tsall.template has<"calibrated"_hash>()); + BOOST_CHECK(tsall.template has<"projector"_hash>()); + BOOST_CHECK(!tsall.template has< + "uncalibratedSourceLink"_hash>()); // separate optional + // mechanism: nullptr + BOOST_CHECK(tsall.template has<"referenceSurface"_hash>()); + BOOST_CHECK(tsall.template has<"measdim"_hash>()); + BOOST_CHECK(tsall.template has<"chi2"_hash>()); + BOOST_CHECK(tsall.template has<"pathLength"_hash>()); + BOOST_CHECK(tsall.template has<"typeFlags"_hash>()); + + tsall.unset(TrackStatePropMask::Predicted); + BOOST_CHECK(!tsall.template has<"predicted"_hash>()); + tsall.unset(TrackStatePropMask::Filtered); + BOOST_CHECK(!tsall.template has<"filtered"_hash>()); + tsall.unset(TrackStatePropMask::Smoothed); + BOOST_CHECK(!tsall.template has<"smoothed"_hash>()); + tsall.unset(TrackStatePropMask::Jacobian); + BOOST_CHECK(!tsall.template has<"jacobian"_hash>()); + tsall.unset(TrackStatePropMask::Calibrated); + BOOST_CHECK(!tsall.template has<"calibrated"_hash>()); + } + + void testTrackStateProxyGetMask() { + using PM = TrackStatePropMask; + + std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, + PM::Jacobian, PM::Calibrated}; + PM all = std::accumulate(values.begin(), values.end(), PM::None, + [](auto a, auto b) { return a | b; }); + + trajectory_t mj = m_factory.create(); + { + auto ts = mj.getTrackState(mj.addTrackState(PM::All)); + // Calibrated is ignored because we haven't allocated yet + BOOST_CHECK_EQUAL(ts.getMask(), (all & ~PM::Calibrated)); + ts.allocateCalibrated(4); + BOOST_CHECK_EQUAL(ts.getMask(), all); + } + { + auto ts = + mj.getTrackState(mj.addTrackState(PM::Filtered | PM::Calibrated)); + // Calibrated is ignored because we haven't allocated yet + BOOST_CHECK_EQUAL(ts.getMask(), PM::Filtered); + ts.allocateCalibrated(4); + BOOST_CHECK_EQUAL(ts.getMask(), (PM::Filtered | PM::Calibrated)); + } + { + auto ts = mj.getTrackState( + mj.addTrackState(PM::Filtered | PM::Smoothed | PM::Predicted)); + BOOST_CHECK(ts.getMask() == + (PM::Filtered | PM::Smoothed | PM::Predicted)); + } + { + for (PM mask : values) { + auto ts = mj.getTrackState(mj.addTrackState(mask)); + // Calibrated is ignored because we haven't allocated yet + BOOST_CHECK_EQUAL(ts.getMask(), (mask & ~PM::Calibrated)); + } + } + } + + void testTrackStateProxyCopy(std::default_random_engine& rng) { + using PM = TrackStatePropMask; + + std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, + PM::Jacobian}; + + trajectory_t mj = m_factory.create(); + auto mkts = [&](PM mask) { + auto r = mj.getTrackState(mj.addTrackState(mask)); + return r; + }; + + // orthogonal ones + for (PM a : values) { + for (PM b : values) { + auto tsa = mkts(a); + auto tsb = mkts(b); + // doesn't work + if (a != b) { + BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error); + BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error); + } else { + tsa.copyFrom(tsb); + tsb.copyFrom(tsa); + } + } + } + + { + BOOST_TEST_CHECKPOINT("Calib auto alloc"); + auto tsa = mkts(PM::All); + auto tsb = mkts(PM::All); + tsb.allocateCalibrated(5); + tsb.template calibrated<5>().setRandom(); + tsb.template calibratedCovariance<5>().setRandom(); + tsa.copyFrom(tsb, PM::All); + BOOST_CHECK_EQUAL(tsa.template calibrated<5>(), + tsb.template calibrated<5>()); + BOOST_CHECK_EQUAL(tsa.template calibratedCovariance<5>(), + tsb.template calibratedCovariance<5>()); + } + + { + BOOST_TEST_CHECKPOINT("Copy none"); + auto tsa = mkts(PM::All); + auto tsb = mkts(PM::All); + tsa.copyFrom(tsb, PM::None); + } + + auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both + ts1.filtered().setRandom(); + ts1.filteredCovariance().setRandom(); + ts1.predicted().setRandom(); + ts1.predictedCovariance().setRandom(); + + // ((src XOR dst) & src) == 0 + auto ts2 = mkts(PM::Predicted); + ts2.predicted().setRandom(); + ts2.predictedCovariance().setRandom(); + + // they are different before + BOOST_CHECK(ts1.predicted() != ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); + + // ts1 -> ts2 fails + BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error); + BOOST_CHECK(ts1.predicted() != ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); + + // ts2 -> ts1 is ok + ts1.copyFrom(ts2); + BOOST_CHECK(ts1.predicted() == ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance()); + + size_t i0 = mj.addTrackState(); + size_t i1 = mj.addTrackState(); + ts1 = mj.getTrackState(i0); + ts2 = mj.getTrackState(i1); + TestTrackState rts1(rng, 1u); + TestTrackState rts2(rng, 2u); + fillTrackState(rts1, TrackStatePropMask::All, ts1); + fillTrackState(rts2, TrackStatePropMask::All, ts2); + + auto ots1 = mkts(PM::All); + auto ots2 = mkts(PM::All); + // make full copy for later. We prove full copy works right below + ots1.copyFrom(ts1); + ots2.copyFrom(ts2); + + BOOST_CHECK_NE(ts1.predicted(), ts2.predicted()); + BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance()); + BOOST_CHECK_NE(ts1.filtered(), ts2.filtered()); + BOOST_CHECK_NE(ts1.filteredCovariance(), ts2.filteredCovariance()); + BOOST_CHECK_NE(ts1.smoothed(), ts2.smoothed()); + BOOST_CHECK_NE(ts1.smoothedCovariance(), ts2.smoothedCovariance()); + + BOOST_CHECK_NE( + ts1.getUncalibratedSourceLink().template get(), + ts2.getUncalibratedSourceLink().template get()); + + visit_measurement(ts1.calibratedSize(), [&](auto N) { + constexpr size_t measdim = decltype(N)::value; + BOOST_CHECK_NE(ts1.template calibrated(), + ts2.template calibrated()); + BOOST_CHECK_NE(ts1.template calibratedCovariance(), + ts2.template calibratedCovariance()); + }); + + BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize()); + BOOST_CHECK_NE(ts1.projector(), ts2.projector()); + + BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian()); + BOOST_CHECK_NE(ts1.chi2(), ts2.chi2()); + BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength()); + BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface()); + + ts1.copyFrom(ts2); + + BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted()); + BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance()); + BOOST_CHECK_EQUAL(ts1.filtered(), ts2.filtered()); + BOOST_CHECK_EQUAL(ts1.filteredCovariance(), ts2.filteredCovariance()); + BOOST_CHECK_EQUAL(ts1.smoothed(), ts2.smoothed()); + BOOST_CHECK_EQUAL(ts1.smoothedCovariance(), ts2.smoothedCovariance()); + + BOOST_CHECK_EQUAL( + ts1.getUncalibratedSourceLink().template get(), + ts2.getUncalibratedSourceLink().template get()); + + visit_measurement(ts1.calibratedSize(), [&](auto N) { + constexpr size_t measdim = decltype(N)::value; + BOOST_CHECK_EQUAL(ts1.template calibrated(), + ts2.template calibrated()); + BOOST_CHECK_EQUAL(ts1.template calibratedCovariance(), + ts2.template calibratedCovariance()); + }); + + BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize()); + BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector()); + + BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian()); + BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2()); + BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength()); + BOOST_CHECK_EQUAL(&ts1.referenceSurface(), &ts2.referenceSurface()); + + // full copy proven to work. now let's do partial copy + ts2 = mkts(PM::Predicted | PM::Jacobian | PM::Calibrated); + ts2.copyFrom(ots2, PM::Predicted | PM::Jacobian | PM::Calibrated); + // copy into empty ts, only copy some + ts1.copyFrom(ots1); // reset to original + // is different again + BOOST_CHECK_NE(ts1.predicted(), ts2.predicted()); + BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance()); + + visit_measurement(ts1.calibratedSize(), [&](auto N) { + constexpr size_t measdim = decltype(N)::value; + BOOST_CHECK_NE(ts1.template calibrated(), + ts2.template calibrated()); + BOOST_CHECK_NE(ts1.template calibratedCovariance(), + ts2.template calibratedCovariance()); + }); + + BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize()); + BOOST_CHECK_NE(ts1.projector(), ts2.projector()); + + BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian()); + BOOST_CHECK_NE(ts1.chi2(), ts2.chi2()); + BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength()); + BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface()); + + ts1.copyFrom(ts2); + + // some components are same now + BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted()); + BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance()); + + visit_measurement(ts1.calibratedSize(), [&](auto N) { + constexpr size_t measdim = decltype(N)::value; + BOOST_CHECK_EQUAL(ts1.template calibrated(), + ts2.template calibrated()); + BOOST_CHECK_EQUAL(ts1.template calibratedCovariance(), + ts2.template calibratedCovariance()); + }); + + BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize()); + BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector()); + + BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian()); + BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2()); // always copied + BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength()); // always copied + BOOST_CHECK_EQUAL(&ts1.referenceSurface(), + &ts2.referenceSurface()); // always copied + } + + void testTrackStateProxyCopyDiffMTJ() { + using PM = TrackStatePropMask; + + std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, + PM::Jacobian}; + + trajectory_t mj = m_factory.create(); + trajectory_t mj2 = m_factory.create(); + auto mkts = [&](PM mask) { + auto r = mj.getTrackState(mj.addTrackState(mask)); + return r; + }; + auto mkts2 = [&](PM mask) { + auto r = mj2.getTrackState(mj2.addTrackState(mask)); + return r; + }; + + // orthogonal ones + for (PM a : values) { + for (PM b : values) { + auto tsa = mkts(a); + auto tsb = mkts2(b); + // doesn't work + if (a != b) { + BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error); + BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error); + } else { + tsa.copyFrom(tsb); + tsb.copyFrom(tsa); + } + } + } + + // make sure they are actually on different MultiTrajectories + BOOST_CHECK_EQUAL(mj.size(), values.size() * values.size()); + BOOST_CHECK_EQUAL(mj2.size(), values.size() * values.size()); + + auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both + ts1.filtered().setRandom(); + ts1.filteredCovariance().setRandom(); + ts1.predicted().setRandom(); + ts1.predictedCovariance().setRandom(); + + // ((src XOR dst) & src) == 0 + auto ts2 = mkts2(PM::Predicted); + ts2.predicted().setRandom(); + ts2.predictedCovariance().setRandom(); + + // they are different before + BOOST_CHECK(ts1.predicted() != ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); + + // ts1 -> ts2 fails + BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error); + BOOST_CHECK(ts1.predicted() != ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); + + // ts2 -> ts1 is ok + ts1.copyFrom(ts2); + BOOST_CHECK(ts1.predicted() == ts2.predicted()); + BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance()); + + { + BOOST_TEST_CHECKPOINT("Calib auto alloc"); + auto tsa = mkts(PM::All); + auto tsb = mkts(PM::All); + tsb.allocateCalibrated(5); + tsb.template calibrated<5>().setRandom(); + tsb.template calibratedCovariance<5>().setRandom(); + tsa.copyFrom(tsb, PM::All); + BOOST_CHECK_EQUAL(tsa.template calibrated<5>(), + tsb.template calibrated<5>()); + BOOST_CHECK_EQUAL(tsa.template calibratedCovariance<5>(), + tsb.template calibratedCovariance<5>()); + } + + { + BOOST_TEST_CHECKPOINT("Copy none"); + auto tsa = mkts(PM::All); + auto tsb = mkts(PM::All); + tsa.copyFrom(tsb, PM::None); + } + } + + void testProxyAssignment() { + constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; + trajectory_t t = m_factory.create(); + auto i0 = t.addTrackState(kMask); + + typename trajectory_t::TrackStateProxy tp = t.getTrackState(i0); // mutable + typename trajectory_t::TrackStateProxy tp2{tp}; // mutable to mutable + typename trajectory_t::ConstTrackStateProxy tp3{tp}; // mutable to const + // const to mutable: this won't compile + // MultiTrajectory::TrackStateProxy tp4{tp3}; + } + + void testCopyFromConst() { + // Check if the copy from const does compile, assume the copy is done + // correctly + + using PM = TrackStatePropMask; + trajectory_t mj = m_factory.create(); + + const auto idx_a = mj.addTrackState(PM::All); + const auto idx_b = mj.addTrackState(PM::All); + + typename trajectory_t::TrackStateProxy mutableProxy = + mj.getTrackState(idx_a); + + const trajectory_t& cmj = mj; + typename trajectory_t::ConstTrackStateProxy constProxy = + cmj.getTrackState(idx_b); + + mutableProxy.copyFrom(constProxy); + + // copy mutable to const: this won't compile + // constProxy.copyFrom(mutableProxy); + } + + void testTrackStateProxyShare(std::default_random_engine& rng) { + TestTrackState pc(rng, 2u); + + { + trajectory_t traj = m_factory.create(); + size_t ia = traj.addTrackState(TrackStatePropMask::All); + size_t ib = traj.addTrackState(TrackStatePropMask::None); + + auto tsa = traj.getTrackState(ia); + auto tsb = traj.getTrackState(ib); + + fillTrackState(pc, TrackStatePropMask::All, tsa); + + BOOST_CHECK(tsa.hasPredicted()); + BOOST_CHECK(!tsb.hasPredicted()); + tsb.shareFrom(tsa, TrackStatePropMask::Predicted); + BOOST_CHECK(tsa.hasPredicted()); + BOOST_CHECK(tsb.hasPredicted()); + BOOST_CHECK_EQUAL(tsa.predicted(), tsb.predicted()); + BOOST_CHECK_EQUAL(tsa.predictedCovariance(), tsb.predictedCovariance()); + + BOOST_CHECK(tsa.hasFiltered()); + BOOST_CHECK(!tsb.hasFiltered()); + tsb.shareFrom(tsa, TrackStatePropMask::Filtered); + BOOST_CHECK(tsa.hasFiltered()); + BOOST_CHECK(tsb.hasFiltered()); + BOOST_CHECK_EQUAL(tsa.filtered(), tsb.filtered()); + BOOST_CHECK_EQUAL(tsa.filteredCovariance(), tsb.filteredCovariance()); + + BOOST_CHECK(tsa.hasSmoothed()); + BOOST_CHECK(!tsb.hasSmoothed()); + tsb.shareFrom(tsa, TrackStatePropMask::Smoothed); + BOOST_CHECK(tsa.hasSmoothed()); + BOOST_CHECK(tsb.hasSmoothed()); + BOOST_CHECK_EQUAL(tsa.smoothed(), tsb.smoothed()); + BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), tsb.smoothedCovariance()); + + BOOST_CHECK(tsa.hasJacobian()); + BOOST_CHECK(!tsb.hasJacobian()); + tsb.shareFrom(tsa, TrackStatePropMask::Jacobian); + BOOST_CHECK(tsa.hasJacobian()); + BOOST_CHECK(tsb.hasJacobian()); + BOOST_CHECK_EQUAL(tsa.jacobian(), tsb.jacobian()); + } + + { + trajectory_t traj = m_factory.create(); + size_t i = traj.addTrackState(TrackStatePropMask::All & + ~TrackStatePropMask::Filtered & + ~TrackStatePropMask::Smoothed); + + auto ts = traj.getTrackState(i); + + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK(!ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + ts.predicted().setRandom(); + ts.predictedCovariance().setRandom(); + + ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Filtered); + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK(ts.hasFiltered()); + BOOST_CHECK(!ts.hasSmoothed()); + BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered()); + BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance()); + + ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Smoothed); + BOOST_CHECK(ts.hasPredicted()); + BOOST_CHECK(ts.hasFiltered()); + BOOST_CHECK(ts.hasSmoothed()); + BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered()); + BOOST_CHECK_EQUAL(ts.predicted(), ts.smoothed()); + BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance()); + BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.smoothedCovariance()); + } + } + + void testMultiTrajectoryExtraColumns() { + using namespace HashedStringLiteral; + + auto test = [&](const std::string& col, auto value) { + using T = decltype(value); + std::string col2 = col + "_2"; + HashedString h{hashString(col)}; + HashedString h2{hashString(col2)}; + + trajectory_t traj = m_factory.create(); + BOOST_CHECK(!traj.hasColumn(h)); + traj.template addColumn(col); + BOOST_CHECK(traj.hasColumn(h)); + + BOOST_CHECK(!traj.hasColumn(h2)); + traj.template addColumn(col2); + BOOST_CHECK(traj.hasColumn(h2)); + + auto ts1 = traj.getTrackState(traj.addTrackState()); + auto ts2 = traj.getTrackState( + traj.addTrackState(TrackStatePropMask::All, ts1.index())); + auto ts3 = traj.getTrackState( + traj.addTrackState(TrackStatePropMask::All, ts2.index())); + + BOOST_CHECK(ts1.has(h)); + BOOST_CHECK(ts2.has(h)); + BOOST_CHECK(ts3.has(h)); + + BOOST_CHECK(ts1.has(h2)); + BOOST_CHECK(ts2.has(h2)); + BOOST_CHECK(ts3.has(h2)); + + ts1.template component(col) = value; + BOOST_CHECK_EQUAL(ts1.template component(col), value); + }; + + test("uint32_t", uint32_t(1)); + test("uint64_t", uint64_t(2)); + test("int32_t", int32_t(-3)); + test("int64_t", int64_t(-4)); + test("float", float(8.9)); + test("double", double(656.2)); + + trajectory_t traj = m_factory.create(); + traj.template addColumn("extra_column"); + traj.template addColumn("another_column"); + + auto ts1 = traj.getTrackState(traj.addTrackState()); + auto ts2 = traj.getTrackState( + traj.addTrackState(TrackStatePropMask::All, ts1.index())); + auto ts3 = traj.getTrackState( + traj.addTrackState(TrackStatePropMask::All, ts2.index())); + + BOOST_CHECK(ts1.template has<"extra_column"_hash>()); + BOOST_CHECK(ts2.template has<"extra_column"_hash>()); + BOOST_CHECK(ts3.template has<"extra_column"_hash>()); + + BOOST_CHECK(ts1.template has<"another_column"_hash>()); + BOOST_CHECK(ts2.template has<"another_column"_hash>()); + BOOST_CHECK(ts3.template has<"another_column"_hash>()); + + ts2.template component() = 6; + + BOOST_CHECK_EQUAL((ts2.template component()), 6); + + ts3.template component() = 7.2f; + BOOST_CHECK_EQUAL((ts3.template component()), + 7.2f); + } + + void testMultiTrajectoryExtraColumnsRuntime() { + auto runTest = [&](auto&& fn) { + trajectory_t mt = m_factory.create(); + std::vector columns = {"one", "two", "three", "four"}; + for (const auto& c : columns) { + BOOST_CHECK(!mt.hasColumn(fn(c))); + mt.template addColumn(c); + BOOST_CHECK(mt.hasColumn(fn(c))); + } + for (const auto& c : columns) { + auto ts1 = mt.getTrackState(mt.addTrackState()); + auto ts2 = mt.getTrackState(mt.addTrackState()); + BOOST_CHECK(ts1.has(fn(c))); + BOOST_CHECK(ts2.has(fn(c))); + ts1.template component(fn(c)) = 674; + ts2.template component(fn(c)) = 421; + BOOST_CHECK_EQUAL(ts1.template component(fn(c)), 674); + BOOST_CHECK_EQUAL(ts2.template component(fn(c)), 421); + } + }; + + runTest([](const std::string& c) { return hashString(c.c_str()); }); + // runTest([](const std::string& c) { return c.c_str(); }); + // runTest([](const std::string& c) { return c; }); + // runTest([](std::string_view c) { return c; }); + } +}; +} // namespace Acts::Test diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/NonCompileTestHelpers.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/NonCompileTestHelpers.hpp new file mode 100644 index 00000000000..7070250e5e5 --- /dev/null +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/NonCompileTestHelpers.hpp @@ -0,0 +1,15 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#define ACTS_DOES_NOT_COMPILE_SUITE_BEGIN(name) int main() { +#define ACTS_DOES_NOT_COMPILE_SUITE_END() } + +#define ACTS_DOES_NOT_COMPILE_BEGIN(name) { +#define ACTS_DOES_NOT_COMPILE_END() } diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/TestTrackState.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/TestTrackState.hpp index 8c0f99c5147..6aa7a33929a 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/TestTrackState.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/TestTrackState.hpp @@ -89,7 +89,7 @@ struct TestTrackState { // @param[in] mask Specifies which components are used/filled // @param[out] ts TrackStateProxy which is filled // @param [in] measdim Dimension of the measurement -template +template void fillTrackState(const TestTrackState& pc, TrackStatePropMask mask, track_state_t& ts) { // always set the reference surface @@ -119,8 +119,7 @@ void fillTrackState(const TestTrackState& pc, TrackStatePropMask mask, ts.setUncalibratedSourceLink(Acts::SourceLink{pc.sourceLink}); // create calibrated measurements from source link if (ACTS_CHECK_BIT(mask, TrackStatePropMask::Calibrated)) { - testSourceLinkCalibrator(Acts::GeometryContext{}, - ts); + testSourceLinkCalibrator(Acts::GeometryContext{}, ts); } } diff --git a/Tests/IntegrationTests/Legacy/ATLSeedingIntegrationTest.cpp b/Tests/IntegrationTests/Legacy/ATLSeedingIntegrationTest.cpp index d4ed361fd76..0e6c730bfac 100644 --- a/Tests/IntegrationTests/Legacy/ATLSeedingIntegrationTest.cpp +++ b/Tests/IntegrationTests/Legacy/ATLSeedingIntegrationTest.cpp @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(number_of_seeds_correct_) { sp->x = xVec.at(i); sp->y = yVec.at(i); sp->z = zVec.at(i); - sp->r = std::sqrt(sp->x * sp->x + sp->y * sp->y); + sp->r = std::hypot(sp->x, sp->y); if (sp->r < 200.) { sp->setClusterList(1, 0); } diff --git a/Tests/IntegrationTests/PropagationTests.hpp b/Tests/IntegrationTests/PropagationTests.hpp index 15f18a9ea4a..5ba4cbaff93 100644 --- a/Tests/IntegrationTests/PropagationTests.hpp +++ b/Tests/IntegrationTests/PropagationTests.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" @@ -250,8 +251,7 @@ inline std::pair transportFreely( // setup propagation options options_t options(geoCtx, magCtx); - options.direction = (0 <= pathLength) ? Acts::NavigationDirection::Forward - : Acts::NavigationDirection::Backward; + options.direction = Acts::Direction::fromScalar(pathLength); options.pathLimit = pathLength; options.maxStepSize = 1_cm; @@ -278,7 +278,7 @@ inline std::pair transportToSurface( // setup propagation options options_t options(geoCtx, magCtx); - options.direction = Acts::NavigationDirection::Forward; + options.direction = Acts::Direction::Forward; options.pathLimit = pathLimit; options.maxStepSize = 1_cm; @@ -302,7 +302,7 @@ inline void runForwardBackwardTest( const Acts::MagneticFieldContext& magCtx, const Acts::SingleCurvilinearTrackParameters& initialParams, double pathLength, double epsPos, double epsDir, double epsMom) { - // propagate parameters NavigationDirection::Forward + // propagate parameters Acts::Direction::Forward auto [fwdParams, fwdPathLength] = transportFreely( propagator, geoCtx, magCtx, initialParams, pathLength); diff --git a/Tests/UnitTests/CMakeLists.txt b/Tests/UnitTests/CMakeLists.txt index 7344f6dbbaf..b5f280dc270 100644 --- a/Tests/UnitTests/CMakeLists.txt +++ b/Tests/UnitTests/CMakeLists.txt @@ -25,6 +25,86 @@ macro(add_unittest _name) add_test(NAME ${_name} COMMAND ${_target}) endmacro() +# This function adds a non compile test. To this end it +# - Adds a target to process the file at `src`, and converts begin/end macros +# to `#if defined` in an extra file +# - Adds an executable target for that source file, excludes it from the default build +# Set a preprocessor define to enable ONE critical section. +# - Adds a test job to ctest which invokes CMake to build this executable target. +# The test is set to fail if the build succeeds, i.e. testing if something doesn't compile +# - Adds a closure test where it's supposed to actually compile if all of the +# critical sections are removed +function(add_non_compile_test name src) + + # Don't add anything if the corresponding flag is not set + if(NOT ACTS_BUILD_NONCOMPILE_TESTS) + return() + endif() + + # Figure out where to put the output file + cmake_path(ABSOLUTE_PATH src) + get_filename_component(_filename ${src} NAME) + set(_processed_source "${CMAKE_CURRENT_BINARY_DIR}/${_filename}") + + # Add a build step to generate the source file by invoking a CMake script + add_custom_command( + OUTPUT ${_processed_source} + DEPENDS ${src} + COMMAND "${CMAKE_COMMAND}" + -DINPUT_FILE=${src} + -DOUTPUT_FILE=${_processed_source} + -P ${CMAKE_SOURCE_DIR}/cmake/ActsGenerateNonCompileTest.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + add_custom_target( + "${name}_generated_source" + DEPENDS ${_processed_source} + ) + + # Create the executable target, add the generated source code as a + # dependencies, so that it's generated when required. + set(test "ActsNonCompileTest${name}Closure") + set(target "${test}_Executable") + add_executable(${target} ${_processed_source}) + target_link_libraries(${target} PUBLIC ActsCore ActsTestsCommonHelpers) + add_dependencies(${target} "${name}_generated_source") + + # Don't build this target by default + set_target_properties(${target} PROPERTIES + EXCLUDE_FROM_ALL ON + EXCLUDE_FROM_DEFAULT_BUILD ON) + + # Add the test that calls into CMake to build the target. This one we expect to succeed + add_test(NAME ${test} + COMMAND ${CMAKE_COMMAND} --build . --target ${target} --config $ + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + + # Loop over critical section markers and add one pair of executable targets and + # and test jobs. The test jobs are flipped, so success is failure. + file(READ ${src} content) + string(REGEX MATCHALL "ACTS_DOES_NOT_COMPILE_BEGIN\\(([A-Za-z0-9]+)\\)" matches ${content}) + foreach(match ${matches}) + string(REGEX REPLACE "ACTS_DOES_NOT_COMPILE_BEGIN\\(([A-Za-z0-9]+)\\)" "\\1" match ${match}) + + set(test "ActsNonCompileTest${name}${match}") + set(target "${test}_Executable") + add_executable(${target} ${_processed_source}) + target_link_libraries(${target} PUBLIC ActsCore ActsTestsCommonHelpers) + target_compile_definitions(${target} PRIVATE "-D${match}") + add_dependencies(${target} "${name}_generated_source") + + set_target_properties(${target} PROPERTIES + EXCLUDE_FROM_ALL ON + EXCLUDE_FROM_DEFAULT_BUILD ON) + + add_test(NAME ${test} + COMMAND ${CMAKE_COMMAND} --build . --target ${target} --config $ + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + set_tests_properties(${test} PROPERTIES WILL_FAIL ON) + endforeach() + +endfunction() + add_subdirectory(Core) add_subdirectory_if(Examples ACTS_BUILD_EXAMPLES) add_subdirectory_if(Benchmarks ACTS_BUILD_BENCHMARKS) diff --git a/Tests/UnitTests/Core/Definitions/CMakeLists.txt b/Tests/UnitTests/Core/Definitions/CMakeLists.txt index 1b6599bff1b..77caaf19e0f 100644 --- a/Tests/UnitTests/Core/Definitions/CMakeLists.txt +++ b/Tests/UnitTests/Core/Definitions/CMakeLists.txt @@ -1,2 +1,2 @@ -add_unittest(Common CommonTests.cpp) +add_unittest(Direction DirectionTests.cpp) add_unittest(Units UnitsTests.cpp) diff --git a/Tests/UnitTests/Core/Definitions/CommonTests.cpp b/Tests/UnitTests/Core/Definitions/CommonTests.cpp deleted file mode 100644 index 7b2fffffe57..00000000000 --- a/Tests/UnitTests/Core/Definitions/CommonTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2017-2020 CERN for the benefit of the Acts project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include - -#include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" - -using namespace Acts; - -BOOST_AUTO_TEST_SUITE(CommonDefinitions) - -BOOST_AUTO_TEST_CASE(NavigationDirectionTests) { - BOOST_CHECK(indexFromDirection(NavigationDirection::Backward) == 0u); - BOOST_CHECK(indexFromDirection(NavigationDirection::Forward) == 1u); - - BOOST_CHECK(directionFromStepSize(-1.) == NavigationDirection::Backward); - BOOST_CHECK(directionFromStepSize(1.) == NavigationDirection::Forward); - - BOOST_CHECK(invertDirection(NavigationDirection::Backward) == - NavigationDirection::Forward); - BOOST_CHECK(invertDirection(NavigationDirection::Forward) == - NavigationDirection::Backward); - - NavigationDirection nFwd = NavigationDirection::Forward; - NavigationDirection nBwd = NavigationDirection::Backward; - - BOOST_CHECK(2. * nFwd == 2.); - BOOST_CHECK(7 * nFwd == 7); - BOOST_CHECK(Vector3(1., 1., 1.) * nFwd == Vector3(1., 1., 1.)); - - BOOST_CHECK(2. * nBwd == -2.); - BOOST_CHECK(7 * nBwd == -7); - BOOST_CHECK(Vector3(1., 1., 1.) * nBwd == Vector3(-1., -1., -1.)); - - double a = 7.; - a *= nFwd; - BOOST_CHECK(a == 7.); - a *= nBwd; - BOOST_CHECK(a == -7.); - - float b = 8.; - b *= nFwd; - BOOST_CHECK(b == 8.); - b *= nBwd; - BOOST_CHECK(b == -8.); - - Vector3 c(9., 9., 9.); - c *= nFwd; - BOOST_CHECK(c == Vector3(9., 9., 9.)); - c *= nBwd; - BOOST_CHECK(c == Vector3(-9., -9., -9.)); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Definitions/DirectionTests.cpp b/Tests/UnitTests/Core/Definitions/DirectionTests.cpp new file mode 100644 index 00000000000..4d89d5b0e4f --- /dev/null +++ b/Tests/UnitTests/Core/Definitions/DirectionTests.cpp @@ -0,0 +1,70 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2020 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Direction.hpp" + +using namespace Acts; + +BOOST_AUTO_TEST_SUITE(DefinitionsDirection) + +BOOST_AUTO_TEST_CASE(DirectionTests) { + constexpr Direction bwd = Direction::Backward; + constexpr Direction fwd = Direction::Forward; + + BOOST_CHECK(bwd == Direction::Negative); + BOOST_CHECK(fwd == Direction::Positive); + + BOOST_CHECK(Direction::fromScalar(-1.) == bwd); + BOOST_CHECK(Direction::fromScalar(1.) == fwd); + BOOST_CHECK(Direction::fromScalarZeroAsPositive(0) == fwd); + + BOOST_CHECK(Direction::fromIndex(0) == bwd); + BOOST_CHECK(Direction::fromIndex(1) == fwd); + + BOOST_CHECK(bwd.index() == 0u); + BOOST_CHECK(fwd.index() == 1u); + + BOOST_CHECK(bwd.sign() == -1); + BOOST_CHECK(fwd.sign() == +1); + + BOOST_CHECK(bwd.invert() == fwd); + BOOST_CHECK(fwd.invert() == bwd); + + BOOST_CHECK(bwd.toString() == "backward"); + BOOST_CHECK(fwd.toString() == "forward"); + + BOOST_CHECK(2. * fwd == 2.); + BOOST_CHECK(7 * fwd == 7); + BOOST_CHECK(Vector3(1., 1., 1.) * fwd == Vector3(1., 1., 1.)); + + BOOST_CHECK(2. * bwd == -2.); + BOOST_CHECK(7 * bwd == -7); + BOOST_CHECK(Vector3(1., 1., 1.) * bwd == Vector3(-1., -1., -1.)); + + double a = 7.; + a *= fwd; + BOOST_CHECK(a == 7.); + a *= bwd; + BOOST_CHECK(a == -7.); + + float b = 8.; + b *= fwd; + BOOST_CHECK(b == 8.); + b *= bwd; + BOOST_CHECK(b == -8.); + + Vector3 c(9., 9., 9.); + c *= fwd; + BOOST_CHECK(c == Vector3(9., 9., 9.)); + c *= bwd; + BOOST_CHECK(c == Vector3(-9., -9., -9.)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index be38f8489ee..324c1ea9f57 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -1,8 +1,15 @@ +add_unittest(CylindricalContainerBuilder CylindricalContainerBuilderTests.cpp) +add_unittest(CylindricalDetectorHelper CylindricalDetectorHelperTests.cpp) add_unittest(GridAxisGenerators GridAxisGeneratorsTests.cpp) add_unittest(Detector DetectorTests.cpp) add_unittest(DetectorVolume DetectorVolumeTests.cpp) +add_unittest(DetectorVolumeBuilder DetectorVolumeBuilderTests.cpp) add_unittest(IndexedSurfaceGridFiller IndexedSurfaceGridFillerTests.cpp) add_unittest(IndexedSurfacesGenerator IndexedSurfacesGeneratorTests.cpp) +add_unittest(KdtSurfacesProvider KdtSurfacesProviderTests.cpp) +add_unittest(LayerStructureBuilder LayerStructureBuilderTests.cpp) +add_unittest(ReferenceGenerators ReferenceGeneratorsTests.cpp) +add_unittest(SupportHelper SupportHelperTests.cpp) add_unittest(ProtoDetector ProtoDetectorTests.cpp) add_unittest(Portal PortalTests.cpp) add_unittest(PortalGenerators PortalGeneratorsTests.cpp) diff --git a/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp new file mode 100644 index 00000000000..c29cecfa59c --- /dev/null +++ b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp @@ -0,0 +1,286 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/CylindricalContainerBuilder.hpp" +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +using namespace Acts; +using namespace Acts::Experimental; + +GeometryContext tContext; + +/// @brief A mockup volume builder, it generates volumes with +/// a single surface filled in in order to use the CylindricalContainerBuilder +/// infrastructure. +template +class CylindricalVolumeBuilder : public IDetectorComponentBuilder { + public: + CylindricalVolumeBuilder(const Transform3& transform, + const CylinderVolumeBounds& vBounds, + const surface_bounds_type& sBounds, + const std::string& vName) + : IDetectorComponentBuilder(), + m_transform(transform), + m_volumeBounds(vBounds), + m_surfaceBounds(sBounds), + m_name(vName) {} + + DetectorComponent construct( + RootDetectorVolumes& roots, + [[maybe_unused]] const GeometryContext& gctx) const final { + // Ingredients + auto surface = Surface::makeShared( + (m_transform), std::make_shared(m_surfaceBounds)); + + auto bounds = std::make_unique(m_volumeBounds); + auto portalGenerator = defaultPortalGenerator(); + auto volume = DetectorVolumeFactory::construct( + portalGenerator, tContext, m_name, m_transform, std::move(bounds), + {surface}, {}, tryNoVolumes(), tryAllPortalsAndSurfaces()); + + // Add to the roots + roots.volumes.push_back(volume); + + DetectorComponent::PortalContainer dContainer; + for (auto [ip, p] : enumerate(volume->portalPtrs())) { + dContainer[ip] = p; + } + return DetectorComponent{{volume}, dContainer}; + } + + private: + Transform3 m_transform; + CylinderVolumeBounds m_volumeBounds; + surface_bounds_type m_surfaceBounds; + std::string m_name; +}; + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(CylindricaContainerBuilder_Misconfiguration) { + // misconfiruation: no builders + CylindricalContainerBuilder::Config misCfg; + BOOST_CHECK_THROW(auto a = CylindricalContainerBuilder(misCfg), + std::invalid_argument); + // misconfiguration - 1D binning not in z, r, phi + misCfg.builders = {nullptr}; + misCfg.binning = {Acts::binX}; + BOOST_CHECK_THROW(auto b = CylindricalContainerBuilder(misCfg), + std::invalid_argument); + + // misconfiguration - 2D binning not in z, r, + misCfg.builders = {nullptr, nullptr}; + misCfg.binning = {Acts::binZ, Acts::binPhi}; + BOOST_CHECK_THROW(auto c = CylindricalContainerBuilder(misCfg), + std::invalid_argument); + + // misconfiguration - 2D binning in z, r, but not exaclty 2 builders + misCfg.builders = {nullptr, nullptr, nullptr}; + misCfg.binning = {Acts::binZ, Acts::binR}; + BOOST_CHECK_THROW(auto d = CylindricalContainerBuilder(misCfg), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingZ) { + // Declare a negative disc builder + Transform3 negZ = Transform3::Identity(); + negZ.pretranslate(Vector3(0., 0., -300.)); + auto negDisc = + std::make_shared>( + negZ, CylinderVolumeBounds(50., 200., 100.), RadialBounds(60., 190.), + "NegativeDisc"); + + // Declare a barrel builder + auto barrel = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(50., 200., 200.), + CylinderBounds(80., 190.), "Barrel"); + // Declare a positive disc builder + Transform3 posZ = Transform3::Identity(); + posZ.pretranslate(Vector3(0., 0., 300.)); + auto posDisc = + std::make_shared>( + posZ, CylinderVolumeBounds(50., 200., 100.), RadialBounds(60., 190.), + "PositiveDisc"); + + // Create the container builder + CylindricalContainerBuilder::Config tripleZCfg; + tripleZCfg.auxilliary = "*** Test 0 - Build triple in Z ***"; + tripleZCfg.builders = {negDisc, barrel, posDisc}; + tripleZCfg.binning = {binZ}; + + auto tripleZ = std::make_shared( + tripleZCfg, getDefaultLogger("TripleBuilderZ", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto tz = tripleZ->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 3u); + BOOST_CHECK(tz.portals.size() == 4u); +} + +BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingR) { + // Declare a barrel builder + auto barrel0 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(50., 80., 200.), + CylinderBounds(65., 190.), "Barrel0"); + + // Declare a barrel builder + auto barrel1 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(80., 110., 200.), + CylinderBounds(95., 190.), "Barrel1"); + + // Declare a barrel builder + auto barrel2 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(110., 140., 200.), + CylinderBounds(125., 190.), "Barrel2"); + + // Create the container builder + CylindricalContainerBuilder::Config barrelRCfg; + barrelRCfg.auxilliary = "*** Test 1 - Build multilayer barrel ***"; + barrelRCfg.builders = {barrel0, barrel1, barrel2}; + barrelRCfg.binning = {binR}; + + auto barrelR = std::make_shared( + barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto br = barrelR->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 3u); + BOOST_CHECK(br.portals.size() == 4u); +} + +BOOST_AUTO_TEST_CASE(CylindricaContainerBuildingPhi) { + // Create the container builder + CylindricalContainerBuilder::Config barrelPhiCfg; + barrelPhiCfg.auxilliary = "*** Test 2 - Build segmented phi barrel ***"; + barrelPhiCfg.binning = {binPhi}; + + unsigned int phiSectors = 5; + Acts::ActsScalar phiHalfSector = M_PI / phiSectors; + + std::vector> phiVolumes = {}; + for (unsigned int i = 0; i < phiSectors; ++i) { + // The volume bounds + Acts::CylinderVolumeBounds volumeBounds( + 10., 100., 100., phiHalfSector, -M_PI + (2u * i + 1u) * phiHalfSector); + // The surface boudns + Acts::CylinderBounds surfaceBounds(50., 90., 0.99 * phiHalfSector, + -M_PI + (2u * i + 1u) * phiHalfSector); + + auto builder = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), volumeBounds, surfaceBounds, + std::string("Sector_") + std::to_string(i)); + barrelPhiCfg.builders.push_back(builder); + } + + auto barrelPhi = std::make_shared( + barrelPhiCfg, getDefaultLogger("BarrelBuilderPhi", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto bphi = barrelPhi->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 5u); + BOOST_CHECK(bphi.portals.size() == 4u); +} + +BOOST_AUTO_TEST_CASE(CylindricalContainerBuilderDetector) { + // Declare a barrel sub builder + auto beampipe = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(0., 50., 600.), + CylinderBounds(25., 590.), "BeamPipe"); + + // Declare a negative disc builder + Transform3 negZ = Transform3::Identity(); + negZ.pretranslate(Vector3(0., 0., -300.)); + auto endcapN = + std::make_shared>( + negZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 130.), + "NegativeEndcap"); + + // Declare a barrel sub builder + auto barrel0 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(50., 80., 200.), + CylinderBounds(65., 190.), "Barrel0"); + + // Declare a barrel sub builder + auto barrel1 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(80., 110., 200.), + CylinderBounds(95., 190.), "Barrel1"); + + // Declare a barrel sub builder + auto barrel2 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(110., 140., 200.), + CylinderBounds(125., 190.), "Barrel2"); + + // Create the barrel container builder + CylindricalContainerBuilder::Config barrelRCfg; + barrelRCfg.builders = {barrel0, barrel1, barrel2}; + barrelRCfg.binning = {binR}; + + auto barrel = std::make_shared( + barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::VERBOSE)); + + Transform3 posZ = Transform3::Identity(); + posZ.pretranslate(Vector3(0., 0., 300.)); + auto endcapP = + std::make_shared>( + posZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 130.), + "PositiveEndcap"); + + // Create the barrel container builder + CylindricalContainerBuilder::Config barrelEndcapCfg; + barrelEndcapCfg.builders = {endcapN, barrel, endcapP}; + barrelEndcapCfg.binning = {binZ}; + + auto barrelEndcap = std::make_shared( + barrelEndcapCfg, + getDefaultLogger("BarrelEndcapBuilder", Logging::VERBOSE)); + + // Create the barrel container builder + CylindricalContainerBuilder::Config detectorCfg; + detectorCfg.builders = {beampipe, barrelEndcap}; + detectorCfg.binning = {binR}; + + auto detector = std::make_shared( + detectorCfg, getDefaultLogger("DetectorBuilder", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto d = detector->construct(roots, tContext); + BOOST_CHECK(d.portals.size() == 3u); + BOOST_CHECK(roots.volumes.size() == 6u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/CylindricalDetectorHelperTests.cpp b/Tests/UnitTests/Core/Detector/CylindricalDetectorHelperTests.cpp new file mode 100644 index 00000000000..24a733dd6bb --- /dev/null +++ b/Tests/UnitTests/Core/Detector/CylindricalDetectorHelperTests.cpp @@ -0,0 +1,603 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/Detector.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/detail/CylindricalDetectorHelper.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Navigation/NavigationStateUpdators.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +using namespace Acts::Experimental; +using namespace Acts::Experimental::detail; +using namespace Acts::Experimental::detail::CylindricalDetectorHelper; + +Acts::Logging::Level logLevel = Acts::Logging::VERBOSE; + +Acts::GeometryContext tContext; +std::vector> eVolumes = {}; + +auto portalGenerator = defaultPortalGenerator(); + +BOOST_AUTO_TEST_SUITE(Experimental) + +BOOST_AUTO_TEST_CASE(ConnectVolumeExceptions) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Faulty setups", logLevel)); + + auto cBounds0 = std::make_unique(0., 100., 100); + auto volume0 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume0", Acts::Transform3::Identity(), + std::move(cBounds0), tryAllPortals()); + + auto cBounds1 = + std::make_unique(0., 100., 100, 0.2, 1.); + auto volume1 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume0", Acts::Transform3::Identity(), + std::move(cBounds1), tryAllPortals()); + + ACTS_INFO("*** Test: nullptr in the list of volumes"); + + // Invalid arguments: nullptr + std::vector> volumesWithNullptr = { + volume0, nullptr, volume1}; + BOOST_CHECK_THROW(connectInR(tContext, volumesWithNullptr, {}, logLevel), + std::invalid_argument); + + ACTS_INFO("*** Test: non-cylinder in the list of volumes"); + + auto cubeBounds = std::make_unique(100., 100., 100); + auto cube = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Cube", Acts::Transform3::Identity(), + std::move(cubeBounds), tryAllPortals()); + + // Invalid arguments: cube + std::vector> volumesWithCube = { + volume0, volume1, cube}; + BOOST_CHECK_THROW(connectInR(tContext, volumesWithCube, {}, logLevel), + std::invalid_argument); + + ACTS_INFO("*** Test: non-aligned volume in the list of volumes"); + Acts::Transform3 rotated = Acts::Transform3::Identity(); + Acts::AngleAxis3 rotX(0.1234, Acts::Vector3::UnitX()); + rotated *= rotX; + + auto cBounds2 = std::make_unique(0., 100., 100); + auto volume2 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume2", rotated, std::move(cBounds2), + tryAllPortals()); + + // Invalid arguments: non-aligned + std::vector> volumesWithNonaligned = { + volume0, volume1, volume2}; + BOOST_CHECK_THROW(connectInR(tContext, volumesWithNonaligned, {}, logLevel), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(ConnectInR) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Connect: R", logLevel)); + ACTS_INFO("*** Test: connect DetectorVolumes in R, create proto container"); + // Test with different opening angles + std::vector testOpenings = {M_PI, 0.5 * M_PI}; + + std::vector radii = {0., 10., 100., 200.}; + Acts::ActsScalar halfZ = 100.; + + // This should work for full cylinder and sector openings + for (auto [io, opening] : Acts::enumerate(testOpenings)) { + ACTS_INFO(" -> test with phi openeing: " << opening); + std::string opStr = "opening_" + std::to_string(io); + std::vector> rVolumes = {}; + // Create the voluems + for (auto [i, r] : Acts::enumerate(radii)) { + if (i > 0) { + auto cBounds = std::make_unique( + radii[i - 1u], r, halfZ, opening, 0.); + rVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "Cylinder_r" + std::to_string(i), + Acts::Transform3::Identity(), std::move(cBounds), tryAllPortals())); + } + } + + auto protoContainer = connectInR(tContext, rVolumes, {}, logLevel); + // Check the portal setup + BOOST_CHECK(rVolumes[0u]->portalPtrs()[2u] == + rVolumes[1u]->portalPtrs()[3u]); + BOOST_CHECK(rVolumes[1u]->portalPtrs()[2u] == + rVolumes[2u]->portalPtrs()[3u]); + BOOST_CHECK(rVolumes[0u]->portalPtrs()[0u] == + rVolumes[1u]->portalPtrs()[0u]); + BOOST_CHECK(rVolumes[1u]->portalPtrs()[0u] == + rVolumes[2u]->portalPtrs()[0u]); + BOOST_CHECK(rVolumes[0u]->portalPtrs()[1u] == + rVolumes[1u]->portalPtrs()[1u]); + BOOST_CHECK(rVolumes[1u]->portalPtrs()[1u] == + rVolumes[2u]->portalPtrs()[1u]); + BOOST_CHECK(rVolumes[0u]->portalPtrs()[0u] == protoContainer[0u]); + BOOST_CHECK(rVolumes[0u]->portalPtrs()[1u] == protoContainer[1u]); + + // A detector construction that should work + auto detector = + Detector::makeShared("DetectorInR", rVolumes, tryRootVolumes()); + + // Make a rzphi grid + const auto& volumes = detector->volumes(); + auto boundaries = rzphiBoundaries(tContext, volumes); + const auto& rBoundaries = boundaries[0u]; + const auto& zBoundaries = boundaries[1u]; + + // Check the radii + std::vector zvalues = {-halfZ, halfZ}; + BOOST_CHECK(radii == rBoundaries); + BOOST_CHECK(zvalues == zBoundaries); + } + + // Invalid arguments + ACTS_INFO("*** Test: faulty empty vector"); + BOOST_CHECK_THROW(connectInR(tContext, eVolumes, {}, logLevel), + std::invalid_argument); + + // Faulty setups, not matchint in R + ACTS_INFO("*** Test: volumes are not matching in R"); + + auto cBounds00 = std::make_unique(0., 100., 100); + auto volume00 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume00", Acts::Transform3::Identity(), + std::move(cBounds00), tryAllPortals()); + + auto cBounds01 = + std::make_unique(101., 200., 100); + auto volume01 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume01", Acts::Transform3::Identity(), + std::move(cBounds01), tryAllPortals()); + + std::vector> volumesNotMatching = {volume00, + volume01}; + BOOST_CHECK_THROW(connectInR(tContext, volumesNotMatching, {}, logLevel), + std::runtime_error); + + ACTS_INFO("*** Test: volume bounds are not aligned"); + Acts::Transform3 shifted = Acts::Transform3::Identity(); + shifted.pretranslate(Acts::Vector3(0., 0., 10.)); + + auto cBounds10 = std::make_unique(0., 100., 100); + auto volume10 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume10", shifted, std::move(cBounds10), + tryAllPortals()); + + auto cBounds11 = std::make_unique(100., 200., 90); + auto volume11 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume11", shifted, std::move(cBounds11), + tryAllPortals()); + + std::vector> volumesNotAligned = {volume10, + volume11}; + BOOST_CHECK_THROW(connectInR(tContext, volumesNotAligned, {}, logLevel), + std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(ConnectInZ) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Connect: Z", logLevel)); + ACTS_INFO("*** Test: connect DetectorVolumes in Z, create proto container"); + + // @TODO: test with different transforms, this should work in, not used yet + std::vector transforms = {Acts::Transform3::Identity()}; + std::vector> radii = {{0., 100.}, + {20., 120.}}; + std::vector zValues = {-100., -20, 10., 100., 200.}; + + for (auto [it, t] : Acts::enumerate(transforms)) { + ACTS_INFO(" -> test series with transfrom id " << it); + + std::string trfStr = "_transform_" + std::to_string(it); + for (auto [ir, r] : Acts::enumerate(radii)) { + ACTS_INFO(" -> test series with radii setup " + << radii[ir][0u] << ", " << radii[ir][1u]); + + std::string radStr = "_radii_" + std::to_string(ir); + std::vector> zVolumes = {}; + for (auto [i, z] : Acts::enumerate(zValues)) { + if (i > 0) { + auto cBounds = std::make_unique( + r[0], r[1], 0.5 * (z - zValues[i - 1u])); + // z center + Acts::ActsScalar zCenter = 0.5 * (z + zValues[i - 1u]); + Acts::Transform3 ti = Acts::Transform3::Identity(); + ti.pretranslate(t.translation() + + zCenter * t.rotation().matrix().col(2)); + ti.prerotate(t.rotation()); + // create the volume + zVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, + "Cylinder_z" + std::to_string(i) + trfStr + radStr, ti, + std::move(cBounds), tryAllPortals())); + } + } + // Now call the connector + auto protoContainer = connectInZ(tContext, zVolumes, {}, logLevel); + + // Check the portal setup. + // Glued, remainders are outside skin + BOOST_CHECK(zVolumes[0u]->portalPtrs()[1u] == + zVolumes[1u]->portalPtrs()[0u]); + BOOST_CHECK(zVolumes[1u]->portalPtrs()[1u] == + zVolumes[2u]->portalPtrs()[0u]); + BOOST_CHECK(zVolumes[2u]->portalPtrs()[1u] == + zVolumes[3u]->portalPtrs()[0u]); + BOOST_CHECK(protoContainer[0u] == zVolumes[0u]->portalPtrs()[0u]); + BOOST_CHECK(protoContainer[1u] == zVolumes[3u]->portalPtrs()[1u]); + + // Covered with the same surface, shich is the outside skin + std::vector checkShared = {2u}; + if (radii[ir][0u] > 0.) { + checkShared.push_back(3u); + } + + for (const auto& ip : checkShared) { + BOOST_CHECK(zVolumes[0u]->portalPtrs()[ip] == + zVolumes[1u]->portalPtrs()[ip]); + BOOST_CHECK(zVolumes[1u]->portalPtrs()[ip] == + zVolumes[2u]->portalPtrs()[ip]); + BOOST_CHECK(zVolumes[2u]->portalPtrs()[ip] == + zVolumes[3u]->portalPtrs()[ip]); + BOOST_CHECK(protoContainer[ip] == zVolumes[0u]->portalPtrs()[ip]); + } + + auto detector = + Detector::makeShared("DetectorInZ", zVolumes, tryRootVolumes()); + } + } + + // Invalid arguments + BOOST_CHECK_THROW(connectInZ(tContext, eVolumes, {}, logLevel), + std::invalid_argument); + + // Volumes have different radii - other bounds will be the same + auto cBounds00 = std::make_unique(0., 100., 100); + auto volume00 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume00", + Acts::Transform3::Identity() * Acts::Translation3(0., 0., -100.), + std::move(cBounds00), tryAllPortals()); + + auto cBounds01 = std::make_unique(0., 105., 100); + auto volume01 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume01", + Acts::Transform3::Identity() * Acts::Translation3(0., 0., 100.), + std::move(cBounds01), tryAllPortals()); + + std::vector> volumesNonalingedBounds = { + volume00, volume01}; + BOOST_CHECK_THROW(connectInZ(tContext, volumesNonalingedBounds, {}, logLevel), + std::runtime_error); + + // Volumes are not attached + auto cBounds10 = std::make_unique(0., 100., 100); + auto volume10 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume00", + Acts::Transform3::Identity() * Acts::Translation3(0., 0., -105.), + std::move(cBounds10), tryAllPortals()); + + auto cBounds11 = std::make_unique(0., 100., 100); + auto volume11 = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Volume01", + Acts::Transform3::Identity() * Acts::Translation3(0., 0., 100.), + std::move(cBounds11), tryAllPortals()); + + std::vector> volumesNotAttached = {volume10, + volume11}; + BOOST_CHECK_THROW(connectInZ(tContext, volumesNotAttached, {}, logLevel), + std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(ConnectInPhi) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Connect: Phi", logLevel)); + ACTS_INFO("*** Test: connect DetectorVolumes in Phi, create proto container"); + + std::vector transforms = {Acts::Transform3::Identity()}; + unsigned int phiSectors = 5; + Acts::ActsScalar phiHalfSector = M_PI / phiSectors; + + for (auto [it, t] : Acts::enumerate(transforms)) { + ACTS_INFO(" -> test series with transfrom id " << it); + + std::vector> phiVolumes = {}; + for (unsigned int i = 0; i < phiSectors; ++i) { + auto cBounds = std::make_unique( + 10., 100., 100., phiHalfSector, + -M_PI + (2u * i + 1u) * phiHalfSector); + + // create the volume + phiVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "Cylinder_phi" + std::to_string(i), t, + std::move(cBounds), tryAllPortals())); + } + + auto protoContainer = connectInPhi(tContext, phiVolumes, {}, logLevel); + + // All phiVolumes share : inner tube, outer cover, negative & positive disc + std::vector checkShared = {0u, 1u, 2u, 3u}; + for (auto [iv, v] : Acts::enumerate(phiVolumes)) { + if (iv > 0u) { + auto current = v; + auto last = phiVolumes[iv - 1u]; + for (const auto& ch : checkShared) { + BOOST_CHECK(current->portalPtrs()[ch] == last->portalPtrs()[ch]); + } + } + } + + auto detector = + Detector::makeShared("DetectorInPhi", phiVolumes, tryRootVolumes()); + } + + // Invalid arguments + BOOST_CHECK_THROW(connectInPhi(tContext, eVolumes, {}, logLevel), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(WrapVolumeinRZ) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Wrap: Z-R", logLevel)); + ACTS_INFO( + "*** Test: wrap volume in Z-R with CutoutCylinderVolume, create proto " + "container"); + + // @TODO: test with different transforms, this should work in, not used yet + std::vector transforms = {Acts::Transform3::Identity()}; + + // Test with different inner radii + std::vector> radii = {{0., 100., 500.}, + {20., 120., 500.}}; + + Acts::ActsScalar innerHalfZ = 150.; + Acts::ActsScalar outerHalfZ = 175.; + + // Set up all the different tests + for (auto [it, tf] : Acts::enumerate(transforms)) { + ACTS_INFO(" Test series with transfrom id " << it); + + std::string trfStr = "_transform_" + std::to_string(it); + for (auto [ir, r] : Acts::enumerate(radii)) { + ACTS_INFO(" -> test series with radii setup " << radii[ir][0u] << ", " + << radii[ir][1u]); + + std::vector> volumes = {}; + + std::string radStr = "_radii_" + std::to_string(ir); + // Create the inner bounds + auto iBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], innerHalfZ); + volumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "InnerCylinder" + radStr + trfStr, tf, + std::move(iBounds), tryAllPortals())); + + // Create the wrapping bounds + auto wBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], radii[ir][2u], outerHalfZ, innerHalfZ); + + volumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "WrappingCylinder" + radStr + trfStr, tf, + std::move(wBounds), tryAllPortals())); + + wrapInZR(tContext, volumes, logLevel); + } + } + + // Invalid arguments + BOOST_CHECK_THROW(wrapInZR(tContext, eVolumes, logLevel), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(ProtoContainerZR) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Container: Z-R", logLevel)); + ACTS_INFO("*** Test: create a container in Z-R."); + + auto transform = Acts::Transform3::Identity(); + + std::vector innerMostRadii = {0., 2.}; + + for (auto [ir, imr] : Acts::enumerate(innerMostRadii)) { + ACTS_INFO(" -> test series innermost radius setup " + << innerMostRadii[ir]); + + // A container in R + std::vector radii = {25., 100., 200.}; + Acts::ActsScalar halfZ = 200; + + // An innermost Pipe + auto bBounds = + std::make_unique(imr, radii[0u], halfZ); + + auto innerPipe = DetectorVolumeFactory::construct( + portalGenerator, tContext, "InnerPipe", transform, std::move(bBounds), + tryAllPortals()); + + // Make a container representation out of it + std::map> ipContainer; + for (auto [ip, p] : Acts::enumerate(innerPipe->portalPtrs())) { + ipContainer[ip] = p; + } + + // Create the r - sorted volumes + std::vector> rVolumes = {}; + // Create the voluems + for (auto [i, r] : Acts::enumerate(radii)) { + if (i > 0) { + auto cBounds = std::make_unique( + radii[i - 1u], r, halfZ); + rVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "Cylinder_r" + std::to_string(i), + transform, std::move(cBounds), tryAllPortals())); + } + } + + auto protoContainerInR = connectInR(tContext, rVolumes, {}, logLevel); + + std::vector zValues = {-200., -120, 10., 100., 200.}; + std::vector> zVolumes = {}; + for (auto [i, z] : Acts::enumerate(zValues)) { + if (i > 0) { + auto cBounds = std::make_unique( + 200., 300., 0.5 * (z - zValues[i - 1u])); + // z center + Acts::ActsScalar zCenter = 0.5 * (z + zValues[i - 1u]); + Acts::Transform3 ti = transform; + ti.pretranslate(transform.translation() + + zCenter * transform.rotation().matrix().col(2)); + + // create the volume + zVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "Cylinder_z" + std::to_string(i), ti, + std::move(cBounds), tryAllPortals())); + } + } + // Now call the connector + auto protoContainerInZ = connectInZ(tContext, zVolumes, {}, logLevel); + auto centralContainer = connectInR( + tContext, {ipContainer, protoContainerInR, protoContainerInZ}, {}, + logLevel); + + // Let's make two endcaps + // Nec + auto necBounds = + std::make_unique(imr, 300., 50.); + + auto necTransform = Acts::Transform3::Identity(); + necTransform.pretranslate(Acts::Vector3(0., 0., -250)); + auto necVolume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "Nec", necTransform, std::move(necBounds), + tryAllPortals()); + + std::map> necContainer; + for (auto [ip, p] : Acts::enumerate(necVolume->portalPtrs())) { + necContainer[ip] = p; + } + + // Pec container + auto pecInnerBounds = + std::make_unique(imr, 175., 100.); + + auto pecOuterBounds = + std::make_unique(175., 300., 100.); + + auto pecTransform = Acts::Transform3::Identity(); + pecTransform.pretranslate(Acts::Vector3(0., 0., 300)); + auto pecInner = DetectorVolumeFactory::construct( + portalGenerator, tContext, "PecInner", pecTransform, + std::move(pecInnerBounds), tryAllPortals()); + auto pecOuter = DetectorVolumeFactory::construct( + portalGenerator, tContext, "PecOuter", pecTransform, + std::move(pecOuterBounds), tryAllPortals()); + + std::vector> pecVolumes = {pecInner, + pecOuter}; + auto pecContainer = connectInR(tContext, pecVolumes, {}, logLevel); + + auto overallContainer = connectInZ( + tContext, {necContainer, centralContainer, pecContainer}, {}, logLevel); + + // Add them togeter + std::vector> dVolumes; + dVolumes.push_back(innerPipe); + dVolumes.push_back(necVolume); + dVolumes.insert(dVolumes.end(), rVolumes.begin(), rVolumes.end()); + dVolumes.insert(dVolumes.end(), zVolumes.begin(), zVolumes.end()); + dVolumes.push_back(pecInner); + dVolumes.push_back(pecOuter); + + auto detector = Detector::makeShared("DetectorFromProtoContainer", dVolumes, + tryRootVolumes()); + } // test with different innermost radii +} + +BOOST_AUTO_TEST_CASE(WrapContainernRZ) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Container: Wrap", logLevel)); + ACTS_INFO("*** Test: create a container in Z-R by wrapping."); + + // Test with different inner radii + std::vector> radii = {{0., 100., 500.}, + {20., 120., 500.}}; + + Acts::ActsScalar innerHalfZ = 150.; + Acts::ActsScalar innerBarrelHalfZ = 75.; + Acts::ActsScalar innerEndcapHalfZ = 0.5 * (innerHalfZ - innerBarrelHalfZ); + Acts::ActsScalar outerHalfZ = 175.; + + Acts::Transform3 tf = Acts::Transform3::Identity(); + + // Set up all the different tests + for (auto [ir, r] : Acts::enumerate(radii)) { + std::string radStr = "_radii_" + std::to_string(ir); + ACTS_INFO(" -> test series innermost radius setup " << radii[ir][0u]); + + // Let's create the inner container first + std::vector> iVolumes = {}; + + auto iNecBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], innerEndcapHalfZ); + Acts::Transform3 ntf = tf; + ntf.pretranslate( + Acts::Vector3(0., 0., -innerBarrelHalfZ - innerEndcapHalfZ)); + iVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "InnerNec" + radStr, ntf, + std::move(iNecBounds), tryAllPortals())); + + auto iBarrelBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], innerBarrelHalfZ); + iVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "InnerBarrel" + radStr, tf, + std::move(iBarrelBounds), tryAllPortals())); + + auto iPecBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], innerEndcapHalfZ); + Acts::Transform3 ptf = tf; + ptf.pretranslate( + Acts::Vector3(0., 0., innerBarrelHalfZ + innerEndcapHalfZ)); + iVolumes.push_back(DetectorVolumeFactory::construct( + portalGenerator, tContext, "InnerPec" + radStr, ptf, + std::move(iPecBounds), tryAllPortals())); + + auto innerContainer = connectInZ(tContext, iVolumes, {}, logLevel); + + // Create the wrapping volume + auto wBounds = std::make_unique( + radii[ir][0u], radii[ir][1u], radii[ir][2u], outerHalfZ, innerHalfZ); + auto wVolume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "WrappingVolume" + radStr, tf, + std::move(wBounds), tryAllPortals()); + + std::vector containers; + containers.push_back(innerContainer); + + DetectorComponent::PortalContainer outerContainer; + for (auto [ip, p] : Acts::enumerate(wVolume->portalPtrs())) { + outerContainer[ip] = p; + } + containers.push_back(outerContainer); + + auto detector = wrapInZR(tContext, containers, logLevel); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp b/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp new file mode 100644 index 00000000000..d93ffdfe076 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/DetectorVolumeBuilderTests.cpp @@ -0,0 +1,231 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/DetectorVolumeBuilder.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IInternalStructureBuilder.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Navigation/DetectorVolumeUpdators.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include + +using namespace Acts; +using namespace Acts::Experimental; + +GeometryContext tContext; + +/// @brief Mockup external structure builder +/// @tparam bounds_type the volume bounds type that is constructed +template +class ExternalsBuilder : public IExternalStructureBuilder { + public: + ExternalsBuilder(const Transform3& transform, const bounds_type& bounds) + : IExternalStructureBuilder(), + m_transform(transform), + m_bounds(std::move(bounds)) {} + + ExternalStructure construct( + [[maybe_unused]] const GeometryContext& gctx) const final { + return {m_transform, std::make_unique(m_bounds), + defaultPortalGenerator()}; + } + + private: + Transform3 m_transform = Transform3::Identity(); + bounds_type m_bounds; +}; + +/// @brief Mockup internal surface builder +/// @tparam surface_type the surface type to be constructed +/// @tparam bounds_type the bounds type that is contructed +template +class InternalSurfaceBuilder : public IInternalStructureBuilder { + public: + InternalSurfaceBuilder(const Transform3& transform, const bounds_type& bounds) + : IInternalStructureBuilder(), + m_transform(transform), + m_bounds(std::move(bounds)) {} + + InternalStructure construct( + [[maybe_unused]] const GeometryContext& gctx) const final { + auto surface = Surface::makeShared( + m_transform, std::make_shared(m_bounds)); + return {{surface}, {}, tryAllPortalsAndSurfaces(), tryNoVolumes()}; + } + + private: + Transform3 m_transform = Transform3::Identity(); + bounds_type m_bounds; +}; + +/// @brief Mockup internal surface builder +/// @tparam surface_type the surface type to be constructed +/// @tparam bounds_type the bounds type that is contructed +template +class InternalVolumeBuilder : public IInternalStructureBuilder { + public: + InternalVolumeBuilder(const Transform3& transform, const bounds_type& bounds) + : IInternalStructureBuilder(), + m_transform(transform), + m_bounds(std::move(bounds)) {} + + InternalStructure construct( + [[maybe_unused]] const GeometryContext& gctx) const final { + auto bounds = std::make_unique(m_bounds); + auto portalGenerator = defaultPortalGenerator(); + auto volume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "InternalVolume", m_transform, + std::move(bounds), tryAllPortals()); + return {{}, {volume}, tryAllPortals(), tryRootVolumes()}; + } + + private: + Transform3 m_transform = Transform3::Identity(); + bounds_type m_bounds; +}; + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_Misconfigured) { + // Internal and external structure builder is empty + DetectorVolumeBuilder::Config dvCfg; + dvCfg.auxilliary = "*** Test X * Misconfigued ***"; + dvCfg.name = "EmptyCylinder"; + dvCfg.externalsBuilder = nullptr; + dvCfg.internalsBuilder = nullptr; + + BOOST_CHECK_THROW(auto a = DetectorVolumeBuilder(dvCfg), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_EmptyVolume) { + CylinderVolumeBounds cBounds(10, 100, 200); + auto cBuilder = std::make_shared>( + Transform3::Identity(), cBounds); + + DetectorVolumeBuilder::Config dvCfg; + dvCfg.auxilliary = "*** Test 0 - Empty Cylinder ***"; + dvCfg.name = "EmptyCylinder"; + dvCfg.externalsBuilder = cBuilder; + dvCfg.internalsBuilder = nullptr; + + auto dvBuilder = std::make_shared( + dvCfg, getDefaultLogger("DetectorVolumeBuilder", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto dvComponents = dvBuilder->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 1u); + BOOST_CHECK(dvComponents.portals.size() == 4u); + + // Get the volume and check it is empty + auto volume = roots.volumes.front(); + BOOST_CHECK(volume->surfaces().empty()); + BOOST_CHECK(volume->volumes().empty()); +} + +BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_VolumeWithSurface) { + CylinderVolumeBounds cvBounds(10, 100, 200); + auto cBuilder = std::make_shared>( + Transform3::Identity(), cvBounds); + + CylinderBounds csBounds(55., 195.); + auto sBuilder = + std::make_shared>( + Transform3::Identity(), csBounds); + + DetectorVolumeBuilder::Config dvCfg; + dvCfg.auxilliary = "*** Test 1 - Cylinder with internal Surface ***"; + dvCfg.name = "CylinderWithSurface"; + dvCfg.externalsBuilder = cBuilder; + dvCfg.internalsBuilder = sBuilder; + + auto dvBuilder = std::make_shared( + dvCfg, getDefaultLogger("DetectorVolumeBuilder", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto dvComponents = dvBuilder->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 1u); + BOOST_CHECK(dvComponents.portals.size() == 4u); + + // Get the volume and check it is empty + auto volume = roots.volumes.front(); + BOOST_CHECK(volume->surfaces().size() == 1u); + BOOST_CHECK(volume->volumes().empty()); +} + +BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_VolumeWithVolume) { + CylinderVolumeBounds cvBounds(10, 100, 200); + auto cBuilder = std::make_shared>( + Transform3::Identity(), cvBounds); + + CylinderVolumeBounds ciBounds(15., 95., 195.); + auto iBuilder = std::make_shared>( + Transform3::Identity(), ciBounds); + + DetectorVolumeBuilder::Config dvCfg; + dvCfg.auxilliary = "*** Test 2 - Cylinder with internal Volume ***"; + dvCfg.name = "CylinderWithVolume"; + dvCfg.externalsBuilder = cBuilder; + dvCfg.internalsBuilder = iBuilder; + dvCfg.addToRoot = false; + dvCfg.addInternalsToRoot = false; + + auto dvBuilder = std::make_shared( + dvCfg, getDefaultLogger("DetectorVolumeBuilder", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto dvComponents = dvBuilder->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.empty()); + BOOST_CHECK(dvComponents.portals.size() == 4u); +} + +BOOST_AUTO_TEST_CASE(DetectorVolumeBuilder_VolumeWithVolumeToRoot) { + CylinderVolumeBounds cvBounds(10, 100, 200); + auto cBuilder = std::make_shared>( + Transform3::Identity(), cvBounds); + + CylinderVolumeBounds ciBounds(15., 95., 195.); + auto iBuilder = std::make_shared>( + Transform3::Identity(), ciBounds); + + DetectorVolumeBuilder::Config dvCfg; + dvCfg.auxilliary = + "*** Test 3 - Cylinder with internal Volume, adding to root ***"; + dvCfg.name = "CylinderWithVolume"; + dvCfg.externalsBuilder = cBuilder; + dvCfg.internalsBuilder = iBuilder; + dvCfg.addToRoot = true; + dvCfg.addInternalsToRoot = true; + + auto dvBuilder = std::make_shared( + dvCfg, getDefaultLogger("DetectorVolumeBuilder", Logging::VERBOSE)); + + RootDetectorVolumes roots; + auto dvComponents = dvBuilder->construct(roots, tContext); + + BOOST_CHECK(roots.volumes.size() == 2u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/DetectorVolumeTests.cpp b/Tests/UnitTests/Core/Detector/DetectorVolumeTests.cpp index 2a5f004f1d2..5f39e8b5bcf 100644 --- a/Tests/UnitTests/Core/Detector/DetectorVolumeTests.cpp +++ b/Tests/UnitTests/Core/Detector/DetectorVolumeTests.cpp @@ -10,7 +10,7 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/PortalGenerators.hpp" -#include "Acts/Detector/PortalHelper.hpp" +#include "Acts/Detector/detail/PortalHelper.hpp" #include "Acts/Geometry/CuboidVolumeBounds.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -178,4 +178,51 @@ BOOST_AUTO_TEST_CASE(CuboidWithCuboid) { BOOST_CHECK(nState.surfaceCandidates.size() == 12u); } +BOOST_AUTO_TEST_CASE(CylinderWithSurfacesTestExtractors) { + auto portalGenerator = defaultPortalGenerator(); + + std::vector radii = {100, 102, 104, 106, 108, 110}; + auto cylinderVoumeBounds = + std::make_unique(80, 130, 200); + std::vector> surfaces = {}; + for (const auto& r : radii) { + surfaces.push_back(Acts::Surface::makeShared( + Acts::Transform3::Identity(), + std::make_shared(r, 190))); + } + + // A full cylinder + auto cylinderVolume = DetectorVolumeFactory::construct( + portalGenerator, tContext, "CylinderVolume", Acts::Transform3::Identity(), + std::move(cylinderVoumeBounds), surfaces, {}, tryNoVolumes(), + tryAllPortalsAndSurfaces()); + + // The navigation state + NavigationState nState; + AllPortalsExtractor allPortals; + AllSurfacesExtractor allSurfaces; + IndexedSurfacesExtractor indexedSurfaces; + + // First check exception behaviour + BOOST_CHECK_THROW(allPortals.extract(tContext, nState), std::runtime_error); + BOOST_CHECK_THROW(allSurfaces.extract(tContext, nState), std::runtime_error); + BOOST_CHECK_THROW(indexedSurfaces.extract(tContext, nState, {0u, 1u}), + std::runtime_error); + + // A volume needs to be set + nState.currentVolume = cylinderVolume.get(); + + // This extracts all portals as candidates + auto eportals = allPortals.extract(tContext, nState); + BOOST_CHECK(eportals.size() == 4u); + + auto esurfaces = allSurfaces.extract(tContext, nState); + BOOST_CHECK(esurfaces.size() == 6u); + + esurfaces = indexedSurfaces.extract(tContext, nState, {2u, 4u}); + BOOST_CHECK(esurfaces.size() == 2u); + BOOST_CHECK(esurfaces[0u] == surfaces[2u].get()); + BOOST_CHECK(esurfaces[1u] == surfaces[4u].get()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/GridAxisGeneratorsTests.cpp b/Tests/UnitTests/Core/Detector/GridAxisGeneratorsTests.cpp index f2dbdb6b105..b74548a7ef5 100644 --- a/Tests/UnitTests/Core/Detector/GridAxisGeneratorsTests.cpp +++ b/Tests/UnitTests/Core/Detector/GridAxisGeneratorsTests.cpp @@ -10,7 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" -#include "Acts/Detector/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" #include "Acts/Utilities/detail/Axis.hpp" #include "Acts/Utilities/detail/Grid.hpp" @@ -19,7 +19,7 @@ using namespace Acts; using namespace Acts::detail; using namespace Acts::Experimental; -using namespace Acts::Experimental::GridAxisGenerators; +using namespace Acts::Experimental::detail::GridAxisGenerators; BOOST_AUTO_TEST_SUITE(Detector) diff --git a/Tests/UnitTests/Core/Detector/IndexedSurfaceGridFillerTests.cpp b/Tests/UnitTests/Core/Detector/IndexedSurfaceGridFillerTests.cpp index 2f02a8bb0ba..c4e968b1864 100644 --- a/Tests/UnitTests/Core/Detector/IndexedSurfaceGridFillerTests.cpp +++ b/Tests/UnitTests/Core/Detector/IndexedSurfaceGridFillerTests.cpp @@ -9,7 +9,7 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Detector/IndexedGridFiller.hpp" +#include "Acts/Detector/detail/IndexedGridFiller.hpp" #include "Acts/Navigation/SurfaceCandidatesUpdators.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/CylinderSurface.hpp" @@ -22,6 +22,7 @@ using namespace Acts; using namespace Acts::detail; using namespace Acts::Experimental; +using namespace Acts::Experimental::detail; GeometryContext tContext; Logging::Level logLevel = Logging::VERBOSE; diff --git a/Tests/UnitTests/Core/Detector/IndexedSurfacesGeneratorTests.cpp b/Tests/UnitTests/Core/Detector/IndexedSurfacesGeneratorTests.cpp index b1f2391bb20..fd6fbc92f53 100644 --- a/Tests/UnitTests/Core/Detector/IndexedSurfacesGeneratorTests.cpp +++ b/Tests/UnitTests/Core/Detector/IndexedSurfacesGeneratorTests.cpp @@ -9,9 +9,9 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Detector/GridAxisGenerators.hpp" -#include "Acts/Detector/IndexedGridFiller.hpp" -#include "Acts/Detector/IndexedSurfacesGenerator.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/IndexedGridFiller.hpp" +#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Surfaces/DiscSurface.hpp" @@ -22,6 +22,7 @@ using namespace Acts; using namespace Acts::Test; using namespace Acts::Experimental; +using namespace Acts::Experimental::detail; GeometryContext tContext; CylindricalTrackingGeometry cGeometry = CylindricalTrackingGeometry(tContext); diff --git a/Tests/UnitTests/Core/Detector/KdtSurfacesProviderTests.cpp b/Tests/UnitTests/Core/Detector/KdtSurfacesProviderTests.cpp new file mode 100644 index 00000000000..c3f5bbfc8b9 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/KdtSurfacesProviderTests.cpp @@ -0,0 +1,104 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Detector/detail/KdtSurfacesProvider.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalTrackingGeometry.hpp" +#include "Acts/Utilities/Enumerate.hpp" + +#include +#include + +using namespace Acts; +using namespace Acts::Test; +using namespace Acts::Experimental; + +GeometryContext tContext; +CylindricalTrackingGeometry cGeometry = CylindricalTrackingGeometry(tContext); + +namespace { +/// Helper mehtod that allows to use the already existing testing +/// infrastructure with the new const-correct detector design +/// +std::vector> unpackSurfaces( + const std::vector& surfaces) { + std::vector> uSurfaces; + uSurfaces.reserve(surfaces.size()); + for (const auto& s : surfaces) { + Surface* ncs = const_cast(s); + uSurfaces.push_back(ncs->getSharedPtr()); + } + return uSurfaces; +} + +std::vector> pixelSurfaces( + CylindricalTrackingGeometry::DetectorStore& dStore) { + // The surfaces for the KDTree structure + std::vector> pixelSurfaces; + // Fill Discs + std::vector pixelDiscs = {-800., -700., -600., + 600., 700., 800.}; + for (const auto& z : pixelDiscs) { + auto rSurfaces = cGeometry.surfacesRing(dStore, 6.4, 12.4, 36., 0.125, 0., + 55., z, 2., 22u); + auto urSurfaces = unpackSurfaces(rSurfaces); + pixelSurfaces.insert(pixelSurfaces.end(), urSurfaces.begin(), + urSurfaces.end()); + } + // Fill Barrels + std::vector pixelBarrels = {32., 72., 116., 172.}; + std::vector> pixelBinning = { + {16, 14}, {32, 14}, {52, 14}, {78, 14}}; + for (auto [ir, r] : enumerate(pixelBarrels)) { + auto cSurfaces = cGeometry.surfacesCylinder(dStore, 8.4, 36., 0.15, 0.145, + r, 3., 2., pixelBinning[ir]); + + auto ucSurfaces = unpackSurfaces(cSurfaces); + pixelSurfaces.insert(pixelSurfaces.end(), ucSurfaces.begin(), + ucSurfaces.end()); + } + return pixelSurfaces; +} + +} // namespace + +BOOST_AUTO_TEST_SUITE(Detector) + +// Test only the KDT infrastructure +BOOST_AUTO_TEST_CASE(KdtSurfacesProvider) { + // Detector store + CylindricalTrackingGeometry::DetectorStore dStore; + auto pSurfaces = pixelSurfaces(dStore); + // Count the number of surfacees + size_t refNumber = 6u * 22u + 14u * (16u + 32u + 52u + 78u); + BOOST_CHECK(pSurfaces.size() == refNumber); + + using KDTS = Acts::Experimental::detail::KdtSurfaces<>; + auto skdt = std::make_shared(KDTS(tContext, pSurfaces, {binZ, binR})); + + // query: Negative disc 3, it should yield 22 surfaces + Acts::Experimental::detail::KdtSurfacesProvider<> end3; + end3.kdt = skdt; + end3.region.set(binZ, -820, -780); + end3.region.set(binR, 0., 200.); + auto nd3 = end3(); + BOOST_CHECK(nd3.size() == 22u); + + // query: 2nd Pixel barrel + Acts::Experimental::detail::KdtSurfacesProvider<> ba1; + ba1.kdt = skdt; + ba1.region.set(binZ, -580, 580); + ba1.region.set(binR, 60., 80.); + auto b1 = ba1(); + refNumber = 32u * 14u; + BOOST_CHECK(b1.size() == refNumber); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/LayerStructureBuilderTests.cpp b/Tests/UnitTests/Core/Detector/LayerStructureBuilderTests.cpp new file mode 100644 index 00000000000..fd28e840576 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/LayerStructureBuilderTests.cpp @@ -0,0 +1,185 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Detector/LayerStructureBuilder.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalTrackingGeometry.hpp" +#include "Acts/Utilities/Enumerate.hpp" + +#include +#include + +using namespace Acts; +using namespace Acts::Test; +using namespace Acts::Experimental; + +GeometryContext tContext; +CylindricalTrackingGeometry cGeometry = CylindricalTrackingGeometry(tContext); + +namespace { +/// Helper mehtod that allows to use the already existing testing +/// infrastructure with the new const-correct detector design +/// +std::vector> unpackSurfaces( + const std::vector& surfaces) { + std::vector> uSurfaces; + uSurfaces.reserve(surfaces.size()); + for (const auto& s : surfaces) { + Surface* ncs = const_cast(s); + uSurfaces.push_back(ncs->getSharedPtr()); + } + return uSurfaces; +} + +/// @brief Simple helper struct acting as a surface provider +struct SurfaceProvider { + std::vector> surfaces; + + std::vector> operator()() { return surfaces; } +}; + +} // namespace + +BOOST_AUTO_TEST_SUITE(Detector) + +// Test the creation of the Ring +BOOST_AUTO_TEST_CASE(LayerStructureBuilder_creationRing) { + // Detector store + CylindricalTrackingGeometry::DetectorStore dStore; + auto rSurfaces = cGeometry.surfacesRing(dStore, 6.4, 12.4, 36., 0.125, 0., + 55., -800, 2., 22u); + + SurfaceProvider endcap{unpackSurfaces(rSurfaces)}; + + using LayerBinning = Acts::Experimental::LayerStructureBuilder::Binning; + + // Configure the layer structure builder + Acts::Experimental::LayerStructureBuilder::Config lsConfig; + lsConfig.auxilliary = "*** Endcap with 22 surfaces ***"; + lsConfig.surfaces = endcap; + lsConfig.binnings = {LayerBinning{ + Acts::BinningData(Acts::closed, Acts::binPhi, 22u, -M_PI, M_PI), 1u}}; + + auto endcapBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("EndcapBuilder", Logging::VERBOSE)); + + auto [surfaces0, volumes0, surfacesUpdator0, volumeUpdator0] = + endcapBuilder.construct(tContext); + + BOOST_CHECK(surfaces0.size() == 22u); + BOOST_CHECK(surfacesUpdator0.connected()); + BOOST_CHECK(volumes0.empty()); + BOOST_CHECK(volumeUpdator0.connected()); + + using LayerSupport = Acts::Experimental::LayerStructureBuilder::Support; + + lsConfig.auxilliary = "*** Endcap with 22 surfaces + 1 support disc ***"; + lsConfig.supports = {LayerSupport{ + {15., 10., 10., 0., 0.}, Acts::Surface::SurfaceType::Disc, {binZ, binR}}}; + endcapBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("EndcapBuilder", Logging::VERBOSE)); + + auto [surfaces1, volumes1, surfacesUpdator1, volumeUpdator1] = + endcapBuilder.construct(tContext); + + BOOST_CHECK(surfaces1.size() == 22u + 1u); + BOOST_CHECK(surfaces1.back()->type() == Acts::Surface::SurfaceType::Disc); + BOOST_CHECK(surfacesUpdator1.connected()); + BOOST_CHECK(volumes1.empty()); + BOOST_CHECK(volumeUpdator1.connected()); + + lsConfig.auxilliary = + "*** Endcap with 22 surfaces + 1 support -> split into 11 planes ***"; + lsConfig.supports = {LayerSupport{{15., 10., 10., 0., 0.}, + Acts::Surface::SurfaceType::Disc, + {binZ, binR}, + 11u}}; + endcapBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("EndcapBuilder", Logging::VERBOSE)); + + auto [surfaces2, volumes2, surfacesUpdator2, volumeUpdator2] = + endcapBuilder.construct(tContext); + + BOOST_CHECK(surfaces2.size() == 22u + 11u); + BOOST_CHECK(surfaces2.back()->type() == Acts::Surface::SurfaceType::Plane); + BOOST_CHECK(surfacesUpdator2.connected()); + BOOST_CHECK(volumes2.empty()); + BOOST_CHECK(volumeUpdator2.connected()); +} + +// Test the creation of the Cylinder +BOOST_AUTO_TEST_CASE(LayerStructureKDT_creationCylinder) { + CylindricalTrackingGeometry::DetectorStore dStore; + auto cSurfaces = cGeometry.surfacesCylinder(dStore, 8.4, 36., 0.15, 0.145, 72, + 3., 2., {32u, 14u}); + + SurfaceProvider barrel{unpackSurfaces(cSurfaces)}; + + using LayerBinning = Acts::Experimental::LayerStructureBuilder::Binning; + + // Configure the layer structure builder + Acts::Experimental::LayerStructureBuilder::Config lsConfig; + lsConfig.auxilliary = "*** Barrel with 448 surfaces ***"; + lsConfig.surfaces = barrel; + lsConfig.binnings = { + LayerBinning{Acts::BinningData(Acts::open, Acts::binZ, 14u, -480., 480.), + 1u}, + LayerBinning{ + Acts::BinningData(Acts::closed, Acts::binPhi, 32u, -M_PI, M_PI), 1u}}; + + auto barrelBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("BarrelBuilder", Logging::VERBOSE)); + + auto [surfaces0, volumes0, surfacesUpdator0, volumeUpdator0] = + barrelBuilder.construct(tContext); + + BOOST_CHECK(surfaces0.size() == 448u); + BOOST_CHECK(surfacesUpdator0.connected()); + BOOST_CHECK(volumes0.empty()); + BOOST_CHECK(volumeUpdator0.connected()); + + using LayerSupport = Acts::Experimental::LayerStructureBuilder::Support; + + lsConfig.auxilliary = "*** Barrel with 448 surfaces + 1 support cylinder ***"; + lsConfig.supports = {LayerSupport{{15., 10., 10., 0., 0.}, + Acts::Surface::SurfaceType::Cylinder, + {binZ, binR}}}; + + barrelBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("BarrelBuilder", Logging::VERBOSE)); + + auto [surfaces1, volumes1, surfacesUpdator1, volumeUpdator1] = + barrelBuilder.construct(tContext); + + BOOST_CHECK(surfaces1.size() == 448u + 1u); + BOOST_CHECK(surfacesUpdator1.connected()); + BOOST_CHECK(volumes1.empty()); + BOOST_CHECK(volumeUpdator1.connected()); + + lsConfig.auxilliary = + "*** Barrel with 448 surfaces + 1 support -> split into 32 planes ***"; + lsConfig.supports = {LayerSupport{{15., 10., 10., 0., 0.}, + Acts::Surface::SurfaceType::Cylinder, + {binZ, binR}, + 32u}}; + + barrelBuilder = Acts::Experimental::LayerStructureBuilder( + lsConfig, Acts::getDefaultLogger("BarrelBuilder", Logging::VERBOSE)); + + auto [surfaces2, volumes2, surfacesUpdator2, volumeUpdator2] = + barrelBuilder.construct(tContext); + + BOOST_CHECK(surfaces2.size() == 448u + 32u); + BOOST_CHECK(surfacesUpdator2.connected()); + BOOST_CHECK(volumes2.empty()); + BOOST_CHECK(volumeUpdator2.connected()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/PortalTests.cpp b/Tests/UnitTests/Core/Detector/PortalTests.cpp index 7ed905cd4bc..b7e0939a77a 100644 --- a/Tests/UnitTests/Core/Detector/PortalTests.cpp +++ b/Tests/UnitTests/Core/Detector/PortalTests.cpp @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(PortalTest) { auto linkToAImpl = std::make_unique(volumeA); DetectorVolumeUpdator linkToA; linkToA.connect<&LinkToVolumeImpl::link>(std::move(linkToAImpl)); - portalA->assignDetectorVolumeUpdator(Acts::NavigationDirection::Forward, + portalA->assignDetectorVolumeUpdator(Acts::Direction::Positive, std::move(linkToA), {volumeA}); auto attachedDetectorVolumes = portalA->attachedDetectorVolumes(); @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(PortalTest) { DetectorVolumeUpdator linkToB; auto linkToBImpl = std::make_unique(volumeB); linkToB.connect<&LinkToVolumeImpl::link>(std::move(linkToBImpl)); - portalB->assignDetectorVolumeUpdator(Acts::NavigationDirection::Backward, + portalB->assignDetectorVolumeUpdator(Acts::Direction::Negative, std::move(linkToB), {volumeB}); // Reverse: positive volume nullptr, negative volume volumeB @@ -149,13 +149,13 @@ BOOST_AUTO_TEST_CASE(PortalTest) { auto portalAI = Portal::makeShared(surface); DetectorVolumeUpdator linkToAI; linkToAI.connect<&LinkToVolumeImpl::link>(std::move(linkToAIImpl)); - portalAI->assignDetectorVolumeUpdator(Acts::NavigationDirection::Forward, + portalAI->assignDetectorVolumeUpdator(Acts::Direction::Positive, std::move(linkToAI), {volumeA}); auto portalBI = Portal::makeShared(surface); DetectorVolumeUpdator linkToBI; linkToBI.connect<&LinkToVolumeImpl::link>(std::move(linkToBIImpl)); - portalBI->assignDetectorVolumeUpdator(Acts::NavigationDirection::Forward, + portalBI->assignDetectorVolumeUpdator(Acts::Direction::Positive, std::move(linkToBI), {volumeB}); BOOST_CHECK_THROW(portalAI->fuse(portalBI), std::runtime_error); diff --git a/Tests/UnitTests/Core/Detector/ReferenceGeneratorsTests.cpp b/Tests/UnitTests/Core/Detector/ReferenceGeneratorsTests.cpp new file mode 100644 index 00000000000..caa31d4b302 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/ReferenceGeneratorsTests.cpp @@ -0,0 +1,53 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/detail/ReferenceGenerators.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" + +#include + +using namespace Acts::Experimental::detail; + +Acts::GeometryContext tContext; + +auto rBounds = std::make_shared(10, 20); +auto sTransform = Acts::Transform3::Identity(); +auto pSurface = Acts::Surface::makeShared( + sTransform.pretranslate(Acts::Vector3(20., 20., 100.)), std::move(rBounds)); + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(CenterReference) { + // Simply return the cetner + auto center = CenterReferenceGenerator{}.references(tContext, *pSurface); + BOOST_CHECK(center.size() == 1u); + BOOST_CHECK(center.front().isApprox(Acts::Vector3(20., 20., 100.))); +} + +BOOST_AUTO_TEST_CASE(BinningPositionReference) { + // Simply return binning position, we test only the behavior of the generator + // not the output + auto binningPosition = BinningValueReferenceGenerator{Acts::binZ}.references( + tContext, *pSurface); + BOOST_CHECK(binningPosition.size() == 1u); +} + +BOOST_AUTO_TEST_CASE(PolyhedronReference) { + // Simply return binning position, we test only the behavior of the generator + // not the output + auto referencePositions = + PolyhedronReferenceGenerator{}.references(tContext, *pSurface); + // 4 corners with center of gravity + BOOST_CHECK(referencePositions.size() == 5u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Detector/SupportHelperTests.cpp b/Tests/UnitTests/Core/Detector/SupportHelperTests.cpp new file mode 100644 index 00000000000..69f9c21c061 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/SupportHelperTests.cpp @@ -0,0 +1,178 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Detector/detail/SupportHelper.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" + +Acts::GeometryContext tContext; + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(CylindricalSupport) { + // As a single cylinder + auto singleSupport = + Acts::Experimental::detail::SupportHelper::cylindricalSupport( + Acts::Transform3::Identity(), {100., 400., M_PI, 0., 0., 0.}, 1u); + BOOST_CHECK(singleSupport.size() == 1u); + BOOST_CHECK(singleSupport[0u]->type() == + Acts::Surface::SurfaceType::Cylinder); + + // As a split cylinder + auto splitSupport = + Acts::Experimental::detail::SupportHelper::cylindricalSupport( + Acts::Transform3::Identity(), {100., 400., M_PI, 0., 0., 0.}, 32u); + BOOST_CHECK(splitSupport.size() == 32u); + for (const auto& ss : splitSupport) { + BOOST_CHECK(ss->type() == Acts::Surface::SurfaceType::Plane); + } + + // As a split cylinder - sectoral + auto splitSectoralSupport = + Acts::Experimental::detail::SupportHelper::cylindricalSupport( + Acts::Transform3::Identity(), + {100., 400., 0.25 * M_PI, 0.75 * M_PI, 0., 0.}, 128u); + BOOST_CHECK(splitSectoralSupport.size() == 128u); + for (const auto& ss : splitSectoralSupport) { + BOOST_CHECK(ss->type() == Acts::Surface::SurfaceType::Plane); + } +} + +BOOST_AUTO_TEST_CASE(DiscSupport) { + // As a single disc + auto singleSupport = Acts::Experimental::detail::SupportHelper::discSupport( + Acts::Transform3::Identity(), {100., 400., M_PI, 0.}, 1u); + BOOST_CHECK(singleSupport.size() == 1u); + BOOST_CHECK(singleSupport[0u]->type() == Acts::Surface::SurfaceType::Disc); + + // As a split disc + auto splitSupport = Acts::Experimental::detail::SupportHelper::discSupport( + Acts::Transform3::Identity(), {100., 400., M_PI, 0.}, 32u); + BOOST_CHECK(splitSupport.size() == 32u); + for (const auto& ss : splitSupport) { + BOOST_CHECK(ss->type() == Acts::Surface::SurfaceType::Plane); + } + + // As a split disc - sectoral + auto splitSectoralSupport = + Acts::Experimental::detail::SupportHelper::discSupport( + Acts::Transform3::Identity(), {100., 400., 0.5 * M_PI, 0.}, 16u); + BOOST_CHECK(splitSectoralSupport.size() == 16u); + for (const auto& ss : splitSectoralSupport) { + BOOST_CHECK(ss->type() == Acts::Surface::SurfaceType::Plane); + } +} + +BOOST_AUTO_TEST_CASE(addCylinderSupport) { + std::vector> lSurfaces; + std::vector assignToAll; + + // The Extent + Acts::Extent lExtent; + lExtent.set(Acts::binR, 100., 110.); + lExtent.set(Acts::binZ, -400., -400.); + + // Get the main support parameters: + // - doff .. offset (in r.z) + // - demin, demax .. envelop min, max (in z,r) + // - dphimin, dphimin .. envelop min, max (in phi) + std::array sValues = {10, 5, 5, 0., 0.}; + // Add a single support cylinder + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Cylinder, + sValues, std::nullopt, 1u); + BOOST_CHECK(lSurfaces.size() == 1u); + BOOST_CHECK(assignToAll.size() == 1u); + BOOST_CHECK(assignToAll[0u] == 0u); + // The radius of the newly created suport surface should be 10 out of the + // maximum + CHECK_CLOSE_ABS(lSurfaces[0u]->bounds().values()[0u], 120, 1e-3); + + // Clear up for the next test + lSurfaces.clear(); + assignToAll.clear(); + // Add split surfaces as support to already exisint surfaces + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Cylinder, + sValues, std::nullopt, 16u); + BOOST_CHECK(lSurfaces.size() == 16u); + BOOST_CHECK(assignToAll.empty()); +} + +BOOST_AUTO_TEST_CASE(addDiscSupport) { + std::vector> lSurfaces; + std::vector assignToAll; + + // The Extent + Acts::Extent lExtent; + lExtent.set(Acts::binR, 100., 400.); + lExtent.set(Acts::binZ, -110., -100.); + + // Get the main support parameters: + // - doff .. offset (in r.z) + // - demin, demax .. envelop min, max (in z,r) + // - dphimin, dphimin .. envelop min, max (in phi) + std::array sValues = {-10, 10, 20, 0., 0.}; + // Add a single disc as a support cylinder + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Disc, + sValues, std::nullopt, 1u); + BOOST_CHECK(lSurfaces.size() == 1u); + BOOST_CHECK(assignToAll.size() == 1u); + BOOST_CHECK(assignToAll[0u] == 0u); + // The radius of the newly created suport surface should be 10 out of the + // minimum + CHECK_CLOSE_ABS(lSurfaces[0u]->transform(tContext).translation().z(), -120, + 1e-3); + + // Clear up for the next test + lSurfaces.clear(); + assignToAll.clear(); + // Add split surfaces as support disc + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Disc, + sValues, std::nullopt, 16u); + BOOST_CHECK(lSurfaces.size() == 16u); + BOOST_CHECK(assignToAll.empty()); +} + +BOOST_AUTO_TEST_CASE(addMisconfiguredSupport) { + std::vector> lSurfaces; + std::vector assignToAll; + + // Get the main support parameters: + // - doff .. offset (in r.z) + // - demin, demax .. envelop min, max (in z,r) + // - dphimin, dphimin .. envelop min, max (in phi) + std::array sValues = {-10, 10, 20, 0., 0.}; + + // Unconstrainted extent + Acts::Extent lExtent; + + // R - Z are not constrained + // Add a single disc as a support cylinder + BOOST_CHECK_THROW( + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Cylinder, + sValues, std::nullopt, 1u), + std::runtime_error); + + // The Extent + lExtent.set(Acts::binR, 100., 400.); + lExtent.set(Acts::binZ, -110., -100.); + + // Add a single disc as a support cylinder + BOOST_CHECK_THROW( + Acts::Experimental::detail::SupportHelper::addSupport( + lSurfaces, assignToAll, lExtent, Acts::Surface::SurfaceType::Cone, + sValues, std::nullopt, 1u), + std::invalid_argument); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/EventData/CMakeLists.txt b/Tests/UnitTests/Core/EventData/CMakeLists.txt index b6f3f8171fa..833dc82b517 100644 --- a/Tests/UnitTests/Core/EventData/CMakeLists.txt +++ b/Tests/UnitTests/Core/EventData/CMakeLists.txt @@ -11,3 +11,7 @@ add_unittest(TransformFreeToBound TransformFreeToBoundTests.cpp) add_unittest(CorrectedTransformFreeToBound CorrectedTransformFreeToBoundTests.cpp) add_unittest(Track TrackTests.cpp) add_unittest(SourceLink SourceLinkTests.cpp) + +add_unittest(TrackStatePropMask TrackStatePropMaskTests.cpp) + +add_non_compile_test(MultiTrajectory TrackContainerComplianceTests.cpp) diff --git a/Tests/UnitTests/Core/EventData/MultiTrajectoryTests.cpp b/Tests/UnitTests/Core/EventData/MultiTrajectoryTests.cpp index b9b10fef092..8aefd0d4a3d 100644 --- a/Tests/UnitTests/Core/EventData/MultiTrajectoryTests.cpp +++ b/Tests/UnitTests/Core/EventData/MultiTrajectoryTests.cpp @@ -19,6 +19,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Tests/CommonHelpers/GenerateParameters.hpp" +#include "Acts/Tests/CommonHelpers/MultiTrajectoryTestsCommon.hpp" #include "Acts/Tests/CommonHelpers/TestSourceLink.hpp" #include "Acts/Tests/CommonHelpers/TestTrackState.hpp" #include "Acts/Utilities/Helpers.hpp" @@ -44,78 +45,23 @@ const GeometryContext gctx; // fixed seed for reproducible tests std::default_random_engine rng(31415); -} // namespace - -BOOST_AUTO_TEST_SUITE(EventDataMultiTrajectory) - -BOOST_AUTO_TEST_CASE(Build) { - constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; - - // construct trajectory w/ multiple components - VectorMultiTrajectory t; +struct Factory { + using trajectory_t = VectorMultiTrajectory; + using const_trajectory_t = ConstVectorMultiTrajectory; - auto i0 = t.addTrackState(kMask); - // trajectory bifurcates here into multiple hypotheses - auto i1a = t.addTrackState(kMask, i0); - auto i1b = t.addTrackState(kMask, i0); - auto i2a = t.addTrackState(kMask, i1a); - auto i2b = t.addTrackState(kMask, i1b); + VectorMultiTrajectory create() { return {}; } + ConstVectorMultiTrajectory createConst() { return {}; } +}; - // print each trajectory component - std::vector act; - auto collect = [&](auto p) { - act.push_back(p.index()); - BOOST_CHECK(!p.hasCalibrated()); - BOOST_CHECK(!p.hasFiltered()); - BOOST_CHECK(!p.hasSmoothed()); - BOOST_CHECK(!p.hasJacobian()); - BOOST_CHECK(!p.hasProjector()); - }; +using CommonTests = MultiTrajectoryTestsCommon; - std::vector exp = {i2a, i1a, i0}; - t.visitBackwards(i2a, collect); - BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end()); - - act.clear(); - for (const auto& p : t.trackStateRange(i2a)) { - act.push_back(p.index()); - } - BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end()); - - act.clear(); - exp = {i2b, i1b, i0}; - t.visitBackwards(i2b, collect); - BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end()); - - act.clear(); - for (const auto& p : t.trackStateRange(i2b)) { - act.push_back(p.index()); - } - BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end()); - - act.clear(); - t.applyBackwards(i2b, collect); - BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end()); +} // namespace - auto r = t.trackStateRange(i2b); - BOOST_CHECK_EQUAL(std::distance(r.begin(), r.end()), 3); +BOOST_AUTO_TEST_SUITE(EventDataMultiTrajectory) - // check const-correctness - const auto& ct = t; - std::vector predicteds; - // mutation in this loop works! - for (auto p : t.trackStateRange(i2b)) { - predicteds.push_back(BoundVector::Random()); - p.predicted() = predicteds.back(); - } - std::vector predictedsAct; - for (const auto& p : ct.trackStateRange(i2b)) { - predictedsAct.push_back(p.predicted()); - // mutation in this loop doesn't work: does not compile - // p.predicted() = BoundVector::Random(); - } - BOOST_CHECK_EQUAL_COLLECTIONS(predictedsAct.begin(), predictedsAct.end(), - predicteds.begin(), predicteds.end()); +BOOST_AUTO_TEST_CASE(Build) { + CommonTests ct; + ct.testBuild(); } BOOST_AUTO_TEST_CASE(ConstCorrectness) { @@ -137,17 +83,20 @@ BOOST_AUTO_TEST_CASE(ConstCorrectness) { // ctsp.predicted().setRandom(); } + // is this something we actually want? ConstVectorMultiTrajectory ct = t; + BOOST_CHECK_EQUAL(ct.size(), t.size()); ConstVectorMultiTrajectory ctm{std::move(t)}; + BOOST_CHECK_EQUAL(ctm.size(), ct.size()); { static_assert( std::is_same_v, + decltype(ctm.getTrackState(i0))>, "Got mutable track state proxy"); ConstVectorMultiTrajectory::ConstTrackStateProxy ctsp = - ct.getTrackState(i0); + ctm.getTrackState(i0); static_cast(ctsp); // doesn't compile: @@ -160,962 +109,80 @@ BOOST_AUTO_TEST_CASE(ConstCorrectness) { } BOOST_AUTO_TEST_CASE(Clear) { - constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; - VectorMultiTrajectory t; - BOOST_CHECK_EQUAL(t.size(), 0); - - auto i0 = t.addTrackState(kMask); - // trajectory bifurcates here into multiple hypotheses - auto i1a = t.addTrackState(kMask, i0); - auto i1b = t.addTrackState(kMask, i0); - t.addTrackState(kMask, i1a); - t.addTrackState(kMask, i1b); - - BOOST_CHECK_EQUAL(t.size(), 5); - t.clear(); - BOOST_CHECK_EQUAL(t.size(), 0); + CommonTests ct; + ct.testClear(); } BOOST_AUTO_TEST_CASE(ApplyWithAbort) { - constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; - - // construct trajectory with three components - VectorMultiTrajectory t; - auto i0 = t.addTrackState(kMask); - auto i1 = t.addTrackState(kMask, i0); - auto i2 = t.addTrackState(kMask, i1); - - size_t n = 0; - t.applyBackwards(i2, [&](const auto&) { - n++; - return false; - }); - BOOST_CHECK_EQUAL(n, 1u); - - n = 0; - t.applyBackwards(i2, [&](const auto& ts) { - n++; - if (ts.index() == i1) { - return false; - } - return true; - }); - BOOST_CHECK_EQUAL(n, 2u); - - n = 0; - t.applyBackwards(i2, [&](const auto&) { - n++; - return true; - }); - BOOST_CHECK_EQUAL(n, 3u); -} - -BOOST_AUTO_TEST_CASE(BitmaskOperators) { - using PM = TrackStatePropMask; - - auto bs1 = PM::Predicted; - - BOOST_CHECK(ACTS_CHECK_BIT(bs1, PM::Predicted)); - BOOST_CHECK(!ACTS_CHECK_BIT(bs1, PM::Calibrated)); - - auto bs2 = PM::Calibrated; - - BOOST_CHECK(!ACTS_CHECK_BIT(bs2, PM::Predicted)); - BOOST_CHECK(ACTS_CHECK_BIT(bs2, PM::Calibrated)); - - auto bs3 = PM::Calibrated | PM::Predicted; - - BOOST_CHECK(ACTS_CHECK_BIT(bs3, PM::Predicted)); - BOOST_CHECK(ACTS_CHECK_BIT(bs3, PM::Calibrated)); - - BOOST_CHECK(ACTS_CHECK_BIT(PM::All, PM::Predicted)); - BOOST_CHECK(ACTS_CHECK_BIT(PM::All, PM::Calibrated)); - - auto bs4 = PM::Predicted | PM::Jacobian | PM::Smoothed; - BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Predicted)); - BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Jacobian)); - BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Smoothed)); - BOOST_CHECK(!ACTS_CHECK_BIT(bs4, PM::Calibrated)); - BOOST_CHECK(!ACTS_CHECK_BIT(bs4, PM::Filtered)); - - auto cnv = [](auto a) -> std::bitset<8> { - return static_cast::type>(a); - }; - - BOOST_CHECK(cnv(PM::All).all()); // all ones - BOOST_CHECK(cnv(PM::None).none()); // all zeros - - // test orthogonality - std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, - PM::Jacobian, PM::Calibrated}; - for (size_t i = 0; i < values.size(); i++) { - for (size_t j = 0; j < values.size(); j++) { - PM a = values[i]; - PM b = values[j]; - - if (i == j) { - BOOST_CHECK(cnv(a & b).count() == 1); - } else { - BOOST_CHECK(cnv(a & b).none()); - } - } - } - - BOOST_CHECK(cnv(PM::Predicted ^ PM::Filtered).count() == 2); - BOOST_CHECK(cnv(PM::Predicted ^ PM::Predicted).none()); - BOOST_CHECK(~(PM::Predicted | PM::Calibrated) == - (PM::All ^ PM::Predicted ^ PM::Calibrated)); - - PM base = PM::None; - BOOST_CHECK(cnv(base) == 0); - - base &= PM::Filtered; - BOOST_CHECK(cnv(base) == 0); - - base |= PM::Filtered; - BOOST_CHECK(base == PM::Filtered); - - base |= PM::Calibrated; - BOOST_CHECK(base == (PM::Filtered | PM::Calibrated)); - - base ^= PM::All; - BOOST_CHECK(base == ~(PM::Filtered | PM::Calibrated)); + CommonTests ct; + ct.testApplyWithAbort(); } BOOST_AUTO_TEST_CASE(AddTrackStateWithBitMask) { - using PM = TrackStatePropMask; - using namespace Acts::HashedStringLiteral; - - VectorMultiTrajectory t; - - auto alwaysPresent = [](auto& ts) { - BOOST_CHECK(ts.template has<"referenceSurface"_hash>()); - BOOST_CHECK(ts.template has<"measdim"_hash>()); - BOOST_CHECK(ts.template has<"chi2"_hash>()); - BOOST_CHECK(ts.template has<"pathLength"_hash>()); - BOOST_CHECK(ts.template has<"typeFlags"_hash>()); - }; - - auto ts = t.getTrackState(t.addTrackState(PM::All)); - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK(ts.hasFiltered()); - BOOST_CHECK(ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(ts.hasProjector()); - BOOST_CHECK(ts.hasJacobian()); - alwaysPresent(ts); - ts.allocateCalibrated(5); - BOOST_CHECK(ts.hasCalibrated()); - - ts = t.getTrackState(t.addTrackState(PM::None)); - BOOST_CHECK(!ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(!ts.hasProjector()); - BOOST_CHECK(!ts.hasJacobian()); - alwaysPresent(ts); - - ts = t.getTrackState(t.addTrackState(PM::Predicted)); - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(!ts.hasProjector()); - BOOST_CHECK(!ts.hasJacobian()); - alwaysPresent(ts); - - ts = t.getTrackState(t.addTrackState(PM::Filtered)); - BOOST_CHECK(!ts.hasPredicted()); - BOOST_CHECK(ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(!ts.hasProjector()); - BOOST_CHECK(!ts.hasJacobian()); - alwaysPresent(ts); - - ts = t.getTrackState(t.addTrackState(PM::Smoothed)); - BOOST_CHECK(!ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(!ts.hasProjector()); - BOOST_CHECK(!ts.hasJacobian()); - alwaysPresent(ts); - - ts = t.getTrackState(t.addTrackState(PM::Calibrated)); - BOOST_CHECK(!ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(ts.hasProjector()); - BOOST_CHECK(!ts.hasJacobian()); - ts.allocateCalibrated(5); - BOOST_CHECK(ts.hasCalibrated()); - - ts = t.getTrackState(t.addTrackState(PM::Jacobian)); - BOOST_CHECK(!ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK(!ts.hasCalibrated()); - BOOST_CHECK(!ts.hasProjector()); - BOOST_CHECK(ts.hasJacobian()); - alwaysPresent(ts); + CommonTests ct; + ct.testAddTrackStateWithBitMask(); } // assert expected "cross-talk" between trackstate proxies BOOST_AUTO_TEST_CASE(TrackStateProxyCrossTalk) { - TestTrackState pc(rng, 2u); - - // multi trajectory w/ a single, fully set, track state - VectorMultiTrajectory traj; - size_t index = traj.addTrackState(); - { - auto ts = traj.getTrackState(index); - fillTrackState(pc, TrackStatePropMask::All, ts); - } - // get two TrackStateProxies that reference the same data - auto tsa = traj.getTrackState(index); - auto tsb = traj.getTrackState(index); - // then modify one and check that the other was modified as well - { - auto [par, cov] = generateBoundParametersCovariance(rng); - tsb.predicted() = par; - tsb.predictedCovariance() = cov; - BOOST_CHECK_EQUAL(tsa.predicted(), par); - BOOST_CHECK_EQUAL(tsa.predictedCovariance(), cov); - BOOST_CHECK_EQUAL(tsb.predicted(), par); - BOOST_CHECK_EQUAL(tsb.predictedCovariance(), cov); - } - { - auto [par, cov] = generateBoundParametersCovariance(rng); - tsb.filtered() = par; - tsb.filteredCovariance() = cov; - BOOST_CHECK_EQUAL(tsa.filtered(), par); - BOOST_CHECK_EQUAL(tsa.filteredCovariance(), cov); - BOOST_CHECK_EQUAL(tsb.filtered(), par); - BOOST_CHECK_EQUAL(tsb.filteredCovariance(), cov); - } - { - auto [par, cov] = generateBoundParametersCovariance(rng); - tsb.smoothed() = par; - tsb.smoothedCovariance() = cov; - BOOST_CHECK_EQUAL(tsa.smoothed(), par); - BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), cov); - BOOST_CHECK_EQUAL(tsb.smoothed(), par); - BOOST_CHECK_EQUAL(tsb.smoothedCovariance(), cov); - } - { - // create a new (invalid) source link - TestSourceLink invalid; - invalid.sourceId = -1; - BOOST_CHECK_NE(tsa.getUncalibratedSourceLink().get(), - invalid); - BOOST_CHECK_NE(tsb.getUncalibratedSourceLink().get(), - invalid); - tsb.setUncalibratedSourceLink(SourceLink{invalid}); - BOOST_CHECK_EQUAL(tsa.getUncalibratedSourceLink().get(), - invalid); - BOOST_CHECK_EQUAL(tsb.getUncalibratedSourceLink().get(), - invalid); - } - { - // reset measurements w/ full parameters - auto [measPar, measCov] = generateBoundParametersCovariance(rng); - tsb.allocateCalibrated(eBoundSize); - tsb.calibrated() = measPar; - tsb.calibratedCovariance() = measCov; - BOOST_CHECK_EQUAL(tsa.calibrated(), measPar); - BOOST_CHECK_EQUAL(tsa.calibratedCovariance(), measCov); - BOOST_CHECK_EQUAL(tsb.calibrated(), measPar); - BOOST_CHECK_EQUAL(tsb.calibratedCovariance(), measCov); - } - { - // reset only the effective measurements - auto [measPar, measCov] = generateBoundParametersCovariance(rng); - size_t nMeasurements = tsb.effectiveCalibrated().rows(); - auto effPar = measPar.head(nMeasurements); - auto effCov = measCov.topLeftCorner(nMeasurements, nMeasurements); - tsb.allocateCalibrated(eBoundSize); - tsb.effectiveCalibrated() = effPar; - tsb.effectiveCalibratedCovariance() = effCov; - BOOST_CHECK_EQUAL(tsa.effectiveCalibrated(), effPar); - BOOST_CHECK_EQUAL(tsa.effectiveCalibratedCovariance(), effCov); - BOOST_CHECK_EQUAL(tsb.effectiveCalibrated(), effPar); - BOOST_CHECK_EQUAL(tsb.effectiveCalibratedCovariance(), effCov); - } - { - Jacobian jac = Jacobian::Identity(); - BOOST_CHECK_NE(tsa.jacobian(), jac); - BOOST_CHECK_NE(tsb.jacobian(), jac); - tsb.jacobian() = jac; - BOOST_CHECK_EQUAL(tsa.jacobian(), jac); - BOOST_CHECK_EQUAL(tsb.jacobian(), jac); - } - { - tsb.chi2() = 98.0; - BOOST_CHECK_EQUAL(tsa.chi2(), 98.0); - BOOST_CHECK_EQUAL(tsb.chi2(), 98.0); - } - { - tsb.pathLength() = 66.0; - BOOST_CHECK_EQUAL(tsa.pathLength(), 66.0); - BOOST_CHECK_EQUAL(tsb.pathLength(), 66.0); - } + CommonTests ct; + ct.testTrackStateProxyCrossTalk(rng); } BOOST_AUTO_TEST_CASE(TrackStateReassignment) { - TestTrackState pc(rng, 1u); - - VectorMultiTrajectory t; - size_t index = t.addTrackState(); - auto ts = t.getTrackState(index); - fillTrackState(pc, TrackStatePropMask::All, ts); - - // assert contents of original measurement (just to be safe) - BOOST_CHECK_EQUAL(ts.calibratedSize(), 1u); - BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), - (pc.sourceLink.parameters.head<1>())); - BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(), - (pc.sourceLink.covariance.topLeftCorner<1, 1>())); - - // use temporary measurement to reset calibrated data - TestTrackState ttsb(rng, 2u); - ts.setUncalibratedSourceLink(SourceLink{ttsb.sourceLink}); - auto meas = testSourceLinkCalibratorReturn(gctx, ts); - auto m2 = std::get>(meas); - - BOOST_CHECK_EQUAL(ts.calibratedSize(), 2); - BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), m2.parameters()); - BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(), m2.covariance()); - BOOST_CHECK_EQUAL(ts.effectiveProjector(), m2.projector()); + CommonTests ct; + ct.testTrackStateReassignment(rng); } BOOST_DATA_TEST_CASE(TrackStateProxyStorage, bd::make({1u, 2u}), nMeasurements) { - TestTrackState pc(rng, nMeasurements); - - // create trajectory with a single fully-filled random track state - VectorMultiTrajectory t; - size_t index = t.addTrackState(); - auto ts = t.getTrackState(index); - fillTrackState(pc, TrackStatePropMask::All, ts); - - // check that the surface is correctly set - BOOST_CHECK_EQUAL(&ts.referenceSurface(), pc.surface.get()); - BOOST_CHECK_EQUAL(ts.referenceSurface().geometryId(), - pc.sourceLink.geometryId()); - - // check that the track parameters are set - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK_EQUAL(ts.predicted(), pc.predicted.parameters()); - BOOST_CHECK(pc.predicted.covariance().has_value()); - BOOST_CHECK_EQUAL(ts.predictedCovariance(), *pc.predicted.covariance()); - BOOST_CHECK(ts.hasFiltered()); - BOOST_CHECK_EQUAL(ts.filtered(), pc.filtered.parameters()); - BOOST_CHECK(pc.filtered.covariance().has_value()); - BOOST_CHECK_EQUAL(ts.filteredCovariance(), *pc.filtered.covariance()); - BOOST_CHECK(ts.hasSmoothed()); - BOOST_CHECK_EQUAL(ts.smoothed(), pc.smoothed.parameters()); - BOOST_CHECK(pc.smoothed.covariance().has_value()); - BOOST_CHECK_EQUAL(ts.smoothedCovariance(), *pc.smoothed.covariance()); - - // check that the jacobian is set - BOOST_CHECK(ts.hasJacobian()); - BOOST_CHECK_EQUAL(ts.jacobian(), pc.jacobian); - BOOST_CHECK_EQUAL(ts.pathLength(), pc.pathLength); - // check that chi2 is set - BOOST_CHECK_EQUAL(ts.chi2(), pc.chi2); - - // check that the uncalibratedSourceLink source link is set - BOOST_CHECK_EQUAL(ts.getUncalibratedSourceLink().get(), - pc.sourceLink); - - // check that the calibrated measurement is set - BOOST_CHECK(ts.hasCalibrated()); - BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), - pc.sourceLink.parameters.head(nMeasurements)); - BOOST_CHECK_EQUAL( - ts.effectiveCalibratedCovariance(), - pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements)); - { - ParametersVector mParFull = ParametersVector::Zero(); - CovarianceMatrix mCovFull = CovarianceMatrix::Zero(); - mParFull.head(nMeasurements) = pc.sourceLink.parameters.head(nMeasurements); - mCovFull.topLeftCorner(nMeasurements, nMeasurements) = - pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements); - - auto expMeas = pc.sourceLink.parameters.head(nMeasurements); - auto expCov = - pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements); - - visit_measurement(ts.calibratedSize(), [&](auto N) { - constexpr size_t measdim = decltype(N)::value; - BOOST_CHECK_EQUAL(ts.calibrated(), expMeas); - BOOST_CHECK_EQUAL(ts.calibratedCovariance(), expCov); - }); - } - - BOOST_CHECK(ts.hasProjector()); - ActsMatrix fullProj; - fullProj.setZero(); - { - // create a temporary measurement to extract the projector matrix - auto meas = testSourceLinkCalibratorReturn(gctx, ts); - std::visit( - [&](const auto& m) { - fullProj.topLeftCorner(nMeasurements, eBoundSize) = m.projector(); - }, - meas); - } - BOOST_CHECK_EQUAL(ts.effectiveProjector(), - fullProj.topLeftCorner(nMeasurements, eBoundSize)); - BOOST_CHECK_EQUAL(ts.projector(), fullProj); + CommonTests ct; + ct.testTrackStateProxyStorage(rng, nMeasurements); } BOOST_AUTO_TEST_CASE(TrackStateProxyAllocations) { - using namespace Acts::HashedStringLiteral; - - TestTrackState pc(rng, 2u); - - // this should allocate for all components in the trackstate, plus filtered - VectorMultiTrajectory t; - size_t i = t.addTrackState(TrackStatePropMask::Predicted | - TrackStatePropMask::Filtered | - TrackStatePropMask::Jacobian); - auto tso = t.getTrackState(i); - fillTrackState(pc, TrackStatePropMask::Predicted, tso); - fillTrackState(pc, TrackStatePropMask::Filtered, tso); - fillTrackState(pc, TrackStatePropMask::Jacobian, tso); - - BOOST_CHECK(tso.hasPredicted()); - BOOST_CHECK(tso.hasFiltered()); - BOOST_CHECK(!tso.hasSmoothed()); - BOOST_CHECK(!tso.hasCalibrated()); - BOOST_CHECK(tso.hasJacobian()); - - auto tsnone = t.getTrackState(t.addTrackState(TrackStatePropMask::None)); - BOOST_CHECK(!tsnone.has<"predicted"_hash>()); - BOOST_CHECK(!tsnone.has<"filtered"_hash>()); - BOOST_CHECK(!tsnone.has<"smoothed"_hash>()); - BOOST_CHECK(!tsnone.has<"jacobian"_hash>()); - BOOST_CHECK(!tsnone.has<"calibrated"_hash>()); - BOOST_CHECK(!tsnone.has<"projector"_hash>()); - BOOST_CHECK( - !tsnone.has<"uncalibratedSourceLink"_hash>()); // separate optional - // mechanism - BOOST_CHECK(tsnone.has<"referenceSurface"_hash>()); - BOOST_CHECK(tsnone.has<"measdim"_hash>()); - BOOST_CHECK(tsnone.has<"chi2"_hash>()); - BOOST_CHECK(tsnone.has<"pathLength"_hash>()); - BOOST_CHECK(tsnone.has<"typeFlags"_hash>()); - - auto tsall = t.getTrackState(t.addTrackState(TrackStatePropMask::All)); - BOOST_CHECK(tsall.has<"predicted"_hash>()); - BOOST_CHECK(tsall.has<"filtered"_hash>()); - BOOST_CHECK(tsall.has<"smoothed"_hash>()); - BOOST_CHECK(tsall.has<"jacobian"_hash>()); - BOOST_CHECK(!tsall.has<"calibrated"_hash>()); - tsall.allocateCalibrated(5); - BOOST_CHECK(tsall.has<"calibrated"_hash>()); - BOOST_CHECK(tsall.has<"projector"_hash>()); - BOOST_CHECK( - !tsall.has<"uncalibratedSourceLink"_hash>()); // separate optional - // mechanism: nullptr - BOOST_CHECK(tsall.has<"referenceSurface"_hash>()); - BOOST_CHECK(tsall.has<"measdim"_hash>()); - BOOST_CHECK(tsall.has<"chi2"_hash>()); - BOOST_CHECK(tsall.has<"pathLength"_hash>()); - BOOST_CHECK(tsall.has<"typeFlags"_hash>()); - - tsall.unset(TrackStatePropMask::Predicted); - BOOST_CHECK(!tsall.has<"predicted"_hash>()); - tsall.unset(TrackStatePropMask::Filtered); - BOOST_CHECK(!tsall.has<"filtered"_hash>()); - tsall.unset(TrackStatePropMask::Smoothed); - BOOST_CHECK(!tsall.has<"smoothed"_hash>()); - tsall.unset(TrackStatePropMask::Jacobian); - BOOST_CHECK(!tsall.has<"jacobian"_hash>()); - tsall.unset(TrackStatePropMask::Calibrated); - BOOST_CHECK(!tsall.has<"calibrated"_hash>()); + CommonTests ct; + ct.testTrackStateProxyAllocations(rng); } BOOST_AUTO_TEST_CASE(TrackStateProxyGetMask) { - using PM = TrackStatePropMask; - - std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, - PM::Jacobian, PM::Calibrated}; - PM all = std::accumulate(values.begin(), values.end(), PM::None, - [](auto a, auto b) { return a | b; }); - - VectorMultiTrajectory mj; - { - auto ts = mj.getTrackState(mj.addTrackState(PM::All)); - // Calibrated is ignored because we haven't allocated yet - BOOST_CHECK_EQUAL(ts.getMask(), (all & ~PM::Calibrated)); - ts.allocateCalibrated(4); - BOOST_CHECK_EQUAL(ts.getMask(), all); - } - { - auto ts = mj.getTrackState(mj.addTrackState(PM::Filtered | PM::Calibrated)); - // Calibrated is ignored because we haven't allocated yet - BOOST_CHECK_EQUAL(ts.getMask(), PM::Filtered); - ts.allocateCalibrated(4); - BOOST_CHECK_EQUAL(ts.getMask(), (PM::Filtered | PM::Calibrated)); - } - { - auto ts = mj.getTrackState( - mj.addTrackState(PM::Filtered | PM::Smoothed | PM::Predicted)); - BOOST_CHECK(ts.getMask() == (PM::Filtered | PM::Smoothed | PM::Predicted)); - } - { - for (PM mask : values) { - auto ts = mj.getTrackState(mj.addTrackState(mask)); - // Calibrated is ignored because we haven't allocated yet - BOOST_CHECK_EQUAL(ts.getMask(), (mask & ~PM::Calibrated)); - } - } + CommonTests ct; + ct.testTrackStateProxyGetMask(); } BOOST_AUTO_TEST_CASE(TrackStateProxyCopy) { - using PM = TrackStatePropMask; - - std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, - PM::Jacobian}; - - VectorMultiTrajectory mj; - auto mkts = [&](PM mask) { - auto r = mj.getTrackState(mj.addTrackState(mask)); - return r; - }; - - // orthogonal ones - for (PM a : values) { - for (PM b : values) { - auto tsa = mkts(a); - auto tsb = mkts(b); - // doesn't work - if (a != b) { - BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error); - BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error); - } else { - tsa.copyFrom(tsb); - tsb.copyFrom(tsa); - } - } - } - - { - BOOST_TEST_CHECKPOINT("Calib auto alloc"); - auto tsa = mkts(PM::All); - auto tsb = mkts(PM::All); - tsb.allocateCalibrated(5); - tsb.calibrated<5>().setRandom(); - tsb.calibratedCovariance<5>().setRandom(); - tsa.copyFrom(tsb, PM::All); - BOOST_CHECK_EQUAL(tsa.calibrated<5>(), tsb.calibrated<5>()); - BOOST_CHECK_EQUAL(tsa.calibratedCovariance<5>(), - tsb.calibratedCovariance<5>()); - } - - { - BOOST_TEST_CHECKPOINT("Copy none"); - auto tsa = mkts(PM::All); - auto tsb = mkts(PM::All); - tsa.copyFrom(tsb, PM::None); - } - - auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both - ts1.filtered().setRandom(); - ts1.filteredCovariance().setRandom(); - ts1.predicted().setRandom(); - ts1.predictedCovariance().setRandom(); - - // ((src XOR dst) & src) == 0 - auto ts2 = mkts(PM::Predicted); - ts2.predicted().setRandom(); - ts2.predictedCovariance().setRandom(); - - // they are different before - BOOST_CHECK(ts1.predicted() != ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); - - // ts1 -> ts2 fails - BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error); - BOOST_CHECK(ts1.predicted() != ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); - - // ts2 -> ts1 is ok - ts1.copyFrom(ts2); - BOOST_CHECK(ts1.predicted() == ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance()); - - size_t i0 = mj.addTrackState(); - size_t i1 = mj.addTrackState(); - ts1 = mj.getTrackState(i0); - ts2 = mj.getTrackState(i1); - TestTrackState rts1(rng, 1u); - TestTrackState rts2(rng, 2u); - fillTrackState(rts1, TrackStatePropMask::All, ts1); - fillTrackState(rts2, TrackStatePropMask::All, ts2); - - auto ots1 = mkts(PM::All); - auto ots2 = mkts(PM::All); - // make full copy for later. We prove full copy works right below - ots1.copyFrom(ts1); - ots2.copyFrom(ts2); - - BOOST_CHECK_NE(ts1.predicted(), ts2.predicted()); - BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance()); - BOOST_CHECK_NE(ts1.filtered(), ts2.filtered()); - BOOST_CHECK_NE(ts1.filteredCovariance(), ts2.filteredCovariance()); - BOOST_CHECK_NE(ts1.smoothed(), ts2.smoothed()); - BOOST_CHECK_NE(ts1.smoothedCovariance(), ts2.smoothedCovariance()); - - BOOST_CHECK_NE(ts1.getUncalibratedSourceLink().get(), - ts2.getUncalibratedSourceLink().get()); - - visit_measurement(ts1.calibratedSize(), [&](auto N) { - constexpr size_t measdim = decltype(N)::value; - BOOST_CHECK_NE(ts1.calibrated(), ts2.calibrated()); - BOOST_CHECK_NE(ts1.calibratedCovariance(), - ts2.calibratedCovariance()); - }); - - BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize()); - BOOST_CHECK_NE(ts1.projector(), ts2.projector()); - - BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian()); - BOOST_CHECK_NE(ts1.chi2(), ts2.chi2()); - BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength()); - BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface()); - - ts1.copyFrom(ts2); - - BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted()); - BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance()); - BOOST_CHECK_EQUAL(ts1.filtered(), ts2.filtered()); - BOOST_CHECK_EQUAL(ts1.filteredCovariance(), ts2.filteredCovariance()); - BOOST_CHECK_EQUAL(ts1.smoothed(), ts2.smoothed()); - BOOST_CHECK_EQUAL(ts1.smoothedCovariance(), ts2.smoothedCovariance()); - - BOOST_CHECK_EQUAL(ts1.getUncalibratedSourceLink().get(), - ts2.getUncalibratedSourceLink().get()); - - visit_measurement(ts1.calibratedSize(), [&](auto N) { - constexpr size_t measdim = decltype(N)::value; - BOOST_CHECK_EQUAL(ts1.calibrated(), ts2.calibrated()); - BOOST_CHECK_EQUAL(ts1.calibratedCovariance(), - ts2.calibratedCovariance()); - }); - - BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize()); - BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector()); - - BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian()); - BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2()); - BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength()); - BOOST_CHECK_EQUAL(&ts1.referenceSurface(), &ts2.referenceSurface()); - - // full copy proven to work. now let's do partial copy - ts2 = mkts(PM::Predicted | PM::Jacobian | PM::Calibrated); - ts2.copyFrom(ots2, PM::Predicted | PM::Jacobian | PM::Calibrated); - // copy into empty ts, only copy some - ts1.copyFrom(ots1); // reset to original - // is different again - BOOST_CHECK_NE(ts1.predicted(), ts2.predicted()); - BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance()); - - visit_measurement(ts1.calibratedSize(), [&](auto N) { - constexpr size_t measdim = decltype(N)::value; - BOOST_CHECK_NE(ts1.calibrated(), ts2.calibrated()); - BOOST_CHECK_NE(ts1.calibratedCovariance(), - ts2.calibratedCovariance()); - }); - - BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize()); - BOOST_CHECK_NE(ts1.projector(), ts2.projector()); - - BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian()); - BOOST_CHECK_NE(ts1.chi2(), ts2.chi2()); - BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength()); - BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface()); - - ts1.copyFrom(ts2); - - // some components are same now - BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted()); - BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance()); - - visit_measurement(ts1.calibratedSize(), [&](auto N) { - constexpr size_t measdim = decltype(N)::value; - BOOST_CHECK_EQUAL(ts1.calibrated(), ts2.calibrated()); - BOOST_CHECK_EQUAL(ts1.calibratedCovariance(), - ts2.calibratedCovariance()); - }); - - BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize()); - BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector()); - - BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian()); - BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2()); // always copied - BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength()); // always copied - BOOST_CHECK_EQUAL(&ts1.referenceSurface(), - &ts2.referenceSurface()); // always copied + CommonTests ct; + ct.testTrackStateProxyCopy(rng); } BOOST_AUTO_TEST_CASE(TrackStateProxyCopyDiffMTJ) { - using PM = TrackStatePropMask; - - std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, - PM::Jacobian}; - - VectorMultiTrajectory mj; - VectorMultiTrajectory mj2; - auto mkts = [&](PM mask) { - auto r = mj.getTrackState(mj.addTrackState(mask)); - return r; - }; - auto mkts2 = [&](PM mask) { - auto r = mj2.getTrackState(mj2.addTrackState(mask)); - return r; - }; - - // orthogonal ones - for (PM a : values) { - for (PM b : values) { - auto tsa = mkts(a); - auto tsb = mkts2(b); - // doesn't work - if (a != b) { - BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error); - BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error); - } else { - tsa.copyFrom(tsb); - tsb.copyFrom(tsa); - } - } - } - - // make sure they are actually on different MultiTrajectories - BOOST_CHECK_EQUAL(mj.size(), values.size() * values.size()); - BOOST_CHECK_EQUAL(mj2.size(), values.size() * values.size()); - - auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both - ts1.filtered().setRandom(); - ts1.filteredCovariance().setRandom(); - ts1.predicted().setRandom(); - ts1.predictedCovariance().setRandom(); - - // ((src XOR dst) & src) == 0 - auto ts2 = mkts2(PM::Predicted); - ts2.predicted().setRandom(); - ts2.predictedCovariance().setRandom(); - - // they are different before - BOOST_CHECK(ts1.predicted() != ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); - - // ts1 -> ts2 fails - BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error); - BOOST_CHECK(ts1.predicted() != ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance()); - - // ts2 -> ts1 is ok - ts1.copyFrom(ts2); - BOOST_CHECK(ts1.predicted() == ts2.predicted()); - BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance()); - - { - BOOST_TEST_CHECKPOINT("Calib auto alloc"); - auto tsa = mkts(PM::All); - auto tsb = mkts(PM::All); - tsb.allocateCalibrated(5); - tsb.calibrated<5>().setRandom(); - tsb.calibratedCovariance<5>().setRandom(); - tsa.copyFrom(tsb, PM::All); - BOOST_CHECK_EQUAL(tsa.calibrated<5>(), tsb.calibrated<5>()); - BOOST_CHECK_EQUAL(tsa.calibratedCovariance<5>(), - tsb.calibratedCovariance<5>()); - } - - { - BOOST_TEST_CHECKPOINT("Copy none"); - auto tsa = mkts(PM::All); - auto tsb = mkts(PM::All); - tsa.copyFrom(tsb, PM::None); - } + CommonTests ct; + ct.testTrackStateProxyCopyDiffMTJ(); } BOOST_AUTO_TEST_CASE(ProxyAssignment) { - constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted; - VectorMultiTrajectory t; - auto i0 = t.addTrackState(kMask); - - VectorMultiTrajectory::TrackStateProxy tp = t.getTrackState(i0); // mutable - VectorMultiTrajectory::TrackStateProxy tp2{tp}; // mutable to mutable - VectorMultiTrajectory::ConstTrackStateProxy tp3{tp}; // mutable to const - // const to mutable: this won't compile - // MultiTrajectory::TrackStateProxy tp4{tp3}; + CommonTests ct; + ct.testProxyAssignment(); } -// Check if the copy from const does compile, assume the copy is done correctly BOOST_AUTO_TEST_CASE(CopyFromConst) { - using PM = TrackStatePropMask; - VectorMultiTrajectory mj; - - const auto idx_a = mj.addTrackState(PM::All); - const auto idx_b = mj.addTrackState(PM::All); - - VectorMultiTrajectory::TrackStateProxy mutableProxy = mj.getTrackState(idx_a); - - const VectorMultiTrajectory& cmj = mj; - VectorMultiTrajectory::ConstTrackStateProxy constProxy = - cmj.getTrackState(idx_b); - - mutableProxy.copyFrom(constProxy); - - // copy mutable to const: this won't compile - // constProxy.copyFrom(mutableProxy); + CommonTests ct; + ct.testCopyFromConst(); } BOOST_AUTO_TEST_CASE(TrackStateProxyShare) { - TestTrackState pc(rng, 2u); - - { - VectorMultiTrajectory traj; - size_t ia = traj.addTrackState(TrackStatePropMask::All); - size_t ib = traj.addTrackState(TrackStatePropMask::None); - - auto tsa = traj.getTrackState(ia); - auto tsb = traj.getTrackState(ib); - - fillTrackState(pc, TrackStatePropMask::All, tsa); - - BOOST_CHECK(tsa.hasPredicted()); - BOOST_CHECK(!tsb.hasPredicted()); - tsb.shareFrom(tsa, TrackStatePropMask::Predicted); - BOOST_CHECK(tsa.hasPredicted()); - BOOST_CHECK(tsb.hasPredicted()); - BOOST_CHECK_EQUAL(tsa.predicted(), tsb.predicted()); - BOOST_CHECK_EQUAL(tsa.predictedCovariance(), tsb.predictedCovariance()); - - BOOST_CHECK(tsa.hasFiltered()); - BOOST_CHECK(!tsb.hasFiltered()); - tsb.shareFrom(tsa, TrackStatePropMask::Filtered); - BOOST_CHECK(tsa.hasFiltered()); - BOOST_CHECK(tsb.hasFiltered()); - BOOST_CHECK_EQUAL(tsa.filtered(), tsb.filtered()); - BOOST_CHECK_EQUAL(tsa.filteredCovariance(), tsb.filteredCovariance()); - - BOOST_CHECK(tsa.hasSmoothed()); - BOOST_CHECK(!tsb.hasSmoothed()); - tsb.shareFrom(tsa, TrackStatePropMask::Smoothed); - BOOST_CHECK(tsa.hasSmoothed()); - BOOST_CHECK(tsb.hasSmoothed()); - BOOST_CHECK_EQUAL(tsa.smoothed(), tsb.smoothed()); - BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), tsb.smoothedCovariance()); - - BOOST_CHECK(tsa.hasJacobian()); - BOOST_CHECK(!tsb.hasJacobian()); - tsb.shareFrom(tsa, TrackStatePropMask::Jacobian); - BOOST_CHECK(tsa.hasJacobian()); - BOOST_CHECK(tsb.hasJacobian()); - BOOST_CHECK_EQUAL(tsa.jacobian(), tsb.jacobian()); - } - - { - VectorMultiTrajectory traj; - size_t i = traj.addTrackState(TrackStatePropMask::All & - ~TrackStatePropMask::Filtered & - ~TrackStatePropMask::Smoothed); - - auto ts = traj.getTrackState(i); - - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK(!ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - ts.predicted().setRandom(); - ts.predictedCovariance().setRandom(); - - ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Filtered); - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK(ts.hasFiltered()); - BOOST_CHECK(!ts.hasSmoothed()); - BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered()); - BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance()); - - ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Smoothed); - BOOST_CHECK(ts.hasPredicted()); - BOOST_CHECK(ts.hasFiltered()); - BOOST_CHECK(ts.hasSmoothed()); - BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered()); - BOOST_CHECK_EQUAL(ts.predicted(), ts.smoothed()); - BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance()); - BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.smoothedCovariance()); - } + CommonTests ct; + ct.testTrackStateProxyShare(rng); } BOOST_AUTO_TEST_CASE(MultiTrajectoryExtraColumns) { - using namespace HashedStringLiteral; - using MTJ = VectorMultiTrajectory; - - struct TestColumn { - double value; - }; - - MTJ traj; - traj.addColumn("extra_column"); - traj.addColumn("another_column"); - - auto ts1 = traj.getTrackState(traj.addTrackState()); - auto ts2 = traj.getTrackState( - traj.addTrackState(TrackStatePropMask::All, ts1.index())); - auto ts3 = traj.getTrackState( - traj.addTrackState(TrackStatePropMask::All, ts2.index())); - - BOOST_CHECK(ts1.has<"extra_column"_hash>()); - BOOST_CHECK(ts2.has<"extra_column"_hash>()); - BOOST_CHECK(ts3.has<"extra_column"_hash>()); - - BOOST_CHECK(ts1.has<"another_column"_hash>()); - BOOST_CHECK(ts2.has<"another_column"_hash>()); - BOOST_CHECK(ts3.has<"another_column"_hash>()); - - ts2.component() = 6; - - BOOST_CHECK_EQUAL((ts2.component()), 6); - - ts3.component().value = 7; - BOOST_CHECK_EQUAL((ts3.component().value), - 7); + CommonTests ct; + ct.testMultiTrajectoryExtraColumns(); } BOOST_AUTO_TEST_CASE(MultiTrajectoryExtraColumnsRuntime) { - auto runTest = [](auto&& fn) { - VectorMultiTrajectory mt; - std::vector columns = {"one", "two", "three", "four"}; - for (const auto& c : columns) { - BOOST_CHECK(!mt.hasColumn(fn(c))); - mt.addColumn(c); - BOOST_CHECK(mt.hasColumn(fn(c))); - } - for (const auto& c : columns) { - auto ts1 = mt.getTrackState(mt.addTrackState()); - auto ts2 = mt.getTrackState(mt.addTrackState()); - BOOST_CHECK(ts1.has(fn(c))); - BOOST_CHECK(ts2.has(fn(c))); - ts1.component(fn(c)) = 674; - ts2.component(fn(c)) = 421; - BOOST_CHECK_EQUAL(ts1.component(fn(c)), 674); - BOOST_CHECK_EQUAL(ts2.component(fn(c)), 421); - } - }; - - runTest([](const std::string& c) { return hashString(c.c_str()); }); - // runTest([](const std::string& c) { return c.c_str(); }); - // runTest([](const std::string& c) { return c; }); - // runTest([](std::string_view c) { return c; }); + CommonTests ct; + ct.testMultiTrajectoryExtraColumnsRuntime(); } BOOST_AUTO_TEST_CASE(MemoryStats) { @@ -1146,7 +213,7 @@ BOOST_AUTO_TEST_CASE(MemoryStats) { TestTrackState pc(rng, 2u); auto ts = mt.getTrackState(mt.addTrackState()); - fillTrackState(pc, TrackStatePropMask::All, ts); + fillTrackState(pc, TrackStatePropMask::All, ts); stats = mt.statistics(); diff --git a/Tests/UnitTests/Core/EventData/TrackContainerComplianceTests.cpp b/Tests/UnitTests/Core/EventData/TrackContainerComplianceTests.cpp new file mode 100644 index 00000000000..a4e18501ef5 --- /dev/null +++ b/Tests/UnitTests/Core/EventData/TrackContainerComplianceTests.cpp @@ -0,0 +1,92 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/EventData/VectorTrackContainer.hpp" +#include "Acts/Tests/CommonHelpers/NonCompileTestHelpers.hpp" +#include "Acts/Utilities/ThrowAssert.hpp" + +using namespace Acts; + +ACTS_DOES_NOT_COMPILE_SUITE_BEGIN(BuildFromConstRef) + +{ + VectorTrackContainer mutVtc; + VectorMultiTrajectory mutMtj; + + TrackContainer mutTc{mutVtc, mutMtj}; + static_assert(!mutTc.ReadOnly, "Unexpectedly read only"); + + auto t = mutTc.getTrack(mutTc.addTrack()); + t.appendTrackState(); + t.appendTrackState(); + t.appendTrackState(); + t = mutTc.getTrack(mutTc.addTrack()); + t.appendTrackState(); + + ConstVectorTrackContainer vtc{std::move(mutVtc)}; + ConstVectorMultiTrajectory mtj{std::move(mutMtj)}; + + TrackContainer ctc{vtc, mtj}; + ACTS_DOES_NOT_COMPILE_BEGIN(AddTrackToConstTrackContainer) + ctc.addTrack(); + ACTS_DOES_NOT_COMPILE_END() +} + +{ // const correctness + VectorTrackContainer vtc; + VectorMultiTrajectory mtj; + { + TrackContainer tc{vtc, mtj}; + + ACTS_DOES_NOT_COMPILE_BEGIN(TrackMutateConstProxyRef) + for (const auto track : tc) { + track.parameters().setRandom(); + } + ACTS_DOES_NOT_COMPILE_END() + } + + ConstVectorTrackContainer cvtc{std::move(vtc)}; + ConstVectorMultiTrajectory cmtj{std::move(mtj)}; + { + TrackContainer tc{cvtc, cmtj}; + + ACTS_DOES_NOT_COMPILE_BEGIN(TrackMutateConstProxy) + for (auto track : tc) { + track.parameters().setRandom(); + } + ACTS_DOES_NOT_COMPILE_END() + } +} + +{ + VectorTrackContainer vtc; + VectorMultiTrajectory mtj; + TrackContainer tc{vtc, mtj}; + auto t = tc.getTrack(tc.addTrack()); + (void)t; + + ConstTrackAccessor caccNMeasuements("nMeasurements"); + ACTS_DOES_NOT_COMPILE_BEGIN(ConstAccessorMutate) + caccNMeasuements(t) = 66; + ACTS_DOES_NOT_COMPILE_END() + + ACTS_DOES_NOT_COMPILE_BEGIN(MutationThroughContainerConstRef) + const auto& ctc = tc; + ctc.getTrack(idx).covariance().setRandom(); + ACTS_DOES_NOT_COMPILE_END() + + ACTS_DOES_NOT_COMPILE_BEGIN(MutationThroughProxyConstRef) + const auto& ctp = t; + ctp.covariance().setRandom(); + ACTS_DOES_NOT_COMPILE_END() +} + +ACTS_DOES_NOT_COMPILE_SUITE_END() diff --git a/Tests/UnitTests/Core/EventData/TrackStatePropMaskTests.cpp b/Tests/UnitTests/Core/EventData/TrackStatePropMaskTests.cpp new file mode 100644 index 00000000000..0ed82932bb9 --- /dev/null +++ b/Tests/UnitTests/Core/EventData/TrackStatePropMaskTests.cpp @@ -0,0 +1,91 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +#include "Acts/EventData/TrackStatePropMask.hpp" +#include "Acts/Utilities/Helpers.hpp" + +using namespace Acts; + +BOOST_AUTO_TEST_SUITE(TrackStatePropMaskTest) + +BOOST_AUTO_TEST_CASE(BitmaskOperators) { + using PM = TrackStatePropMask; + + auto bs1 = PM::Predicted; + + BOOST_CHECK(ACTS_CHECK_BIT(bs1, PM::Predicted)); + BOOST_CHECK(!ACTS_CHECK_BIT(bs1, PM::Calibrated)); + + auto bs2 = PM::Calibrated; + + BOOST_CHECK(!ACTS_CHECK_BIT(bs2, PM::Predicted)); + BOOST_CHECK(ACTS_CHECK_BIT(bs2, PM::Calibrated)); + + auto bs3 = PM::Calibrated | PM::Predicted; + + BOOST_CHECK(ACTS_CHECK_BIT(bs3, PM::Predicted)); + BOOST_CHECK(ACTS_CHECK_BIT(bs3, PM::Calibrated)); + + BOOST_CHECK(ACTS_CHECK_BIT(PM::All, PM::Predicted)); + BOOST_CHECK(ACTS_CHECK_BIT(PM::All, PM::Calibrated)); + + auto bs4 = PM::Predicted | PM::Jacobian | PM::Smoothed; + BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Predicted)); + BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Jacobian)); + BOOST_CHECK(ACTS_CHECK_BIT(bs4, PM::Smoothed)); + BOOST_CHECK(!ACTS_CHECK_BIT(bs4, PM::Calibrated)); + BOOST_CHECK(!ACTS_CHECK_BIT(bs4, PM::Filtered)); + + auto cnv = [](auto a) -> std::bitset<8> { + return static_cast::type>(a); + }; + + BOOST_CHECK(cnv(PM::All).all()); // all ones + BOOST_CHECK(cnv(PM::None).none()); // all zeros + + // test orthogonality + std::array values{PM::Predicted, PM::Filtered, PM::Smoothed, + PM::Jacobian, PM::Calibrated}; + for (size_t i = 0; i < values.size(); i++) { + for (size_t j = 0; j < values.size(); j++) { + PM a = values[i]; + PM b = values[j]; + + if (i == j) { + BOOST_CHECK(cnv(a & b).count() == 1); + } else { + BOOST_CHECK(cnv(a & b).none()); + } + } + } + + BOOST_CHECK(cnv(PM::Predicted ^ PM::Filtered).count() == 2); + BOOST_CHECK(cnv(PM::Predicted ^ PM::Predicted).none()); + BOOST_CHECK(~(PM::Predicted | PM::Calibrated) == + (PM::All ^ PM::Predicted ^ PM::Calibrated)); + + PM base = PM::None; + BOOST_CHECK(cnv(base) == 0); + + base &= PM::Filtered; + BOOST_CHECK(cnv(base) == 0); + + base |= PM::Filtered; + BOOST_CHECK(base == PM::Filtered); + + base |= PM::Calibrated; + BOOST_CHECK(base == (PM::Filtered | PM::Calibrated)); + + base ^= PM::All; + BOOST_CHECK(base == ~(PM::Filtered | PM::Calibrated)); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/EventData/TrackTests.cpp b/Tests/UnitTests/Core/EventData/TrackTests.cpp index d0805724b50..648b34c5b65 100644 --- a/Tests/UnitTests/Core/EventData/TrackTests.cpp +++ b/Tests/UnitTests/Core/EventData/TrackTests.cpp @@ -272,13 +272,13 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(TrackStateAccess, factory_t, holder_types) { auto ts = traj.getTrackState(traj.addTrackState(TrackStatePropMask::All, prev)); TestTrackState pc(rng, 2u); - fillTrackState(pc, TrackStatePropMask::All, ts); + fillTrackState(pc, TrackStatePropMask::All, ts); return ts; } else { auto ts = traj.getTrackState( traj.addTrackState(TrackStatePropMask::All, prev.index())); TestTrackState pc(rng, 2u); - fillTrackState(pc, TrackStatePropMask::All, ts); + fillTrackState(pc, TrackStatePropMask::All, ts); return ts; } }; diff --git a/Tests/UnitTests/Core/Geometry/AlignmentContextTests.cpp b/Tests/UnitTests/Core/Geometry/AlignmentContextTests.cpp index 027fd17266e..b6fa7923dc4 100644 --- a/Tests/UnitTests/Core/Geometry/AlignmentContextTests.cpp +++ b/Tests/UnitTests/Core/Geometry/AlignmentContextTests.cpp @@ -62,8 +62,7 @@ class AlignableDetectorElement : public DetectorElementBase { : DetectorElementBase(), m_elementTransform(std::move(transform)), m_elementThickness(thickness) { - auto mutableSurface = Surface::makeShared(pBounds, *this); - m_elementSurface = mutableSurface; + m_elementSurface = Surface::makeShared(pBounds, *this); } /// Destructor @@ -79,6 +78,9 @@ class AlignableDetectorElement : public DetectorElementBase { /// Return surface associated with this detector element const Surface& surface() const override; + /// Non-const access to the surface associated with this detector element + Surface& surface() override; + /// The maximal thickness of the detector element wrt normal axis double thickness() const override; @@ -86,7 +88,7 @@ class AlignableDetectorElement : public DetectorElementBase { /// the transform for positioning in 3D space std::shared_ptr m_elementTransform; /// the surface represented by it - std::shared_ptr m_elementSurface{nullptr}; + std::shared_ptr m_elementSurface{nullptr}; /// the element thickness double m_elementThickness{0.}; }; @@ -105,6 +107,10 @@ inline const Surface& AlignableDetectorElement::surface() const { return *m_elementSurface; } +inline Surface& AlignableDetectorElement::surface() { + return *m_elementSurface; +} + inline double AlignableDetectorElement::thickness() const { return m_elementThickness; } diff --git a/Tests/UnitTests/Core/Geometry/CuboidVolumeBoundsTests.cpp b/Tests/UnitTests/Core/Geometry/CuboidVolumeBoundsTests.cpp index f8adf9aa783..cfd3989a636 100644 --- a/Tests/UnitTests/Core/Geometry/CuboidVolumeBoundsTests.cpp +++ b/Tests/UnitTests/Core/Geometry/CuboidVolumeBoundsTests.cpp @@ -105,10 +105,9 @@ BOOST_AUTO_TEST_CASE(CuboidVolumeBoundarySurfaces) { for (auto& os : cvbOrientedSurfaces) { auto osCenter = os.first->center(geoCtx); auto osNormal = os.first->normal(geoCtx); - double nDir = (double)os.second; // Check if you step inside the volume with the oriented normal - auto insideBox = osCenter + nDir * osNormal; - auto outsideBox = osCenter - nDir * osNormal; + Vector3 insideBox = osCenter + os.second * osNormal; + Vector3 outsideBox = osCenter - os.second * osNormal; BOOST_CHECK(box.inside(insideBox)); BOOST_CHECK(!box.inside(outsideBox)); } diff --git a/Tests/UnitTests/Core/Geometry/CutoutCylinderVolumeBoundsTests.cpp b/Tests/UnitTests/Core/Geometry/CutoutCylinderVolumeBoundsTests.cpp index b548d458a85..330eec85250 100644 --- a/Tests/UnitTests/Core/Geometry/CutoutCylinderVolumeBoundsTests.cpp +++ b/Tests/UnitTests/Core/Geometry/CutoutCylinderVolumeBoundsTests.cpp @@ -192,10 +192,9 @@ BOOST_AUTO_TEST_CASE(CutoutCylinderVolumeOrientedBoundaries) { for (auto& os : ccvbOrientedSurfaces) { auto onSurface = os.first->binningPosition(geoCtx, binR); auto osNormal = os.first->normal(geoCtx, onSurface); - double nDir = (double)os.second; // Check if you step inside the volume with the oriented normal - auto insideCcvb = onSurface + nDir * osNormal; - auto outsideCCvb = onSurface - nDir * osNormal; + Vector3 insideCcvb = onSurface + os.second * osNormal; + Vector3 outsideCCvb = onSurface - os.second * osNormal; BOOST_CHECK(ccvb.inside(insideCcvb)); BOOST_CHECK(!ccvb.inside(outsideCCvb)); diff --git a/Tests/UnitTests/Core/Geometry/CylinderVolumeBoundsTests.cpp b/Tests/UnitTests/Core/Geometry/CylinderVolumeBoundsTests.cpp index 8c6c68d9b20..b8326d6f478 100644 --- a/Tests/UnitTests/Core/Geometry/CylinderVolumeBoundsTests.cpp +++ b/Tests/UnitTests/Core/Geometry/CylinderVolumeBoundsTests.cpp @@ -282,10 +282,9 @@ BOOST_AUTO_TEST_CASE(CylinderVolumeOrientedBoundaries) { for (auto& os : cvbOrientedSurfaces) { auto onSurface = os.first->binningPosition(geoCtx, binR); auto osNormal = os.first->normal(geoCtx, onSurface); - double nDir = (double)os.second; // Check if you step inside the volume with the oriented normal - auto insideCvb = onSurface + nDir * osNormal; - auto outsideCvb = onSurface - nDir * osNormal; + Vector3 insideCvb = onSurface + os.second * osNormal; + Vector3 outsideCvb = onSurface - os.second * osNormal; BOOST_CHECK(cvb.inside(insideCvb)); BOOST_CHECK(!cvb.inside(outsideCvb)); diff --git a/Tests/UnitTests/Core/Geometry/ExtentTests.cpp b/Tests/UnitTests/Core/Geometry/ExtentTests.cpp index c3477c3fac4..1ab39b18dac 100644 --- a/Tests/UnitTests/Core/Geometry/ExtentTests.cpp +++ b/Tests/UnitTests/Core/Geometry/ExtentTests.cpp @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(ExtentTest) { double phiMin = std::atan2(-3_mm, 15_mm); double phiMax = std::atan2(3_mm, 15_mm); - double rMin = std::sqrt(15_mm * 15_mm + 3_mm * 3_mm); + double rMin = std::hypot(15_mm, 3_mm); CHECK_CLOSE_ABS(gExt.min(binX), 15_mm, 1e-6); CHECK_CLOSE_ABS(gExt.max(binX), 18_mm, 1e-6); diff --git a/Tests/UnitTests/Core/Geometry/GenericCuboidVolumeBoundsTests.cpp b/Tests/UnitTests/Core/Geometry/GenericCuboidVolumeBoundsTests.cpp index 86a8f4b6a94..fa43b59fc79 100644 --- a/Tests/UnitTests/Core/Geometry/GenericCuboidVolumeBoundsTests.cpp +++ b/Tests/UnitTests/Core/Geometry/GenericCuboidVolumeBoundsTests.cpp @@ -200,10 +200,9 @@ BOOST_AUTO_TEST_CASE(GenericCuboidVolumeBoundarySurfaces) { auto geoCtx = GeometryContext(); auto osCenter = os.first->center(geoCtx); auto osNormal = os.first->normal(geoCtx); - double nDir = (double)os.second; // Check if you step inside the volume with the oriented normal - auto insideGcvb = osCenter + nDir * osNormal; - auto outsideGcvb = osCenter - nDir * osNormal; + Vector3 insideGcvb = osCenter + os.second * osNormal; + Vector3 outsideGcvb = osCenter - os.second * osNormal; BOOST_CHECK(cubo.inside(insideGcvb)); BOOST_CHECK(!cubo.inside(outsideGcvb)); } diff --git a/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp b/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp index 4f380ae23e3..70483813203 100644 --- a/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp +++ b/Tests/UnitTests/Core/Geometry/ProtoLayerTests.cpp @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(ProtoLayerTests) { CHECK_CLOSE_ABS(protoLayer.medium(binZ), 0., 1e-8); CHECK_CLOSE_ABS(protoLayer.min(binZ), -6., 1e-8); CHECK_CLOSE_ABS(protoLayer.max(binZ), 6., 1e-8); - CHECK_CLOSE_ABS(protoLayer.max(binR), std::sqrt(3 * 3 + 6 * 6), 1e-8); + CHECK_CLOSE_ABS(protoLayer.max(binR), std::hypot(3, 6), 1e-8); CHECK_CLOSE_ABS(protoLayer.min(binR), 3., 1e-8); // Test 1a @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(ProtoLayerTests) { CHECK_CLOSE_ABS(protoLayerRot.min(binZ), -6., 1e-8); CHECK_CLOSE_ABS(protoLayerRot.max(binZ), 6., 1e-8); CHECK_CLOSE_ABS(protoLayerRot.min(binR), 3., 1e-8); - CHECK_CLOSE_ABS(protoLayerRot.max(binR), std::sqrt(3 * 3 + 6 * 6), 1e-8); + CHECK_CLOSE_ABS(protoLayerRot.max(binR), std::hypot(3, 6), 1e-8); std::stringstream sstream; protoLayerRot.toStream(sstream); diff --git a/Tests/UnitTests/Core/Geometry/TrapezoidVolumeBoundsTests.cpp b/Tests/UnitTests/Core/Geometry/TrapezoidVolumeBoundsTests.cpp index 5a58329de86..4c59ad62fc5 100644 --- a/Tests/UnitTests/Core/Geometry/TrapezoidVolumeBoundsTests.cpp +++ b/Tests/UnitTests/Core/Geometry/TrapezoidVolumeBoundsTests.cpp @@ -59,10 +59,9 @@ BOOST_AUTO_TEST_CASE(TrapezoidVolumeBoundarySurfaces) { for (auto& os : tvbOrientedSurfaces) { auto osCenter = os.first->center(geoCtx); auto osNormal = os.first->normal(geoCtx, osCenter); - double nDir = (double)os.second; // Check if you step inside the volume with the oriented normal - auto insideTvb = osCenter + nDir * osNormal; - auto outsideTvb = osCenter - nDir * osNormal; + Vector3 insideTvb = osCenter + os.second * osNormal; + Vector3 outsideTvb = osCenter - os.second * osNormal; BOOST_CHECK(tvb.inside(insideTvb)); BOOST_CHECK(!tvb.inside(outsideTvb)); } diff --git a/Tests/UnitTests/Core/Material/HomogeneousSurfaceMaterialTests.cpp b/Tests/UnitTests/Core/Material/HomogeneousSurfaceMaterialTests.cpp index ecf003b40dd..40e732ade63 100644 --- a/Tests/UnitTests/Core/Material/HomogeneousSurfaceMaterialTests.cpp +++ b/Tests/UnitTests/Core/Material/HomogeneousSurfaceMaterialTests.cpp @@ -83,8 +83,8 @@ BOOST_AUTO_TEST_CASE(HomogeneousSurfaceMaterial_access_test) { BOOST_CHECK_EQUAL(mat, mat3d); BOOST_CHECK_EQUAL(mat, matbin); - NavigationDirection fDir = NavigationDirection::Forward; - NavigationDirection bDir = NavigationDirection::Backward; + Direction fDir = Direction::Forward; + Direction bDir = Direction::Backward; MaterialUpdateStage pre = MaterialUpdateStage::PreUpdate; MaterialUpdateStage full = MaterialUpdateStage::FullUpdate; diff --git a/Tests/UnitTests/Core/Material/ISurfaceMaterialTests.cpp b/Tests/UnitTests/Core/Material/ISurfaceMaterialTests.cpp index 14f13254a17..727d63cef0c 100644 --- a/Tests/UnitTests/Core/Material/ISurfaceMaterialTests.cpp +++ b/Tests/UnitTests/Core/Material/ISurfaceMaterialTests.cpp @@ -48,29 +48,27 @@ BOOST_AUTO_TEST_CASE(ISurfaceMaterial_factor_test) { double splitFactor = 42.0; SurfaceMaterialStub stub{splitFactor}; - BOOST_CHECK_EQUAL(stub.factor(NavigationDirection::Forward, - MaterialUpdateStage::FullUpdate), - 1.0); + BOOST_CHECK_EQUAL( + stub.factor(Direction::Forward, MaterialUpdateStage::FullUpdate), 1.0); - BOOST_CHECK_EQUAL(stub.factor(NavigationDirection::Backward, - MaterialUpdateStage::FullUpdate), - 1.0); + BOOST_CHECK_EQUAL( + stub.factor(Direction::Backward, MaterialUpdateStage::FullUpdate), 1.0); - BOOST_CHECK_EQUAL(stub.factor(NavigationDirection::Forward, - MaterialUpdateStage::PostUpdate), - splitFactor); + BOOST_CHECK_EQUAL( + stub.factor(Direction::Forward, MaterialUpdateStage::PostUpdate), + splitFactor); - BOOST_CHECK_EQUAL(stub.factor(NavigationDirection::Backward, - MaterialUpdateStage::PreUpdate), - splitFactor); + BOOST_CHECK_EQUAL( + stub.factor(Direction::Backward, MaterialUpdateStage::PreUpdate), + splitFactor); BOOST_CHECK_EQUAL( - stub.factor(NavigationDirection::Forward, MaterialUpdateStage::PreUpdate), + stub.factor(Direction::Forward, MaterialUpdateStage::PreUpdate), 1 - splitFactor); - BOOST_CHECK_EQUAL(stub.factor(NavigationDirection::Backward, - MaterialUpdateStage::PostUpdate), - 1 - splitFactor); + BOOST_CHECK_EQUAL( + stub.factor(Direction::Backward, MaterialUpdateStage::PostUpdate), + 1 - splitFactor); } } // namespace Test diff --git a/Tests/UnitTests/Core/Navigation/NextNavigatorTests.cpp b/Tests/UnitTests/Core/Navigation/NextNavigatorTests.cpp index 493ccffaa4d..618f65c87f3 100644 --- a/Tests/UnitTests/Core/Navigation/NextNavigatorTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NextNavigatorTests.cpp @@ -9,7 +9,6 @@ #include #include "Acts/Detector/Portal.hpp" -#include "Acts/Detector/PortalHelper.hpp" #include "Acts/Geometry/CuboidVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/ConstantBField.hpp" diff --git a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp index a447825ffa2..fcfb03dfd28 100644 --- a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp @@ -54,7 +54,7 @@ static constexpr auto eps = 1024 * std::numeric_limits::epsilon(); // propagation settings static constexpr auto stepSize = 10_mm; static constexpr auto tolerance = 10_um; -static constexpr NavigationDirection navDir = NavigationDirection::Backward; +static constexpr Direction navDir = Direction::Backward; static auto magneticField = std::make_shared(Vector3(0.1_T, -0.2_T, 2_T)); @@ -336,13 +336,13 @@ BOOST_AUTO_TEST_CASE(Reset) { newAbsMom, newCharge, newCov); FreeVector freeParams = detail::transformBoundToFreeParameters( cp.referenceSurface(), geoCtx, cp.parameters()); - NavigationDirection ndir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; double stepSize = -256.; auto copyState = [&](auto& field, const auto& other) { using field_t = std::decay_t; std::decay_t copy(geoCtx, field.makeCache(magCtx), cp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); copy.state_ready = other.state_ready; copy.navDir = other.navDir; @@ -382,7 +382,7 @@ BOOST_AUTO_TEST_CASE(Reset) { Stepper::State stateCopy(copyState(*magneticField, state.stepping)); BOOST_CHECK(cp.covariance().has_value()); stepper.resetState(stateCopy, cp.parameters(), *cp.covariance(), - cp.referenceSurface(), ndir, stepSize); + cp.referenceSurface(), navDir, stepSize); // Test all components BOOST_CHECK(stateCopy.covTransport); BOOST_CHECK_EQUAL(*stateCopy.covariance, newCov); @@ -394,9 +394,9 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(stateCopy.navDir, navDir); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); - BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), ndir * stepSize); + BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(stateCopy.previousStepSize, state.stepping.previousStepSize); BOOST_CHECK_EQUAL(stateCopy.tolerance, state.stepping.tolerance); @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(Reset) { // Reset all possible parameters except the step size stateCopy = copyState(*magneticField, state.stepping); stepper.resetState(stateCopy, cp.parameters(), *cp.covariance(), - cp.referenceSurface(), ndir); + cp.referenceSurface(), navDir); // Test all components BOOST_CHECK(stateCopy.covTransport); BOOST_CHECK_EQUAL(*stateCopy.covariance, newCov); @@ -416,7 +416,7 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(stateCopy.navDir, navDir); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), std::numeric_limits::max()); @@ -439,7 +439,7 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, NavigationDirection::Forward); + BOOST_CHECK_EQUAL(stateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), std::numeric_limits::max()); @@ -473,7 +473,7 @@ BOOST_AUTO_TEST_CASE(Reset) { // 2) Perigee surface // Setting some parameters - newPos << 1.5, -2.5, 3.5; + newPos << -2.06155, -2.06155, 3.5; newAbsMom *= 0.45; newTime = 2.3; newCharge = 1.; diff --git a/Tests/UnitTests/Core/Propagator/JacobianEngineTests.cpp b/Tests/UnitTests/Core/Propagator/JacobianEngineTests.cpp index 0d9206d6108..7f7a6a543c4 100644 --- a/Tests/UnitTests/Core/Propagator/JacobianEngineTests.cpp +++ b/Tests/UnitTests/Core/Propagator/JacobianEngineTests.cpp @@ -57,8 +57,7 @@ BOOST_AUTO_TEST_CASE(jacobian_engine_helper) { sinTheta = std::sin(theta); cosTheta = std::cos(theta); - const ActsScalar c = - std::sqrt(direction.y() * direction.y() + direction.z() * direction.z()); + const ActsScalar c = std::hypot(direction.y(), direction.z()); const ActsScalar invC = 1. / c; CHECK_CLOSE_REL(f2cJacobian(eBoundLoc0, eFreePos1), -direction.z() * invC, 1e-5); diff --git a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp index 4c5fa98ed89..8cda2cc4661 100644 --- a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp @@ -44,7 +44,7 @@ struct SteppingState { Vector3 dir = Vector3(0., 0., 1); double p = 100_MeV; - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; }; /// @brief mockup of stepping state diff --git a/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp b/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp index c8a5163a18b..1bb2d66fa22 100644 --- a/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp @@ -157,7 +157,7 @@ void runTest(const propagator_t& prop, double pT, double phi, double theta, Options bwdOptions(tgContext, mfContext); bwdOptions.maxStepSize = -25_cm; bwdOptions.pathLimit = -25_cm; - bwdOptions.direction = NavigationDirection::Backward; + bwdOptions.direction = Direction::Backward; // get the material collector and configure it auto& bwdMaterialInteractor = @@ -293,7 +293,7 @@ void runTest(const propagator_t& prop, double pT, double phi, double theta, bwdStepOptions.maxStepSize = -25_cm; bwdStepOptions.pathLimit = -25_cm; - bwdStepOptions.direction = NavigationDirection::Backward; + bwdStepOptions.direction = Direction::Backward; // get the material collector and configure it auto& bwdStepMaterialInteractor = diff --git a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp index 96b3401553d..117ea00f4a3 100644 --- a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp @@ -29,7 +29,7 @@ using SingleStepper = EigenStepper>; const double defaultStepSize = 123.; const double defaultTolerance = 234.; -const auto defaultNDir = NavigationDirection::Backward; +const auto defaultNDir = Direction::Backward; const auto defaultBField = std::make_shared(Vector3(1., 2.5, 33.33)); @@ -336,7 +336,7 @@ void test_components_modifying_accessors() { [](auto &cmp) -> decltype(auto) { return cmp.jacToGlobal(); }); std::apply( - [&](const auto &... projs) { + [&](const auto &...projs) { // clang-format off ( [&]() { modify(projs); check(projs); }(), ...); // clang-format on @@ -380,11 +380,10 @@ void test_multi_stepper_surface_status_update() { .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10)); MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars, - NavigationDirection::Forward, defaultStepSize, - defaultTolerance); + Direction::Forward, defaultStepSize, defaultTolerance); SingleStepper::State single_state( geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]), - NavigationDirection::Forward, defaultStepSize, defaultTolerance); + Direction::Forward, defaultStepSize, defaultTolerance); MultiStepper multi_stepper(defaultNullBField); SingleStepper single_stepper(defaultNullBField); @@ -481,11 +480,10 @@ void test_component_bound_state() { .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10)); MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars, - NavigationDirection::Forward, defaultStepSize, - defaultTolerance); + Direction::Forward, defaultStepSize, defaultTolerance); SingleStepper::State single_state( geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]), - NavigationDirection::Forward, defaultStepSize, defaultTolerance); + Direction::Forward, defaultStepSize, defaultTolerance); MultiStepper multi_stepper(defaultNullBField); SingleStepper single_stepper(defaultNullBField); diff --git a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp index 05f855b722f..80e32cd0273 100644 --- a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp @@ -70,7 +70,7 @@ struct PropagatorState { double q = 0; /// the navigation direction - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; // accummulated path length cache double pathAccumulated = 0.; @@ -90,7 +90,7 @@ struct PropagatorState { /// State resetter void resetState(State& /*state*/, const BoundVector& /*boundParams*/, const BoundSymMatrix& /*cov*/, const Surface& /*surface*/, - const NavigationDirection /*navDir*/, + const Direction /*navDir*/, const double /*stepSize*/) const {} /// Global particle position accessor @@ -208,6 +208,8 @@ struct PropagatorState { size_t debugMsgWidth = 50; const Acts::Logger& logger = Acts::getDummyLogger(); + + ActsScalar targetTolerance = s_onSurfaceTolerance; }; /// Navigation cache: the start surface diff --git a/Tests/UnitTests/Core/Propagator/StepperTests.cpp b/Tests/UnitTests/Core/Propagator/StepperTests.cpp index eb7638a260e..4f6fb1dc506 100644 --- a/Tests/UnitTests/Core/Propagator/StepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/StepperTests.cpp @@ -148,7 +148,7 @@ struct StepCollector { /// These tests are aiming to test whether the state setup is working properly BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { // Set up some variables - NavigationDirection ndir = NavigationDirection::Backward; + Direction navDir = Direction::Backward; double stepSize = 123.; double tolerance = 234.; auto bField = std::make_shared(Vector3(1., 2.5, 33.33)); @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { // Test charged parameters without covariance matrix CurvilinearTrackParameters cp(makeVector4(pos, time), dir, charge / absMom); EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); EigenStepper<> es(bField); @@ -172,9 +172,9 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { BOOST_CHECK_EQUAL(esState.derivative, FreeVector::Zero()); BOOST_CHECK(!esState.covTransport); BOOST_CHECK_EQUAL(esState.cov, Covariance::Zero()); - BOOST_CHECK_EQUAL(esState.navDir, ndir); + BOOST_CHECK_EQUAL(esState.navDir, navDir); BOOST_CHECK_EQUAL(esState.pathAccumulated, 0.); - BOOST_CHECK_EQUAL(esState.stepSize.value(), ndir * stepSize); + BOOST_CHECK_EQUAL(esState.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(esState.previousStepSize, 0.); BOOST_CHECK_EQUAL(esState.tolerance, tolerance); @@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { NeutralCurvilinearTrackParameters ncp(makeVector4(pos, time), dir, 1 / absMom); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); BOOST_CHECK_EQUAL(es.charge(esState), 0.); // Test with covariance matrix @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { ncp = NeutralCurvilinearTrackParameters(makeVector4(pos, time), dir, 1 / absMom, cov); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); BOOST_CHECK_NE(esState.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK(esState.covTransport); BOOST_CHECK_EQUAL(esState.cov, cov); @@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { /// The numerical correctness of the stepper is tested in the integration tests BOOST_AUTO_TEST_CASE(eigen_stepper_test) { // Set up some variables for the state - NavigationDirection ndir = NavigationDirection::Backward; + Direction navDir = Direction::Backward; double stepSize = 123.; double tolerance = 234.; auto bField = std::make_shared(Vector3(1., 2.5, 33.33)); @@ -218,7 +218,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { // Build the state and the stepper EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); EigenStepper<> es(bField); // Test the getters @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { const std::string originalStepSize = esState.stepSize.toString(); es.setStepSize(esState, 1337.); - BOOST_CHECK_EQUAL(esState.previousStepSize, ndir * stepSize); + BOOST_CHECK_EQUAL(esState.previousStepSize, navDir * stepSize); BOOST_CHECK_EQUAL(esState.stepSize.value(), 1337.); es.releaseStepSize(esState); @@ -310,13 +310,13 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { charge2, cov2); FreeVector freeParams = detail::transformBoundToFreeParameters( cp2.referenceSurface(), tgContext, cp2.parameters()); - ndir = NavigationDirection::Forward; + navDir = Direction::Forward; double stepSize2 = -2. * stepSize; auto copyState = [&](auto& field, const auto& state) { using field_t = std::decay_t; std::decay_t copy(tgContext, field.makeCache(mfContext), - cp, ndir, stepSize, tolerance); + cp, navDir, stepSize, tolerance); copy.pars = state.pars; copy.q = state.q; copy.covTransport = state.covTransport; @@ -347,7 +347,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { EigenStepper<>::State esStateCopy(copyState(*bField, ps.stepping)); BOOST_CHECK(cp2.covariance().has_value()); es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), ndir, stepSize2); + cp2.referenceSurface(), navDir, stepSize2); // Test all components BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -363,16 +363,16 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(esStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); - BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), ndir * stepSize2); + BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), navDir * stepSize2); BOOST_CHECK_EQUAL(esStateCopy.previousStepSize, ps.stepping.previousStepSize); BOOST_CHECK_EQUAL(esStateCopy.tolerance, ps.stepping.tolerance); // Reset all possible parameters except the step size esStateCopy = copyState(*bField, ps.stepping); es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), ndir); + cp2.referenceSurface(), navDir); // Test all components BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -388,7 +388,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(esStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, NavigationDirection::Forward); + BOOST_CHECK_EQUAL(esStateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -428,13 +428,13 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { dir, charge / absMom, cov) .value(); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), cp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); // Test the intersection in the context of a surface auto targetSurface = - Surface::makeShared(pos + ndir * 2. * dir, dir); + Surface::makeShared(pos + navDir * 2. * dir, dir); es.updateSurfaceStatus(esState, *targetSurface, BoundaryCheck(false)); - CHECK_CLOSE_ABS(esState.stepSize.value(ConstrainedStep::actor), ndir * 2., + CHECK_CLOSE_ABS(esState.stepSize.value(ConstrainedStep::actor), navDir * 2., eps); // Test the step size modification in the context of a surface @@ -444,7 +444,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { esState.navDir * es.direction(esState), false), false); CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps); - esState.stepSize.setValue(ndir * stepSize); + esState.stepSize.setValue(navDir * stepSize); es.updateStepSize( esState, targetSurface->intersect(esState.geoContext, es.position(esState), @@ -499,7 +499,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { auto nBfield = std::make_shared(); EigenStepper<> nes(nBfield); EigenStepper<>::State nesState(tgContext, nBfield->makeCache(mfContext), cp, - ndir, stepSize, tolerance); + navDir, stepSize, tolerance); PropState nps(copyState(*nBfield, nesState)); // Test that we can reach the minimum step size nps.options.tolerance = 1e-21; diff --git a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp index 6b3fd69c5f4..1b9c0c52715 100644 --- a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { // Set up some variables GeometryContext tgContext = GeometryContext(); MagneticFieldContext mfContext = MagneticFieldContext(); - NavigationDirection ndir = NavigationDirection::Backward; + Direction navDir = Direction::Backward; double stepSize = 123.; double tolerance = 234.; @@ -61,8 +61,8 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { // Test charged parameters without covariance matrix CurvilinearTrackParameters cp(makeVector4(pos, time), dir, absMom, charge); - StraightLineStepper::State slsState(tgContext, mfContext, cp, ndir, stepSize, - tolerance); + StraightLineStepper::State slsState(tgContext, mfContext, cp, navDir, + stepSize, tolerance); StraightLineStepper sls; @@ -77,16 +77,16 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { CHECK_CLOSE_REL(sls.momentum(slsState), absMom, eps); BOOST_CHECK_EQUAL(sls.charge(slsState), charge); CHECK_CLOSE_OR_SMALL(sls.time(slsState), time, eps, eps); - BOOST_CHECK_EQUAL(slsState.navDir, ndir); + BOOST_CHECK_EQUAL(slsState.navDir, navDir); BOOST_CHECK_EQUAL(slsState.pathAccumulated, 0.); - BOOST_CHECK_EQUAL(slsState.stepSize.value(), ndir * stepSize); + BOOST_CHECK_EQUAL(slsState.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(slsState.previousStepSize, 0.); BOOST_CHECK_EQUAL(slsState.tolerance, tolerance); // Test without charge and covariance matrix NeutralCurvilinearTrackParameters ncp(makeVector4(pos, time), dir, 1 / absMom); - slsState = StraightLineStepper::State(tgContext, mfContext, ncp, ndir, + slsState = StraightLineStepper::State(tgContext, mfContext, ncp, navDir, stepSize, tolerance); BOOST_CHECK_EQUAL(slsState.q, 0.); @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { Covariance cov = 8. * Covariance::Identity(); ncp = NeutralCurvilinearTrackParameters(makeVector4(pos, time), dir, 1 / absMom, cov); - slsState = StraightLineStepper::State(tgContext, mfContext, ncp, ndir, + slsState = StraightLineStepper::State(tgContext, mfContext, ncp, navDir, stepSize, tolerance); BOOST_CHECK_NE(slsState.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK(slsState.covTransport); @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { // Set up some variables for the state GeometryContext tgContext = GeometryContext(); MagneticFieldContext mfContext = MagneticFieldContext(); - NavigationDirection ndir = NavigationDirection::Backward; + Direction navDir = Direction::Backward; double stepSize = 123.; double tolerance = 234.; @@ -122,8 +122,8 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { cov); // Build the state and the stepper - StraightLineStepper::State slsState(tgContext, mfContext, cp, ndir, stepSize, - tolerance); + StraightLineStepper::State slsState(tgContext, mfContext, cp, navDir, + stepSize, tolerance); StraightLineStepper sls; // Test the getters @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { const std::string originalStepSize = slsState.stepSize.toString(); sls.setStepSize(slsState, 1337.); - BOOST_CHECK_EQUAL(slsState.previousStepSize, ndir * stepSize); + BOOST_CHECK_EQUAL(slsState.previousStepSize, navDir * stepSize); BOOST_CHECK_EQUAL(slsState.stepSize.value(), 1337.); sls.releaseStepSize(slsState); @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { ps.stepping.covTransport = false; double h = sls.step(ps, mockNavigator).value(); - BOOST_CHECK_EQUAL(ps.stepping.stepSize.value(), ndir * stepSize); + BOOST_CHECK_EQUAL(ps.stepping.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(ps.stepping.stepSize.value(), h); CHECK_CLOSE_COVARIANCE(ps.stepping.cov, cov, 1e-6); BOOST_CHECK_GT(sls.position(ps.stepping).norm(), newPos.norm()); @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { ps.stepping.covTransport = true; double h2 = sls.step(ps, mockNavigator).value(); - BOOST_CHECK_EQUAL(ps.stepping.stepSize.value(), ndir * stepSize); + BOOST_CHECK_EQUAL(ps.stepping.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(h2, h); CHECK_CLOSE_COVARIANCE(ps.stepping.cov, cov, 1e-6); BOOST_CHECK_GT(sls.position(ps.stepping).norm(), newPos.norm()); @@ -221,13 +221,13 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { BOOST_CHECK(cp2.covariance().has_value()); FreeVector freeParams = detail::transformBoundToFreeParameters( cp2.referenceSurface(), tgContext, cp2.parameters()); - ndir = NavigationDirection::Forward; + navDir = Direction::Forward; double stepSize2 = -2. * stepSize; // Reset all possible parameters StraightLineStepper::State slsStateCopy(ps.stepping); sls.resetState(slsStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), ndir, stepSize2); + cp2.referenceSurface(), navDir, stepSize2); // Test all components BOOST_CHECK_NE(slsStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(slsStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -243,9 +243,9 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(slsStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); - BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), ndir * stepSize2); + BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), navDir * stepSize2); BOOST_CHECK_EQUAL(slsStateCopy.previousStepSize, ps.stepping.previousStepSize); BOOST_CHECK_EQUAL(slsStateCopy.tolerance, ps.stepping.tolerance); @@ -253,7 +253,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { // Reset all possible parameters except the step size slsStateCopy = ps.stepping; sls.resetState(slsStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), ndir); + cp2.referenceSurface(), navDir); // Test all components BOOST_CHECK_NE(slsStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(slsStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, ndir); + BOOST_CHECK_EQUAL(slsStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, NavigationDirection::Forward); + BOOST_CHECK_EQUAL(slsStateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -310,14 +310,14 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { BoundTrackParameters::create(plane, tgContext, makeVector4(pos, time), dir, charge / absMom, cov) .value(); - slsState = StraightLineStepper::State(tgContext, mfContext, cp, ndir, + slsState = StraightLineStepper::State(tgContext, mfContext, cp, navDir, stepSize, tolerance); // Test the intersection in the context of a surface auto targetSurface = - Surface::makeShared(pos + ndir * 2. * dir, dir); + Surface::makeShared(pos + navDir * 2. * dir, dir); sls.updateSurfaceStatus(slsState, *targetSurface, BoundaryCheck(false)); - CHECK_CLOSE_ABS(slsState.stepSize.value(ConstrainedStep::actor), ndir * 2., + CHECK_CLOSE_ABS(slsState.stepSize.value(ConstrainedStep::actor), navDir * 2., 1e-6); // Test the step size modification in the context of a surface @@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { slsState.navDir * sls.direction(slsState), false), false); CHECK_CLOSE_ABS(slsState.stepSize.value(), 2, 1e-6); - slsState.stepSize.setValue(ndir * stepSize); + slsState.stepSize.setValue(navDir * stepSize); sls.updateStepSize(slsState, targetSurface->intersect( slsState.geoContext, sls.position(slsState), diff --git a/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp b/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp index ebe63f0c107..66b5cc65dbb 100644 --- a/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp @@ -27,7 +27,7 @@ struct StepperState { Vector3 pos, dir; double t = 0, p = 0, q = 0; bool covTransport = false; - NavigationDirection navDir = NavigationDirection::Forward; + Direction navDir = Direction::Forward; }; /// @brief Simplified navigator @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(volume_material_interaction_test) { state.stepping.p = 8.; state.stepping.q = 9.; state.stepping.covTransport = true; - state.stepping.navDir = NavigationDirection::Backward; + state.stepping.navDir = Direction::Backward; state.options.mass = 10.; state.options.absPdgCode = 11; state.navigation.currentVolume = volume.get(); @@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(volume_material_interaction_test) { BOOST_CHECK_EQUAL(volMatInt.pdg, state.options.absPdgCode); BOOST_CHECK_EQUAL(volMatInt.performCovarianceTransport, state.stepping.covTransport); - BOOST_CHECK_EQUAL(volMatInt.nav, state.stepping.navDir); + BOOST_CHECK_EQUAL(volMatInt.navDir, state.stepping.navDir); // Evaluate the material bool result = volMatInt.evaluateMaterialSlab(state, navigator); diff --git a/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp b/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp index 65fa9abb067..03e27cfde2b 100644 --- a/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp +++ b/Tests/UnitTests/Core/Seeding/SeedFinderTest.cpp @@ -43,7 +43,7 @@ std::vector readFile(const std::string& filename) { float x = 0, y = 0, z = 0, r = 0, varianceR = 0, varianceZ = 0; if (linetype == "lxyz") { ss >> layer >> x >> y >> z >> varianceR >> varianceZ; - r = std::sqrt(x * x + y * y); + r = std::hypot(x, y); float f22 = varianceR; float wid = varianceZ; float cov = wid * wid * .08333; diff --git a/Tests/UnitTests/Core/Surfaces/IntersectionHelper2DTests.cpp b/Tests/UnitTests/Core/Surfaces/IntersectionHelper2DTests.cpp index 8ba038567f6..a350c8cf249 100644 --- a/Tests/UnitTests/Core/Surfaces/IntersectionHelper2DTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/IntersectionHelper2DTests.cpp @@ -11,7 +11,7 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Tolerance.hpp" #include "Acts/Surfaces/detail/IntersectionHelper2D.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" diff --git a/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp b/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp index e81311c0cc9..0b225d74e1b 100644 --- a/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp @@ -17,8 +17,11 @@ #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Tests/CommonHelpers/LineSurfaceStub.hpp" #include "Acts/Tests/CommonHelpers/PredefinedMaterials.hpp" +#include "Acts/Utilities/UnitVectors.hpp" +#include #include +#include namespace utf = boost::unit_test; @@ -197,6 +200,58 @@ BOOST_AUTO_TEST_CASE(LineSurfaceAlignment) { CHECK_CLOSE_ABS(loc3DToLocBound, expLoc3DToLocBound, 1e-10); } +BOOST_AUTO_TEST_CASE(LineSurfaceTransformRoundTrip) { + LineSurfaceStub surface(Transform3::Identity()); + + auto roundTrip = [&surface](const Vector3& pos, const Vector3& dir) { + auto intersection = surface.intersect(tgContext, pos, dir); + Vector3 global = intersection.intersection.position; + Vector2 local = *surface.globalToLocal(tgContext, global, dir); + Vector3 global2 = surface.localToGlobal(tgContext, local, dir); + return std::make_tuple(global, local, global2); + }; + + { + Vector3 pos = {-0.02801, 0.00475611, 0.285106}; + Vector3 dir = Vector3(-0.03951, -0.221457, -0.564298).normalized(); + + auto [global, local, global2] = roundTrip(pos, dir); + + CHECK_CLOSE_ABS(global, global2, 1e-10); + } + + { + Vector3 pos = {-64.2892, 65.2697, -0.839014}; + Vector3 dir = Vector3(-0.236602, -0.157616, 0.956786).normalized(); + + auto [global, local, global2] = roundTrip(pos, dir); + + CHECK_CLOSE_ABS(global, global2, 1e-10); + } +} + +BOOST_AUTO_TEST_CASE(LineSurfaceTransformRoundTripEtaStability) { + LineSurfaceStub surface(Transform3::Identity()); + + // eta=6 is already crashing + const std::vector etas = {0, 1, 2, 3, 4, 5}; + + for (double eta : etas) { + Vector3 pca = {5, 0, 0}; + Vector3 dir = makeDirectionUnitFromPhiEta(M_PI_2, eta); + Vector3 pos = pca + dir; + + auto intersection = surface.intersect(tgContext, pos, dir); + + Vector3 global = intersection.intersection.position; + Vector2 local = *surface.globalToLocal(tgContext, global, dir); + Vector3 global2 = surface.localToGlobal(tgContext, local, dir); + + CHECK_CLOSE_ABS(global, global2, 1e-10); + CHECK_CLOSE_ABS(pca, global2, 1e-10); + } +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Test diff --git a/Tests/UnitTests/Core/Surfaces/PlaneSurfaceTests.cpp b/Tests/UnitTests/Core/Surfaces/PlaneSurfaceTests.cpp index c7a416d128a..dbd203c78c1 100644 --- a/Tests/UnitTests/Core/Surfaces/PlaneSurfaceTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/PlaneSurfaceTests.cpp @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(PlaneSurfaceExtent) { CHECK_CLOSE_ABS(planeExtent.min(binY), yPs, s_onSurfaceTolerance); CHECK_CLOSE_ABS(planeExtent.max(binY), yPs, s_onSurfaceTolerance); CHECK_CLOSE_ABS(planeExtent.min(binR), yPs, s_onSurfaceTolerance); - CHECK_CLOSE_ABS(planeExtent.max(binR), std::sqrt(yPs * yPs + rHy * rHy), + CHECK_CLOSE_ABS(planeExtent.max(binR), std::hypot(yPs, rHy), s_onSurfaceTolerance); // Now rotate diff --git a/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp b/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp index 01d2d9f085b..cb5462f2ceb 100644 --- a/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/PolyhedronSurfacesTests.cpp @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(PlaneSurfacePolyhedrons) { CHECK_CLOSE_ABS((extent.range(binY).max() rhY, 1e-6); CHECK_CLOSE_ABS((extent.range(binR).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binR).max() - std::sqrt(rhX * rhX + rhY * rhY), 1e-6); + std::hypot(rhX, rhY), 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).max() 0., 1e-6); BOOST_CHECK(rectangularPh.vertices.size() == 4); @@ -440,7 +440,7 @@ BOOST_AUTO_TEST_CASE(PlaneSurfacePolyhedrons) { CHECK_CLOSE_ABS((extent.range(binY).max() thY, 1e-6); CHECK_CLOSE_ABS((extent.range(binR).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binR).max() - std::sqrt(thX * thX + thY * thY), 1e-6); + std::hypot(thX, thY), 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).max() 0., 1e-6); BOOST_CHECK(trapezoidalPh.vertices.size() == 4); @@ -515,7 +515,7 @@ BOOST_AUTO_TEST_CASE(PlaneSurfacePolyhedrons) { CHECK_CLOSE_ABS((extent.range(binY).max() hMaxY, 1e-6); CHECK_CLOSE_ABS((extent.range(binR).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binR).max() - std::sqrt(hMaxX * hMaxX + hMaxY * hMaxY), 1e-6); + std::hypot(hMaxX, hMaxY), 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).min() 0., 1e-6); CHECK_CLOSE_ABS((extent.range(binZ).max() 0., 1e-6); } diff --git a/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp b/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp index 7de3f74312e..b2bc7422f80 100644 --- a/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp +++ b/Tests/UnitTests/Core/Surfaces/SurfaceStub.hpp @@ -83,7 +83,8 @@ class SurfaceStub : public Surface { SurfaceIntersection intersect(const GeometryContext& /*gctx*/, const Vector3& /*position*/, const Vector3& /*direction*/, - const BoundaryCheck& /*bcheck*/) const final { + const BoundaryCheck& /*bcheck*/, + const ActsScalar /*tolerance*/) const final { Intersection3D stubIntersection(Vector3(20., 0., 0.), 20., Intersection3D::Status::reachable); return SurfaceIntersection(stubIntersection, this); diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index f91614e6a07..2b81132ec04 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(ZeroFieldForward) { auto options = f.makeCkfOptions(); // this is the default option. set anyways for consistency - options.propagatorPlainOptions.direction = Acts::NavigationDirection::Forward; + options.propagatorPlainOptions.direction = Acts::Direction::Forward; // Construct a plane surface as the target surface auto pSurface = Acts::Surface::makeShared( Acts::Vector3{-3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); @@ -331,8 +331,7 @@ BOOST_AUTO_TEST_CASE(ZeroFieldBackward) { Fixture f(0_T); auto options = f.makeCkfOptions(); - options.propagatorPlainOptions.direction = - Acts::NavigationDirection::Backward; + options.propagatorPlainOptions.direction = Acts::Direction::Backward; // Construct a plane surface as the target surface auto pSurface = Acts::Surface::makeShared( Acts::Vector3{3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); diff --git a/Tests/UnitTests/Core/TrackFitting/FitterTestsCommon.hpp b/Tests/UnitTests/Core/TrackFitting/FitterTestsCommon.hpp index 272cfb96edd..5444c2f67ae 100644 --- a/Tests/UnitTests/Core/TrackFitting/FitterTestsCommon.hpp +++ b/Tests/UnitTests/Core/TrackFitting/FitterTestsCommon.hpp @@ -225,8 +225,7 @@ struct FitterTester { // backward filtering requires a reference surface options.referenceSurface = &start.referenceSurface(); // this is the default option. set anyways for consistency - options.propagatorPlainOptions.direction = - Acts::NavigationDirection::Forward; + options.propagatorPlainOptions.direction = Acts::Direction::Forward; Acts::TrackContainer tracks{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}}; @@ -285,8 +284,7 @@ struct FitterTester { start.charge(), start.covariance()); options.referenceSurface = &startOuter.referenceSurface(); - options.propagatorPlainOptions.direction = - Acts::NavigationDirection::Backward; + options.propagatorPlainOptions.direction = Acts::Direction::Backward; Acts::TrackContainer tracks{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}}; diff --git a/Tests/UnitTests/Core/TrackFitting/GsfComponentMergingTests.cpp b/Tests/UnitTests/Core/TrackFitting/GsfComponentMergingTests.cpp index 76b89d20877..fc6ac827001 100644 --- a/Tests/UnitTests/Core/TrackFitting/GsfComponentMergingTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/GsfComponentMergingTests.cpp @@ -15,7 +15,7 @@ #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" -#include "Acts/Utilities/detail/gaussian_mixture_helpers.hpp" +#include "Acts/Utilities/GaussianMixtureReduction.hpp" #include @@ -173,6 +173,15 @@ BoundVector meanFromFree(std::vector> cmps, mean.segment<3>(eFreeDir0).normalize(); + // Project the position on the surface. + // This is mainly necessary for the perigee surface, where + // the mean might not fulfill the perigee condition. + Vector3 position = mean.head<3>(); + Vector3 direction = mean.segment<3>(eFreeDir0); + auto intersection = + surface.intersect(GeometryContext{}, position, direction, false); + mean.head<3>() = intersection.intersection.position; + return *detail::transformFreeToBoundParameters(mean, surface, GeometryContext{}); } @@ -214,7 +223,7 @@ void test_surface(const Surface &surface, const angle_description_t &desc, } const auto [mean_approx, cov_approx] = - detail::combineGaussianMixture(cmps, proj, desc); + detail::gaussianMixtureMeanCov(cmps, proj, desc); const auto mean_ref = meanFromFree(cmps, surface); @@ -240,7 +249,7 @@ BOOST_AUTO_TEST_CASE(test_with_data) { const auto boundCov_data = boundCov(samples, mean_data); const auto [mean_test, boundCov_test] = - detail::combineGaussianMixture(cmps, Identity{}, std::tuple<>{}); + detail::gaussianMixtureMeanCov(cmps, Identity{}, std::tuple<>{}); CHECK_CLOSE_MATRIX(mean_data, mean_test, 1.e-1); CHECK_CLOSE_MATRIX(boundCov_data, boundCov_test, 1.e-1); @@ -271,7 +280,7 @@ BOOST_AUTO_TEST_CASE(test_with_data_circular) { using detail::CyclicAngle; const auto d = std::tuple, CyclicAngle>{}; const auto [mean_test, boundCov_test] = - detail::combineGaussianMixture(cmps, Identity{}, d); + detail::gaussianMixtureMeanCov(cmps, Identity{}, d); BOOST_CHECK(std::abs(detail::difference_periodic(mean_data[0], mean_test[0], 2 * M_PI)) < 1.e-1); @@ -337,5 +346,5 @@ BOOST_AUTO_TEST_CASE(test_perigee_surface) { const LocPosArray p{{{d, z}, {d, -z}, {2 * d, z}, {2 * d, -z}}}; // Here we expect a very bad approximation - test_surface(*surface, desc, p, 1.); + test_surface(*surface, desc, p, 1.1); } diff --git a/Tests/UnitTests/Core/Utilities/BinningDataTests.cpp b/Tests/UnitTests/Core/Utilities/BinningDataTests.cpp index cdc97a2a71d..1b254df65bf 100644 --- a/Tests/UnitTests/Core/Utilities/BinningDataTests.cpp +++ b/Tests/UnitTests/Core/Utilities/BinningDataTests.cpp @@ -134,8 +134,7 @@ BOOST_AUTO_TEST_CASE(BinningData_BinningValue) { BOOST_CHECK_EQUAL(xData_arb_binary.value(xyPosition), 0.5); // r/phi/rphiData - CHECK_CLOSE_REL(rData_eq.value(xyzPosition), sqrt(0.5 * 0.5 + 1.5 * 1.5), - 1e-5); + CHECK_CLOSE_REL(rData_eq.value(xyzPosition), std::hypot(0.5, 1.5), 1e-5); BOOST_CHECK_EQUAL(rData_eq.value(rphiPosition), 3.5); CHECK_SMALL(phiData_eq.value(phi0Position), 1e-6 * M_PI); diff --git a/Tests/UnitTests/Core/Utilities/HelpersTests.cpp b/Tests/UnitTests/Core/Utilities/HelpersTests.cpp index 131ab607bdb..6f5b960c35a 100644 --- a/Tests/UnitTests/Core/Utilities/HelpersTests.cpp +++ b/Tests/UnitTests/Core/Utilities/HelpersTests.cpp @@ -8,10 +8,13 @@ #include +#include "Acts/Definitions/Common.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/Helpers.hpp" #include +#include +#include using namespace Acts::VectorHelpers; @@ -179,6 +182,34 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(BlockedMatrixMultiplication, Matrices, } } +BOOST_AUTO_TEST_CASE(min_max) { + std::vector ordered = {-3., -2., -1., 0., 1., 2., 3.}; + auto [min0, max0] = Acts::min_max(ordered); + + CHECK_CLOSE_ABS(min0, -3., std::numeric_limits::epsilon()); + CHECK_CLOSE_ABS(max0, 3., std::numeric_limits::epsilon()); + + std::vector unordered = {3., -3., -2., -1., 0., 1., 2.}; + auto [min1, max1] = Acts::min_max(unordered); + + CHECK_CLOSE_ABS(min1, -3., std::numeric_limits::epsilon()); + CHECK_CLOSE_ABS(max1, 3., std::numeric_limits::epsilon()); +} + +BOOST_AUTO_TEST_CASE(range_medium) { + std::vector ordered = {-3., -2., -1., 0., 1., 2., 3.}; + auto [range0, medium0] = Acts::range_medium(ordered); + + CHECK_CLOSE_ABS(range0, 6., std::numeric_limits::epsilon()); + CHECK_CLOSE_ABS(medium0, 0., std::numeric_limits::epsilon()); + + std::vector unordered = {-2., -1., 0., 1., 2., 3., -3.}; + auto [range1, medium1] = Acts::range_medium(unordered); + + CHECK_CLOSE_ABS(range1, 6., std::numeric_limits::epsilon()); + CHECK_CLOSE_ABS(medium1, 0., std::numeric_limits::epsilon()); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Test } // namespace Acts diff --git a/Tests/UnitTests/Core/Utilities/IntersectionTests.cpp b/Tests/UnitTests/Core/Utilities/IntersectionTests.cpp index 0b4e5eead05..306726daf77 100644 --- a/Tests/UnitTests/Core/Utilities/IntersectionTests.cpp +++ b/Tests/UnitTests/Core/Utilities/IntersectionTests.cpp @@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(ObjectIntersectionTest) { Intersection3D::Status::reachable), psf9.get()); PlaneIntersection int9b( - Intersection3D(Vector3(9., 1., 0.), std::sqrt(9. * 9. + 1.), + Intersection3D(Vector3(9., 1., 0.), std::hypot(9., 1.), Intersection3D::Status::reachable), psf9.get()); PlaneIntersection int10(Intersection3D(Vector3(10., 0., 0.), 10., diff --git a/Tests/UnitTests/Core/Utilities/LoggerTests.cpp b/Tests/UnitTests/Core/Utilities/LoggerTests.cpp index 355fec2443f..1c8b3cb9a52 100644 --- a/Tests/UnitTests/Core/Utilities/LoggerTests.cpp +++ b/Tests/UnitTests/Core/Utilities/LoggerTests.cpp @@ -104,6 +104,12 @@ void debug_level_test(const char* output_file, Logging::Level lvl) { test(std::move(copy), "TestLoggerClone"); BOOST_CHECK_EQUAL(log->name(), "TestLogger"); + auto copy2 = log->clone("TestLoggerClone"); + BOOST_CHECK_EQUAL(copy2->level(), log->level()); + + auto copy3 = log->cloneWithSuffix("Suffix"); + BOOST_CHECK_EQUAL(log->level(), copy3->level()); + logfile = std::ofstream{output_file}; // clear output test(std::move(log), "TestLogger"); diff --git a/Tests/UnitTests/Core/Utilities/MPLTests.cpp b/Tests/UnitTests/Core/Utilities/MPLTests.cpp index 138dae90af4..ae432e43b68 100644 --- a/Tests/UnitTests/Core/Utilities/MPLTests.cpp +++ b/Tests/UnitTests/Core/Utilities/MPLTests.cpp @@ -150,8 +150,8 @@ BOOST_AUTO_TEST_CASE(type_collector_test) { "Didn't find expected results"); // check unpack - using found_results_tuple = decltype( - hana::unpack(found_results, hana::template_))::type::tuple; + using found_results_tuple = decltype(hana::unpack( + found_results, hana::template_))::type::tuple; using expected_results_tuple = std::tuple; static_assert( std::is_same::value, @@ -166,8 +166,8 @@ BOOST_AUTO_TEST_CASE(type_collector_test) { "Didn't find expected actions"); // check unpack - using found_actions_tuple = decltype( - hana::unpack(found_actions, hana::template_))::type::tuple; + using found_actions_tuple = decltype(hana::unpack( + found_actions, hana::template_))::type::tuple; using expected_actions_tuple = std::tuple; static_assert( std::is_same::value, diff --git a/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp b/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp index b1e7ba832fb..9fced5e734a 100644 --- a/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp @@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(billoir_vertex_fitter_defaulttrack_test) { std::shared_ptr perigeeSurface = Surface::makeShared(Vector3(0., 0., 0.)); // Calculate d0 and z0 corresponding to vertex position - double d0V = sqrt(x * x + y * y); + double d0V = std::hypot(x, y); double z0V = z; // Start constructing nTracks tracks in the following @@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(billoir_vertex_fitter_usertrack_test) { Surface::makeShared(Vector3(0., 0., 0.)); // Calculate d0 and z0 corresponding to vertex position - double d0V = sqrt(x * x + y * y); + double d0V = std::hypot(x, y); double z0V = z; // Start constructing nTracks tracks in the following diff --git a/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp index f2b0a780e9f..b4df0d9a8a0 100644 --- a/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp @@ -100,13 +100,6 @@ BOOST_AUTO_TEST_CASE(grid_density_vertex_finder_test) { for (unsigned int i = 0; i < nTracks; i++) { // The position of the particle Vector3 pos(xdist(gen), ydist(gen), 0); - // Produce most of the tracks at near z1 position, - // some near z2. Highest track density then expected at z1 - if ((i % 4) == 0) { - pos[eZ] = z2dist(gen); - } else { - pos[eZ] = z1dist(gen); - } // Create momentum and charge of track double pt = pTDist(gen); @@ -114,10 +107,18 @@ BOOST_AUTO_TEST_CASE(grid_density_vertex_finder_test) { double eta = etaDist(gen); double charge = etaDist(gen) > 0 ? 1 : -1; + // project the position on the surface + Vector3 direction = makeDirectionUnitFromPhiEta(phi, eta); + auto intersection = perigeeSurface->intersect(geoContext, pos, direction); + pos = intersection.intersection.position; + + // Produce most of the tracks at near z1 position, + // some near z2. Highest track density then expected at z1 + pos[eZ] = ((i % 4) == 0) ? z2dist(gen) : z1dist(gen); + trackVec.push_back(BoundTrackParameters::create( perigeeSurface, geoContext, makeVector4(pos, 0), - makeDirectionUnitFromPhiEta(phi, eta), pt, charge, - covMat) + direction, pt, charge, covMat) .value()); } @@ -210,19 +211,27 @@ BOOST_AUTO_TEST_CASE(grid_density_vertex_finder_track_caching_test) { // Create nTracks tracks for test case for (unsigned int i = 0; i < nTracks; i++) { - double x = xdist(gen); - double y = ydist(gen); - // Produce most of the tracks at near z1 position, - // some near z2. Highest track density then expected at z1 - double z = ((i % 4) == 0) ? z2dist(gen) : z1dist(gen); + // The position of the particle + Vector3 pos(xdist(gen), ydist(gen), 0); + + // Create momentum and charge of track double pt = pTDist(gen); double phi = phiDist(gen); double eta = etaDist(gen); double charge = etaDist(gen) > 0 ? 1 : -1; + + // project the position on the surface + Vector3 direction = makeDirectionUnitFromPhiEta(phi, eta); + auto intersection = perigeeSurface->intersect(geoContext, pos, direction); + pos = intersection.intersection.position; + + // Produce most of the tracks at near z1 position, + // some near z2. Highest track density then expected at z1 + pos[eZ] = ((i % 4) == 0) ? z2dist(gen) : z1dist(gen); + trackVec.push_back(BoundTrackParameters::create( - perigeeSurface, geoContext, Vector4(x, y, z, 0), - makeDirectionUnitFromPhiEta(phi, eta), pt, charge, - covMat) + perigeeSurface, geoContext, makeVector4(pos, 0), + direction, pt, charge, covMat) .value()); } @@ -366,17 +375,25 @@ BOOST_AUTO_TEST_CASE(grid_density_vertex_finder_seed_width_test) { // Create nTracks tracks for test case for (unsigned int i = 0; i < nTracks; i++) { - double x = xdist(gen); - double y = ydist(gen); - double z = z1dist(gen); + // The position of the particle + Vector3 pos(xdist(gen), ydist(gen), 0); + + // Create momentum and charge of track double pt = pTDist(gen); double phi = phiDist(gen); double eta = etaDist(gen); double charge = etaDist(gen) > 0 ? 1 : -1; + + // project the position on the surface + Vector3 direction = makeDirectionUnitFromPhiEta(phi, eta); + auto intersection = perigeeSurface->intersect(geoContext, pos, direction); + pos = intersection.intersection.position; + + pos[eZ] = z1dist(gen); + trackVec.push_back(BoundTrackParameters::create( - perigeeSurface, geoContext, Vector4(x, y, z, 0), - makeDirectionUnitFromPhiEta(phi, eta), pt, charge, - covMat) + perigeeSurface, geoContext, makeVector4(pos, 0), + direction, pt, charge, covMat) .value()); } diff --git a/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp index 64fadc8d936..ff15f3a7a06 100644 --- a/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(iterative_finder_test) { std::vector> tracksAtTrueVtx; // Calculate d0 and z0 corresponding to vertex position - double d0_v = sqrt(x * x + y * y); + double d0_v = std::hypot(x, y); double z0_v = z; // Construct random track emerging from vicinity of vertex position @@ -397,7 +397,7 @@ BOOST_AUTO_TEST_CASE(iterative_finder_test_user_track_type) { std::vector> tracksAtTrueVtx; // Calculate d0 and z0 corresponding to vertex position - double d0_v = sqrt(x * x + y * y); + double d0_v = std::hypot(x, y); double z0_v = z; // Construct random track emerging from vicinity of vertex position diff --git a/Tests/UnitTests/Core/Vertexing/LinearizedTrackFactoryTests.cpp b/Tests/UnitTests/Core/Vertexing/LinearizedTrackFactoryTests.cpp index 54cdd45812b..49028e09acd 100644 --- a/Tests/UnitTests/Core/Vertexing/LinearizedTrackFactoryTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/LinearizedTrackFactoryTests.cpp @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(linearized_track_factory_test) { double z = vZDist(gen); // Calculate d0 and z0 corresponding to vertex position - double d0v = sqrt(x * x + y * y); + double d0v = std::hypot(x, y); double z0v = z; // Start constructing nTracks tracks in the following @@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(linearized_track_factory_straightline_test) { double z = vZDist(gen); // Calculate d0 and z0 corresponding to vertex position - double d0v = sqrt(x * x + y * y); + double d0v = std::hypot(x, y); double z0v = z; // Start constructing nTracks tracks in the following diff --git a/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp index 7b342b265a5..c3556146768 100644 --- a/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp @@ -42,11 +42,11 @@ MagneticFieldContext magFieldContext = MagneticFieldContext(); BOOST_AUTO_TEST_CASE(track_density_finder_test) { // Define some track parameter properties Vector3 pos0{0, 0, 0}; - Vector3 pos1a{2_mm, 1_mm, -10_mm}; + Vector3 pos1a{1.86052_mm, -1.24035_mm, -10_mm}; Vector3 mom1a{400_MeV, 600_MeV, 200_MeV}; - Vector3 pos1b{1_mm, 2_mm, -3_mm}; + Vector3 pos1b{-1.24035_mm, 1.86052_mm, -3_mm}; Vector3 mom1b{600_MeV, 400_MeV, -200_MeV}; - Vector3 pos1c{1.2_mm, 1.3_mm, -7_mm}; + Vector3 pos1c{1.69457_mm, -0.50837_mm, -7_mm}; Vector3 mom1c{300_MeV, 1000_MeV, 100_MeV}; VertexingOptions vertexingOptions(geoContext, @@ -109,11 +109,11 @@ BOOST_AUTO_TEST_CASE(track_density_finder_test) { BOOST_AUTO_TEST_CASE(track_density_finder_constr_test) { // Define some track parameter properties Vector3 pos0{0, 0, 0}; - Vector3 pos1a{2_mm, 1_mm, -10_mm}; + Vector3 pos1a{1.86052_mm, -1.24035_mm, -10_mm}; Vector3 mom1a{400_MeV, 600_MeV, 200_MeV}; - Vector3 pos1b{1_mm, 2_mm, -3_mm}; + Vector3 pos1b{-1.24035_mm, 1.86052_mm, -3_mm}; Vector3 mom1b{600_MeV, 400_MeV, -200_MeV}; - Vector3 pos1c{1.2_mm, 1.3_mm, -7_mm}; + Vector3 pos1c{1.69457_mm, -0.50837_mm, -7_mm}; Vector3 mom1c{300_MeV, 1000_MeV, 100_MeV}; // From Athena VertexSeedFinderTestAlg @@ -221,19 +221,27 @@ BOOST_AUTO_TEST_CASE(track_density_finder_random_test) { // Create nTracks tracks for test case for (unsigned int i = 0; i < nTracks; i++) { - double x = xdist(gen); - double y = ydist(gen); - // Produce most of the tracks at near z1 position, - // some near z2. Highest track density then expected at z1 - double z = ((i % 4) == 0) ? z2dist(gen) : z1dist(gen); + // The position of the particle + Vector3 pos(xdist(gen), ydist(gen), 0); + + // Create momentum and charge of track double pt = pTDist(gen); double phi = phiDist(gen); double eta = etaDist(gen); double charge = etaDist(gen) > 0 ? 1 : -1; + + // project the position on the surface + Vector3 direction = makeDirectionUnitFromPhiEta(phi, eta); + auto intersection = perigeeSurface->intersect(geoContext, pos, direction); + pos = intersection.intersection.position; + + // Produce most of the tracks at near z1 position, + // some near z2. Highest track density then expected at z1 + pos[eZ] = ((i % 4) == 0) ? z2dist(gen) : z1dist(gen); + trackVec.push_back(BoundTrackParameters::create( - perigeeSurface, geoContext, Vector4(x, y, z, 0), - makeDirectionUnitFromPhiEta(phi, eta), pt, charge, - covMat) + perigeeSurface, geoContext, makeVector4(pos, 0), + direction, pt, charge, covMat) .value()); } @@ -273,11 +281,11 @@ struct InputTrack { BOOST_AUTO_TEST_CASE(track_density_finder_usertrack_test) { // Define some track parameter properties Vector3 pos0{0, 0, 0}; - Vector3 pos1a{2_mm, 1_mm, -10_mm}; + Vector3 pos1a{1.86052_mm, -1.24035_mm, -10_mm}; Vector3 mom1a{400_MeV, 600_MeV, 200_MeV}; - Vector3 pos1b{1_mm, 2_mm, -3_mm}; + Vector3 pos1b{-1.24035_mm, 1.86052_mm, -3_mm}; Vector3 mom1b{600_MeV, 400_MeV, -200_MeV}; - Vector3 pos1c{1.2_mm, 1.3_mm, -7_mm}; + Vector3 pos1c{1.69457_mm, -0.50837_mm, -7_mm}; Vector3 mom1c{300_MeV, 1000_MeV, 100_MeV}; // From Athena VertexSeedFinderTestAlg diff --git a/Tests/UnitTests/Core/Vertexing/ZScanVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/ZScanVertexFinderTests.cpp index 4f18119c815..58d60cf8f23 100644 --- a/Tests/UnitTests/Core/Vertexing/ZScanVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/ZScanVertexFinderTests.cpp @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(zscan_finder_test) { double z = vZDist(gen); // Calculate d0 and z0 corresponding to vertex position - double d0_v = sqrt(x * x + y * y); + double d0_v = std::hypot(x, y); double z0_v = z; // Start constructing nTracks tracks in the following @@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(zscan_finder_usertrack_test) { double z = vZDist(gen); // Calculate d0 and z0 corresponding to vertex position - double d0_v = sqrt(x * x + y * y); + double d0_v = std::hypot(x, y); double z0_v = z; // Start constructing nTracks tracks in the following diff --git a/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp index 86781ea7e20..60f52e17f88 100644 --- a/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/IndexedSurfacesSvgConverterTests.cpp @@ -9,9 +9,9 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Detector/GridAxisGenerators.hpp" -#include "Acts/Detector/IndexedGridFiller.hpp" -#include "Acts/Detector/IndexedSurfacesGenerator.hpp" +#include "Acts/Detector/detail/GridAxisGenerators.hpp" +#include "Acts/Detector/detail/IndexedGridFiller.hpp" +#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Plugins/ActSVG/IndexedSurfacesSvgConverter.hpp" @@ -26,6 +26,7 @@ using namespace Acts; using namespace Acts::Svg; using namespace Acts::Test; using namespace Acts::Experimental; +using namespace Acts::Experimental::detail; GeometryContext tContext; CylindricalTrackingGeometry cGeometry = CylindricalTrackingGeometry(tContext); diff --git a/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp b/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp index aa4f59237f2..b7b70d4522f 100644 --- a/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp +++ b/Tests/UnitTests/Plugins/Cuda/Seeding/SeedFinderCudaTest.cpp @@ -46,7 +46,7 @@ std::vector readFile(std::string filename) { float x, y, z, r, varianceR, varianceZ; if (linetype == "lxyz") { ss >> layer >> x >> y >> z >> varianceR >> varianceZ; - r = std::sqrt(x * x + y * y); + r = std::hypot(x, y); float f22 = varianceR; float wid = varianceZ; float cov = wid * wid * .08333; diff --git a/Tests/UnitTests/Plugins/Cuda/Seeding2/ReadSeedFile.cpp b/Tests/UnitTests/Plugins/Cuda/Seeding2/ReadSeedFile.cpp index 1e8b3bef6c9..0de5b7385fe 100644 --- a/Tests/UnitTests/Plugins/Cuda/Seeding2/ReadSeedFile.cpp +++ b/Tests/UnitTests/Plugins/Cuda/Seeding2/ReadSeedFile.cpp @@ -45,7 +45,7 @@ std::vector > readSeedFile( int layer; float x, y, z, varianceR, varianceZ; ss >> layer >> x >> y >> z >> varianceR >> varianceZ; - const float r = std::sqrt(x * x + y * y); + const float r = std::hypot(x, y); const float f22 = varianceR; const float wid = varianceZ; float cov = wid * wid * .08333; diff --git a/Tests/UnitTests/Plugins/Sycl/Seeding/SeedFinderSyclTest.cpp b/Tests/UnitTests/Plugins/Sycl/Seeding/SeedFinderSyclTest.cpp index 8700384f30c..44387b79501 100644 --- a/Tests/UnitTests/Plugins/Sycl/Seeding/SeedFinderSyclTest.cpp +++ b/Tests/UnitTests/Plugins/Sycl/Seeding/SeedFinderSyclTest.cpp @@ -61,7 +61,7 @@ auto readFile(const std::string& filename) -> std::vector { float varianceZ; int layer; ss >> layer >> x >> y >> z >> varianceR >> varianceZ; - r = std::sqrt(x * x + y * y); + r = std::hypot(x, y); float f22 = varianceR; float wid = varianceZ; float cov = wid * wid * .08333; diff --git a/cmake/ActsGenerateNonCompileTest.cmake b/cmake/ActsGenerateNonCompileTest.cmake new file mode 100644 index 00000000000..3ec2f3ee07c --- /dev/null +++ b/cmake/ActsGenerateNonCompileTest.cmake @@ -0,0 +1,9 @@ +# message(STATUS "${INPUT_FILE} -> ${OUTPUT_FILE}") + +file(READ ${INPUT_FILE} content) + +string(REGEX REPLACE "ACTS_DOES_NOT_COMPILE_BEGIN\\(([A-Za-z0-9]+)\\)" "#if defined(\\1)" processed "${content}") +string(REPLACE "ACTS_DOES_NOT_COMPILE_END()" "#endif" processed "${processed}") + + +file(WRITE ${OUTPUT_FILE} "${processed}") diff --git a/docs/getting_started.md b/docs/getting_started.md index ca4a356f4e1..f0e6ee3e9a4 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -305,6 +305,7 @@ components. | ACTS_BUILD_BENCHMARKS | Build benchmarks
type: `bool`, default: `OFF` | | ACTS_BUILD_INTEGRATIONTESTS | Build integration tests
type: `bool`, default: `OFF` | | ACTS_BUILD_UNITTESTS | Build unit tests
type: `bool`, default: `OFF` | +| ACTS_BUILD_NONCOMPILE_TESTS | Build tests that check build failure
invariants
type: `bool`, default: `OFF` | | ACTS_RUN_CLANG_TIDY | Run clang-tidy static analysis
type: `bool`, default: `OFF` | | ACTS_BUILD_DOCS | Build documentation
type: `bool`, default: `OFF` | | ACTS_SETUP_BOOST | Explicitly set up Boost for the project
type: `bool`, default: `ON` |