diff --git a/.github/issue_template.md b/.github/issue_template.md index abbfa1c495c..207f573aa3f 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -3,24 +3,16 @@ FOR YOUR INFORMATION AND MUST BE REMOVED BEFORE SUBMITTING THE ISSUE. ## Checklist -- [ ] Is the issue marked with exactly one of the following labels +- [ ] Does the issue indicate clearly what kind it is? Examples are - Bug - Feature - Improvement - - infrastructure + - Infrastructure - [ ] If the issue reports a bug: - - [ ] Is it also marked with one of the `Impact - {Minor,Major,Critical}` labels? + - [ ] Please suggest whether this bug is critical / a blocker. One of the + maintainers will assign a corresponding labels. - [ ] Does the description contain all necessary information to reproduce - the error? - -- [ ] Is the issue assigned to a milestone? This should be `next` unless you - target a specific release. - -- [ ] If the issue is a question or the start of a discussion, please add the - `Needs discussion` label. - -- [ ] If the issue tracks a long development or multiple underlying issues - please add the `Long Term` label. + the error? \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bbee2beabb9..f9c87e2ba2c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -19,17 +19,23 @@ REQUEST. - [ ] Does the PR title contain a `!` to indicate a breaking change? - [ ] Is there section starting with `BREAKING CHANGE:` in the PR body - that explains the breaking change? + that explains the breaking change? - [ ] Is the PR ready to be merged? - - [ ] If not: is it marked as work-in-progress using the `WIP` label? - -- [ ] Is the PR assigned to a milestone? This should be `next` unless you - target a specific release. + - [ ] If not: is it marked as a draft PR? - [ ] Does this PR close an existing issue? - [ ] Is the issue correctly linked so it will be automatically closed upon successful merge (See closing keywords link in the sidebar)? - - [ ] Does the PR milestone match the issue milestone? + +- The CI will initially report a missing milestone. One of the maintainers will + handle assigning a milestone for book-keeping. + +- An automated workflow will assign labels based on changed files, and whether + or not reference files were changed. These do not have to be set manually. + +- If you push updates, and you know they will be superceded later on, consider adding + `[skip ci]` in the commit message. This will instruct the CI system not to run any + jobs on this commit. \ No newline at end of file diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 288bb20187b..af5645db84e 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -22,115 +22,6 @@ env: CCACHE_KEY_SUFFIX: r1 jobs: - lcg: - runs-on: ubuntu-latest - container: ghcr.io/acts-project/${{ matrix.image }}:v41 - strategy: - matrix: - image: - - centos7-lcg100-gcc10 - - centos7-lcg101-gcc11 - - centos8-lcg100-gcc10 - - centos8-lcg101-gcc11 - env: - SETUP: source /opt/lcg_view/setup.sh - INSTALL_DIR: ${{ github.workspace }}/install - ACTS_LOG_FAILURE_THRESHOLD: WARNING - steps: - - uses: actions/checkout@v3 - - - name: Restore ccache - uses: actions/cache/restore@v3 - id: ccache-restore - with: - path: ${{ github.workspace }}/ccache - key: ${{ runner.os }}-ccache-${{ matrix.image }}_${{ env.CCACHE_KEY_SUFFIX }}_${{ github.sha }} - restore-keys: | - ${{ runner.os }}-ccache-${{ matrix.image }}_${{ env.CCACHE_KEY_SUFFIX }}_ - - - name: Configure - # setting CMAKE_CXX_STANDARD=17 is a workaround for a bug in the - # dd4hep CMake configuration that gets triggered on recent CMake - # versions - run: > - ${SETUP} && - ccache -z && - cmake -B build -S . - -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_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_BENCHMARKS=ON - -DACTS_BUILD_UNITTESTS=ON - -DACTS_BUILD_INTEGRATIONTESTS=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 - -DACTS_BUILD_PLUGIN_ACTSVG=ON - - - name: Build - run: ${SETUP} && cmake --build build - - - name: ccache stats - run: ${SETUP} && ccache -s - - - name: Save ccache - uses: actions/cache/save@v3 - if: always() - with: - path: ${{ github.workspace }}/ccache - key: ${{ steps.ccache-restore.outputs.cache-primary-key }} - - - name: Unit tests - run: ${SETUP} && cmake --build build --target test - - - name: Integration tests - run: ${SETUP} && cmake --build build --target integrationtests - - - name: Install - run: ${SETUP} && cmake --build build --target install - - - uses: actions/upload-artifact@v3 - with: - name: acts-${{ matrix.image }} - path: ${{ env.INSTALL_DIR }} - - - name: Downstream configure - run: > - ${SETUP} && - cmake -B build-downstream -S Tests/DownstreamProject - -GNinja - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_CXX_FLAGS=-Werror - -DCMAKE_CXX_STANDARD=17 - -DCMAKE_PREFIX_PATH="${INSTALL_DIR}" - -DDD4HEP=OFF - - - name: Downstream build - run: ${SETUP} && cmake --build build-downstream - - - name: Downstream run - run: ${SETUP} && ./build-downstream/bin/ShowActsVersion - linux_ubuntu: runs-on: ubuntu-latest container: ghcr.io/acts-project/ubuntu2204:v41 @@ -575,6 +466,7 @@ jobs: && sudo chown $USER /usr/local/acts && wget --verbose --progress=dot:giga --continue --retry-connrefused --tries=5 --timeout=2 -O deps.tar.gz https://acts.web.cern.ch/ACTS/ci/macOS/deps.67dd08d.tar.gz && tar -xf deps.tar.gz -C /usr/local/acts + && python3 -m pip install pyyaml jinja2 - name: Restore ccache uses: actions/cache/restore@v3 @@ -594,6 +486,7 @@ jobs: cmake -B build -S . -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DPython_EXECUTABLE=$(command -v python3) -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-Werror -DCMAKE_CXX_STANDARD=17 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 82272c617e5..3448adf687f 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -63,6 +63,14 @@ jobs: - name: Check run: > CI/check_pragma_once.sh + end_of_line: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - name: Check + run: > + CI/check_end_of_file.py . --exclude "thirdparty/*" --reject-multiple-newlines --github boost_test_macro: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 9f5f26dd994..c0a676728ea 100644 --- a/.gitignore +++ b/.gitignore @@ -18,25 +18,19 @@ x86_64-slc6-gcc49-opt build* .asetup.save -# do not ignore changes in the requirements file -# as they are actually important -!cmt/requirements - -# doxygen documentation -# ===================== -!doc/doxygen.config - #json output *.json #root output *.root +#csv output +*.csv *.pyc /cmake_cmd /cleanup.sh # dont ignore hidden configs !.clang-format -!.github/ +!.github/** !.gitignore !.gitlab-ci.yml !.clang-tidy diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ce51018f60..397fc5505e8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ variables: CCACHE_DIR: ${CI_PROJECT_DIR}/ccache CCACHE_MAXSIZE: 2G CCACHE_KEY_SUFFIX: r1 + CTEST_OUTPUT_ON_FAILURE: 1 clang_tidy: stage: build @@ -49,6 +50,7 @@ clang_tidy: clang-tidy/clang-tidy.log clang-tidy/clang-tidy.json --exclude "*thirdparty*" + --exclude "*ActsPodioEdm*" # Check the combined report against the defined limits - CI/clang_tidy/check_clang_tidy.py --report clang-tidy/clang-tidy.json --config CI/clang_tidy/limits.yml @@ -58,7 +60,7 @@ clang_tidy: build_exatrkx: stage: build - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker @@ -104,7 +106,7 @@ test_exatrkx_unittests: stage: test needs: - build_exatrkx - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker-gpu-nvidia script: @@ -114,11 +116,11 @@ test_exatrkx_python: stage: test needs: - build_exatrkx - image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v43 tags: - docker-gpu-nvidia script: - - apt-get update -y || true # TODO revert + - apt-get update -y - apt-get install -y python3 libxxhash0 - source /usr/local/bin/thisroot.sh - source build/python/setup.sh @@ -358,7 +360,19 @@ linux_ubuntu_2204_clang: before_script: - 'echo "LCG_VERSION: ${LCG_VERSION}"' - 'echo "COMPILER: ${COMPILER}"' - - 'if [ "$OS" = "alma9" ]; then export LCG_PLATFORM="centos9"; else export LCG_PLATFORM="$OS"; fi' + + # Figure out LCG platform name based on version number and OS + - > + if [ "$OS" = "alma9" ]; then + if [ "$LCG_VERSION" = "104" ]; then + export LCG_PLATFORM="el9" + else + export LCG_PLATFORM="centos9" + fi + 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 @@ -383,7 +397,7 @@ linux_ubuntu_2204_clang: -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" -DACTS_LOG_FAILURE_THRESHOLD=WARNING -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON - -DACTS_FORCE_ASSERTIONS=ON + -DACTS_FORCE_ASSERTIONS=OFF -DACTS_BUILD_UNITTESTS=ON -DACTS_BUILD_INTEGRATIONTESTS=ON -DACTS_BUILD_BENCHMARKS=ON @@ -394,7 +408,7 @@ linux_ubuntu_2204_clang: -DACTS_BUILD_PLUGIN_JSON=ON -DACTS_BUILD_FATRAS=ON -DACTS_BUILD_PLUGIN_LEGACY=ON - -DACTS_BUILD_PLUGIN_AUTODIFF=ON + -DACTS_BUILD_PLUGIN_AUTODIFF=OFF -DACTS_BUILD_EXAMPLES_DD4HEP=OFF -DACTS_BUILD_PLUGIN_EDM4HEP=OFF -DACTS_BUILD_EXAMPLES_GEANT4=ON @@ -424,14 +438,11 @@ lcg_102b: - OS: [centos7] COMPILER: [gcc11] - - OS: [centos8, alma9] - COMPILER: [gcc11] - -lcg_103: +lcg_104: <<: *lcg_base_job variables: - LCG_VERSION: "103" + LCG_VERSION: "104" parallel: matrix: @@ -439,16 +450,14 @@ lcg_103: COMPILER: - gcc11 - gcc12 - # currently failing: - # - clang12 - # - clang15 - OS: [alma9] COMPILER: - gcc11 - gcc12 + - gcc13 + - clang16 + + - OS: [centos8] + COMPILER: [gcc11] - rules: - - if: '$COMPILER == "clang12" || $COMPILER == "clang15"' - allow_failure: true - - when: on_success diff --git a/Alignment/include/ActsAlignment/Kernel/Alignment.hpp b/Alignment/include/ActsAlignment/Kernel/Alignment.hpp index e4da69df52a..6a902deaffa 100644 --- a/Alignment/include/ActsAlignment/Kernel/Alignment.hpp +++ b/Alignment/include/ActsAlignment/Kernel/Alignment.hpp @@ -74,7 +74,7 @@ struct AlignmentOptions { // The detector elements to be aligned std::vector alignedDetElements; - // The alignment tolerance to determine if the alignment is coveraged + // The alignment tolerance to determine if the alignment is covered double averageChi2ONdfCutOff = 0.5; // The delta of average chi2/ndf within a couple of iterations to determine if @@ -101,7 +101,7 @@ struct AlignmentResult { // The covariance of alignment parameters Acts::ActsDynamicMatrix alignmentCovariance; - // The avarage chi2/ndf (ndf is the measurement dim) + // The average chi2/ndf (ndf is the measurement dim) double averageChi2ONdf = std::numeric_limits::max(); // The delta chi2 diff --git a/Alignment/include/ActsAlignment/Kernel/Alignment.ipp b/Alignment/include/ActsAlignment/Kernel/Alignment.ipp index badb913b0f4..5c29ee90789 100644 --- a/Alignment/include/ActsAlignment/Kernel/Alignment.ipp +++ b/Alignment/include/ActsAlignment/Kernel/Alignment.ipp @@ -59,14 +59,14 @@ void ActsAlignment::Alignment::calculateAlignmentParameters( const fit_options_t& fitOptions, ActsAlignment::AlignmentResult& alignResult, const ActsAlignment::AlignmentMask& alignMask) const { - // The number of trajectories must be eual to the number of starting + // The number of trajectories must be equal to the number of starting // parameters assert(trajectoryCollection.size() == startParametersCollection.size()); // The total alignment degree of freedom alignResult.alignmentDof = alignResult.idxedAlignSurfaces.size() * Acts::eAlignmentSize; - // Initialize derivative of chi2 w.r.t. aligment parameters for all tracks + // Initialize derivative of chi2 w.r.t. alignment parameters for all tracks Acts::ActsDynamicVector sumChi2Derivative = Acts::ActsDynamicVector::Zero(alignResult.alignmentDof); Acts::ActsDynamicMatrix sumChi2SecondDerivative = @@ -97,7 +97,7 @@ void ActsAlignment::Alignment::calculateAlignmentParameters( const auto& alignState = evaluateRes.value(); for (const auto& [rowSurface, rows] : alignState.alignedSurfaces) { const auto& [dstRow, srcRow] = rows; - // Fill the results into full chi2 derivative matrixs + // Fill the results into full chi2 derivative matrix sumChi2Derivative.segment(dstRow * Acts::eAlignmentSize) += alignState.alignmentToChi2Derivative.segment( @@ -262,7 +262,7 @@ ActsAlignment::Alignment::align( if (std::abs(recentChi2ONdf.front() - alignResult.averageChi2ONdf) <= alignOptions.deltaAverageChi2ONdfCutOff.second) { ACTS_INFO( - "Alignment has converaged with change of chi2/ndf < " + "Alignment has converged with change of chi2/ndf < " << alignOptions.deltaAverageChi2ONdfCutOff.second << " in the last " << alignOptions.deltaAverageChi2ONdfCutOff.first << " iterations" << " after " << iIter << " iteration(s)"); @@ -273,7 +273,7 @@ ActsAlignment::Alignment::align( } // 2. or the average chi2/ndf (is this correct?) if (alignResult.averageChi2ONdf <= alignOptions.averageChi2ONdfCutOff) { - ACTS_INFO("Alignment has converaged with average chi2/ndf < " + ACTS_INFO("Alignment has converged with average chi2/ndf < " << alignOptions.averageChi2ONdfCutOff << " after " << iIter << " iteration(s)"); converged = true; @@ -320,7 +320,7 @@ ActsAlignment::Alignment::align( ACTS_VERBOSE("Center (cenX, cenY, cenZ) = " << translation.transpose()); ACTS_VERBOSE( "Euler angles (rotZ, rotY, rotX) = " << rotAngles.transpose()); - ACTS_VERBOSE("Rotation marix = \n" << rotation); + ACTS_VERBOSE("Rotation matrix = \n" << rotation); } } else { ACTS_DEBUG("Alignment parameters is not updated."); diff --git a/Alignment/include/ActsAlignment/Kernel/detail/AlignmentEngine.hpp b/Alignment/include/ActsAlignment/Kernel/detail/AlignmentEngine.hpp index 27461296614..3367cedb373 100644 --- a/Alignment/include/ActsAlignment/Kernel/detail/AlignmentEngine.hpp +++ b/Alignment/include/ActsAlignment/Kernel/detail/AlignmentEngine.hpp @@ -149,7 +149,7 @@ TrackAlignmentState trackAlignmentState( alignState.alignedSurfaces[surface].first = it->second; nAlignSurfaces++; } - // Rember the index of the state within the trajectory and whether it's + // Remember the index of the state within the trajectory and whether it's // alignable measurementStates.push_back({ts.index(), isAlignable}); // Add up measurement dimension @@ -167,7 +167,7 @@ TrackAlignmentState trackAlignmentState( // Dimension of global track parameters (from only measurement states) alignState.trackParametersDim = eBoundSize * measurementStates.size(); - // Initialize the alignment matrixs with components from the measurement + // Initialize the alignment matrices with components from the measurement // states // The measurement covariance alignState.measurementCovariance = ActsDynamicMatrix::Zero( @@ -190,7 +190,7 @@ TrackAlignmentState trackAlignmentState( // should be same as eBoundSize * nSmoothedStates const auto& [sourceTrackParamsCov, stateRowIndices] = globalTrackParamsCov; - // Loop over the measurement states to fill those alignment matrixs + // Loop over the measurement states to fill those alignment matrices // This is done in reverse order size_t iMeasurement = alignState.measurementDim; size_t iParams = alignState.trackParametersDim; @@ -227,7 +227,7 @@ TrackAlignmentState trackAlignmentState( // The direction const Vector3 direction = freeParams.segment<3>(eFreeDir0); // The derivative of free parameters w.r.t. path length. @note Here, we - // assumes a linear track model, i.e. negecting the change of track + // assume a linear track model, i.e. neglecting the change of track // direction. Otherwise, we need to know the magnetic field at the free // parameters FreeVector pathDerivative = FreeVector::Zero(); diff --git a/CI/check_end_of_file.py b/CI/check_end_of_file.py new file mode 100755 index 00000000000..d87aae712b6 --- /dev/null +++ b/CI/check_end_of_file.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse +from subprocess import check_output + + +def main(): + p = argparse.ArgumentParser() + p.add_argument("input") + p.add_argument("--exclude", nargs="+") + p.add_argument("--fix", action="store_true") + p.add_argument("--reject-multiple-newlines", action="store_true") + p.add_argument("--github", action="store_true") + args = p.parse_args() + + files = ( + str( + check_output( + [ + "find", + args.input, + "-iname", + "*.cpp", + "-or", + "-iname", + "*.hpp", + "-or", + "-iname", + "*.ipp", + ] + + sum((["-not", "-path", exclude] for exclude in args.exclude), []) + ), + "utf-8", + ) + .strip() + .split("\n") + ) + + failed = [] + + for file in files: + file = os.path.normpath(file) + + with open(file) as f: + lines = f.readlines() + + if not lines[-1].endswith("\n"): + print(f"Missing newline at end of file: {file}") + if args.fix: + with open(file, "a") as f: + f.write("\n") + else: + failed.append(file) + if args.github: + print( + f"::error file={file},line={len(lines)},title=End of file check::missing newline" + ) + elif args.reject_multiple_newlines and lines[-1] == "\n": + print(f"Multiple newlines at end of file: {file}") + if args.fix: + while lines[-1] == "\n": + lines.pop(-1) + with open(file, "w") as f: + f.write("".join(lines)) + else: + failed.append(file) + if args.github: + print( + f"::error file={{{file}}},line={{{len(lines)}}},title=End of file check::multiple newlines" + ) + + if failed: + print(f"failed for files: {' '.join(failed)}") + return 1 + + print("success") + return 0 + + +if "__main__" == __name__: + sys.exit(main()) diff --git a/CI/physmon/fpe_masks.yml b/CI/physmon/fpe_masks.yml index 1c8d227d8b6..33fd9a6381c 100644 --- a/CI/physmon/fpe_masks.yml +++ b/CI/physmon/fpe_masks.yml @@ -1,8 +1,8 @@ "Fatras/include/ActsFatras/Physics/ElectroMagnetic/BetheHeitler.hpp:66": FLTUND: 1 -"Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp:177": - FLTUND: 1 "Core/include/Acts/TrackFitting/detail/GsfUtils.hpp:197": - FLTUND: 1 + FLTUND: 1 +"Core/include/Acts/TrackFitting/detail/GsfUtils.hpp:201": + FLTUND: 1 "Core/include/Acts/Vertexing/AdaptiveMultiVertexFinder.ipp:120": FLTUND: 1 diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index 8b4e023be7c..fc6cc6f3d3f 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -1,6 +1,7 @@ #!/bin/bash set -e +set -x mode=${1:all} @@ -296,6 +297,22 @@ if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then --config CI/physmon/vertexing_config.yml ec=$(($ec | $?)) + Examples/Scripts/generic_plotter.py \ + $outdir/tracksummary_ckf_ttbar.root \ + tracksummary \ + $outdir/tracksummary_ckf_ttbar_hist.root \ + --config CI/physmon/tracksummary_ckf_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/tracksummary_ckf_ttbar.root + + run_histcmp \ + $outdir/tracksummary_ckf_ttbar_hist.root \ + $refdir/tracksummary_ckf_ttbar_hist.root \ + "Track Summary CKF ttbar" \ + tracksummary_ckf_ttbar + # remove ntuple file because it's large rm $outdir/performance_amvf_ttbar.root diff --git a/CI/physmon/reference/particles_final_fatras_hist.root b/CI/physmon/reference/particles_final_fatras_hist.root index 87d4da3af71..7ff4b0510fc 100644 Binary files a/CI/physmon/reference/particles_final_fatras_hist.root and b/CI/physmon/reference/particles_final_fatras_hist.root differ diff --git a/CI/physmon/reference/particles_final_geant4_hist.root b/CI/physmon/reference/particles_final_geant4_hist.root index b3f3e3f692d..9593e91ae97 100644 Binary files a/CI/physmon/reference/particles_final_geant4_hist.root and b/CI/physmon/reference/particles_final_geant4_hist.root differ diff --git a/CI/physmon/reference/particles_initial_fatras_hist.root b/CI/physmon/reference/particles_initial_fatras_hist.root index 05441d19ab6..b947805b36e 100644 Binary files a/CI/physmon/reference/particles_initial_fatras_hist.root and b/CI/physmon/reference/particles_initial_fatras_hist.root differ diff --git a/CI/physmon/reference/particles_initial_geant4_hist.root b/CI/physmon/reference/particles_initial_geant4_hist.root index bc36f236cb0..73bccd4edee 100644 Binary files a/CI/physmon/reference/particles_initial_geant4_hist.root and b/CI/physmon/reference/particles_initial_geant4_hist.root differ diff --git a/CI/physmon/reference/performance_ambi_orthogonal.root b/CI/physmon/reference/performance_ambi_orthogonal.root index d7eb451a72d..5fdb67ad260 100644 Binary files a/CI/physmon/reference/performance_ambi_orthogonal.root and b/CI/physmon/reference/performance_ambi_orthogonal.root differ diff --git a/CI/physmon/reference/performance_ambi_seeded.root b/CI/physmon/reference/performance_ambi_seeded.root index 22205d19e77..6533f539cb0 100644 Binary files a/CI/physmon/reference/performance_ambi_seeded.root and b/CI/physmon/reference/performance_ambi_seeded.root differ diff --git a/CI/physmon/reference/performance_ambi_ttbar.root b/CI/physmon/reference/performance_ambi_ttbar.root index f6881fb8505..6bfe9250999 100644 Binary files a/CI/physmon/reference/performance_ambi_ttbar.root and b/CI/physmon/reference/performance_ambi_ttbar.root differ diff --git a/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root b/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root index 68d63d5aba4..9a1fe0d9f0a 100644 Binary files a/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root and b/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root b/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root index 03dfbd2a390..60f6d54088e 100644 Binary files a/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root and b/CI/physmon/reference/performance_amvf_gridseeder_ttbar_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_orthogonal_hist.root b/CI/physmon/reference/performance_amvf_orthogonal_hist.root index eec6b742536..d48ce93c6d1 100644 Binary files a/CI/physmon/reference/performance_amvf_orthogonal_hist.root 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 index 32b996c9501..b241a2e1d98 100644 Binary files a/CI/physmon/reference/performance_amvf_seeded_hist.root 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 index 3d141f77833..9aa8250a1a4 100644 Binary files a/CI/physmon/reference/performance_amvf_truth_estimated_hist.root and b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root index acf32965203..21a84cef063 100644 Binary files a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root and b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_amvf_ttbar_hist.root b/CI/physmon/reference/performance_amvf_ttbar_hist.root index 3bdd6a02aa2..33e77040382 100644 Binary files a/CI/physmon/reference/performance_amvf_ttbar_hist.root and b/CI/physmon/reference/performance_amvf_ttbar_hist.root differ diff --git a/CI/physmon/reference/performance_ckf_orthogonal.root b/CI/physmon/reference/performance_ckf_orthogonal.root index 360a2180982..0018523ba5f 100644 Binary files a/CI/physmon/reference/performance_ckf_orthogonal.root and b/CI/physmon/reference/performance_ckf_orthogonal.root differ diff --git a/CI/physmon/reference/performance_ckf_seeded.root b/CI/physmon/reference/performance_ckf_seeded.root index f2f570a2893..5b7cd66bc82 100644 Binary files a/CI/physmon/reference/performance_ckf_seeded.root and b/CI/physmon/reference/performance_ckf_seeded.root differ diff --git a/CI/physmon/reference/performance_ckf_truth_estimated.root b/CI/physmon/reference/performance_ckf_truth_estimated.root index 2deb9405130..0900d27766f 100644 Binary files a/CI/physmon/reference/performance_ckf_truth_estimated.root and b/CI/physmon/reference/performance_ckf_truth_estimated.root differ diff --git a/CI/physmon/reference/performance_ckf_truth_smeared.root b/CI/physmon/reference/performance_ckf_truth_smeared.root index ad81f99f99a..791b64d9053 100644 Binary files a/CI/physmon/reference/performance_ckf_truth_smeared.root and b/CI/physmon/reference/performance_ckf_truth_smeared.root differ diff --git a/CI/physmon/reference/performance_ckf_ttbar.root b/CI/physmon/reference/performance_ckf_ttbar.root index edc0ae36e8f..8bce7567c63 100644 Binary files a/CI/physmon/reference/performance_ckf_ttbar.root and b/CI/physmon/reference/performance_ckf_ttbar.root differ diff --git a/CI/physmon/reference/performance_gsf.root b/CI/physmon/reference/performance_gsf.root index 5bb7e151b4d..05f33a8f130 100644 Binary files a/CI/physmon/reference/performance_gsf.root and b/CI/physmon/reference/performance_gsf.root differ diff --git a/CI/physmon/reference/performance_ivf_orthogonal_hist.root b/CI/physmon/reference/performance_ivf_orthogonal_hist.root index bab8c251daf..dadc91df434 100644 Binary files a/CI/physmon/reference/performance_ivf_orthogonal_hist.root 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 index cce4117ab05..445e038cf1c 100644 Binary files a/CI/physmon/reference/performance_ivf_seeded_hist.root 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 index 6783cccc531..93d69a88111 100644 Binary files a/CI/physmon/reference/performance_ivf_truth_estimated_hist.root and b/CI/physmon/reference/performance_ivf_truth_estimated_hist.root differ diff --git a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root index f0b20b4287d..349e70671ce 100644 Binary files a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root and b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_seeding_orthogonal.root b/CI/physmon/reference/performance_seeding_orthogonal.root index 21a7a3ada8f..7b99661a293 100644 Binary files a/CI/physmon/reference/performance_seeding_orthogonal.root and b/CI/physmon/reference/performance_seeding_orthogonal.root differ diff --git a/CI/physmon/reference/performance_seeding_seeded.root b/CI/physmon/reference/performance_seeding_seeded.root index dde45fd7db3..f10e1527c31 100644 Binary files a/CI/physmon/reference/performance_seeding_seeded.root and b/CI/physmon/reference/performance_seeding_seeded.root differ diff --git a/CI/physmon/reference/performance_seeding_truth_estimated.root b/CI/physmon/reference/performance_seeding_truth_estimated.root index 1b5188be0cb..1bada056776 100644 Binary files a/CI/physmon/reference/performance_seeding_truth_estimated.root and b/CI/physmon/reference/performance_seeding_truth_estimated.root differ diff --git a/CI/physmon/reference/performance_seeding_ttbar.root b/CI/physmon/reference/performance_seeding_ttbar.root index 7ef18d19e55..38a6722ece5 100644 Binary files a/CI/physmon/reference/performance_seeding_ttbar.root and b/CI/physmon/reference/performance_seeding_ttbar.root differ diff --git a/CI/physmon/reference/performance_truth_tracking.root b/CI/physmon/reference/performance_truth_tracking.root index 5ea8a5a3eda..28ffe8c18d7 100644 Binary files a/CI/physmon/reference/performance_truth_tracking.root and b/CI/physmon/reference/performance_truth_tracking.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root b/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root index 5074a1b56cc..652704f22c0 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root 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 index 71cbf7f38c3..7c8268b1987 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_seeded_hist.root 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 index 855efbfc3e6..254ee6b56dc 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_truth_estimated_hist.root 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 index a52cc0dc0d7..fae0eb9dc70 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_truth_smeared_hist.root and b/CI/physmon/reference/tracksummary_ckf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root b/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root new file mode 100644 index 00000000000..54c2b202133 Binary files /dev/null and b/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root differ diff --git a/CI/physmon/workflows/physmon_ckf_tracking.py b/CI/physmon/workflows/physmon_ckf_tracking.py index 9b81f311dce..0ce835a6eaf 100755 --- a/CI/physmon/workflows/physmon_ckf_tracking.py +++ b/CI/physmon/workflows/physmon_ckf_tracking.py @@ -143,9 +143,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None - if label in ["seeded", "orthogonal"] - else "particles_input", outputProtoVertices="ivf_protovertices", outputVertices="ivf_fittedVertices", vertexFinder=VertexFinder.Iterative, @@ -156,9 +153,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None - if label in ["seeded", "orthogonal"] - else "particles_input", outputProtoVertices="amvf_protovertices", outputVertices="amvf_fittedVertices", vertexFinder=VertexFinder.AMVF, @@ -172,7 +166,6 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.field, seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, - associatedParticles=None, outputProtoVertices="amvf_gridseeder_protovertices", outputVertices="amvf_gridseeder_fittedVertices", vertexFinder=VertexFinder.AMVF, diff --git a/CI/physmon/workflows/physmon_track_finding_ttbar.py b/CI/physmon/workflows/physmon_track_finding_ttbar.py index 6be473b551f..bbc023bb7cb 100755 --- a/CI/physmon/workflows/physmon_track_finding_ttbar.py +++ b/CI/physmon/workflows/physmon_track_finding_ttbar.py @@ -45,7 +45,13 @@ ), acts.examples.Sequencer.FpeMask( "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp", - (172, 173), + (235, 236), + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp", + (235, 236), acts.FpeType.FLTOVF, 1, ), @@ -69,7 +75,7 @@ ), acts.examples.Sequencer.FpeMask( "Fatras/include/ActsFatras/Kernel/Simulation.hpp", - (98, 99), + (96, 97), acts.FpeType.FLTOVF, 1, ), @@ -161,7 +167,6 @@ s, setup.field, seeder=acts.VertexSeedFinder.GaussianSeeder, - associatedParticles=None, outputProtoVertices="amvf_protovertices", outputVertices="amvf_fittedVertices", vertexFinder=VertexFinder.AMVF, @@ -172,7 +177,6 @@ s, setup.field, seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, - associatedParticles=None, outputProtoVertices="amvf_gridseeder_protovertices", outputVertices="amvf_gridseeder_fittedVertices", vertexFinder=VertexFinder.AMVF, diff --git a/CI/physmon/workflows/physmon_truth_tracking_gsf.py b/CI/physmon/workflows/physmon_truth_tracking_gsf.py index 518fc04af95..ca92f7b183a 100755 --- a/CI/physmon/workflows/physmon_truth_tracking_gsf.py +++ b/CI/physmon/workflows/physmon_truth_tracking_gsf.py @@ -12,7 +12,7 @@ with tempfile.TemporaryDirectory() as temp: s = acts.examples.Sequencer( - events=500, + events=10000, numThreads=-1, logLevel=acts.logging.INFO, fpeMasks=acts.examples.Sequencer.FpeMask.fromFile( @@ -23,8 +23,8 @@ tp = Path(temp) runTruthTrackingGsf( setup.trackingGeometry, - setup.digiConfig, setup.field, + setup.digiConfig, outputDir=tp, s=s, ) diff --git a/CI/physmon/workflows/physmon_truth_tracking_kalman.py b/CI/physmon/workflows/physmon_truth_tracking_kalman.py index 325ba8968ad..c01e1a0e182 100755 --- a/CI/physmon/workflows/physmon_truth_tracking_kalman.py +++ b/CI/physmon/workflows/physmon_truth_tracking_kalman.py @@ -24,7 +24,7 @@ runTruthTrackingKalman( setup.trackingGeometry, setup.field, - digiConfigFile=setup.digiConfig, + setup.digiConfig, outputDir=tp, s=s, ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1735449311e..946452b2bd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ option(ACTS_USE_SYSTEM_ACTSVG "Use the ActSVG system library" ${ACTS_USE_SYSTEM_ option(ACTS_BUILD_PLUGIN_ACTSVG "Build SVG display plugin" OFF) option(ACTS_BUILD_PLUGIN_CUDA "Build CUDA plugin" OFF) option(ACTS_BUILD_PLUGIN_DD4HEP "Build DD4hep plugin" OFF) +option(ACTS_BUILD_PLUGIN_PODIO "Build Podio plugin" OFF) option(ACTS_BUILD_PLUGIN_EDM4HEP "Build EDM4hep plugin" OFF) option(ACTS_BUILD_PLUGIN_FPEMON "Build FPE monitoring plugin" OFF) option(ACTS_BUILD_PLUGIN_GEANT4 "Build Geant4 plugin" OFF) @@ -128,6 +129,9 @@ set_option_if( set_option_if( ACTS_BUILD_PLUGIN_EDM4HEP ACTS_BUILD_EXAMPLES_EDM4HEP OR ACTS_BUILD_EVERYTHING) +set_option_if( + ACTS_BUILD_PLUGIN_PODIO + ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_GEANT4 ACTS_BUILD_EXAMPLES_GEANT4 OR ACTS_BUILD_EVERYTHING) @@ -136,7 +140,7 @@ set_option_if( ACTS_BUILD_PLUGIN_DD4HEP OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_IDENTIFICATION - ACTS_BUILD_PLUGIN_TGEO OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) + ACTS_BUILD_PLUGIN_TGEO OR ACTS_BUILD_PLUGIN_PODIO OR ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) set_option_if( ACTS_BUILD_PLUGIN_JSON ACTS_BUILD_EXAMPLES OR ACTS_BUILD_EVERYTHING) @@ -187,7 +191,7 @@ set(_acts_autodiff_version 0.6) set(_acts_boost_version 1.71.0) set(_acts_dd4hep_version 1.21) set(_acts_edm4hep_version 0.7) -set(_acts_podio_version 0.6) +set(_acts_podio_version 0.16) set(_acts_doxygen_version 1.9.4) set(_acts_eigen3_version 3.3.7) set(_acts_hepmc3_version 3.2.1) @@ -344,8 +348,11 @@ endif() if(ACTS_BUILD_PLUGIN_ONNX OR ACTS_EXATRKX_ENABLE_ONNX) find_package(OnnxRuntime ${_acts_onnxruntime_version} REQUIRED) endif() -if(ACTS_BUILD_PLUGIN_EDM4HEP) +if(ACTS_BUILD_PLUGIN_EDM4HEP OR ACTS_BUILD_PLUGIN_PODIO) find_package(podio ${_acts_podio_version} REQUIRED CONFIG) + find_package(ROOT ${_acts_root_version} REQUIRED CONFIG COMPONENTS Core) +endif() +if(ACTS_BUILD_PLUGIN_EDM4HEP) find_package(EDM4HEP ${_acts_edm4hep_version} REQUIRED CONFIG) endif() if(ACTS_BUILD_PLUGIN_GEANT4) diff --git a/Core/include/Acts/AmbiguityResolution/GreedyAmbiguityResolution.ipp b/Core/include/Acts/AmbiguityResolution/GreedyAmbiguityResolution.ipp index 0be168f0b47..ee2d14b1564 100644 --- a/Core/include/Acts/AmbiguityResolution/GreedyAmbiguityResolution.ipp +++ b/Core/include/Acts/AmbiguityResolution/GreedyAmbiguityResolution.ipp @@ -35,7 +35,7 @@ void GreedyAmbiguityResolution::computeInitialState( continue; } std::vector measurements; - for (auto ts : track.trackStates()) { + for (auto ts : track.trackStatesReversed()) { if (ts.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) { SourceLink sourceLink = ts.getUncalibratedSourceLink(); // assign a new measurement index if the source link was not seen yet diff --git a/Core/include/Acts/Clusterization/Clusterization.hpp b/Core/include/Acts/Clusterization/Clusterization.hpp index e5971e696ca..806b5c70e2e 100644 --- a/Core/include/Acts/Clusterization/Clusterization.hpp +++ b/Core/include/Acts/Clusterization/Clusterization.hpp @@ -16,8 +16,8 @@ namespace Acts::Ccl { using Label = int; constexpr Label NO_LABEL = 0; -// When looking for a cell connected to a reference cluster, the the -// code always loops backward, starting from the reference cell. Since +// When looking for a cell connected to a reference cluster, the code +// always loops backward, starting from the reference cell. Since // the cells are globally sorted column-wise, the connection function // can therefore tell when the search should be stopped. enum class ConnectResult { diff --git a/Core/include/Acts/Definitions/Algebra.hpp b/Core/include/Acts/Definitions/Algebra.hpp index ac34a91b11c..b7dc446489a 100644 --- a/Core/include/Acts/Definitions/Algebra.hpp +++ b/Core/include/Acts/Definitions/Algebra.hpp @@ -12,6 +12,9 @@ #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmisleading-indentation" +#if __GNUC__ == 13 +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif #include #include #pragma GCC diagnostic pop diff --git a/Core/include/Acts/Definitions/TrackParametrization.hpp b/Core/include/Acts/Definitions/TrackParametrization.hpp index 18a2fa75490..2405294cc92 100644 --- a/Core/include/Acts/Definitions/TrackParametrization.hpp +++ b/Core/include/Acts/Definitions/TrackParametrization.hpp @@ -22,8 +22,8 @@ namespace Acts { // Note: -// The named indices are use to access raw data vectors and matrices at the -// lowest level. Since the interpretation of some of the components, e.g. local +// The named indices are used to access raw data vectors and matrices at the +// lowest level. Since the interpretation of some components, e.g. local // position and the inverse-momentum-like component, depend on additional // information the names have some ambiguity. This can only be resolved at a // higher logical level and no attempt is made to resolve it here. diff --git a/Core/include/Acts/Detector/Blueprint.hpp b/Core/include/Acts/Detector/Blueprint.hpp new file mode 100644 index 00000000000..a6d6fccbc50 --- /dev/null +++ b/Core/include/Acts/Detector/Blueprint.hpp @@ -0,0 +1,179 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Common.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include +#include +#include + +namespace Acts { +namespace Experimental { + +class IGeometryIdGenerator; +class IInternalStructureBuilder; +class IRootVolumeFinderBuilder; + +/// A Blueprint is an instruction tree that allows you to defina a tree sequence +/// of volume building using the provided tools. +/// +/// It follows tree nomenclature and can define: +/// +/// - a root node (the top of the tree) +/// - a branch node (also called inner node) +/// - leaf node (also called terminal node) +/// +/// Leaf nodes can have internal builders attached, while all the external +/// builders will be created when the blueprint is interpreted. +namespace Blueprint { + +struct Node final { + /// Branch constructor + /// + /// @param n name of the node + /// @param t the transform + /// @param bt the boundary type + /// @param bv the boundary values + /// @param bss the binning values + /// @param cs the children of the node + Node(const std::string& n, const Transform3& t, VolumeBounds::BoundsType bt, + const std::vector& bv, const std::vector& bss, + std::vector> cs = {}) + : name(n), + transform(t), + boundsType(bt), + boundaryValues(bv), + children(std::move(cs)), + binning(bss) { + for_each(children.begin(), children.end(), + [this](std::unique_ptr& c) { c->parent = this; }); + } + + /// Leaf constructor + /// + /// @param n name of the node + /// @param t the transform + /// @param bt the boundary type + /// @param bv the boundary values + /// @param isb the internal structure builder (optional) + Node(const std::string& n, const Transform3& t, VolumeBounds::BoundsType bt, + const std::vector& bv, + std::shared_ptr isb = nullptr) + : name(n), + transform(t), + boundsType(bt), + boundaryValues(bv), + internalsBuilder(std::move(isb)) {} + + /// Name identification of this node + std::string name = ""; + /// Transform definition of this node + Transform3 transform = Transform3::Identity(); + /// Boundary definition of this node + VolumeBounds::BoundsType boundsType = VolumeBounds::eOther; + /// The boundary type + std::vector boundaryValues = {}; + /// Parent node - nullptr for root only + const Node* parent = nullptr; + /// Branch definitions: children + std::vector> children = {}; + /// Branch definition binning + std::vector binning = {}; + + /// Builders and helper tools that can be attached + std::shared_ptr rootVolumeFinderBuilder = + nullptr; + /// Geometry id generator + std::shared_ptr geoIdGenerator = nullptr; + /// Internal structure builder - for leaf nodes + std::shared_ptr internalsBuilder = nullptr; + + /// @brief Check if it is a leaf node + bool isLeaf() const { return children.empty(); } + + /// @brief Check is it is a root + bool isRoot() const { return parent == nullptr; } + + /// @brief Method to add a child to this branch + /// @param c the child to be added + void add(std::unique_ptr c) { + c->parent = this; + children.push_back(std::move(c)); + } + + /// @brief Turn into a dot output + template + void dotStream(stream_type& ss, + const std::string& graphName = "blueprint") const { + if (isRoot()) { + ss << "digraph " << graphName << " {" << '\n'; + ss << name + << " [shape=\"circle\";style=\"filled\";fillcolor=\"darkorange\"];" + << '\n'; + + } else if (isLeaf()) { + std::string color = + (internalsBuilder != nullptr) ? "darkolivegreen1" : "darkolivegreen3"; + + ss << name << " [shape=\"box\";style=\"filled\";fillcolor=\""; + ss << color << "\"];" << '\n'; + } else { + ss << name << " [shape=\"diamond\"];" << '\n'; + } + + ss << name << " [label=\"" << name << "\"];" << '\n'; + for (const auto& c : children) { + ss << name << " -> " << c->name << ";" << '\n'; + c->dotStream(ss); + } + if (children.empty()) { + ss << name + "_shape" + << " [shape=\"cylinder\";style=\"filled\";fillcolor=\"lightgrey\"];" + << '\n'; + ss << name << " -> " << name + "_shape" + << ";" << '\n'; + } + + if (internalsBuilder != nullptr) { + ss << name + "_int" + << " [shape=\"doubleoctagon\";style=\"filled\";fillcolor=" + "\"cadetblue1\"];" + << '\n'; + ss << name << " -> " << name + "_int" + << ";" << '\n'; + } + + if (geoIdGenerator != nullptr) { + ss << name + "_geoID" + << " [shape=\"note\";style=\"filled\";fillcolor=\"azure\"];" << '\n'; + ss << name << " -> " << name + "_geoID" + << ";" << '\n'; + } + + if (rootVolumeFinderBuilder != nullptr) { + ss << name + "_roots" + << " [shape=\"Msquare\";style=\"filled\";fillcolor=\"darkkhaki\"];" + << '\n'; + ss << name << " -> " << name + "_roots" + << ";" << '\n'; + } + + if (isRoot()) { + ss << "}" << '\n'; + } + } +}; + +} // namespace Blueprint +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp index e6fa47960c9..0d7b546dfef 100644 --- a/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp +++ b/Core/include/Acts/Detector/CylindricalContainerBuilder.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Detector/Blueprint.hpp" #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -21,12 +22,19 @@ namespace Acts { namespace Experimental { +class IRootVolumeFinderBuilder; +class IGeometryIdGenerator; + /// @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 /// +/// There exists an option to create this container builder (recursively) +/// from a blueprint tree, which attempts to fill in the gap volumes +/// accordingly. +/// /// @note the builder expects a fully consistent set of sub volume builders /// that will be executed in a chain /// @@ -41,11 +49,18 @@ class CylindricalContainerBuilder : public IDetectorComponentBuilder { std::vector> builders = {}; /// Binning prescription of attachment std::vector binning = {}; + /// The root volume finder + std::shared_ptr rootVolumeFinderBuilder = + nullptr; + /// The geometry id generator + std::shared_ptr geoIdGenerator = nullptr; + /// An eventual reverse geometry id generation + bool geoIdReverseGen = false; /// Auxiliary information, mainly for screen output std::string auxiliary = ""; }; - /// Constructor with configuration arguments + /// Constructor with configuration struct /// /// @param cfg is the configuration struct /// @param logger logging instance for screen output @@ -54,6 +69,24 @@ class CylindricalContainerBuilder : public IDetectorComponentBuilder { std::unique_ptr logger = getDefaultLogger("CylindricalContainerBuilder", Logging::INFO)); + /// Constructor from blueprint and logging level + /// + /// It will create recursively the builders of sub volumes + /// + /// @param bpNode is the entry blue print node + /// @param logLevel is the logging output level for the builder tools + /// + /// @note no checking is being done on consistency of the blueprint, + /// it is assumed it has passed first through gap filling via the + /// blueprint helper. + /// + /// @note that the naming of the builders is taken from the bluprint nodes + /// + /// @return a cylindrical container builder representing this blueprint + CylindricalContainerBuilder( + const Acts::Experimental::Blueprint::Node& bpNode, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + /// The final implementation of the cylindrical container builder /// /// @param gctx The geometry context for this call diff --git a/Core/include/Acts/Detector/Detector.hpp b/Core/include/Acts/Detector/Detector.hpp index 7c9aaf99e02..adbccfce213 100644 --- a/Core/include/Acts/Detector/Detector.hpp +++ b/Core/include/Acts/Detector/Detector.hpp @@ -12,6 +12,8 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Navigation/NavigationDelegates.hpp" #include "Acts/Utilities/Delegate.hpp" @@ -23,6 +25,9 @@ #include namespace Acts { + +class Surface; + namespace Experimental { struct NavigationState; @@ -90,6 +95,11 @@ class Detector : public std::enable_shared_from_this { /// @return a vector to const DetectorVolume raw pointers const std::vector& volumes() const; + /// Const access to the hierarchy map of all sensitive surfaces + /// + /// @return the map which can be queried with GeometryID for ranges + const GeometryHierarchyMap& sensitiveHierarchyMap() const; + /// Update the current volume of a given navigation state /// /// @param gctx is the Geometry context of the call @@ -142,6 +152,9 @@ class Detector : public std::enable_shared_from_this { /// Name/index map to find volumes by name and detect duplicates std::unordered_map m_volumeNameIndex; + + /// Geometry Id hierarchy map of all sensitive surfaces + GeometryHierarchyMap m_sensitiveHierarchyMap; }; } // namespace Experimental diff --git a/Core/include/Acts/Detector/DetectorBuilder.hpp b/Core/include/Acts/Detector/DetectorBuilder.hpp index 6c3bc2c503c..2638f20a28f 100644 --- a/Core/include/Acts/Detector/DetectorBuilder.hpp +++ b/Core/include/Acts/Detector/DetectorBuilder.hpp @@ -18,6 +18,8 @@ namespace Acts { namespace Experimental { +class IGeometryIdGenerator; + /// @brief Standard generic Detector builder that calls /// the top level component builder and transfers the /// result into a detector object @@ -33,6 +35,8 @@ class DetectorBuilder final : public IDetectorBuilder { std::string name = "unnamed"; /// An external builder std::shared_ptr builder = nullptr; + /// A geometry id generator + std::shared_ptr geoIdGenerator = nullptr; /// Auxiliary information std::string auxiliary = ""; }; diff --git a/Core/include/Acts/Detector/DetectorVolume.hpp b/Core/include/Acts/Detector/DetectorVolume.hpp index e10e456c491..1f831960520 100644 --- a/Core/include/Acts/Detector/DetectorVolume.hpp +++ b/Core/include/Acts/Detector/DetectorVolume.hpp @@ -330,6 +330,12 @@ class DetectorVolume : public std::enable_shared_from_this { /// @return the geometry identifier const GeometryIdentifier& geometryId() const; + /// Set the geometry identifier + /// @note no checking is done, it will overwrite any existing id + /// + /// @param geoID is the geometry Id that is set to the object + void assignGeometryId(const GeometryIdentifier& geoID); + /// Assign Detector to this volume (for back navigation issues) /// @param detector the parenting detector class void assignDetector(const Detector& detector); diff --git a/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp index 0cf3f0bc252..a5ca7b5fb75 100644 --- a/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp +++ b/Core/include/Acts/Detector/DetectorVolumeBuilder.hpp @@ -10,8 +10,6 @@ #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" -#include "Acts/Detector/interface/IExternalStructureBuilder.hpp" -#include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/Logger.hpp" @@ -22,6 +20,7 @@ namespace Acts { namespace Experimental { class IExternalStructureBuilder; class IInternalStructureBuilder; +class IGeometryIdGenerator; /// @brief A generic detector volume builder that uses /// an external builder for shape and portals and an internal @@ -41,6 +40,8 @@ class DetectorVolumeBuilder : public IDetectorComponentBuilder { std::shared_ptr externalsBuilder = nullptr; /// An internal builder std::shared_ptr internalsBuilder = nullptr; + /// The geometry id generator + std::shared_ptr geoIdGenerator = nullptr; /// Add eventual internal volume to root bool addInternalsToRoot = false; /// Auxiliary information diff --git a/Core/include/Acts/Detector/GeometryIdGenerator.hpp b/Core/include/Acts/Detector/GeometryIdGenerator.hpp new file mode 100644 index 00000000000..013ffb6c210 --- /dev/null +++ b/Core/include/Acts/Detector/GeometryIdGenerator.hpp @@ -0,0 +1,125 @@ +// 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/interface/IGeometryIdGenerator.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include + +namespace Acts { + +class Surface; + +namespace Experimental { + +class Portal; +class DetectorVolume; + +/// @brief This is the default implementation of the geometry id generator +/// +/// It is a simple counter based generator that assigns ids to the geometry +/// and increments the counter for each object type. +/// +/// Sub counters, i.e. for the sensitive surfaces, are reset after the volume +/// call, such that a new volume or layer volume would start from counter 0 +/// again. +/// +/// If the generator is configured to override existing ids, it will do so +/// and not respect previously assigned ids. +/// +/// If the generator is configured in container mode, it will increase the +/// layer id for each layer volume with confined surfaces. +/// +class GeometryIdGenerator final : public IGeometryIdGenerator { + public: + /// @brief Nested config struct + struct Config { + /// Container mode + bool containerMode = false; + /// Container id (if container mode), will not be incremented + unsigned int containerId = 0u; + /// Resetting mode + bool resetSubCounters = true; + /// Force override existing ids + bool overrideExistingIds = false; + }; + + /// @brief Nested cache struct + struct Cache { + /// Cache count of the volume, for non-container mode + unsigned int volumeCount = 0u; + /// Cache count of the layer volume, for container mode + unsigned int layerCount = 0u; + /// Cache count of the portal surfaces + unsigned int portalCount = 0u; + /// Cache count of passive surfaces + unsigned int passiveCount = 0u; + /// Cache count of sensitive surfaces + unsigned int sensitiveCount = 0u; + }; + + /// @brief Constructor with config + /// + /// @param cfg is the geometry configuration object + /// @param mlogger is the logging instance + GeometryIdGenerator(const Config& cfg, + std::unique_ptr mlogger = getDefaultLogger( + "GeometryIdGenerator", Logging::INFO)) + : m_cfg(cfg), m_logger(std::move(mlogger)) {} + + ~GeometryIdGenerator() override = default; + + /// @brief Interface method to generata a geometry id cache + /// @return a geometry id cache decorated in a std::any object + IGeometryIdGenerator::GeoIdCache generateCache() const final; + + /// @brief Method for assigning a geometry id to a detector volume + /// + /// @param cache is the cache object for e.g. object counting + /// @param dVolume the detector volume to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + DetectorVolume& dVolume) const final; + + /// @brief Method for assigning a geometry id to a portal + /// + /// @param cache is the cache object for e.g. object counting + /// @param portal the portal to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + Portal& portal) const final; + + /// @brief Method for assigning a geometry id to a surface + /// + /// @param cache is the cache object for e.g. object counting + /// @param surface the surface to assign the geometry id to + void assignGeometryId(IGeometryIdGenerator::GeoIdCache& cache, + Surface& surface) const final; + + private: + /// @brief Helper method to get the volume id from the cache + /// + /// @param cache the provided cache + /// @param incrementLayer if true, the layer counter is incremented + /// + /// @return a valid geometry identifier + GeometryIdentifier volumeId(Cache& cache, bool incrementLayer = true) const; + + /// Configuration object + Config m_cfg; + + /// Private access 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/IndexedRootVolumeFinderBuilder.hpp b/Core/include/Acts/Detector/IndexedRootVolumeFinderBuilder.hpp new file mode 100644 index 00000000000..c9959bf670c --- /dev/null +++ b/Core/include/Acts/Detector/IndexedRootVolumeFinderBuilder.hpp @@ -0,0 +1,49 @@ +// 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/interface/IRootVolumeFinderBuilder.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/DetectorVolumeUpdators.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include +#include + +namespace Acts { + +namespace Experimental { + +class DetectorVolume; + +/// @brief This is the interface for builders that create root volume finder +/// delegates +class IndexedRootVolumeFinderBuilder final : public IRootVolumeFinderBuilder { + public: + /// @brief Constructor with binning casts + /// @param binning the cast values for the grid binning + IndexedRootVolumeFinderBuilder(std::vector binning); + + /// The virtual interface definition for root volume finder builders + /// + /// @param gctx the geometry context at the creation of the internal structure + /// @param rootVolumes the root volumes to be used for the finder + /// + /// @return a shared detector object + DetectorVolumeUpdator construct( + const GeometryContext& gctx, + const std::vector>& rootVolumes) + const final; + + private: + std::vector m_casts; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/KdtSurfacesProvider.hpp b/Core/include/Acts/Detector/KdtSurfacesProvider.hpp index b2120a6f17f..0798c33aed2 100644 --- a/Core/include/Acts/Detector/KdtSurfacesProvider.hpp +++ b/Core/include/Acts/Detector/KdtSurfacesProvider.hpp @@ -30,7 +30,8 @@ namespace Experimental { /// KDTree lookup positions /// template + typename reference_generator = + detail::PolyhedronReferenceGenerator<1u, false>> class KdtSurfaces { public: /// Broadcast the surface KDT type @@ -49,11 +50,11 @@ class KdtSurfaces { /// @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 = detail::PolyhedronReferenceGenerator{}) + KdtSurfaces(const GeometryContext& gctx, + const std::vector>& surfaces, + const std::array& casts, + const reference_generator& rgen = + detail::PolyhedronReferenceGenerator<1u, false>{}) : m_kdt(nullptr), m_casts(casts), m_rGenerator(rgen) { // Simple check if the dimension is correct if (kDIM == 0u) { @@ -66,11 +67,16 @@ class KdtSurfaces { 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}); + std::vector castedReferences; + castedReferences.reserve(references.size()); + for (const auto& r : references) { + // Now cast into the correct fill position + Query rc = {}; + fillCasts(r, rc, std::make_integer_sequence{}); + castedReferences.push_back(rc); + } + // Calculate the center of gravity in casted frame + kdtEntries.push_back({cog(castedReferences), s}); } // Create the KDTree m_kdt = std::make_unique(std::move(kdtEntries)); @@ -123,22 +129,30 @@ class KdtSurfaces { ((a[idx] = VectorHelpers::cast(position, m_casts[idx])), ...); } - /// Helper method to calculate the center of gravity + /// Helper method to calculate the center of gravity in the + /// casted frame (i.e. query frame) /// - /// @param positions are the reference positions that go in + /// @param cQueries are the casted query positions /// @note will do nothing if vector size is equal to 1 /// - /// @note no checking on positions.empty() is done as the + /// @note no checking on qQueries.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 { + /// @return the center of gravity as a query object + Query cog(const std::vector& cQueries) const { + // If there is only one position, return it + if (cQueries.size() == 1) { + return cQueries.front(); + } // 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; }); + Query c{}; + float weight = 1. / cQueries.size(); + for (auto& q : cQueries) { + std::transform(c.begin(), c.end(), q.begin(), c.begin(), + std::plus()); + } + std::for_each(c.begin(), c.end(), [&](auto& v) { v *= weight; }); return c; } }; @@ -148,7 +162,8 @@ class KdtSurfaces { /// This allows to create small region based callable structs at /// configuration level that are then connected to an InternalStructureBuilder template + typename reference_generator = + detail::PolyhedronReferenceGenerator<1u, false>> class KdtSurfacesProvider : public ISurfacesProvider { public: /// The prefilled surfaces in a KD tree structure, it is generally shared diff --git a/Core/include/Acts/Detector/LayerStructureBuilder.hpp b/Core/include/Acts/Detector/LayerStructureBuilder.hpp index a91fc372e79..c4ac141b7df 100644 --- a/Core/include/Acts/Detector/LayerStructureBuilder.hpp +++ b/Core/include/Acts/Detector/LayerStructureBuilder.hpp @@ -11,6 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/ProtoBinning.hpp" +#include "Acts/Detector/ProtoSupport.hpp" #include "Acts/Detector/interface/IInternalStructureBuilder.hpp" #include "Acts/Detector/interface/ISurfacesProvider.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -72,20 +73,6 @@ class LayerStructureBuilder : public IInternalStructureBuilder { std::vector> m_surfaces = {}; }; - /// @brief Support parameter definitions - 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 Configuration struct for the LayerStructureBuilder /// /// It contain: @@ -96,7 +83,7 @@ class LayerStructureBuilder : public IInternalStructureBuilder { /// Connection point for a function to provide surfaces std::shared_ptr surfacesProvider = nullptr; /// Definition of Supports - std::vector supports = {}; + std::vector supports = {}; /// Definition of Binnings std::vector binnings = {}; /// Polyhedron approximations diff --git a/Core/include/Acts/Detector/MultiWireStructureBuilder.hpp b/Core/include/Acts/Detector/MultiWireStructureBuilder.hpp new file mode 100644 index 00000000000..e58ce69290c --- /dev/null +++ b/Core/include/Acts/Detector/MultiWireStructureBuilder.hpp @@ -0,0 +1,79 @@ +// 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/LayerStructureBuilder.hpp" +#include "Acts/Detector/ProtoBinning.hpp" +#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Detector/interface/IExternalStructureBuilder.hpp" +#include "Acts/Detector/interface/IInternalStructureBuilder.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include + +namespace Acts { +namespace Experimental { + +class MultiWireStructureBuilder { + public: + /// @brief Configuration struct for the MultiWireStructure Builder + + struct Config { + /// The name of the detector volume component + std::string name = ""; + + /// The surfaces of the Multi Wire + std::vector> mlSurfaces = {}; + + /// The transform of the Multi Wire + Transform3 transform = Transform3::Identity(); + + /// The bounds of the multi-wire volume + std::vector mlBounds = {}; + + // The binning of the multi wire structure + std::vector mlBinning = {}; + + /// A tolerance config + float toleranceOverlap = 10.; + }; + + /// Constructor + /// @param config The configure of the MultiWireStructureBuilder + /// @param logger logging instance for screen output + + MultiWireStructureBuilder( + const Config& config, + std::unique_ptr logger = Acts::getDefaultLogger( + "MultiWireStructureBuilder", Acts::Logging::VERBOSE)); + + ~MultiWireStructureBuilder() = default; + + /// Construct the detector component + + /// @param gctx The Geometry Context of the current geometry + /// @return a detector component object with the detector volume of the multilayer + + Acts::Experimental::DetectorComponent construct( + const Acts::GeometryContext& gctx); + + private: + Config mCfg; + + const Acts::Logger& logger() const { return *mLogger; } + + std::unique_ptr mLogger; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/ProtoSupport.hpp b/Core/include/Acts/Detector/ProtoSupport.hpp new file mode 100644 index 00000000000..e705f0aab24 --- /dev/null +++ b/Core/include/Acts/Detector/ProtoSupport.hpp @@ -0,0 +1,43 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Common.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/detail/AxisFwd.hpp" + +#include +#include +#include + +namespace Acts { + +namespace Experimental { +/// @brief Support parameter definitions +struct ProtoSupport { + /// 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; + /// Alternatively - the support surface can already be provided + std::shared_ptr surface = nullptr; + /// Indicate if the support surface should always be addressed in navigation + bool assignToAll = false; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/BlueprintHelper.hpp b/Core/include/Acts/Detector/detail/BlueprintHelper.hpp new file mode 100644 index 00000000000..076facab8e5 --- /dev/null +++ b/Core/include/Acts/Detector/detail/BlueprintHelper.hpp @@ -0,0 +1,38 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/Blueprint.hpp" +#include "Acts/Utilities/BinningData.hpp" + +namespace Acts { + +namespace Experimental { + +namespace detail { +namespace BlueprintHelper { + +/// @brief Sort the nodes in the blueprint container node +/// +/// @param node the node for which the children should be sorted +/// @param recursive if the sorting should be done recursively to children +void sort(Blueprint::Node& node, bool recursive = true); + +/// @brief Fill the gaps in the blueprint container node +/// +/// @param node the node for with the gaps should be filled +/// @param adjustToParent nodes, if nodes should be adjusted to parent +/// +/// @note currently only cylindrical volumes are supported +void fillGaps(Blueprint::Node& node, bool adjustToParent = true); + +} // namespace BlueprintHelper +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp b/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp index 2dd824e371d..c423f4fa689 100644 --- a/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp +++ b/Core/include/Acts/Detector/detail/CylindricalDetectorHelper.hpp @@ -12,7 +12,9 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/Portal.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" #include "Acts/Utilities/Logger.hpp" #include @@ -166,15 +168,68 @@ DetectorComponent::PortalContainer wrapInZR( /// @brief Helper method to extract r,z,phi boundaries for /// eventual grid volume search /// +/// @tparam volume_container_t the type of the container +/// /// @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 +template std::array, 3u> rzphiBoundaries( - const GeometryContext& gctx, - const std::vector& volumes, - Acts::Logging::Level logLevel = Acts::Logging::INFO); + const GeometryContext& gctx, const volume_container_t& volumes, + Acts::Logging::Level logLevel = Acts::Logging::INFO) { + // 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> uniqueBoundaries; + + // Loop over the volumes and collect boundaries + for (const auto& v : volumes) { + if (v->volumeBounds().type() == VolumeBounds::BoundsType::eCylinder) { + const 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 sets + uniqueBoundaries[0].insert(rMin); + uniqueBoundaries[0].insert(rMax); + uniqueBoundaries[1].insert(zMin); + uniqueBoundaries[1].insert(zMax); + uniqueBoundaries[2].insert(phiMin); + uniqueBoundaries[2].insert(phiMax); + } + } + + ACTS_VERBOSE("- did yield " << uniqueBoundaries[0u].size() + << " boundaries in R."); + ACTS_VERBOSE("- did yield " << uniqueBoundaries[1u].size() + << " boundaries in z."); + ACTS_VERBOSE("- did yield " << uniqueBoundaries[2u].size() + << " boundaries in phi."); + + return {{std::vector(uniqueBoundaries[0].begin(), + uniqueBoundaries[0].end()), + std::vector(uniqueBoundaries[1].begin(), + uniqueBoundaries[1].end()), + std::vector(uniqueBoundaries[2].begin(), + uniqueBoundaries[2].end())}}; +} } // namespace CylindricalDetectorHelper } // namespace detail diff --git a/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp index 9a7a38a86b2..98a9e94c019 100644 --- a/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp +++ b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp @@ -75,12 +75,8 @@ struct IndexedSurfacesGenerator { bvArray[ibv] = bv; } - // The indexed surfaces delegate - // IndexedSurfacesImpl indexedSurfaces(std::move(grid), bvArray, - // transform); indexed_updator indexedSurfaces(std::move(grid), bvArray, transform); - // Fill the bin indices IndexedGridFiller filler{binExpansion}; filler.oLogger = oLogger->cloneWithSuffix("_filler"); @@ -92,13 +88,13 @@ struct IndexedSurfacesGenerator { // The chained delegate: indexed surfaces and all portals using DelegateType = IndexedSurfacesAllPortalsImpl; - auto indesSurfacesAllPortals = std::make_unique( + auto indexedSurfacesAllPortals = std::make_unique( std::tie(allPortals, indexedSurfaces)); // Create the delegate and connect it SurfaceCandidatesUpdator nStateUpdator; nStateUpdator.connect<&DelegateType::update>( - std::move(indesSurfacesAllPortals)); + std::move(indexedSurfacesAllPortals)); return nStateUpdator; } diff --git a/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp b/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp index e0e087db1af..46b9d9b03b8 100644 --- a/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp +++ b/Core/include/Acts/Detector/detail/ReferenceGenerators.hpp @@ -39,12 +39,12 @@ struct CenterReferenceGenerator { /// A struct to access reference positions based on bin values /// +/// @tparam bVAL the binning value to be used for the binning position call +/// /// This generator will provide only one filling point and hence /// only a single bin in the indexed grid. +template struct BinningValueReferenceGenerator { - /// The binning value - BinningValue bValue = BinningValue::binValues; - /// Helper to access a reference position based on binning value /// /// @param gctx the geometry context of this operation @@ -53,7 +53,7 @@ struct BinningValueReferenceGenerator { /// @return a vector of reference points for filling const std::vector references(const GeometryContext& gctx, const Surface& surface) const { - return {surface.binningPosition(gctx, bValue)}; + return {surface.binningPosition(gctx, bVAL)}; } }; @@ -61,15 +61,14 @@ struct BinningValueReferenceGenerator { /// These vertices are then used to find the bin boundary box for the /// indexed grid. /// +/// @tparam nSEGS the number of segments to be used for the polyhedron +/// approximation of arcs between vertices +/// @tparam aBARY if true, the barycenter of the polyhedron is added +/// /// The grid filling then completes the empty bins in between and /// expands if necessary. +template 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 @@ -80,11 +79,11 @@ struct PolyhedronReferenceGenerator { const Surface& surface) const { // Create the return vector std::vector rPositions; - auto pHedron = surface.polyhedronRepresentation(gctx, nSegments); + auto pHedron = surface.polyhedronRepresentation(gctx, nSEGS); rPositions.insert(rPositions.end(), pHedron.vertices.begin(), pHedron.vertices.end()); // Add the barycenter if configured - if (addBarycenter) { + if constexpr (aBARY) { Vector3 bc(0., 0., 0.); std::for_each(rPositions.begin(), rPositions.end(), [&](const auto& p) { bc += p; }); diff --git a/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp b/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp new file mode 100644 index 00000000000..3f6c0bd4552 --- /dev/null +++ b/Core/include/Acts/Detector/interface/IGeometryIdGenerator.hpp @@ -0,0 +1,59 @@ +// 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 + +namespace Acts { + +class Surface; + +namespace Experimental { + +class Portal; +class DetectorVolume; + +/// @brief This is the interface for generating geometry ids and assign them +/// to detector volumes, portals and surfaces +/// +class IGeometryIdGenerator { + public: + using GeoIdCache = std::any; + + virtual ~IGeometryIdGenerator() = default; + + /// @brief Virtual interface method to generata a geometry id cache + /// @return a geometry id cache decorated in a std::any object + virtual GeoIdCache generateCache() const = 0; + + /// The virtual interface definition for assigning a geometry id to + /// a detector volume + /// + /// @param cache is the cache object for e.g. object counting + /// @param dVolume the detector volume to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, + DetectorVolume& dVolume) const = 0; + + /// The virtual interface definition for assigning a geometry id to + /// a portal + /// + /// @param cache is the cache object for e.g. object counting + /// @param portal the portal to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, Portal& portal) const = 0; + + /// @brief The virtual interface definition for assigning a geometry id to + /// a surface + /// + /// @param cache is the cache object for e.g. object counting + /// @param surface the surface to assign the geometry id to + virtual void assignGeometryId(GeoIdCache& cache, Surface& surface) const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/interface/IRootVolumeFinderBuilder.hpp b/Core/include/Acts/Detector/interface/IRootVolumeFinderBuilder.hpp new file mode 100644 index 00000000000..dc3d66f0f69 --- /dev/null +++ b/Core/include/Acts/Detector/interface/IRootVolumeFinderBuilder.hpp @@ -0,0 +1,42 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/DetectorVolumeUpdators.hpp" + +#include +#include + +namespace Acts { + +namespace Experimental { + +class DetectorVolume; + +/// @brief This is the interface for builders that create root volume finder +/// delegates +class IRootVolumeFinderBuilder { + public: + virtual ~IRootVolumeFinderBuilder() = default; + + /// The virtual interface definition for root volume finder builders + /// + /// @param gctx the geometry context at the creation of the internal structure + /// @param rootVolumes the root volumes to be used for the search + /// + /// @return a shared detector object + virtual DetectorVolumeUpdator construct( + const GeometryContext& gctx, + const std::vector>& rootVolumes) + const = 0; +}; + +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Digitization/CartesianSegmentation.hpp b/Core/include/Acts/Digitization/CartesianSegmentation.hpp index a509240642a..ec5891f08d4 100644 --- a/Core/include/Acts/Digitization/CartesianSegmentation.hpp +++ b/Core/include/Acts/Digitization/CartesianSegmentation.hpp @@ -147,4 +147,4 @@ inline std::pair CartesianSegmentation::pitch() const { return std::pair(pitchX, pitchY); } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Digitization/DigitizationCell.hpp b/Core/include/Acts/Digitization/DigitizationCell.hpp index 3f3215fef94..13fe11d9cb5 100644 --- a/Core/include/Acts/Digitization/DigitizationCell.hpp +++ b/Core/include/Acts/Digitization/DigitizationCell.hpp @@ -18,13 +18,13 @@ struct DigitizationCell final { size_t channel1 = 1; float data = 0.; - // connstruct them + // construct them DigitizationCell(size_t ch0, size_t ch1, float d = 0.) : channel0(ch0), channel1(ch1), data(d) {} /// To merge cells in case they are at the same position /// @param dc the cell to be added to the current cell - /// @param analogueReadout flag indicating if we have analgue readout + /// @param analogueReadout flag indicating if we have analogue readout /// @note this function is needed because possible derived classes may /// calculate the energy deposit differently. Furthermore this allows to apply /// an energy cut, because the energy deposit can also be stored for digital diff --git a/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp index fa15ee61cae..e690fa8a4da 100644 --- a/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Definitions/Tolerance.hpp" #include "Acts/EventData/detail/PrintParameters.hpp" #include "Acts/EventData/detail/TransformationFreeToBound.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -23,7 +24,7 @@ namespace Acts { /// Track parameters bound to a reference surface for a single track. /// -/// @tparam charge_t Helper type to interpret the particle charge/momentum +/// @tparam particle_hypothesis_t Helper type to interpret the particle charge/momentum /// /// This is intended as a user-facing data class that adds additional accessors /// and charge/momentum interpretation on top of the pure parameters vector. All @@ -32,55 +33,45 @@ namespace Acts { /// defined by the associated surface. /// /// @note This class holds shared ownership on its reference surface. -template +template class GenericBoundTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = BoundVector; using CovarianceMatrix = BoundSquareMatrix; + using ParticleHypothesis = particle_hypothesis_t; /// Construct from a parameters vector on the surface and particle charge. /// /// @param surface Reference surface the parameters are defined on /// @param params Bound parameters vector - /// @param q Particle charge /// @param cov Bound parameters covariance matrix + /// @param particleHypothesis Particle hypothesis /// /// In principle, only the charge magnitude is needed her to allow unambiguous /// extraction of the absolute momentum. The particle charge is required as /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - GenericBoundTrackParameters( - std::shared_ptr surface, const ParametersVector& params, - Scalar q, std::optional cov = std::nullopt) + GenericBoundTrackParameters(std::shared_ptr surface, + const ParametersVector& params, + std::optional cov, + ParticleHypothesis particleHypothesis) : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)), - m_chargeInterpreter(std::abs(q)) { - assert((0 <= (params[eBoundQOverP] * q)) and - "Inconsistent q/p and q signs"); + m_particleHypothesis(std::move(particleHypothesis)) { assert(m_surface); normalizePhiTheta(); } - /// Construct from a parameters vector on the surface. - /// - /// @param surface Reference surface the parameters are defined on - /// @param params Bound parameters vector - /// @param cov Bound parameters covariance matrix - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge type is default-constructible. - template , int> = 0> + /// Converts a bound track parameter with a different hypothesis. + template GenericBoundTrackParameters( - std::shared_ptr surface, const ParametersVector& params, - std::optional cov = std::nullopt) - : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)) { - assert(m_surface); - normalizePhiTheta(); - } + const GenericBoundTrackParameters& other) + : GenericBoundTrackParameters(other.referenceSurface().getSharedPtr(), + other.parameters(), other.covariance(), + other.particleHypothesis()) {} /// Factory to construct from four-position, direction, absolute momentum, and /// charge. @@ -88,58 +79,31 @@ class GenericBoundTrackParameters { /// @param surface Reference surface the parameters are defined on /// @param geoCtx Geometry context for the local-to-global transformation /// @param pos4 Track position/time four-vector - /// @param dir Track direction three-vector; normalization is ignored. - /// @param p Absolute momentum - /// @param q Particle charge - /// @param cov Bound parameters covariance matrix - /// - /// @note The returned result indicates whether the free parameters could - /// successfully be converted to on-surface parameters. - static Result> create( - std::shared_ptr surface, const GeometryContext& geoCtx, - const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, - std::optional cov = std::nullopt) { - Result bound = detail::transformFreeToBoundParameters( - pos4.segment<3>(ePos0), pos4[eTime], dir, - (q != Scalar(0)) ? (q / p) : (1 / p), *surface, geoCtx); - - if (!bound.ok()) { - return bound.error(); - } - - return GenericBoundTrackParameters{std::move(surface), *bound, q, - std::move(cov)}; - } - - /// Factory to construct from four-position, direction, and - /// charge-over-momentum. - /// - /// @param surface Reference surface the parameters are defined on - /// @param geoCtx Geometry context for the local-to-global transformation - /// @param pos4 Track position/time four-vector - /// @param dir Track direction three-vector; normalization is ignored. - /// @param qOverP Charge-over-momentum-like parameter + /// @param dir Track direction three-vector; normalization is ignored + /// @param qOverP Charge over momentum /// @param cov Bound parameters covariance matrix + /// @param particleHypothesis Particle hypothesis + /// @param tolerance Tolerance used for globalToLocal /// - /// @note This factory is only available if there are no potential charge - /// ambiguities, i.e. the charge type is default-constructible. The - /// position must be located on the surface. /// @note The returned result indicates whether the free parameters could /// successfully be converted to on-surface parameters. - template , int> = 0> - static Result> create( + static Result create( std::shared_ptr surface, const GeometryContext& geoCtx, const Vector4& pos4, const Vector3& dir, Scalar qOverP, - std::optional cov = std::nullopt) { + std::optional cov, + ParticleHypothesis particleHypothesis, + ActsScalar tolerance = s_onSurfaceTolerance) { Result bound = detail::transformFreeToBoundParameters( - pos4.segment<3>(ePos0), pos4[eTime], dir, qOverP, *surface, geoCtx); + pos4.segment<3>(ePos0), pos4[eTime], dir, qOverP, *surface, geoCtx, + tolerance); + if (!bound.ok()) { return bound.error(); } - return GenericBoundTrackParameters{std::move(surface), *bound, - std::move(cov)}; + return GenericBoundTrackParameters{std::move(surface), std::move(*bound), + std::move(cov), + std::move(particleHypothesis)}; } /// Parameters are not default constructible due to the charge type. @@ -156,10 +120,46 @@ class GenericBoundTrackParameters { ParametersVector& parameters() { return m_params; } /// Parameters vector. const ParametersVector& parameters() const { return m_params; } + /// Vector of spatial impact parameters (i.e., d0 and z0) + ActsVector<2> spatialImpactParameters() const { return m_params.head<2>(); } + /// Vector of spatial and temporal impact parameters (i.e., d0, z0, and t) + ActsVector<3> impactParameters() const { + ActsVector<3> ip; + ip.template head<2>() = m_params.template head<2>(); + ip(2) = m_params(eBoundTime); + return ip; + } + /// Optional covariance matrix. std::optional& covariance() { return m_cov; } /// Optional covariance matrix. const std::optional& covariance() const { return m_cov; } + /// Covariance matrix of the spatial impact parameters (i.e., of d0 and z0) + std::optional> spatialImpactParameterCovariance() const { + if (not m_cov.has_value()) { + return std::nullopt; + } + + return m_cov.value().template topLeftCorner<2, 2>(); + } + + /// Covariance matrix of the spatial and temporal impact parameters (i.e., of + /// d0, z0, and t) + std::optional> impactParameterCovariance() const { + if (not m_cov.has_value()) { + return std::nullopt; + } + + ActsSquareMatrix<3> ipCov; + ipCov.template topLeftCorner<2, 2>() = + m_cov.value().template topLeftCorner<2, 2>(); + ipCov.template block<2, 1>(0, 2) = + m_cov.value().template block<2, 1>(0, eBoundTime); + ipCov.template block<1, 2>(2, 0) = + m_cov.value().template block<1, 2>(eBoundTime, 0); + ipCov(2, 2) = m_cov.value()(eBoundTime, eBoundTime); + return ipCov; + } /// Access a single parameter value identified by its index. /// @@ -217,7 +217,7 @@ class GenericBoundTrackParameters { } /// Absolute momentum. Scalar absoluteMomentum() const { - return m_chargeInterpreter.extractMomentum(m_params[eBoundQOverP]); + return m_particleHypothesis.extractMomentum(m_params[eBoundQOverP]); } /// Transverse momentum. Scalar transverseMomentum() const { @@ -228,7 +228,12 @@ class GenericBoundTrackParameters { /// Particle electric charge. Scalar charge() const { - return m_chargeInterpreter.extractCharge(get()); + return m_particleHypothesis.extractCharge(get()); + } + + /// Particle hypothesis. + const ParticleHypothesis& particleHypothesis() const { + return m_particleHypothesis; } /// Reference surface onto which the parameters are bound. @@ -251,7 +256,7 @@ class GenericBoundTrackParameters { /// reference surface std::shared_ptr m_surface; // TODO use [[no_unique_address]] once we switch to C++20 - charge_t m_chargeInterpreter; + ParticleHypothesis m_particleHypothesis; /// Ensure phi and theta angles are within bounds. void normalizePhiTheta() { @@ -271,15 +276,15 @@ class GenericBoundTrackParameters { /// of equality in different contexts. None of that can be handled by /// this operator. Users should think really hard if this is what they /// want and we might decided that we will remove this in the future. - friend bool operator==(const GenericBoundTrackParameters& lhs, - const GenericBoundTrackParameters& rhs) { + friend bool operator==(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return (lhs.m_params == rhs.m_params) and (lhs.m_cov == rhs.m_cov) and (lhs.m_surface == rhs.m_surface) and - (lhs.m_chargeInterpreter == rhs.m_chargeInterpreter); + (lhs.m_particleHypothesis == rhs.m_particleHypothesis); } /// Compare two bound track parameters for bitwise in-equality. - friend bool operator!=(const GenericBoundTrackParameters& lhs, - const GenericBoundTrackParameters& rhs) { + friend bool operator!=(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return not(lhs == rhs); } /// Print information to the output stream. diff --git a/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp index 966175ab99f..2baf0327cff 100644 --- a/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp @@ -9,13 +9,14 @@ #pragma once #include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/TrackParametersConcept.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" namespace Acts { /// Curvilinear track parameters for a single track. /// -/// @tparam charge_t Helper type to interpret the particle charge/momentum +/// @tparam particle_hypothesis_t Helper type to interpret the particle charge/momentum /// /// This is intended as a user-facing data class that adds additional accessors /// and charge/momentum interpretation on top of the pure parameters vector. All @@ -23,91 +24,70 @@ namespace Acts { /// curvilinear parametrization. /// /// @see GenericBoundTrackParameters -template +template class GenericCurvilinearTrackParameters - : public GenericBoundTrackParameters { - using Base = GenericBoundTrackParameters; + : public GenericBoundTrackParameters { + using Base = GenericBoundTrackParameters; public: using Scalar = ActsScalar; using ParametersVector = BoundVector; using CovarianceMatrix = BoundSquareMatrix; + using ParticleHypothesis = particle_hypothesis_t; - /// Construct from four-position, direction, absolute momentum, and charge. + /// Construct from four-position, direction, and qOverP. /// /// @param pos4 Track position/time four-vector /// @param dir Track direction three-vector; normalization is ignored. - /// @param p Absolute momentum - /// @param q Particle charge + /// @param qOverP Charge over momentum /// @param cov Curvilinear bound parameters covariance matrix - GenericCurvilinearTrackParameters( - const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, - std::optional cov = std::nullopt) - : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), - detail::transformFreeToCurvilinearParameters( - pos4[eTime], dir, (q != Scalar(0)) ? (q / p) : (1 / p)), - q, std::move(cov)) { - assert((0 <= p) and "Absolute momentum must be positive"); - } - - /// Construct from four-position, direction, and charge-over-momentum. - /// - /// @param pos4 Track position/time four-vector - /// @param dir Track direction three-vector; normalization is ignored. - /// @param qOverP Charge-over-momentum-like parameter - /// @param cov Curvilinear bound parameters covariance matrix - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge interpretation type is default-constructible. - template , int> = 0> - GenericCurvilinearTrackParameters( - const Vector4& pos4, const Vector3& dir, Scalar qOverP, - std::optional cov = std::nullopt) + /// @param particleHypothesis Particle hypothesis + GenericCurvilinearTrackParameters(const Vector4& pos4, const Vector3& dir, + Scalar qOverP, + std::optional cov, + ParticleHypothesis particleHypothesis) : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), detail::transformFreeToCurvilinearParameters(pos4[eTime], dir, qOverP), - std::move(cov)) {} + std::move(cov), std::move(particleHypothesis)) {} - /// Construct from four-position, angles, absolute momentum, and charge. + /// Construct from four-position, angles, and qOverP. /// /// @param pos4 Track position/time four-vector /// @param phi Transverse track direction angle /// @param theta Longitudinal track direction angle - /// @param p Absolute momentum - /// @param q Particle charge + /// @param qOverP Charge over momentum /// @param cov Curvilinear bound parameters covariance matrix - GenericCurvilinearTrackParameters( - const Vector4& pos4, Scalar phi, Scalar theta, Scalar p, Scalar q, - std::optional cov = std::nullopt) - : Base(Surface::makeShared( - pos4.segment<3>(ePos0), makeDirectionFromPhiTheta(phi, theta)), - detail::transformFreeToCurvilinearParameters( - pos4[eTime], phi, theta, (q != Scalar(0)) ? (q / p) : (1 / p)), - q, std::move(cov)) { - assert((0 <= p) and "Absolute momentum must be positive"); - } - - /// Construct from four-position, angles, and charge-over-momentum. - /// - /// @param pos4 Track position/time four-vector - /// @param phi Transverse track direction angle - /// @param theta Longitudinal track direction angle - /// @param qOverP Charge-over-momentum-like parameter - /// @param cov Curvilinear bound parameters covariance matrix - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge interpretation type is default-constructible. - template , int> = 0> - GenericCurvilinearTrackParameters( - const Vector4& pos4, Scalar phi, Scalar theta, Scalar qOverP, - std::optional cov = std::nullopt) + /// @param particleHypothesis Particle hypothesis + GenericCurvilinearTrackParameters(const Vector4& pos4, Scalar phi, + Scalar theta, Scalar qOverP, + std::optional cov, + ParticleHypothesis particleHypothesis) : Base(Surface::makeShared( pos4.segment<3>(ePos0), makeDirectionFromPhiTheta(phi, theta)), detail::transformFreeToCurvilinearParameters(pos4[eTime], phi, theta, qOverP), - std::move(cov)) {} + std::move(cov), std::move(particleHypothesis)) {} + + /// Converts a bound track parameter with a different hypothesis. + template + GenericCurvilinearTrackParameters( + const GenericCurvilinearTrackParameters& + other) + : GenericCurvilinearTrackParameters(other.fourPosition(), + other.particleHypothesis(), + other.covariance()) {} + + /// Converts an unknown bound track parameter. + template + static GenericCurvilinearTrackParameters create( + const other_track_parameter_t& other) { + static_assert( + Concepts::BoundTrackParametersConcept); + + return GenericCurvilinearTrackParameters( + other.fourPosition(), other.particleHypothesis(), other.covariance()); + } /// Parameters are not default constructible due to the charge type. GenericCurvilinearTrackParameters() = delete; @@ -121,16 +101,16 @@ class GenericCurvilinearTrackParameters GenericCurvilinearTrackParameters& operator=( GenericCurvilinearTrackParameters&&) = default; - using GenericBoundTrackParameters::fourPosition; - using GenericBoundTrackParameters::position; + using GenericBoundTrackParameters::fourPosition; + using GenericBoundTrackParameters::position; /// Space-time position four-vector. Vector4 fourPosition() const { - return GenericBoundTrackParameters::fourPosition({}); + return GenericBoundTrackParameters::fourPosition({}); } /// Spatial position three-vector. Vector3 position() const { - return GenericBoundTrackParameters::position({}); + return GenericBoundTrackParameters::position({}); } }; diff --git a/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp index 738814f05b2..c54ce44697c 100644 --- a/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp @@ -11,6 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" #include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/TrackParametersConcept.hpp" #include "Acts/EventData/detail/PrintParameters.hpp" #include "Acts/Utilities/UnitVectors.hpp" @@ -23,66 +24,50 @@ namespace Acts { /// Track parameters not bound to a surface for a single track. /// -/// @tparam charge_t Helper type to interpret the particle charge +/// @tparam particle_hypothesis_t Helper type to interpret the particle charge/momentum /// /// Parameters and covariance matrix are stored using the free parametrization /// defined in `enum FreeIndices`. -template +template class GenericFreeTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = FreeVector; using CovarianceMatrix = FreeSquareMatrix; + using ParticleHypothesis = particle_hypothesis_t; /// Construct from a parameters vector and particle charge. /// /// @param params Free parameters vector - /// @param q Particle charge /// @param cov Free parameters covariance matrix + /// @param particleHypothesis Particle hypothesis /// /// In principle, only the charge magnitude is needed her to allow unambiguous /// extraction of the absolute momentum. The particle charge is required as /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - GenericFreeTrackParameters(const ParametersVector& params, Scalar q, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const ParametersVector& params, + std::optional cov, + ParticleHypothesis particleHypothesis) : m_params(params), m_cov(std::move(cov)), - m_chargeInterpreter(std::abs(q)) { - assert((0 <= (params[eFreeQOverP] * q)) and "Inconsistent q/p and q signs"); - } - - /// Construct from a parameters vector. - /// - /// @param params Free parameters vector - /// @param cov Free parameters covariance matrix - /// @tparam T Internal helper template be able to check charge type - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge interpretation type is default-constructible. - template , int> = 0> - GenericFreeTrackParameters(const ParametersVector& params, - std::optional cov = std::nullopt) - : m_params(params), m_cov(std::move(cov)) {} + m_particleHypothesis(std::move(particleHypothesis)) {} /// Construct from four-position, angles, absolute momentum, and charge. /// /// @param pos4 Track position/time four-vector /// @param phi Transverse track direction angle /// @param theta Longitudinal track direction angle - /// @param p Absolute momentum - /// @param q Particle charge + /// @param qOverP Charge over momentum /// @param cov Free parameters covariance matrix + /// @param particleHypothesis Particle hypothesis GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar p, Scalar q, - std::optional cov = std::nullopt) + Scalar qOverP, std::optional cov, + ParticleHypothesis particleHypothesis) : m_params(FreeVector::Zero()), m_cov(std::move(cov)), - m_chargeInterpreter(std::abs(q)) { - assert((0 <= p) and "Absolute momentum must be positive"); - + m_particleHypothesis(std::move(particleHypothesis)) { auto dir = makeDirectionFromPhiTheta(phi, theta); m_params[eFreePos0] = pos4[ePos0]; m_params[eFreePos1] = pos4[ePos1]; @@ -91,34 +76,26 @@ class GenericFreeTrackParameters { m_params[eFreeDir0] = dir[eMom0]; m_params[eFreeDir1] = dir[eMom1]; m_params[eFreeDir2] = dir[eMom2]; - m_params[eFreeQOverP] = (q != Scalar(0)) ? (q / p) : (1 / p); + m_params[eFreeQOverP] = qOverP; } - /// Construct from four-position, angles, and charge-over-momentum. - /// - /// @param pos4 Track position/time four-vector - /// @param phi Transverse track direction angle - /// @param theta Longitudinal track direction angle - /// @param qOverP Charge-over-momentum-like parameter - /// @param cov Free parameters covariance matrix - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge interpretation type is default-constructible. - template , int> = 0> - GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar qOverP, - std::optional cov = std::nullopt) - : m_params(FreeVector::Zero()), m_cov(std::move(cov)) { - auto dir = makeDirectionFromPhiTheta(phi, theta); - m_params[eFreePos0] = pos4[ePos0]; - m_params[eFreePos1] = pos4[ePos1]; - m_params[eFreePos2] = pos4[ePos2]; - m_params[eFreeTime] = pos4[eTime]; - m_params[eFreeDir0] = dir[eMom0]; - m_params[eFreeDir1] = dir[eMom1]; - m_params[eFreeDir2] = dir[eMom2]; - m_params[eFreeQOverP] = qOverP; + /// Converts a free track parameter with a different hypothesis. + template + GenericFreeTrackParameters( + const GenericFreeTrackParameters& other) + : GenericFreeTrackParameters(other.parameters(), + other.particleHypothesis(), + other.covariance()) {} + + /// Converts an unknown bound track parameter. + template + static GenericFreeTrackParameters create( + const other_track_parameter_t& other) { + static_assert( + Concepts::FreeTrackParametersConcept); + + return GenericFreeTrackParameters( + other.parameters(), other.particleHypothesis(), other.covariance()); } /// Parameters are not default constructible due to the charge type. @@ -170,7 +147,7 @@ class GenericFreeTrackParameters { } /// Absolute momentum. Scalar absoluteMomentum() const { - return m_chargeInterpreter.extractMomentum(m_params[eFreeQOverP]); + return m_particleHypothesis.extractMomentum(m_params[eFreeQOverP]); } /// Transverse momentum. Scalar transverseMomentum() const { @@ -190,14 +167,19 @@ class GenericFreeTrackParameters { /// Particle electric charge. Scalar charge() const { - return m_chargeInterpreter.extractCharge(get()); + return m_particleHypothesis.extractCharge(get()); + } + + /// Particle hypothesis. + const ParticleHypothesis& particleHypothesis() const { + return m_particleHypothesis; } private: FreeVector m_params; std::optional m_cov; // TODO use [[no_unique_address]] once we switch to C++20 - charge_t m_chargeInterpreter; + ParticleHypothesis m_particleHypothesis; /// Print information to the output stream. friend std::ostream& operator<<(std::ostream& os, diff --git a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp index 3639699cabf..3c5e35d6634 100644 --- a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp +++ b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp @@ -14,6 +14,7 @@ #include "Acts/EventData/ChargeConcept.hpp" #include "Acts/Utilities/Concepts.hpp" +#include #include namespace Acts { @@ -83,8 +84,8 @@ class GenericParticleHypothesis { return m_chargeType.extractCharge(qOverP); } - /// Extracts the signed charge from the `q over p` track parameter using the - /// charge hypothesis. + /// Extracts the particle momentum from the `q over p` track parameter using + /// the charge hypothesis. /// /// @param qOverP the `q over p` track parameter. template @@ -107,6 +108,22 @@ class GenericParticleHypothesis { return m_chargeType; } + std::ostream& toStream(std::ostream& os) const { + os << "ParticleHypothesis{absPdg="; + if (auto shortString = pdgToShortAbsString(absolutePdg())) { + os << *shortString; + } else { + os << absolutePdg(); + } + os << ", mass=" << mass() << ", absCharge=" << absoluteCharge() << "}"; + return os; + } + + friend std::ostream& operator<<( + std::ostream& os, const GenericParticleHypothesis& particleHypothesis) { + return particleHypothesis.toStream(os); + } + private: PdgParticle m_absPdg; float m_mass; diff --git a/Core/include/Acts/EventData/Measurement.hpp b/Core/include/Acts/EventData/Measurement.hpp index 817a6938bb7..7e9eeb9cf64 100644 --- a/Core/include/Acts/EventData/Measurement.hpp +++ b/Core/include/Acts/EventData/Measurement.hpp @@ -41,7 +41,7 @@ namespace Acts { /// This means either, that there needs to be an additional variable type or /// that a pointer to a base object is stored (requiring a `dynamic_cast` later /// on). Both variants add additional complications. Since the geometry object -/// is not required anyways (as discussed above), not storing it removes all +/// is not required anyway (as discussed above), not storing it removes all /// these complications altogether. template class Measurement { diff --git a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp deleted file mode 100644 index 2182a6b2578..00000000000 --- a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp +++ /dev/null @@ -1,242 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2021 CERN for the benefit of the Acts project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/EventData/GenericBoundTrackParameters.hpp" -#include "Acts/Surfaces/Surface.hpp" - -#include -#include -#include -#include - -namespace Acts { - -/// This class is only a light wrapper around a surface and a vector of -/// parameters. Its main purpose is to provide many constructors for the -/// underlying vector. Most accessors are generated from the -/// GenericBoundTrackParameters equivalent and thus may be expensive -/// @tparam charge_t Helper type to interpret the particle charge/momentum -/// @note This class holds shared ownership on its reference surface. -/// @note The accessors for parameters, covariance, position, etc. -/// are the weighted means of the components. -/// @note If all covariances are zero, the accessor for the total -/// covariance does return std::nullopt; -/// TODO Add constructor from range and projector maybe? -template -class MultiComponentBoundTrackParameters { - using SingleParameters = GenericBoundTrackParameters; - - std::vector>> - m_components; - std::shared_ptr m_surface; - - // TODO use [[no_unique_address]] once we switch to C++20 - charge_t m_chargeInterpreter; - - /// Helper function to reduce the component vector to a single representation - template - auto reduce(projector_t&& proj) const { - using Ret = std::decay_t()))>; - - Ret ret; - - if constexpr (std::is_floating_point_v) { - ret = 0.0; - } else { - ret = Ret::Zero(); - } - - for (auto i = 0ul; i < m_components.size(); ++i) { - const auto [weight, single_pars] = (*this)[i]; - ret += weight * proj(single_pars); - } - - return ret; - } - - public: - using Scalar = typename SingleParameters::Scalar; - using ParametersVector = typename SingleParameters::ParametersVector; - using CovarianceMatrix = typename SingleParameters::CovarianceMatrix; - - /// Construct from multiple components with charge scalar q - template - MultiComponentBoundTrackParameters( - std::shared_ptr surface, - const std::vector>& cmps, - ActsScalar q) - : m_surface(std::move(surface)), m_chargeInterpreter(std::abs(q)) { - static_assert( - std::is_same_v || - std::is_same_v, covariance_t>); - if constexpr (std::is_same_v) { - for (const auto& [weight, params, cov] : cmps) { - m_components.push_back({weight, params, cov}); - } - } else { - m_components = cmps; - } - } - - /// Construct from multiple components with charge_t - template , int> = 0> - MultiComponentBoundTrackParameters( - std::shared_ptr surface, - const std::vector>& cmps) - : m_surface(std::move(surface)) { - static_assert( - std::is_same_v || - std::is_same_v, covariance_t>); - if constexpr (std::is_same_v) { - for (const auto& [weight, params, cov] : cmps) { - m_components.push_back({weight, params, cov}); - } - } else { - m_components = cmps; - } - } - - /// Construct from a parameters vector on the surface and particle charge. - /// - /// @param surface Reference surface the parameters are defined on - /// @param params Bound parameters vector - /// @param q Particle charge - /// @param cov Bound parameters covariance matrix - /// - /// In principle, only the charge magnitude is needed her to allow - /// unambiguous extraction of the absolute momentum. The particle charge is - /// required as an input here to be consistent with the other constructors - /// below that that also take the charge as an input. The charge sign is - /// only used in debug builds to check for consistency with the q/p - /// parameter. - MultiComponentBoundTrackParameters( - std::shared_ptr surface, const BoundVector& params, - ActsScalar q, std::optional cov = std::nullopt) - : m_surface(std::move(surface)), m_chargeInterpreter(std::abs(q)) { - m_components.push_back({1., params, std::move(cov)}); - } - - /// Construct from a parameters vector on the surface. - /// - /// @param surface Reference surface the parameters are defined on - /// @param params Bound parameters vector - /// @param cov Bound parameters covariance matrix - /// - /// This constructor is only available if there are no potential charge - /// ambiguities, i.e. the charge type is default-constructible. - template , int> = 0> - MultiComponentBoundTrackParameters( - std::shared_ptr surface, const BoundVector& params, - std::optional cov = std::nullopt) - : m_surface(std::move(surface)) { - m_components.push_back({1., params, std::move(cov)}); - } - - /// Parameters are not default constructible due to the charge type. - MultiComponentBoundTrackParameters() = delete; - MultiComponentBoundTrackParameters( - const MultiComponentBoundTrackParameters&) = default; - MultiComponentBoundTrackParameters(MultiComponentBoundTrackParameters&&) = - default; - ~MultiComponentBoundTrackParameters() = default; - MultiComponentBoundTrackParameters& operator=( - const MultiComponentBoundTrackParameters&) = default; - MultiComponentBoundTrackParameters& operator=( - MultiComponentBoundTrackParameters&&) = default; - - /// Access the parameters - const auto& components() const { return m_components; } - - /// Reference surface onto which the parameters are bound. - const Surface& referenceSurface() const { return *m_surface; } - - /// Get the weight and a GenericBoundTrackParameters object for one component - std::pair operator[](std::size_t i) const { - return std::make_pair( - std::get(m_components[i]), - SingleParameters( - m_surface, std::get(m_components[i]), - std::get>(m_components[i]))); - } - - /// Parameters vector. - ParametersVector parameters() const { - return reduce([](const SingleParameters& p) { return p.parameters(); }); - } - - /// Optional covariance matrix. - std::optional covariance() const { - const auto ret = reduce([](const SingleParameters& p) { - return p.covariance() ? *p.covariance() : CovarianceMatrix::Zero(); - }); - - if (ret == CovarianceMatrix::Zero()) { - return std::nullopt; - } else { - return ret; - } - } - - /// Space-time position four-vector. - /// - /// @param[in] geoCtx Geometry context for the local-to-global - /// transformation - Vector4 fourPosition(const GeometryContext& geoCtx) const { - return reduce( - [&](const SingleParameters& p) { return p.fourPosition(geoCtx); }); - } - - /// Spatial position three-vector. - /// - /// @param[in] geoCtx Geometry context for the local-to-global - /// transformation - Vector3 position(const GeometryContext& geoCtx) const { - return reduce( - [&](const SingleParameters& p) { return p.position(geoCtx); }); - } - - /// Time coordinate. - Scalar time() const { - return reduce([](const SingleParameters& p) { return p.time(); }); - } - - /// Unit direction three-vector, i.e. the normalized momentum - /// three-vector. - Vector3 direction() const { - return reduce([](const SingleParameters& p) { return p.direction(); }) - .normalized(); - } - - /// Absolute momentum. - Scalar absoluteMomentum() const { - return reduce( - [](const SingleParameters& p) { return p.absoluteMomentum(); }); - } - - /// Transverse momentum. - Scalar transverseMomentum() const { - return reduce( - [](const SingleParameters& p) { return p.transverseMomentum(); }); - } - - /// Momentum three-vector. - Vector3 momentum() const { - return reduce([](const SingleParameters& p) { return p.momentum(); }); - } - - /// Particle electric charge. - Scalar charge() const { - return reduce([](const SingleParameters& p) { return p.charge(); }); - } -}; - -} // namespace Acts diff --git a/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp new file mode 100644 index 00000000000..bc2a6a112f6 --- /dev/null +++ b/Core/include/Acts/EventData/MultiComponentTrackParameters.hpp @@ -0,0 +1,303 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include +#include +#include +#include + +namespace Acts { + +/// This class is only a light wrapper around a surface and a vector of +/// parameters. Its main purpose is to provide many constructors for the +/// underlying vector. Most accessors are generated from the +/// BoundTrackParameters equivalent and thus may be expensive +/// @note This class holds shared ownership on its reference surface. +/// @note The accessors for parameters, covariance, position, etc. +/// are the weighted means of the components. +/// @note If all covariances are zero, the accessor for the total +/// covariance does return std::nullopt; +/// TODO Add constructor from range and projector maybe? +class MultiComponentBoundTrackParameters { + public: + using Parameters = BoundTrackParameters; + using ParticleHypothesis = Parameters::ParticleHypothesis; + using Scalar = typename Parameters::Scalar; + using ParametersVector = typename Parameters::ParametersVector; + using CovarianceMatrix = typename Parameters::CovarianceMatrix; + + private: + std::vector>> + m_components; + std::shared_ptr m_surface; + + // TODO use [[no_unique_address]] once we switch to C++20 + ParticleHypothesis m_particleHypothesis; + + /// Helper function to reduce the component vector to a single representation + template + auto reduce(projector_t&& proj) const { + using Ret = std::decay_t()))>; + + Ret ret; + + if constexpr (std::is_floating_point_v) { + ret = 0.0; + } else { + ret = Ret::Zero(); + } + + for (auto i = 0ul; i < m_components.size(); ++i) { + const auto [weight, single_pars] = (*this)[i]; + ret += weight * proj(single_pars); + } + + return ret; + } + + public: + /// Construct from multiple components + template + MultiComponentBoundTrackParameters( + std::shared_ptr surface, + const std::vector>& cmps, + ParticleHypothesis particleHypothesis) + : m_surface(std::move(surface)), + m_particleHypothesis(particleHypothesis) { + static_assert( + std::is_same_v || + std::is_same_v, covariance_t>); + if constexpr (std::is_same_v) { + for (const auto& [weight, params, cov] : cmps) { + m_components.push_back({weight, params, cov}); + } + } else { + m_components = cmps; + } + } + + /// Construct from a parameters vector on the surface and particle charge. + /// + /// @param surface Reference surface the parameters are defined on + /// @param params Bound parameters vector + /// @param particleHypothesis Particle hypothesis for these parameters + /// @param cov Bound parameters covariance matrix + /// + /// In principle, only the charge magnitude is needed her to allow + /// unambiguous extraction of the absolute momentum. The particle charge is + /// required as an input here to be consistent with the other constructors + /// below that that also take the charge as an input. The charge sign is + /// only used in debug builds to check for consistency with the q/p + /// parameter. + MultiComponentBoundTrackParameters(std::shared_ptr surface, + const BoundVector& params, + std::optional cov, + ParticleHypothesis particleHypothesis) + : m_surface(std::move(surface)), + m_particleHypothesis(particleHypothesis) { + m_components.push_back({1., params, std::move(cov)}); + } + + /// Parameters are not default constructible due to the charge type. + MultiComponentBoundTrackParameters() = delete; + MultiComponentBoundTrackParameters( + const MultiComponentBoundTrackParameters&) = default; + MultiComponentBoundTrackParameters(MultiComponentBoundTrackParameters&&) = + default; + ~MultiComponentBoundTrackParameters() = default; + MultiComponentBoundTrackParameters& operator=( + const MultiComponentBoundTrackParameters&) = default; + MultiComponentBoundTrackParameters& operator=( + MultiComponentBoundTrackParameters&&) = default; + + /// Access the parameters + const auto& components() const { return m_components; } + + /// Reference surface onto which the parameters are bound. + const Surface& referenceSurface() const { return *m_surface; } + + /// Get the weight and a GenericBoundTrackParameters object for one component + std::pair operator[](std::size_t i) const { + return std::make_pair( + std::get(m_components[i]), + Parameters(m_surface, std::get(m_components[i]), + std::get>(m_components[i]), + m_particleHypothesis)); + } + + /// Parameters vector. + ParametersVector parameters() const { + return reduce([](const Parameters& p) { return p.parameters(); }); + } + + /// Optional covariance matrix. + std::optional covariance() const { + const auto ret = reduce([](const Parameters& p) { + return p.covariance() ? *p.covariance() : CovarianceMatrix::Zero(); + }); + + if (ret == CovarianceMatrix::Zero()) { + return std::nullopt; + } else { + return ret; + } + } + + /// Access a single parameter value identified by its index. + /// + /// @tparam kIndex Track parameter index + template + Scalar get() const { + return reduce([&](const Parameters& p) { return p.get(); }); + } + + /// Space-time position four-vector. + /// + /// @param[in] geoCtx Geometry context for the local-to-global + /// transformation + Vector4 fourPosition(const GeometryContext& geoCtx) const { + return reduce([&](const Parameters& p) { return p.fourPosition(geoCtx); }); + } + + /// Spatial position three-vector. + /// + /// @param[in] geoCtx Geometry context for the local-to-global + /// transformation + Vector3 position(const GeometryContext& geoCtx) const { + return reduce([&](const Parameters& p) { return p.position(geoCtx); }); + } + + /// Time coordinate. + Scalar time() const { + return reduce([](const Parameters& p) { return p.time(); }); + } + + /// Unit direction three-vector, i.e. the normalized momentum + /// three-vector. + Vector3 direction() const { + return reduce([](const Parameters& p) { return p.direction(); }) + .normalized(); + } + + /// Phi direction. + Scalar phi() const { return VectorHelpers::phi(direction()); } + + /// Theta direction. + Scalar theta() const { return VectorHelpers::theta(direction()); } + + /// Charge over momentum. + Scalar qOverP() const { return get(); } + + /// Absolute momentum. + Scalar absoluteMomentum() const { + return reduce([](const Parameters& p) { return p.absoluteMomentum(); }); + } + + /// Transverse momentum. + Scalar transverseMomentum() const { + return reduce([](const Parameters& p) { return p.transverseMomentum(); }); + } + + /// Momentum three-vector. + Vector3 momentum() const { + return reduce([](const Parameters& p) { return p.momentum(); }); + } + + /// Particle electric charge. + Scalar charge() const { + return reduce([](const Parameters& p) { return p.charge(); }); + } + + /// Particle hypothesis. + const ParticleHypothesis& particleHypothesis() const { + return m_particleHypothesis; + } +}; + +/// This class mimics the behaviour of the curvilinear parameters for ordinary +/// track parameters. To adopt this concept, a "common surface" is constructed, +/// and all parameters are projected onto this surface. The use of this is +/// questionable, and if the result is reasonable depends largely on the initial +/// multi component state. However, the propagator infrastructure forces the +/// existence of this type +/// @tparam charge_t Helper type to interpret the particle charge/momentum +class MultiComponentCurvilinearTrackParameters + : public MultiComponentBoundTrackParameters { + using covariance_t = BoundSquareMatrix; + + public: + using ConstructionTuple = std::tuple; + + private: + using Base = MultiComponentBoundTrackParameters; + + using BaseConstructionTuple = + std::tuple, + std::vector>>; + + /// We need this helper function in order to construct the base class properly + static BaseConstructionTuple construct( + const std::vector& curvi) { + // TODO where to get a geometry context here + Acts::GeometryContext gctx{}; + + // Construct and average surface + Acts::Vector3 avgPos = Acts::Vector3::Zero(); + Acts::Vector3 avgDir = Acts::Vector3::Zero(); + for (const auto& [w, pos4, dir, qop, cov] : curvi) { + avgPos += w * pos4.template segment<3>(0); + avgDir += w * dir; + } + + auto s = Surface::makeShared(avgPos, avgDir); + + std::vector> bound; + bound.reserve(curvi.size()); + + // Project the position onto the surface, keep everything else as is + for (const auto& [w, pos4, dir, qop, cov] : curvi) { + Vector3 newPos = + s->intersect(gctx, pos4.template segment<3>(eFreePos0), dir, false) + .closest() + .position(); + + BoundVector bv = + detail::transformFreeToCurvilinearParameters(pos4[eTime], dir, qop); + + // Because of the projection this should never fail + bv.template segment<2>(eBoundLoc0) = + *(s->globalToLocal(gctx, newPos, dir)); + + bound.emplace_back(w, bv, cov); + } + + return {s, bound}; + } + + /// Private constructor from a tuple + MultiComponentCurvilinearTrackParameters( + const BaseConstructionTuple& t, ParticleHypothesis particleHypothesis) + : Base(std::get<0>(t), std::get<1>(t), particleHypothesis) {} + + public: + MultiComponentCurvilinearTrackParameters( + const std::vector& cmps, + ParticleHypothesis particleHypothesis) + : MultiComponentCurvilinearTrackParameters(construct(cmps), + particleHypothesis) {} +}; + +} // namespace Acts diff --git a/Core/include/Acts/EventData/MultiTrajectory.hpp b/Core/include/Acts/EventData/MultiTrajectory.hpp index 14ad30532e9..5aa42d8cf88 100644 --- a/Core/include/Acts/EventData/MultiTrajectory.hpp +++ b/Core/include/Acts/EventData/MultiTrajectory.hpp @@ -971,9 +971,11 @@ class TrackStateProxy { }; /// Helper type that wraps two iterators -template +template class TrackStateRange { using ProxyType = TrackStateProxy; + using IndexType = typename ProxyType::IndexType; + static constexpr IndexType kInvalid = ProxyType::kInvalid; public: /// Iterator that wraps a track state proxy. The nullopt case signifies the @@ -991,12 +993,24 @@ class TrackStateRange { if (!proxy) { return *this; } - if (proxy->hasPrevious()) { - proxy = proxy->trajectory().getTrackState(proxy->previous()); - return *this; + if constexpr (reverse) { + if (proxy->hasPrevious()) { + proxy = proxy->trajectory().getTrackState(proxy->previous()); + return *this; + } else { + proxy = std::nullopt; + return *this; + } } else { - proxy = std::nullopt; - return *this; + IndexType next = + proxy->template component(); + if (next != kInvalid) { + proxy = proxy->trajectory().getTrackState(next); + return *this; + } else { + proxy = std::nullopt; + return *this; + } } } @@ -1132,9 +1146,9 @@ class MultiTrajectory { /// @param iendpoint Trajectory entry point to start from /// @return Iterator pair to iterate over /// @note Const version - auto trackStateRange(IndexType iendpoint) const { + auto reverseTrackStateRange(IndexType iendpoint) const { using range_t = - decltype(detail_lt::TrackStateRange{getTrackState(iendpoint)}); + detail_lt::TrackStateRange; if (iendpoint == kInvalid) { return range_t{}; } @@ -1142,14 +1156,15 @@ class MultiTrajectory { return range_t{getTrackState(iendpoint)}; } - /// Range for the track states from @p iendpoint to the trajectory start + /// Range for the track states from @p iendpoint to the trajectory start, + /// i.e from the outside in. /// @param iendpoint Trajectory entry point to start from /// @return Iterator pair to iterate over /// @note Mutable version template > - auto trackStateRange(IndexType iendpoint) { + auto reverseTrackStateRange(IndexType iendpoint) { using range_t = - decltype(detail_lt::TrackStateRange{getTrackState(iendpoint)}); + detail_lt::TrackStateRange; if (iendpoint == kInvalid) { return range_t{}; } @@ -1157,6 +1172,38 @@ class MultiTrajectory { return range_t{getTrackState(iendpoint)}; } + /// Range for the track states from @p istartpoint to the trajectory end, + /// i.e from inside out + /// @param istartpoint Trajectory state index for the innermost track + /// state to start from + /// @return Iterator pair to iterate over + /// @note Const version + auto forwardTrackStateRange(IndexType istartpoint) const { + using range_t = + detail_lt::TrackStateRange; + if (istartpoint == kInvalid) { + return range_t{}; + } + + return range_t{getTrackState(istartpoint)}; + } + + /// Range for the track states from @p istartpoint to the trajectory end, + /// i.e from inside out + /// @param istartpoint Trajectory state index for the innermost track + /// state to start from + /// @return Iterator pair to iterate over + template > + auto forwardTrackStateRange(IndexType istartpoint) { + using range_t = + detail_lt::TrackStateRange; + if (istartpoint == kInvalid) { + return range_t{}; + } + + return range_t{getTrackState(istartpoint)}; + } + /// Apply a function to all previous states starting at a given endpoint. /// /// @param iendpoint index of the last state diff --git a/Core/include/Acts/EventData/ParticleHypothesis.hpp b/Core/include/Acts/EventData/ParticleHypothesis.hpp index 198b7d625b9..3fb33b27664 100644 --- a/Core/include/Acts/EventData/ParticleHypothesis.hpp +++ b/Core/include/Acts/EventData/ParticleHypothesis.hpp @@ -43,7 +43,11 @@ class SinglyChargedParticleHypothesis return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); } static SinglyChargedParticleHypothesis electron() { - return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); + return SinglyChargedParticleHypothesis(PdgParticle::eElectron); + } + + static SinglyChargedParticleHypothesis chargedGeantino() { + return SinglyChargedParticleHypothesis(PdgParticle::eInvalid, 0); } }; @@ -68,6 +72,10 @@ class NeutralParticleHypothesis : public GenericParticleHypothesis { static NeutralParticleHypothesis pion0() { return NeutralParticleHypothesis(PdgParticle::ePionZero); } + + static NeutralParticleHypothesis geantino() { + return NeutralParticleHypothesis(PdgParticle::eInvalid, 0); + } }; /// Specialized particle hypothesis for non-neutral particles. @@ -101,6 +109,13 @@ class NonNeutralChargedParticleHypothesis return NonNeutralChargedParticleHypothesis(pion().absolutePdg(), pion().mass(), absQ); } + + static NonNeutralChargedParticleHypothesis chargedGeantino() { + return chargedGeantino(Acts::UnitConstants::e); + } + static NonNeutralChargedParticleHypothesis chargedGeantino(float absQ) { + return NonNeutralChargedParticleHypothesis(PdgParticle::eInvalid, 0, absQ); + } }; /// Specialized particle hypothesis for any kind of charged particles. @@ -138,6 +153,16 @@ class ParticleHypothesis : public GenericParticleHypothesis { static ParticleHypothesis pionLike(float absQ) { return ParticleHypothesis(pion().absolutePdg(), pion().mass(), absQ); } + + static ParticleHypothesis geantino() { + return NeutralParticleHypothesis::geantino(); + } + static ParticleHypothesis chargedGeantino() { + return chargedGeantino(Acts::UnitConstants::e); + } + static ParticleHypothesis chargedGeantino(float absQ) { + return ParticleHypothesis(PdgParticle::eInvalid, 0, absQ); + } }; } // namespace Acts diff --git a/Core/include/Acts/EventData/SpacePointData.hpp b/Core/include/Acts/EventData/SpacePointData.hpp index 73a2693dad9..712bf6ccaf2 100644 --- a/Core/include/Acts/EventData/SpacePointData.hpp +++ b/Core/include/Acts/EventData/SpacePointData.hpp @@ -39,12 +39,12 @@ class SpacePointData { ~SpacePointData() = default; /// @brief Getters - const float& quality(std::size_t idx) const; - const float& deltaR(std::size_t idx) const; + float quality(std::size_t idx) const; + float deltaR(std::size_t idx) const; /// @brief Setters - void setQuality(std::size_t idx, const float& value); - void setDeltaR(std::size_t idx, const float& value); + void setQuality(std::size_t idx, const float value); + void setDeltaR(std::size_t idx, const float value); /// @brief Resize vectors void resize(std::size_t n, bool resizeDynamic = false); @@ -99,21 +99,21 @@ class SpacePointData { std::vector m_topStripCenterPosition{}; }; -inline const float& SpacePointData::quality(std::size_t idx) const { +inline float SpacePointData::quality(std::size_t idx) const { return m_quality[idx]; } -inline const float& SpacePointData::deltaR(std::size_t idx) const { +inline float SpacePointData::deltaR(std::size_t idx) const { return m_deltaR[idx]; } -inline void SpacePointData::setQuality(std::size_t idx, const float& value) { +inline void SpacePointData::setQuality(std::size_t idx, const float value) { if (value > m_quality[idx]) { m_quality[idx] = value; } } -inline void SpacePointData::setDeltaR(std::size_t idx, const float& value) { +inline void SpacePointData::setDeltaR(std::size_t idx, const float value) { m_deltaR[idx] = value; } diff --git a/Core/include/Acts/EventData/TrackContainer.hpp b/Core/include/Acts/EventData/TrackContainer.hpp index 54a74bd0243..0db6e40df84 100644 --- a/Core/include/Acts/EventData/TrackContainer.hpp +++ b/Core/include/Acts/EventData/TrackContainer.hpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/Charge.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackContainerBackendConcept.hpp" #include "Acts/EventData/TrackProxy.hpp" @@ -286,14 +285,31 @@ class TrackContainer { } template > - auto trackStateRange(IndexType itrack) { + auto reverseTrackStateRange(IndexType itrack) { auto tip = component(itrack); - return m_traj->trackStateRange(tip); + return m_traj->reverseTrackStateRange(tip); } - auto trackStateRange(IndexType itrack) const { + auto reverseTrackStateRange(IndexType itrack) const { auto tip = component(itrack); - return m_traj->trackStateRange(tip); + return m_traj->reverseTrackStateRange(tip); + } + + template > + auto forwardTrackStateRange(IndexType itrack) { + auto stem = component(itrack); + if (stem == kInvalid) { + throw std::invalid_argument{"Track has no stem index"}; + } + return m_traj->forwardTrackStateRange(stem); + } + + auto forwardTrackStateRange(IndexType itrack) const { + auto stem = component(itrack); + if (stem == kInvalid) { + throw std::invalid_argument{"Track has no stem index"}; + } + return m_traj->forwardTrackStateRange(stem); } private: diff --git a/Core/include/Acts/EventData/TrackHelpers.hpp b/Core/include/Acts/EventData/TrackHelpers.hpp index a37fc920769..d88d1104850 100644 --- a/Core/include/Acts/EventData/TrackHelpers.hpp +++ b/Core/include/Acts/EventData/TrackHelpers.hpp @@ -33,7 +33,7 @@ void calculateTrackQuantities( track.nSharedHits() = 0; track.nOutliers() = 0; - for (const auto& trackState : track.trackStates()) { + for (const auto& trackState : track.trackStatesReversed()) { auto typeFlags = trackState.typeFlags(); if (typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) { diff --git a/Core/include/Acts/EventData/TrackParameters.hpp b/Core/include/Acts/EventData/TrackParameters.hpp index ad8f1b90ee1..c8f388ef858 100644 --- a/Core/include/Acts/EventData/TrackParameters.hpp +++ b/Core/include/Acts/EventData/TrackParameters.hpp @@ -8,21 +8,33 @@ #pragma once -#include "Acts/EventData/Charge.hpp" #include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/GenericFreeTrackParameters.hpp" +#include "Acts/EventData/ParticleHypothesis.hpp" namespace Acts { -using BoundTrackParameters = GenericBoundTrackParameters; -using CurvilinearTrackParameters = - GenericCurvilinearTrackParameters; -using FreeTrackParameters = GenericFreeTrackParameters; +using SinglyChargedBoundTrackParameters = + GenericBoundTrackParameters; +using SinglyChargedCurvilinearTrackParameters = + GenericCurvilinearTrackParameters; +using SinglyChargedFreeTrackParameters = + GenericFreeTrackParameters; -using NeutralBoundTrackParameters = GenericBoundTrackParameters; +using NeutralBoundTrackParameters = + GenericBoundTrackParameters; using NeutralCurvilinearTrackParameters = - GenericCurvilinearTrackParameters; -using NeutralFreeTrackParameters = GenericFreeTrackParameters; + GenericCurvilinearTrackParameters; +using NeutralFreeTrackParameters = + GenericFreeTrackParameters; + +/// @brief BoundTrackParameters can hold any kind of charge +using BoundTrackParameters = GenericBoundTrackParameters; +/// @brief CurvilinearTrackParameters can hold any kind of charge +using CurvilinearTrackParameters = + GenericCurvilinearTrackParameters; +/// @brief FreeTrackParameters can hold any kind of charge +using FreeTrackParameters = GenericFreeTrackParameters; } // namespace Acts diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index 9ea5ae13620..d9d8747a69f 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -9,10 +9,11 @@ #pragma once #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/Charge.hpp" #include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/ParticleHypothesis.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/Utilities/Concepts.hpp" +#include "Acts/Utilities/HashedString.hpp" #include "Acts/Utilities/UnitVectors.hpp" #include @@ -259,6 +260,14 @@ class TrackProxy { return component(hashString("tipIndex")); } + /// Index of the stem, i.e. the innermost track state of the track. + /// This might be invalid, signifying that the track state is not + /// forward-linked. + /// @return the stem index + IndexType stemIndex() const { + return component(hashString("stemIndex")); + } + /// Get a mutable reference to the tip index, i.e. the entry point into the /// track container /// @return mutable reference to the tip index @@ -267,6 +276,48 @@ class TrackProxy { return component(hashString("tipIndex")); } + /// Index of the stem, i.e. the innermost track state of the track. + /// This might be invalid, signifying that the track state is not + /// forward-linked. + /// @return mutable reference to the stem index + template > + IndexType& stemIndex() { + return component(hashString("stemIndex")); + } + + /// Return a const track state proxy to the innermost track state + /// @note This is only available, if the track is forward linked + /// @return The innermost track state proxy + auto innermostTrackState() const { + using proxy_t = decltype(m_container->trackStateContainer().getTrackState( + std::declval())); + + IndexType stem = component(hashString("stemIndex")); + if (stem == kInvalid) { + return std::optional{}; + } else { + return std::optional{ + m_container->trackStateContainer().getTrackState(stem)}; + } + } + + /// Return a mutable track state proxy to the innermost track state + /// @note This is only available, if the track is forward linked + /// @return The innermost track state proxy + template > + auto innermostTrackState() { + using proxy_t = decltype(m_container->trackStateContainer().getTrackState( + std::declval())); + + IndexType stem = component(hashString("stemIndex")); + if (stem == kInvalid) { + return std::optional{}; + } else { + return std::optional{ + m_container->trackStateContainer().getTrackState(stem)}; + } + } + /// Get the reference surface of the track (e.g. the perigee) /// @return the reference surface const Surface& referenceSurface() const { @@ -320,12 +371,6 @@ class TrackProxy { return m_container->covariance(m_index); } - ActsScalar charge() const { - // Currently, neutral tracks are not supported here - // @TODO: Evaluate if/how neutral 'tracks' should be accounted for - return SinglyCharged{}.extractCharge(parameters()[eBoundQOverP]); - } - /// Access the theta parameter of the track at the reference surface /// @return The theta parameter ActsScalar theta() const { @@ -362,10 +407,31 @@ class TrackProxy { return parameters()[eBoundQOverP]; } + /// Get the particle hypothesis + /// @return the particle hypothesis + ParticleHypothesis particleHypothesis() const { + return component(); + } + + /// Set a new particle hypothesis for this track + /// @param particleHypothesis The particle hypothesis to set + template > + void setParticleHypothesis(const ParticleHypothesis& particleHypothesis) { + m_container->container().setParticleHypothesis_impl(m_index, + particleHypothesis); + } + + /// Get the charge of the tack + /// @note this depends on the charge hypothesis + /// @return The absolute track momentum + ActsScalar charge() const { + return particleHypothesis().qFromQOP(qOverP()); + } + /// Get the absolute momentum of the tack /// @return The absolute track momentum ActsScalar absoluteMomentum() const { - return SinglyCharged{}.extractMomentum(qOverP()); + return particleHypothesis().extractMomentum(qOverP()); } /// Get the transverse momentum of the track @@ -388,17 +454,48 @@ class TrackProxy { /// Get a range over the track states of this track. Return value is /// compatible with range based for loop. Const version + /// @note This range is from the outside inwards! + /// @return Track state range to iterate over + auto trackStatesReversed() const { + return m_container->reverseTrackStateRange(m_index); + } + + /// Get a range over the track states of this track. Return value is + /// compatible with range based for loop. Mutable version + /// @note This range is from the outside inwards! + /// @return Track state range to iterate over + template > + auto trackStatesReversed() { + return m_container->reverseTrackStateRange(m_index); + } + + /// Get a range over the track states of this track. Return value is + /// compatible with range based for loop. Const version + /// @note This range is from the inside out! /// @return Track state range to iterate over auto trackStates() const { - return m_container->trackStateRange(m_index); + return m_container->forwardTrackStateRange(m_index); } /// Get a range over the track states of this track. Return value is /// compatible with range based for loop. Mutable version + /// @note This range is from the inside out! /// @return Track state range to iterate over template > auto trackStates() { - return m_container->trackStateRange(m_index); + return m_container->forwardTrackStateRange(m_index); + } + + /// Forward connect a track, i.e. set indices from the inside out + /// on all track states. + template > + void linkForward() { + IndexType last = kInvalid; + for (auto ts : trackStatesReversed()) { + ts.template component(hashString("next")) = last; + last = ts.index(); + } + stemIndex() = last; } /// Append a track state to this track. This will modify the tip index to @@ -426,7 +523,7 @@ class TrackProxy { // no tip index -> no track states return 0; } - auto tsRange = trackStates(); + auto tsRange = trackStatesReversed(); return std::distance(tsRange.begin(), tsRange.end()); } @@ -502,7 +599,7 @@ class TrackProxy { /// Return a mutable reference to the number of degrees of freedom for the /// track. Mutable version - /// @return The the number of degrees of freedom + /// @return The number of degrees of freedom template > unsigned int& nDoF() { return component(hashString("ndf")); @@ -530,7 +627,7 @@ class TrackProxy { /// Copy the content of another track proxy into this one /// @tparam track_proxy_t the other track proxy's type - /// @param other The the track proxy + /// @param other The track proxy /// @param copyTrackStates Copy the track state sequence from @p other template > @@ -540,7 +637,7 @@ class TrackProxy { if (copyTrackStates) { // append track states (cheap), but they're in the wrong order - for (const auto& srcTrackState : other.trackStates()) { + for (const auto& srcTrackState : other.trackStatesReversed()) { auto destTrackState = appendTrackState(srcTrackState.getMask()); if (srcTrackState.hasCalibrated()) { destTrackState.allocateCalibrated(srcTrackState.calibratedSize()); @@ -555,6 +652,7 @@ class TrackProxy { parameters() = other.parameters(); covariance() = other.covariance(); + setParticleHypothesis(other.particleHypothesis()); if (other.hasReferenceSurface()) { setReferenceSurface(other.referenceSurface().getSharedPtr()); } @@ -574,16 +672,34 @@ class TrackProxy { /// Afterwards, the previous endpoint of the track state sequence will be the /// "innermost" track state /// @note This is dangerous with branching track state sequences, as it will break them + /// @note This also automatically forward-links the track! + /// @param invertJacobians Whether to invert the Jacobians of the track states template > - void reverseTrackStates() { + void reverseTrackStates(bool invertJacobians = false) { IndexType current = tipIndex(); IndexType next = kInvalid; IndexType prev = kInvalid; + stemIndex() = tipIndex(); + + // @TODO: Maybe refactor to not need this variable if invertJacobians == false + BoundMatrix nextJacobian; + while (current != kInvalid) { auto ts = m_container->trackStateContainer().getTrackState(current); prev = ts.previous(); + ts.template component(hashString("next")) = prev; ts.previous() = next; + if (invertJacobians) { + if (next != kInvalid) { + BoundMatrix curJacobian = ts.jacobian(); + ts.jacobian() = nextJacobian.inverse(); + nextJacobian = curJacobian; + } else { + nextJacobian = ts.jacobian(); + ts.jacobian().setZero(); + } + } next = current; tipIndex() = current; current = prev; diff --git a/Core/include/Acts/EventData/TrackStatePropMask.hpp b/Core/include/Acts/EventData/TrackStatePropMask.hpp index 1523b5ab13b..2bba7d6932c 100644 --- a/Core/include/Acts/EventData/TrackStatePropMask.hpp +++ b/Core/include/Acts/EventData/TrackStatePropMask.hpp @@ -31,7 +31,6 @@ enum struct TrackStatePropMask : std::uint8_t { Filtered = 1 << 1, Smoothed = 1 << 2, Jacobian = 1 << 3, - Calibrated = 1 << 4, All = std::numeric_limits::max(), // should be all ones diff --git a/Core/include/Acts/EventData/VectorMultiTrajectory.hpp b/Core/include/Acts/EventData/VectorMultiTrajectory.hpp index 5d23f30379f..7cfb7684cc6 100644 --- a/Core/include/Acts/EventData/VectorMultiTrajectory.hpp +++ b/Core/include/Acts/EventData/VectorMultiTrajectory.hpp @@ -145,7 +145,6 @@ class VectorMultiTrajectoryBase { protected: struct IndexData { - IndexType iprevious = kInvalid; IndexType ipredicted = kInvalid; IndexType ifiltered = kInvalid; IndexType ismoothed = kInvalid; @@ -168,6 +167,7 @@ class VectorMultiTrajectoryBase { VectorMultiTrajectoryBase(const VectorMultiTrajectoryBase& other) : m_index{other.m_index}, m_previous{other.m_previous}, + m_next{other.m_next}, m_params{other.m_params}, m_cov{other.m_cov}, m_meas{other.m_meas}, @@ -209,6 +209,7 @@ class VectorMultiTrajectoryBase { return instance.m_sourceLinks[instance.m_index[istate].iuncalibrated] .has_value(); case "previous"_hash: + case "next"_hash: case "referenceSurface"_hash: case "measdim"_hash: case "chi2"_hash: @@ -230,7 +231,9 @@ class VectorMultiTrajectoryBase { using namespace Acts::HashedStringLiteral; switch (key) { case "previous"_hash: - return &instance.m_index[istate].iprevious; + return &instance.m_previous[istate]; + case "next"_hash: + return &instance.m_next[istate]; case "predicted"_hash: return &instance.m_index[istate].ipredicted; case "filtered"_hash: @@ -272,6 +275,7 @@ class VectorMultiTrajectoryBase { case "jacobian"_hash: case "projector"_hash: case "previous"_hash: + case "next"_hash: case "uncalibratedSourceLink"_hash: case "referenceSurface"_hash: case "measdim"_hash: @@ -303,6 +307,7 @@ class VectorMultiTrajectoryBase { /// index to map track states to the corresponding std::vector m_index; std::vector m_previous; + std::vector m_next; std::vector::Coefficients> m_params; std::vector::Covariance> m_cov; diff --git a/Core/include/Acts/EventData/VectorTrackContainer.hpp b/Core/include/Acts/EventData/VectorTrackContainer.hpp index 9763cb3f4bc..ae7fab530e4 100644 --- a/Core/include/Acts/EventData/VectorTrackContainer.hpp +++ b/Core/include/Acts/EventData/VectorTrackContainer.hpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/ParticleHypothesis.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackContainerBackendConcept.hpp" #include "Acts/EventData/detail/DynamicColumn.hpp" @@ -73,6 +74,10 @@ class VectorTrackContainerBase { switch (key) { case "tipIndex"_hash: return &instance.m_tipIndex[itrack]; + case "stemIndex"_hash: + return &instance.m_stemIndex[itrack]; + case "particleHypothesis"_hash: + return &instance.m_particleHypothesis[itrack]; case "params"_hash: return &instance.m_params[itrack]; case "cov"_hash: @@ -110,6 +115,10 @@ class VectorTrackContainerBase { bool result = true; result = result && m_tipIndex.size() == size; assert(result); + result = result && m_stemIndex.size() == size; + assert(result); + result = result && m_particleHypothesis.size() == size; + assert(result); result = result && m_params.size() == size; assert(result); result = result && m_cov.size() == size; @@ -156,6 +165,8 @@ class VectorTrackContainerBase { // END INTERFACE HELPER std::vector m_tipIndex; + std::vector m_stemIndex; + std::vector m_particleHypothesis; std::vector::Coefficients> m_params; std::vector::Covariance> m_cov; std::vector> m_referenceSurfaces; @@ -241,6 +252,11 @@ class VectorTrackContainer final : public detail_vtc::VectorTrackContainerBase { m_referenceSurfaces[itrack] = std::move(surface); } + void setParticleHypothesis_impl( + IndexType itrack, const ParticleHypothesis& particleHypothesis) { + m_particleHypothesis[itrack] = particleHypothesis; + } + // END INTERFACE }; diff --git a/Core/include/Acts/Geometry/AbstractVolume.hpp b/Core/include/Acts/Geometry/AbstractVolume.hpp index 4375e31448f..d206c583eaa 100644 --- a/Core/include/Acts/Geometry/AbstractVolume.hpp +++ b/Core/include/Acts/Geometry/AbstractVolume.hpp @@ -73,4 +73,4 @@ class AbstractVolume : public Volume { std::vector m_boundarySurfaces; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/ApproachDescriptor.hpp b/Core/include/Acts/Geometry/ApproachDescriptor.hpp index 246e1a11d48..662cbc0faac 100644 --- a/Core/include/Acts/Geometry/ApproachDescriptor.hpp +++ b/Core/include/Acts/Geometry/ApproachDescriptor.hpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Intersection.hpp" #include @@ -42,11 +43,17 @@ class ApproachDescriptor { /// @param position is the position from start of the search /// @param direction is the direction at the start of the search /// @param bcheck is the boundary check directive + /// @param pLimit The path limit + /// @param oLimit The overstep limit + /// @param tolerance The surface tolerance /// /// @return is a surface intersection - virtual ObjectIntersection approachSurface( - const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const = 0; + virtual SurfaceIntersection approachSurface(const GeometryContext& gctx, + const Vector3& position, + const Vector3& direction, + const BoundaryCheck& bcheck, + double pLimit, double oLimit, + double tolerance) const = 0; /// Get all the contained surfaces /// @return all contained surfaces of this approach descriptor diff --git a/Core/include/Acts/Geometry/ConeLayer.hpp b/Core/include/Acts/Geometry/ConeLayer.hpp index a9a6346bb17..bd24bef9fe1 100644 --- a/Core/include/Acts/Geometry/ConeLayer.hpp +++ b/Core/include/Acts/Geometry/ConeLayer.hpp @@ -85,4 +85,4 @@ class ConeLayer : virtual public ConeSurface, public Layer { ConeLayer(const ConeLayer& cla, const Transform3& shift); }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/CuboidVolumeBounds.hpp b/Core/include/Acts/Geometry/CuboidVolumeBounds.hpp index f9464337f8a..ad0a1ed62e3 100644 --- a/Core/include/Acts/Geometry/CuboidVolumeBounds.hpp +++ b/Core/include/Acts/Geometry/CuboidVolumeBounds.hpp @@ -189,4 +189,4 @@ stream_t& CuboidVolumeBounds::dumpT(stream_t& dt) const { << get(eHalfLengthZ) << ")"; return dt; } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/CylinderLayer.hpp b/Core/include/Acts/Geometry/CylinderLayer.hpp index 2493a31f785..ba99af59a9b 100644 --- a/Core/include/Acts/Geometry/CylinderLayer.hpp +++ b/Core/include/Acts/Geometry/CylinderLayer.hpp @@ -93,4 +93,4 @@ class CylinderLayer : public CylinderSurface, public Layer { CylinderLayer(const CylinderLayer& cla, const Transform3& shift); }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/CylinderVolumeBuilder.hpp b/Core/include/Acts/Geometry/CylinderVolumeBuilder.hpp index 87c96de0884..677facfea40 100644 --- a/Core/include/Acts/Geometry/CylinderVolumeBuilder.hpp +++ b/Core/include/Acts/Geometry/CylinderVolumeBuilder.hpp @@ -425,7 +425,7 @@ struct WrappingConfig { sl << "New container built with configuration: " << containerVolumeConfig.toString() << '\n'; } - // go through the new new ones first + // go through the new ones first if (nVolumeConfig) { sl << " - n: Negative Endcap, current configuration: " << nVolumeConfig.toString() << '\n'; @@ -483,7 +483,7 @@ class CylinderVolumeBuilder : public ITrackingVolumeBuilder { struct Config { /// The tracking volume helper for construction std::shared_ptr trackingVolumeHelper = nullptr; - /// The string based indenfication + /// The string based identification std::string volumeName = ""; /// The world material std::shared_ptr volumeMaterial = nullptr; diff --git a/Core/include/Acts/Geometry/GenericApproachDescriptor.hpp b/Core/include/Acts/Geometry/GenericApproachDescriptor.hpp index fae586b6b01..2c69d92d068 100644 --- a/Core/include/Acts/Geometry/GenericApproachDescriptor.hpp +++ b/Core/include/Acts/Geometry/GenericApproachDescriptor.hpp @@ -12,6 +12,7 @@ #include "Acts/Geometry/ApproachDescriptor.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/Intersection.hpp" @@ -28,10 +29,6 @@ class Surface; /// /// Class to decide and return which approaching surface to be taken, /// it's a generic descriptor for n surfaces -/// -/// It is templated in order to allow for BoundarySurfaces from -/// representing volumes of layers to be re-used - class GenericApproachDescriptor : public ApproachDescriptor { public: /// A generic approach descriptor for new Acts::Surface objects @@ -60,11 +57,17 @@ class GenericApproachDescriptor : public ApproachDescriptor { /// @param position The global position to start the approach from /// @param direction The momentum vector /// @param bcheck The boundary check prescription + /// @param pLimit The path limit + /// @param oLimit The overstep limit + /// @param tolerance The surface tolerance /// - /// @return : a SurfaceIntersection - ObjectIntersection approachSurface( - const GeometryContext& gctx, const Vector3& position, - const Vector3& direction, const BoundaryCheck& bcheck) const override; + /// @return : a @c SurfaceIntersection + SurfaceIntersection approachSurface(const GeometryContext& gctx, + const Vector3& position, + const Vector3& direction, + const BoundaryCheck& bcheck, + double pLimit, double oLimit, + double tolerance) const override; /// return all contained surfaces of this approach descriptor const std::vector& containedSurfaces() const override; @@ -84,4 +87,4 @@ class GenericApproachDescriptor : public ApproachDescriptor { std::vector m_surfaceCache; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/GeometryIdentifier.hpp b/Core/include/Acts/Geometry/GeometryIdentifier.hpp index 29d20da89ac..685cf48a19f 100644 --- a/Core/include/Acts/Geometry/GeometryIdentifier.hpp +++ b/Core/include/Acts/Geometry/GeometryIdentifier.hpp @@ -53,6 +53,8 @@ class GeometryIdentifier { constexpr Value layer() const { return getBits(kLayerMask); } /// Return the approach identifier. constexpr Value approach() const { return getBits(kApproachMask); } + /// Return the approach identifier. + constexpr Value passive() const { return getBits(kApproachMask); } /// Return the sensitive identifier. constexpr Value sensitive() const { return getBits(kSensitiveMask); } /// Return the extra identifier @@ -76,6 +78,10 @@ class GeometryIdentifier { constexpr GeometryIdentifier& setApproach(Value approach) { return setBits(kApproachMask, approach); } + /// Set the approach identifier - shared with Passive + constexpr GeometryIdentifier& setPassive(Value approach) { + return setBits(kApproachMask, approach); + } /// Set the sensitive identifier. constexpr GeometryIdentifier& setSensitive(Value sensitive) { return setBits(kSensitiveMask, sensitive); @@ -95,6 +101,7 @@ class GeometryIdentifier { static constexpr Value kLayerMask = 0x0000fff000000000; /// (2^8)-1 = 255 approach surfaces static constexpr Value kApproachMask = 0x0000000ff0000000; + static constexpr Value kPassiveMask = kApproachMask; /// (2^20)-1 = 1048575 sensitive surfaces static constexpr Value kSensitiveMask = 0x000000000fffff00; /// (2^8)-1 = 255 extra values diff --git a/Core/include/Acts/Geometry/IConfinedTrackingVolumeBuilder.hpp b/Core/include/Acts/Geometry/IConfinedTrackingVolumeBuilder.hpp index 22d44a5f4e1..30550a20e69 100644 --- a/Core/include/Acts/Geometry/IConfinedTrackingVolumeBuilder.hpp +++ b/Core/include/Acts/Geometry/IConfinedTrackingVolumeBuilder.hpp @@ -30,4 +30,4 @@ class IConfinedTrackingVolumeBuilder { virtual const std::string& identification() const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/ILayerArrayCreator.hpp b/Core/include/Acts/Geometry/ILayerArrayCreator.hpp index 20fe6e5210f..602c8b9397f 100644 --- a/Core/include/Acts/Geometry/ILayerArrayCreator.hpp +++ b/Core/include/Acts/Geometry/ILayerArrayCreator.hpp @@ -52,4 +52,4 @@ class ILayerArrayCreator { double max, BinningType btype = arbitrary, BinningValue bvalue = binX) const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/ITrackingGeometryBuilder.hpp b/Core/include/Acts/Geometry/ITrackingGeometryBuilder.hpp index ddfd2a3e169..de0b2349e32 100644 --- a/Core/include/Acts/Geometry/ITrackingGeometryBuilder.hpp +++ b/Core/include/Acts/Geometry/ITrackingGeometryBuilder.hpp @@ -38,4 +38,4 @@ class ITrackingGeometryBuilder { virtual std::unique_ptr trackingGeometry( const GeometryContext& gctx) const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/ITrackingVolumeArrayCreator.hpp b/Core/include/Acts/Geometry/ITrackingVolumeArrayCreator.hpp index 91cc60e37bf..003206d7c50 100644 --- a/Core/include/Acts/Geometry/ITrackingVolumeArrayCreator.hpp +++ b/Core/include/Acts/Geometry/ITrackingVolumeArrayCreator.hpp @@ -52,9 +52,9 @@ class ITrackingVolumeArrayCreator { /// @param vols are the TrackingVolumes ordered in a tracker /// @param bVal is the binning value for the volume binning /// - /// @return sahred pointer to a new TrackingVolumeArray + /// @return shared pointer to a new TrackingVolumeArray virtual std::shared_ptr trackingVolumeArray( const GeometryContext& gctx, const TrackingVolumeVector& vols, BinningValue bVal) const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/LayerArrayCreator.hpp b/Core/include/Acts/Geometry/LayerArrayCreator.hpp index 6f87d56b1bb..22ce71c801b 100644 --- a/Core/include/Acts/Geometry/LayerArrayCreator.hpp +++ b/Core/include/Acts/Geometry/LayerArrayCreator.hpp @@ -87,4 +87,4 @@ class LayerArrayCreator : public ILayerArrayCreator { double offset) const; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/LayerCreator.hpp b/Core/include/Acts/Geometry/LayerCreator.hpp index 607a8010624..8bb579405d6 100644 --- a/Core/include/Acts/Geometry/LayerCreator.hpp +++ b/Core/include/Acts/Geometry/LayerCreator.hpp @@ -34,7 +34,7 @@ using MutableLayerPtr = std::shared_ptr; /// @class LayerCreator /// -/// The LayerCreator is able to build cylinde,r disc layers or plane layers from +/// The LayerCreator is able to build cylinder disc layers or plane layers from /// detector elements /// class LayerCreator { @@ -46,9 +46,9 @@ class LayerCreator { struct Config { /// surface array helper std::shared_ptr surfaceArrayCreator = nullptr; - /// cylinder module z tolerance : it counts at same z, if ... + /// cylinder module z tolerance: it counts as same z, if ... double cylinderZtolerance{10.}; - /// cylinder module phi tolerance : it counts at same phi, if ... + /// cylinder module phi tolerance: it counts as same phi, if ... double cylinderPhiTolerance{0.1}; /// standard constructor Config() = default; @@ -203,7 +203,7 @@ class LayerCreator { /// @param newLogger the logger instance void setLogger(std::unique_ptr newLogger); - // associate surfaces contained by this layer to this layer + /// associate surfaces contained by this layer to this layer void associateSurfacesToLayer(Layer& layer) const; private: diff --git a/Core/include/Acts/Geometry/PlaneLayer.hpp b/Core/include/Acts/Geometry/PlaneLayer.hpp index c0a58cf6e73..e7fd4bfe90d 100644 --- a/Core/include/Acts/Geometry/PlaneLayer.hpp +++ b/Core/include/Acts/Geometry/PlaneLayer.hpp @@ -87,4 +87,4 @@ class PlaneLayer : virtual public PlaneSurface, public Layer { PlaneLayer(const PlaneLayer& pla, const Transform3& shift); }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/TrackingVolume.hpp b/Core/include/Acts/Geometry/TrackingVolume.hpp index 7629e0745bb..d344342bb2b 100644 --- a/Core/include/Acts/Geometry/TrackingVolume.hpp +++ b/Core/include/Acts/Geometry/TrackingVolume.hpp @@ -68,13 +68,18 @@ using MutableTrackingVolumeVector = std::vector; using LayerArray = BinnedArray; using LayerVector = std::vector; -// Intersection with Layer +/// Intersection with @c Layer using LayerIntersection = ObjectIntersection; +/// Multi-intersection with @c Layer +using LayerMultiIntersection = ObjectMultiIntersection; /// BoundarySurface of a volume using BoundarySurface = BoundarySurfaceT; /// Intersection with a @c BoundarySurface using BoundaryIntersection = ObjectIntersection; +/// Multi-intersection with a @c BoundarySurface +using BoundaryMultiIntersection = + ObjectMultiIntersection; /// @class TrackingVolume /// diff --git a/Core/include/Acts/Geometry/TrackingVolumeArrayCreator.hpp b/Core/include/Acts/Geometry/TrackingVolumeArrayCreator.hpp index bf1c8598942..44ca5d78602 100644 --- a/Core/include/Acts/Geometry/TrackingVolumeArrayCreator.hpp +++ b/Core/include/Acts/Geometry/TrackingVolumeArrayCreator.hpp @@ -69,4 +69,4 @@ class TrackingVolumeArrayCreator : public ITrackingVolumeArrayCreator { /// logging instance std::unique_ptr m_logger; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Geometry/TrapezoidVolumeBounds.hpp b/Core/include/Acts/Geometry/TrapezoidVolumeBounds.hpp index 02d638af0f5..adf211cb42b 100644 --- a/Core/include/Acts/Geometry/TrapezoidVolumeBounds.hpp +++ b/Core/include/Acts/Geometry/TrapezoidVolumeBounds.hpp @@ -201,4 +201,4 @@ inline void TrapezoidVolumeBounds::checkConsistency() noexcept(false) { } } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Material/AccumulatedMaterialSlab.hpp b/Core/include/Acts/Material/AccumulatedMaterialSlab.hpp index 4f41a2e50a7..d726df1dd2e 100644 --- a/Core/include/Acts/Material/AccumulatedMaterialSlab.hpp +++ b/Core/include/Acts/Material/AccumulatedMaterialSlab.hpp @@ -54,7 +54,7 @@ class AccumulatedMaterialSlab { /// @param useEmptyTrack indicate whether to consider an empty track store /// /// The material variance can be used to optimised the mapping process as it - /// should be inversly proportionnal to the map quality + /// should be inversely proportional to the map quality void trackVariance(MaterialSlab slabReference, bool useEmptyTrack = false); /// Add the accumulated material for the current track to the total average. @@ -88,7 +88,7 @@ class AccumulatedMaterialSlab { /// Only contains the information up to the last `.trackVariance(...)` call. /// If there have been additional calls to `.accumulate(...)` afterwards, the /// information is not part of the total average. The number of tracks is only - /// opdated on the call of `.trackAverage(...)` + /// updated on the call of `.trackAverage(...)` std::pair totalVariance() const; private: diff --git a/Core/include/Acts/Material/MaterialInteraction.hpp b/Core/include/Acts/Material/MaterialInteraction.hpp index bc59a08de2c..7b310d3245d 100644 --- a/Core/include/Acts/Material/MaterialInteraction.hpp +++ b/Core/include/Acts/Material/MaterialInteraction.hpp @@ -9,13 +9,52 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" #include "Acts/Material/MaterialSlab.hpp" namespace Acts { class Surface; -class TrackingVolume; + +/// @brief The Material interaction volume struct +/// It acts as a switch between detctor and tracking volume +/// as long as those co-exist alongside +struct InteractionVolume { + /// The tracking volume + const TrackingVolume* trackingVolume = nullptr; + /// The detector volume + const Experimental::DetectorVolume* detectorVolume = nullptr; + + /// Empty constructor + InteractionVolume() = default; + + /// Constructor from tracking volume + /// @param tv The tracking volume + InteractionVolume(const TrackingVolume* tv) : trackingVolume(tv) {} + + /// Constructor from detector volume + /// @param dv The detector volume + InteractionVolume(const Experimental::DetectorVolume* dv) + : detectorVolume(dv) {} + + /// Forward the geometry identifier + GeometryIdentifier geometryId() const { + if (trackingVolume != nullptr) { + return trackingVolume->geometryId(); + } else if (detectorVolume != nullptr) { + return detectorVolume->geometryId(); + } else { + return GeometryIdentifier(); + } + } + + /// Check if the volume is valid + bool empty() const { + return trackingVolume == nullptr and detectorVolume == nullptr; + } +}; /// @brief The Material interaction struct /// It records the surface and the passed material @@ -42,7 +81,7 @@ struct MaterialInteraction { /// The surface where the interaction occurred. const Surface* surface = nullptr; /// The volume where the interaction occurred. - const TrackingVolume* volume = nullptr; + InteractionVolume volume{}; /// Update the volume step to implement the proper step size bool updatedVolumeStep = false; /// The path correction factor due to non-zero incidence on the surface. diff --git a/Core/include/Acts/Navigation/DetectorNavigator.hpp b/Core/include/Acts/Navigation/DetectorNavigator.hpp index 60f64ad2065..cdde93f8858 100644 --- a/Core/include/Acts/Navigation/DetectorNavigator.hpp +++ b/Core/include/Acts/Navigation/DetectorNavigator.hpp @@ -114,6 +114,10 @@ class DetectorNavigator { bool targetReached(const State& state) const { return state.targetReached; } + bool endOfWorldReached(State& state) const { + return state.currentVolume == nullptr; + } + bool navigationBreak(const State& state) const { return state.navigationBreak; } @@ -137,7 +141,7 @@ class DetectorNavigator { /// Initialize call - start of propagation /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -177,6 +181,7 @@ class DetectorNavigator { << posInfo(state, stepper) << "Entering navigator::preStep."); auto& nState = state.navigation; + fillNavigationState(state, stepper, nState); if (inactive()) { ACTS_VERBOSE(volInfo(state) @@ -374,7 +379,7 @@ class DetectorNavigator { /// - attempted volume switch /// Target finding by association will not be done again /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -406,6 +411,18 @@ class DetectorNavigator { } nState.currentVolume->updateNavigationState(state.geoContext, nState); + + // Sort properly the surface candidates + auto& nCandidates = nState.surfaceCandidates; + std::sort(nCandidates.begin(), nCandidates.end(), + [&](const auto& a, const auto& b) { + // The two path lengths + ActsScalar pathToA = a.objectIntersection.pathLength(); + ActsScalar pathToB = b.objectIntersection.pathLength(); + return pathToA < pathToB; + }); + // Set the surface candidate + nState.surfaceCandidate = nCandidates.begin(); } template diff --git a/Core/include/Acts/Navigation/MultiLayerSurfacesUpdator.hpp b/Core/include/Acts/Navigation/MultiLayerSurfacesUpdator.hpp new file mode 100644 index 00000000000..95e90066c77 --- /dev/null +++ b/Core/include/Acts/Navigation/MultiLayerSurfacesUpdator.hpp @@ -0,0 +1,145 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Navigation/NavigationDelegates.hpp" +#include "Acts/Navigation/NavigationStateFillers.hpp" +#include "Acts/Navigation/NavigationStateUpdators.hpp" +#include "Acts/Utilities/VectorHelpers.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { + +template +class MultiLayerSurfacesUpdatorImpl : public INavigationDelegate { + public: + /// Broadcast the grid type + using grid_type = grid_t; + + /// The grid where the indices are stored + grid_type grid; + + /// The path generator + path_generator pgenerator; + + /// These are the cast parameters - copied from constructor + std::array casts{}; + + /// A transform to be applied to the position + Transform3 transform = Transform3::Identity(); + + /// @brief Constructor for a grid based surface attacher + /// @param igrid the grid that is moved into this attacher + /// @param icasts is the cast values array + /// @param itr a transform applied to the global position + MultiLayerSurfacesUpdatorImpl( + grid_type&& igrid, const std::array& icasts, + const Transform3& itr = Transform3::Identity()) + : grid(std::move(igrid)), casts(icasts), transform(itr) {} + + MultiLayerSurfacesUpdatorImpl() = delete; + + void update(const GeometryContext& gctx, NavigationState& nState) const { + auto step = std::sqrt(std::pow(grid.binWidth()[0], 2) + + std::pow(grid.binWidth()[1], 2)); + auto path = pgenerator(nState.position, nState.direction, step, + grid.numLocalBins()[1]); + + std::vector surfCandidates = {}; + + for (const auto& p : path) { + const auto& entry = grid.atPosition(castPosition(p)); + const auto extracted = + IndexedSurfacesExtractor::extract(gctx, nState, entry); + surfCandidates.insert(surfCandidates.end(), extracted.begin(), + extracted.end()); + } + + resolveDuplicates(gctx, surfCandidates); + SurfacesFiller::fill(nState, surfCandidates); + + updateCandidates(gctx, nState); + } + + /// Cast into a lookup position + /// + /// @param position is the position of the update call + std::array castPosition( + const Vector3& position) const { + // Transform into local 3D frame + Vector3 tposition = transform * position; + + std::array casted{}; + fillCasts(tposition, casted, + std::make_integer_sequence{}); + return casted; + } + + /// Resolve duplicate on surface candidates + /// + /// @param gctx The geometry context of the current geometry + /// @param surfaces is the surface candidates to check and resolve for duplicates + void resolveDuplicates(const GeometryContext& gctx, + std::vector& surfaces) const { + // sorting the surfaces according to their radial distance + std::sort(surfaces.begin(), surfaces.end(), + [&gctx](const auto& surf1, const auto& surf2) { + if (surf1->center(gctx).x() != surf2->center(gctx).x()) { + return surf1->center(gctx).x() < surf2->center(gctx).x(); + } + if (surf1->center(gctx).y() != surf2->center(gctx).y()) { + return surf1->center(gctx).y() < surf2->center(gctx).y(); + } + return surf1->center(gctx).z() < surf2->center(gctx).z(); + }); + + // Remove the duplicates + surfaces.erase(std::unique(surfaces.begin(), surfaces.end()), + surfaces.end()); + } + + private: + /// 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, casts[idx])), ...); + } +}; + +struct PathGridSurfacesGenerator { + std::vector operator()(Vector3 startPosition, + const Vector3& direction, ActsScalar stepSize, + std::size_t numberOfSteps) const { + std::vector pathCoordinates = {}; + pathCoordinates.reserve(numberOfSteps); + + auto tposition = std::move(startPosition); + auto stepSizeY = stepSize * sin(Acts::VectorHelpers::phi(direction)); + auto stepSizeX = stepSize * cos(Acts::VectorHelpers::phi(direction)); + + for (std::size_t i = 0; i < numberOfSteps; i++) { + pathCoordinates.push_back(tposition); + tposition.y() = tposition.y() + stepSizeY; + tposition.x() = tposition.x() + stepSizeX; + } + + return pathCoordinates; + } +}; + +} // namespace Experimental + +} // namespace Acts diff --git a/Core/include/Acts/Navigation/NavigationStateFillers.hpp b/Core/include/Acts/Navigation/NavigationStateFillers.hpp index db4d5b8db81..9fcda96bca7 100644 --- a/Core/include/Acts/Navigation/NavigationStateFillers.hpp +++ b/Core/include/Acts/Navigation/NavigationStateFillers.hpp @@ -51,7 +51,7 @@ struct SurfacesFiller { const std::vector& surfaces) { std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { nState.surfaceCandidates.push_back(NavigationState::SurfaceCandidate{ - ObjectIntersection{}, s, nullptr, + ObjectIntersection::invalid(), s, nullptr, nState.surfaceBoundaryCheck}); }); } @@ -68,7 +68,7 @@ struct PortalsFiller { const std::vector& portals) { std::for_each(portals.begin(), portals.end(), [&](const auto& p) { nState.surfaceCandidates.push_back(NavigationState::SurfaceCandidate{ - ObjectIntersection{}, nullptr, p, true}); + ObjectIntersection::invalid(), nullptr, p, true}); }); } }; diff --git a/Core/include/Acts/Navigation/NavigationStateUpdators.hpp b/Core/include/Acts/Navigation/NavigationStateUpdators.hpp index 3f5dc4cb3d8..b1cdb1278e1 100644 --- a/Core/include/Acts/Navigation/NavigationStateUpdators.hpp +++ b/Core/include/Acts/Navigation/NavigationStateUpdators.hpp @@ -10,7 +10,9 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" +#include "Acts/Detector/Portal.hpp" #include "Acts/Navigation/NavigationDelegates.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/IAxis.hpp" @@ -20,8 +22,37 @@ #include namespace Acts { + namespace Experimental { +/// Helper method to update the candidates (portals/surfaces), +/// this can be called for initial surface/portal estimation, +/// but also during the navigation to update the current list +/// of candidates. +/// +/// @param gctx is the Geometry context of this call +/// @param nState [in,out] is the navigation state to be updated +/// +/// @todo for surfaces skip the non-reached ones, while keep for portals +inline void updateCandidates(const GeometryContext& gctx, + NavigationState& nState) { + const auto& position = nState.position; + const auto& direction = nState.direction; + auto& nCandidates = nState.surfaceCandidates; + + for (auto& c : nCandidates) { + // Get the surface representation: either native surfcae of portal + const Surface& sRep = + c.surface != nullptr ? *c.surface : c.portal->surface(); + + // Get the intersection @todo make a templated intersector + // TODO surface tolerance + auto sIntersection = sRep.intersect(gctx, position, direction, + c.boundaryCheck, s_onSurfaceTolerance); + c.objectIntersection = sIntersection[c.objectIntersection.index()]; + } +} + /// @brief This sets a single object, e.g. single surface or single volume /// @tparam object_type the type of the object to be filled /// @tparam filler_type is the helper to fill the object into nState @@ -121,6 +152,8 @@ class IndexedUpdatorImpl : public INavigationDelegate { const auto& entry = grid.atPosition(castPosition(nState.position)); auto extracted = extractor.extract(gctx, nState, entry); filler_type::fill(nState, extracted); + + updateCandidates(gctx, nState); } /// Cast into a lookup position diff --git a/Core/include/Acts/Navigation/SurfaceCandidatesUpdators.hpp b/Core/include/Acts/Navigation/SurfaceCandidatesUpdators.hpp index 53379492170..f516d29e766 100644 --- a/Core/include/Acts/Navigation/SurfaceCandidatesUpdators.hpp +++ b/Core/include/Acts/Navigation/SurfaceCandidatesUpdators.hpp @@ -13,6 +13,7 @@ #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/Portal.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/MultiLayerSurfacesUpdator.hpp" #include "Acts/Navigation/NavigationState.hpp" #include "Acts/Navigation/NavigationStateFillers.hpp" #include "Acts/Navigation/NavigationStateUpdators.hpp" @@ -24,58 +25,6 @@ namespace Acts { namespace Experimental { -/// Helper method to update the candidates (portals/surfaces), -/// this can be called for initial surface/portal estimation, -/// but also during the navigation to update the current list -/// of candidates. -/// -/// @param gctx is the Geometry context of this call -/// @param nState [in,out] is the navigation state to be updated -/// -/// @todo for surfaces skip the non-reached ones, while keep for portals -inline static void updateCandidates(const GeometryContext& gctx, - NavigationState& nState) { - const auto& position = nState.position; - const auto& direction = nState.direction; - auto& nCandidates = nState.surfaceCandidates; - - for (auto& c : nCandidates) { - // Get the surface representation: either native surfcae of portal - const Surface& sRep = - (c.surface != nullptr) ? (*c.surface) : (c.portal->surface()); - - // Get the intersection @todo make a templated intersector - // TODO surface tolerance - auto sIntersection = sRep.intersect(gctx, position, direction, - c.boundaryCheck, s_onSurfaceTolerance); - // Re-order and swap if necessary - if (sIntersection.intersection.pathLength + s_onSurfaceTolerance < - nState.overstepTolerance and - sIntersection.alternative.status >= Intersection3D::Status::reachable) { - sIntersection.swapSolutions(); - } - c.objectIntersection = sIntersection; - } - // Sort and stuff non-allowed solutions to the end - std::sort( - nCandidates.begin(), nCandidates.end(), - [&](const auto& a, const auto& b) { - // The two path lengths - ActsScalar pathToA = a.objectIntersection.intersection.pathLength; - ActsScalar pathToB = b.objectIntersection.intersection.pathLength; - if (pathToA + s_onSurfaceTolerance < nState.overstepTolerance or - std::abs(pathToA) < s_onSurfaceTolerance) { - return false; - } else if (pathToB + s_onSurfaceTolerance < nState.overstepTolerance or - std::abs(pathToB) < s_onSurfaceTolerance) { - return true; - } - return pathToA < pathToB; - }); - // Set the surface candidate - nState.surfaceCandidate = nCandidates.begin(); -} - struct AllPortalsImpl : public INavigationDelegate { /// A ordered portal provider /// @@ -192,6 +141,13 @@ template using IndexedSurfacesImpl = IndexedUpdatorImpl; +/// @brief An indexed multi layer surface implementation access +/// +/// @tparam grid_type is the grid type used for this indexed lookup +template +using MultiLayerSurfacesImpl = + MultiLayerSurfacesUpdatorImpl; + /// @brief An indexed surface implementation with portal access /// ///@tparam inexed_updator is the updator for the indexed surfaces diff --git a/Core/include/Acts/Propagator/ActionList.hpp b/Core/include/Acts/Propagator/ActionList.hpp index fce63d90617..cd0081766ec 100644 --- a/Core/include/Acts/Propagator/ActionList.hpp +++ b/Core/include/Acts/Propagator/ActionList.hpp @@ -26,7 +26,7 @@ namespace Acts { /// @brief ActionList implementation to be used with the propagator /// /// This is the ActionList struct that is used in the propagator -/// to define a list of different actors_t that are eacch +/// to define a list of different actors_t that are each /// executed during the stepping procedure template struct ActionList : public detail::Extendable { diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp index 565e306ea83..570e69f11b7 100644 --- a/Core/include/Acts/Propagator/AtlasStepper.hpp +++ b/Core/include/Acts/Propagator/AtlasStepper.hpp @@ -28,7 +28,7 @@ #include #include -// This is based original stepper code from the ATLAS RungeKuttePropagagor +// This is based original stepper code from the ATLAS RungeKuttaPropagator namespace Acts { /// @brief the AtlasStepper implementation for the @@ -59,7 +59,8 @@ class AtlasStepper { MagneticFieldProvider::Cache fieldCacheIn, const Parameters& pars, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) - : field(0., 0., 0.), + : particleHypothesis(pars.particleHypothesis()), + field(0., 0., 0.), stepSize(ssize), tolerance(stolerance), fieldCache(std::move(fieldCacheIn)), @@ -227,8 +228,7 @@ class AtlasStepper { state_ready = true; } - /// The absolute charge as the free vector can be 1/p or q/p - double absCharge = UnitConstants::e; + ParticleHypothesis particleHypothesis; // optimisation that init is not called twice bool state_ready = false; @@ -291,12 +291,11 @@ class AtlasStepper { }; AtlasStepper(std::shared_ptr bField) - : m_bField(std::move(bField)){}; + : m_bField(std::move(bField)) {} - template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const GenericBoundTrackParameters& par, + const BoundTrackParameters& par, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { return State{gctx, m_bField->makeCache(mctx), par, ssize, stolerance}; @@ -350,8 +349,11 @@ class AtlasStepper { double qOverP(const State& state) const { return state.pVector[7]; } + /// Absolute momentum accessor + /// + /// @param state [in] The stepping state (thread-local cache) double absoluteMomentum(const State& state) const { - return 1. / std::abs(qOverP(state)); + return particleHypothesis(state).extractMomentum(qOverP(state)); } Vector3 momentum(const State& state) const { @@ -359,12 +361,26 @@ class AtlasStepper { } /// Charge access + /// + /// @param state [in] The stepping state (thread-local cache) double charge(const State& state) const { - return qOverP(state) > 0. ? 1. : -1.; + return particleHypothesis(state).extractCharge(qOverP(state)); + } + + /// Particle hypothesis + /// + /// @param state [in] The stepping state (thread-local cache) + const ParticleHypothesis& particleHypothesis(const State& state) const { + return state.particleHypothesis; } /// Overstep limit - double overstepLimit(const State& /*state*/) const { return m_overstepLimit; } + /// + /// @param state The stepping state (thread-local cache) + double overstepLimit(const State& state) const { + (void)state; + return -m_overstepLimit; + } /// Time access double time(const State& state) const { return state.pVector[3]; } @@ -401,7 +417,7 @@ class AtlasStepper { /// @param release [in] boolean to trigger step size release template void updateStepSize(State& state, const object_intersection_t& oIntersection, - bool release = true) const { + Direction /*direction*/, bool release = true) const { detail::updateSingleStepSize(state, oIntersection, release); } @@ -480,9 +496,9 @@ class AtlasStepper { } // Fill the end parameters - auto parameters = - BoundTrackParameters::create(surface.getSharedPtr(), state.geoContext, - pos4, dir, qOverP, std::move(covOpt)); + auto parameters = BoundTrackParameters::create( + surface.getSharedPtr(), state.geoContext, pos4, dir, qOverP, + std::move(covOpt), state.particleHypothesis); if (!parameters.ok()) { return parameters.error(); } @@ -527,7 +543,8 @@ class AtlasStepper { covOpt = state.cov; } - CurvilinearTrackParameters parameters(pos4, dir, qOverP, std::move(covOpt)); + CurvilinearTrackParameters parameters(pos4, dir, qOverP, std::move(covOpt), + state.particleHypothesis); Jacobian jacobian(state.jacobian); @@ -1222,7 +1239,7 @@ class AtlasStepper { if (std::abs(EST) > std::abs(state.options.tolerance)) { h = h * .5; // neutralize the sign of h again - state.stepping.stepSize.setValue(h * state.options.direction); + state.stepping.stepSize.setAccuracy(h * state.options.direction); // dltm = 0.; nStepTrials++; continue; @@ -1252,17 +1269,17 @@ class AtlasStepper { sA[1] = B6 * Sl; sA[2] = C6 * Sl; + double mass = particleHypothesis(state.stepping).mass(); + // Evaluate the time propagation - double dtds = - std::hypot(1, state.options.mass / absoluteMomentum(state.stepping)); + double dtds = std::hypot(1, mass / absoluteMomentum(state.stepping)); state.stepping.pVector[3] += h * dtds; state.stepping.pVector[59] = dtds; state.stepping.field = f; state.stepping.newfield = false; if (Jac) { - double dtdl = h * state.options.mass * state.options.mass * - charge(state.stepping) / + double dtdl = h * mass * mass * charge(state.stepping) / (absoluteMomentum(state.stepping) * dtds); state.stepping.pVector[43] += dtdl; @@ -1416,7 +1433,7 @@ class AtlasStepper { std::shared_ptr m_bField; /// Overstep limit: could/should be dynamic - double m_overstepLimit = -100 * UnitConstants::um; + double m_overstepLimit = 100 * UnitConstants::um; }; } // namespace Acts diff --git a/Core/include/Acts/Propagator/ConstrainedStep.hpp b/Core/include/Acts/Propagator/ConstrainedStep.hpp index d8012207e6c..6d7de1bed0e 100644 --- a/Core/include/Acts/Propagator/ConstrainedStep.hpp +++ b/Core/include/Acts/Propagator/ConstrainedStep.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -45,11 +46,10 @@ class ConstrainedStep { using Scalar = ActsScalar; /// the types of constraints - /// from accuracy - this can vary up and down given a good step estimator /// from actor - this would be a typical navigation step /// from aborter - this would be a target condition /// from user - this is user given for what reason ever - enum Type : int { accuracy = 0, actor = 1, aborter = 2, user = 3 }; + enum Type : int { actor = 0, aborter = 1, user = 2 }; /// Number of iterations needed by the stepsize finder /// (e.g. Runge-Kutta) of the stepper. @@ -59,7 +59,7 @@ class ConstrainedStep { /// constructor from Scalar /// @param value is the user given initial value - constexpr explicit ConstrainedStep(Scalar value) { m_values[user] = value; } + constexpr explicit ConstrainedStep(Scalar value) { setUser(value); } /// set accuracy by one Scalar /// @@ -67,30 +67,50 @@ class ConstrainedStep { /// exposed to the Propagator /// /// @param value is the new accuracy value - constexpr void setValue(Scalar value) { - /// set the accuracy value - m_values[accuracy] = value; + constexpr void setAccuracy(Scalar value) { + assert(value > 0 && "ConstrainedStep accuracy must be > 0."); + // set the accuracy value + m_accuracy = value; + } + + /// set user by one Scalar + /// + /// @param value is the new user value + constexpr void setUser(Scalar value) { + // TODO enable assert; see https://github.com/acts-project/acts/issues/2543 + // assert(value != 0 && "ConstrainedStep user must be != 0."); + // set the user value + m_values[user] = value; } /// returns the min step size - constexpr Scalar value() const { return value(currentType()); } + constexpr Scalar value() const { + Scalar min = *std::min_element(m_values.begin(), m_values.end()); + // accuracy is always positive and therefore handled separately + Scalar result = std::min(std::abs(min), m_accuracy); + return std::signbit(min) ? -result : result; + } /// Access a specific value /// /// @param type is the requested parameter type constexpr Scalar value(Type type) const { return m_values[type]; } - /// Access the currently leading type - constexpr Type currentType() const { - return Type(std::min_element(m_values.begin(), m_values.end()) - - m_values.begin()); - } + /// Access the accuracy value + /// + /// @param type is the requested parameter type + constexpr Scalar accuracy() const { return m_accuracy; } /// release a certain constraint value /// /// @param type is the constraint type to be released constexpr void release(Type type) { m_values[type] = kNotSet; } + /// release accuracy + /// + /// @param type is the constraint type to be released + constexpr void releaseAccuracy() { m_accuracy = kNotSet; } + /// Update the step size of a certain type /// /// Only navigation and target abortion step size @@ -106,19 +126,16 @@ class ConstrainedStep { // check the current value and set it if appropriate // this will also allow signed values due to overstepping if (std::abs(value) < std::abs(m_values[type])) { + // TODO enable assert; see + // https://github.com/acts-project/acts/issues/2543 + // assert(value != 0 && "ConstrainedStep user must be != 0."); m_values[type] = value; } } - constexpr void scale(Scalar factor) { - assert(factor > 0 && "ConstrainedStep scale factor was zero or negative."); - m_values[accuracy] = value() * factor; - } - std::ostream& toStream(std::ostream& os) const { // Helper method to avoid unreadable screen output - auto streamValue = [&](Type type) { - Scalar val = value(type); + auto streamValue = [&](Scalar val) { os << std::setw(5); if (std::abs(val) == kNotSet) { os << (val > 0 ? "+∞" : "-∞"); @@ -128,13 +145,13 @@ class ConstrainedStep { }; os << "("; - streamValue(accuracy); + streamValue(m_accuracy); os << ", "; - streamValue(actor); + streamValue(value(actor)); os << ", "; - streamValue(aborter); + streamValue(value(aborter)); os << ", "; - streamValue(user); + streamValue(value(user)); os << ")"; return os; @@ -150,7 +167,9 @@ class ConstrainedStep { inline static constexpr auto kNotSet = std::numeric_limits::max(); /// the step size tuple - std::array m_values = {kNotSet, kNotSet, kNotSet, kNotSet}; + std::array m_values = {kNotSet, kNotSet, kNotSet}; + /// the accuracy value - this can vary up and down given a good step estimator + Scalar m_accuracy = kNotSet; }; inline std::ostream& operator<<(std::ostream& os, const ConstrainedStep& step) { diff --git a/Core/include/Acts/Propagator/DenseEnvironmentExtension.hpp b/Core/include/Acts/Propagator/DenseEnvironmentExtension.hpp index dcab0c720bf..68a62927378 100644 --- a/Core/include/Acts/Propagator/DenseEnvironmentExtension.hpp +++ b/Core/include/Acts/Propagator/DenseEnvironmentExtension.hpp @@ -58,8 +58,6 @@ struct DenseStepperPropagatorOptions eoptions(this->geoContext, this->magFieldContext); // Copy the options over eoptions.direction = this->direction; - eoptions.absPdgCode = this->absPdgCode; - eoptions.mass = this->mass; eoptions.maxSteps = this->maxSteps; eoptions.maxStepSize = this->maxStepSize; eoptions.targetTolerance = this->targetTolerance; diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp index ce9bd131e4b..9d05549c01e 100644 --- a/Core/include/Acts/Propagator/DirectNavigator.hpp +++ b/Core/include/Acts/Propagator/DirectNavigator.hpp @@ -168,6 +168,10 @@ class DirectNavigator { bool targetReached(const State& state) const { return state.targetReached; } + bool endOfWorldReached(State& state) const { + return state.currentVolume == nullptr; + } + bool navigationBreak(const State& state) const { return state.navigationBreak; } @@ -186,7 +190,7 @@ class DirectNavigator { /// @brief Initialize call - start of propagation /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object diff --git a/Core/include/Acts/Propagator/EigenStepper.hpp b/Core/include/Acts/Propagator/EigenStepper.hpp index e288a2b6fb8..471ef6921d9 100644 --- a/Core/include/Acts/Propagator/EigenStepper.hpp +++ b/Core/include/Acts/Propagator/EigenStepper.hpp @@ -64,20 +64,17 @@ class EigenStepper { /// Constructor from the initial bound track parameters /// - /// @tparam charge_t Type of the bound parameter charge - /// /// @param [in] gctx is the context object for the geometry /// @param [in] fieldCacheIn is the cache object for the magnetic field /// @param [in] par The track parameters at start /// @param [in] ssize is the maximum step size /// /// @note the covariance matrix is copied when needed - template explicit State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, - const GenericBoundTrackParameters& par, + const BoundTrackParameters& par, double ssize = std::numeric_limits::max()) - : absCharge(std::abs(par.charge())), + : particleHypothesis(par.particleHypothesis()), stepSize(ssize), fieldCache(std::move(fieldCacheIn)), geoContext(gctx) { @@ -100,8 +97,8 @@ class EigenStepper { /// Internal free vector parameters FreeVector pars = FreeVector::Zero(); - /// The absolute charge as the free vector can be 1/p or q/p - double absCharge = UnitConstants::e; + /// Particle hypothesis + ParticleHypothesis particleHypothesis = ParticleHypothesis::pion(); /// Covariance matrix (and indicator) /// associated with the initial error on track parameters @@ -158,10 +155,9 @@ class EigenStepper { EigenStepper(std::shared_ptr bField, double overstepLimit = 100 * UnitConstants::um); - template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const GenericBoundTrackParameters& par, + const BoundTrackParameters& par, double ssize = std::numeric_limits::max()) const; /// @brief Resets the state @@ -210,8 +206,7 @@ class EigenStepper { /// /// @param state [in] The stepping state (thread-local cache) double absoluteMomentum(const State& state) const { - auto q = charge(state); - return std::abs((q == 0 ? 1 : q) / qOverP(state)); + return particleHypothesis(state).extractMomentum(qOverP(state)); } /// Momentum accessor @@ -225,7 +220,14 @@ class EigenStepper { /// /// @param state [in] The stepping state (thread-local cache) double charge(const State& state) const { - return std::copysign(state.absCharge, qOverP(state)); + return particleHypothesis(state).extractCharge(qOverP(state)); + } + + /// Particle hypothesis + /// + /// @param state [in] The stepping state (thread-local cache) + const ParticleHypothesis& particleHypothesis(const State& state) const { + return state.particleHypothesis; } /// Time access @@ -265,7 +267,7 @@ class EigenStepper { /// @param release [in] boolean to trigger step size release template void updateStepSize(State& state, const object_intersection_t& oIntersection, - bool release = true) const { + Direction /*direction*/, bool release = true) const { detail::updateSingleStepSize(state, oIntersection, release); } @@ -305,7 +307,10 @@ class EigenStepper { } /// Overstep limit - double overstepLimit(const State& /*state*/) const { + /// + /// @param state The stepping state (thread-local cache) + double overstepLimit(const State& state) const { + (void)state; // A dynamic overstep limit could sit here return -m_overstepLimit; } diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index cd3b4a0513a..675190ca381 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -7,6 +7,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include "Acts/EventData/detail/TransformationBoundToFree.hpp" +#include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Propagator/detail/CovarianceEngine.hpp" template @@ -15,12 +16,10 @@ Acts::EigenStepper::EigenStepper( : m_bField(std::move(bField)), m_overstepLimit(overstepLimit) {} template -template auto Acts::EigenStepper::makeState( std::reference_wrapper gctx, std::reference_wrapper mctx, - const GenericBoundTrackParameters& par, double ssize) const - -> State { + const BoundTrackParameters& par, double ssize) const -> State { return State{gctx, m_bField->makeCache(mctx), par, ssize}; } @@ -53,7 +52,7 @@ auto Acts::EigenStepper::boundState( -> Result { return detail::boundState( state.geoContext, state.cov, state.jacobian, state.jacTransport, - state.derivative, state.jacToGlobal, state.pars, + state.derivative, state.jacToGlobal, state.pars, state.particleHypothesis, state.covTransport && transportCov, state.pathAccumulated, surface, freeToBoundCorrection); } @@ -64,8 +63,8 @@ auto Acts::EigenStepper::curvilinearState(State& state, -> CurvilinearState { return detail::curvilinearState( state.cov, state.jacobian, state.jacTransport, state.derivative, - state.jacToGlobal, state.pars, state.covTransport && transportCov, - state.pathAccumulated); + state.jacToGlobal, state.pars, state.particleHypothesis, + state.covTransport && transportCov, state.pathAccumulated); } template @@ -139,13 +138,11 @@ Acts::Result Acts::EigenStepper::step( // size, going up to the point where it can return an estimate of the local // integration error. The results are stated in the local variables above, // allowing integration to continue once the error is deemed satisfactory - const auto tryRungeKuttaStep = - [&](const ConstrainedStep& step) -> Result { + const auto tryRungeKuttaStep = [&](const double h) -> Result { // helpers because bool and std::error_code are ambiguous constexpr auto success = &Result::success; constexpr auto failure = &Result::failure; - const double h = step.value() * state.options.direction; // State the square and half of the step size h2 = h * h; half_h = h * 0.5; @@ -190,12 +187,14 @@ Acts::Result Acts::EigenStepper::step( return success(error_estimate <= state.options.tolerance); }; - double stepSizeScaling = 1.; + const double initialH = + state.stepping.stepSize.value() * state.options.direction; + double h = initialH; size_t nStepTrials = 0; // Select and adjust the appropriate Runge-Kutta step size as given // ATL-SOFT-PUB-2009-001 while (true) { - auto res = tryRungeKuttaStep(state.stepping.stepSize); + auto res = tryRungeKuttaStep(h); if (!res.ok()) { return res.error(); } @@ -203,17 +202,16 @@ Acts::Result Acts::EigenStepper::step( break; } - stepSizeScaling = + const double stepSizeScaling = std::min(std::max(0.25f, std::sqrt(std::sqrt(static_cast( state.options.tolerance / std::abs(2. * error_estimate))))), 4.0f); - state.stepping.stepSize.scale(stepSizeScaling); + h *= stepSizeScaling; // If step size becomes too small the particle remains at the initial // place - if (std::abs(state.stepping.stepSize.value()) < - std::abs(state.options.stepSizeCutOff)) { + if (std::abs(h) < std::abs(state.options.stepSizeCutOff)) { // Not moving due to too low momentum needs an aborter return EigenStepperError::StepSizeStalled; } @@ -227,9 +225,6 @@ Acts::Result Acts::EigenStepper::step( nStepTrials++; } - // use the adjusted step size - const double h = state.stepping.stepSize.value() * state.options.direction; - // When doing error propagation, update the associated Jacobian matrix if (state.stepping.covTransport) { // The step transport matrix in global coordinates @@ -259,16 +254,17 @@ Acts::Result Acts::EigenStepper::step( state.stepping.derivative.template segment<3>(4) = sd.k4; } state.stepping.pathAccumulated += h; - if (state.stepping.stepSize.currentType() == - ConstrainedStep::Type::accuracy) { - stepSizeScaling = std::min( - std::max(0.25f, - std::sqrt(std::sqrt(static_cast( - state.options.tolerance / std::abs(error_estimate))))), - 4.0f); - state.stepping.stepSize.scale(stepSizeScaling); + const double stepSizeScaling = std::min( + std::max(0.25f, + std::sqrt(std::sqrt(static_cast( + state.options.tolerance / std::abs(error_estimate))))), + 4.0f); + const double nextAccuracy = std::abs(h * stepSizeScaling); + const double previousAccuracy = std::abs(state.stepping.stepSize.accuracy()); + const double initialStepLength = std::abs(initialH); + if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) { + state.stepping.stepSize.setAccuracy(nextAccuracy); } - state.stepping.stepSize.nStepTrials = nStepTrials; return h; diff --git a/Core/include/Acts/Propagator/MaterialInteractor.hpp b/Core/include/Acts/Propagator/MaterialInteractor.hpp index add25dc9ede..a1de77f0885 100644 --- a/Core/include/Acts/Propagator/MaterialInteractor.hpp +++ b/Core/include/Acts/Propagator/MaterialInteractor.hpp @@ -41,7 +41,7 @@ struct MaterialInteractor { /// multiple scattering and energy loss is applied according to the /// configuration. /// - /// @tparam propagator_state_t is the type of Propagagor state + /// @tparam propagator_state_t is the type of Propagator state /// @tparam stepper_t Type of the stepper of the propagation /// @tparam navigator_t Type of the navigator of the propagation /// @@ -56,8 +56,8 @@ struct MaterialInteractor { const navigator_t& navigator, result_type& result, const Logger& logger) const { // In case of Volume material update the result of the previous step - if (recordInteractions && !result.materialInteractions.empty() && - result.materialInteractions.back().volume != nullptr && + if (recordInteractions and not result.materialInteractions.empty() and + not result.materialInteractions.back().volume.empty() and result.materialInteractions.back().updatedVolumeStep == false) { updateResult(state, stepper, result); } @@ -146,7 +146,7 @@ struct MaterialInteractor { mi.sigmaTheta2 = d.varianceTheta; mi.sigmaQoP2 = d.varianceQoverP; mi.surface = d.surface; - mi.volume = nullptr; + mi.volume = InteractionVolume(); mi.pathCorrection = d.pathCorrection; mi.materialSlab = d.slab; result.materialInteractions.push_back(std::move(mi)); diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp index 4bca59cae70..38e0d07dfcf 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" +#include "Acts/EventData/MultiComponentTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" @@ -23,12 +23,14 @@ #include "Acts/Propagator/EigenStepper.hpp" #include "Acts/Propagator/EigenStepperError.hpp" #include "Acts/Propagator/Propagator.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/GaussianMixtureReduction.hpp" #include "Acts/Utilities/Intersection.hpp" #include "Acts/Utilities/Result.hpp" #include #include +#include #include #include #include @@ -81,9 +83,9 @@ struct WeightedComponentReducerLoop { static ActsScalar absoluteMomentum(const stepper_state_t& s) { return std::accumulate( s.components.begin(), s.components.end(), ActsScalar{0.}, - [](const auto& sum, const auto& cmp) -> ActsScalar { - return sum + cmp.weight * std::abs(cmp.state.absCharge / - cmp.state.pars[eFreeQOverP]); + [&s](const auto& sum, const auto& cmp) -> ActsScalar { + return sum + cmp.weight * s.particleHypothesis.extractMomentum( + cmp.state.pars[eFreeQOverP]); }); } @@ -91,10 +93,10 @@ struct WeightedComponentReducerLoop { static Vector3 momentum(const stepper_state_t& s) { return std::accumulate( s.components.begin(), s.components.end(), Vector3::Zero().eval(), - [](const auto& sum, const auto& cmp) -> Vector3 { + [&s](const auto& sum, const auto& cmp) -> Vector3 { return sum + cmp.weight * - std::abs(cmp.state.absCharge / - cmp.state.pars[eFreeQOverP]) * + s.particleHypothesis.extractMomentum( + cmp.state.pars[eFreeQOverP]) * cmp.state.pars.template segment<3>(eFreeDir0); }); } @@ -103,9 +105,9 @@ struct WeightedComponentReducerLoop { static ActsScalar charge(const stepper_state_t& s) { return std::accumulate( s.components.begin(), s.components.end(), ActsScalar{0.}, - [](const auto& sum, const auto& cmp) -> ActsScalar { - return sum + cmp.weight * std::copysign(cmp.state.absCharge, - cmp.state.pars[eFreeQOverP]); + [&s](const auto& sum, const auto& cmp) -> ActsScalar { + return sum + cmp.weight * s.particleHypothesis.extractCharge( + cmp.state.pars[eFreeQOverP]); }); } @@ -221,10 +223,6 @@ class MultiEigenStepperLoop /// surface std::size_t m_stepLimitAfterFirstComponentOnSurface = 50; - /// How to extract a single component state when calling .boundState() or - /// .curvilinearState() - MixtureReductionMethod m_finalReductionMethod = MixtureReductionMethod::eMean; - /// The logger (used if no logger is provided by caller of methods) std::unique_ptr m_logger; @@ -241,11 +239,17 @@ class MultiEigenStepperLoop using SingleState = typename SingleStepper::State; /// @brief Use the definitions from the Single-stepper - using typename SingleStepper::BoundState; using typename SingleStepper::Covariance; - using typename SingleStepper::CurvilinearState; using typename SingleStepper::Jacobian; + /// @brief Define an own bound state + using BoundState = + std::tuple; + + /// @brief Define an own curvilinear state + using CurvilinearState = std::tuple; + /// @brief The reducer type using Reducer = component_reducer_t; @@ -254,12 +258,9 @@ class MultiEigenStepperLoop /// Constructor from a magnetic field and a optionally provided Logger MultiEigenStepperLoop(std::shared_ptr bField, - MixtureReductionMethod finalReductionMethod = - MixtureReductionMethod::eMean, std::unique_ptr logger = getDefaultLogger("GSF", Logging::INFO)) : EigenStepper(std::move(bField)), - m_finalReductionMethod(finalReductionMethod), m_logger(std::move(logger)) {} struct State { @@ -270,6 +271,9 @@ class MultiEigenStepperLoop Intersection3D::Status status; }; + /// Particle hypothesis + ParticleHypothesis particleHypothesis = ParticleHypothesis::pion(); + /// The components of which the state consists SmallVector components; @@ -292,8 +296,6 @@ class MultiEigenStepperLoop /// Constructor from the initial bound track parameters /// - /// @tparam charge_t Type of the bound parameter charge - /// /// @param [in] gctx is the context object for the geometry /// @param [in] mctx is the context object for the magnetic field /// @param [in] bfield the shared magnetic filed provider @@ -301,13 +303,14 @@ class MultiEigenStepperLoop /// @param [in] ssize is the maximum step size /// /// @note the covariance matrix is copied when needed - template - explicit State( - const GeometryContext& gctx, const MagneticFieldContext& mctx, - const std::shared_ptr& bfield, - const MultiComponentBoundTrackParameters& multipars, - double ssize = std::numeric_limits::max()) - : geoContext(gctx), magContext(mctx) { + explicit State(const GeometryContext& gctx, + const MagneticFieldContext& mctx, + const std::shared_ptr& bfield, + const MultiComponentBoundTrackParameters& multipars, + double ssize = std::numeric_limits::max()) + : particleHypothesis(multipars.particleHypothesis()), + geoContext(gctx), + magContext(mctx) { if (multipars.components().empty()) { throw std::invalid_argument( "Cannot construct MultiEigenStepperLoop::State with empty " @@ -317,10 +320,10 @@ class MultiEigenStepperLoop const auto surface = multipars.referenceSurface().getSharedPtr(); for (auto i = 0ul; i < multipars.components().size(); ++i) { - const auto [weight, singlePars] = multipars[i]; - components.push_back({SingleState(gctx, bfield->makeCache(mctx), - std::move(singlePars), ssize), - weight, Intersection3D::Status::onSurface}); + const auto& [weight, singlePars] = multipars[i]; + components.push_back( + {SingleState(gctx, bfield->makeCache(mctx), singlePars, ssize), + weight, Intersection3D::Status::onSurface}); } if (std::get<2>(multipars.components().front())) { @@ -330,10 +333,9 @@ class MultiEigenStepperLoop }; /// Construct and initialize a state - template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const MultiComponentBoundTrackParameters& par, + const MultiComponentBoundTrackParameters& par, double ssize = std::numeric_limits::max()) const { return State(gctx, mctx, SingleStepper::m_bField, par, ssize); } @@ -466,13 +468,14 @@ class MultiEigenStepperLoop cmp.state, state.navigation, state.options, state.geoContext); } - Result boundState( + Result boundState( const Surface& surface, bool transportCov, const FreeToBoundCorrection& freeToBoundCorrection) { return detail::boundState( all_state.geoContext, cov(), jacobian(), jacTransport(), derivative(), - jacToGlobal(), pars(), all_state.covTransport && transportCov, - cmp.state.pathAccumulated, surface, freeToBoundCorrection); + jacToGlobal(), pars(), all_state.particleHypothesis, + all_state.covTransport && transportCov, cmp.state.pathAccumulated, + surface, freeToBoundCorrection); } void update(const FreeVector& freeParams, const BoundVector& boundParams, @@ -606,10 +609,9 @@ class MultiEigenStepperLoop /// valid in the end. /// @note The returned component-proxy is only garantueed to be valid until /// the component number is again modified - template - Result addComponent( - State& state, const GenericBoundTrackParameters& pars, - double weight) const { + Result addComponent(State& state, + const BoundTrackParameters& pars, + double weight) const { state.components.push_back( {SingleState(state.geoContext, SingleStepper::m_bField->makeCache(state.magContext), @@ -669,6 +671,13 @@ class MultiEigenStepperLoop /// @param state [in] The stepping state (thread-local cache) double charge(const State& state) const { return Reducer::charge(state); } + /// Particle hypothesis + /// + /// @param state [in] The stepping state (thread-local cache) + ParticleHypothesis particleHypothesis(const State& state) const { + return state.particleHypothesis; + } + /// Time access /// /// @param state [in] The stepping state (thread-local cache) @@ -762,30 +771,21 @@ class MultiEigenStepperLoop /// /// @param state [in,out] The stepping state (thread-local cache) /// @param oIntersection [in] The ObjectIntersection to layer, boundary, etc + /// @param direction [in] The propagation direction /// @param release [in] boolean to trigger step size release template void updateStepSize(State& state, const object_intersection_t& oIntersection, - bool release = true) const { - const Surface& surface = *oIntersection.representation; + Direction direction, bool release = true) const { + const Surface& surface = *oIntersection.representation(); for (auto& component : state.components) { auto intersection = surface.intersect( component.state.geoContext, SingleStepper::position(component.state), - SingleStepper::direction(component.state), true); - - // We don't know whatever was done to manipulate the intersection before - // (e.g. in Layer.ipp:240), so we trust and just adjust the sign - if (std::signbit(oIntersection.intersection.pathLength) != - std::signbit(intersection.intersection.pathLength)) { - intersection.intersection.pathLength *= -1; - } - - if (std::signbit(oIntersection.alternative.pathLength) != - std::signbit(intersection.alternative.pathLength)) { - intersection.alternative.pathLength *= -1; - } + direction * SingleStepper::direction(component.state), + true)[oIntersection.index()]; - SingleStepper::updateStepSize(component.state, intersection, release); + SingleStepper::updateStepSize(component.state, intersection, direction, + release); } } diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp index 722abf493ee..2c6d4e4970f 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.ipp @@ -17,12 +17,8 @@ auto MultiEigenStepperLoop::boundState( -> Result { assert(!state.components.empty()); - if (numberComponents(state) == 1) { - return SingleStepper::boundState(state.components.front().state, surface, - transportCov, freeToBoundCorrection); - } - - SmallVector> states; + std::vector> cmps; + cmps.reserve(numberComponents(state)); double accumulatedPathLength = 0.0; for (auto i = 0ul; i < numberComponents(state); ++i) { @@ -39,14 +35,15 @@ auto MultiEigenStepperLoop::boundState( .intersect(state.geoContext, cmpState.pars.template segment<3>(eFreePos0), cmpState.pars.template segment<3>(eFreeDir0), false) - .intersection.position; + .closest() + .position(); auto bs = SingleStepper::boundState(cmpState, surface, transportCov, freeToBoundCorrection); if (bs.ok()) { const auto& btp = std::get(*bs); - states.emplace_back( + cmps.emplace_back( state.components[i].weight, btp.parameters(), btp.covariance().value_or(Acts::BoundSquareMatrix::Zero())); accumulatedPathLength += @@ -54,21 +51,13 @@ auto MultiEigenStepperLoop::boundState( } } - if (states.empty()) { + if (cmps.empty()) { return MultiStepperError::AllComponentsConversionToBoundFailed; } - const auto [finalPars, cov] = - Acts::reduceGaussianMixture(states, surface, m_finalReductionMethod); - - std::optional finalCov = std::nullopt; - if (cov != BoundSquareMatrix::Zero()) { - finalCov = cov; - } - - return BoundState{ - BoundTrackParameters(surface.getSharedPtr(), finalPars, finalCov), - Jacobian::Zero(), accumulatedPathLength}; + return BoundState{MultiComponentBoundTrackParameters( + surface.getSharedPtr(), cmps, state.particleHypothesis), + Jacobian::Zero(), accumulatedPathLength}; } template @@ -77,52 +66,26 @@ auto MultiEigenStepperLoop::curvilinearState(State& state, -> CurvilinearState { assert(!state.components.empty()); - if (numberComponents(state) == 1) { - return SingleStepper::curvilinearState(state.components.front().state, - transportCov); - } else if (m_finalReductionMethod == MixtureReductionMethod::eMaxWeight) { - auto cmpIt = std::max_element( - state.components.begin(), state.components.end(), - [](const auto& a, const auto& b) { return a.weight < b.weight; }); - - return SingleStepper::curvilinearState(cmpIt->state, transportCov); - } else { - Vector4 pos4 = Vector4::Zero(); - Vector3 dir = Vector3::Zero(); - ActsScalar qop = 0.0; - BoundSquareMatrix cov = BoundSquareMatrix::Zero(); - ActsScalar pathLenth = 0.0; - ActsScalar sumOfWeights = 0.0; - - for (auto i = 0ul; i < numberComponents(state); ++i) { - const auto [cp, jac, pl] = SingleStepper::curvilinearState( - state.components[i].state, transportCov); - - pos4 += state.components[i].weight * cp.fourPosition(state.geoContext); - dir += state.components[i].weight * cp.direction(); - qop += state.components[i].weight * (cp.charge() / cp.absoluteMomentum()); - if (cp.covariance()) { - cov += state.components[i].weight * *cp.covariance(); - } - pathLenth += state.components[i].weight * pathLenth; - sumOfWeights += state.components[i].weight; - } - - pos4 /= sumOfWeights; - dir /= sumOfWeights; - qop /= sumOfWeights; - pathLenth /= sumOfWeights; - cov /= sumOfWeights; - - std::optional finalCov = std::nullopt; - if (cov != BoundSquareMatrix::Zero()) { - finalCov = cov; - } + std::vector< + std::tuple> + cmps; + cmps.reserve(numberComponents(state)); + double accumulatedPathLength = 0.0; - return CurvilinearState{ - CurvilinearTrackParameters(pos4, dir, qop, finalCov), Jacobian::Zero(), - pathLenth}; + for (auto i = 0ul; i < numberComponents(state); ++i) { + const auto [cp, jac, pl] = SingleStepper::curvilinearState( + state.components[i].state, transportCov); + + cmps.emplace_back(state.components[i].weight, + cp.fourPosition(state.geoContext), cp.direction(), + (cp.charge() / cp.absoluteMomentum()), + cp.covariance().value_or(BoundSquareMatrix::Zero())); + accumulatedPathLength += state.components[i].weight * pl; } + + return CurvilinearState{ + MultiComponentCurvilinearTrackParameters(cmps, state.particleHypothesis), + Jacobian::Zero(), accumulatedPathLength}; } template diff --git a/Core/include/Acts/Propagator/MultiStepperAborters.hpp b/Core/include/Acts/Propagator/MultiStepperAborters.hpp index ac8f3806dfe..fa5ebe7931c 100644 --- a/Core/include/Acts/Propagator/MultiStepperAborters.hpp +++ b/Core/include/Acts/Propagator/MultiStepperAborters.hpp @@ -78,13 +78,15 @@ struct MultiStepperSurfaceReached { // However, if mean of all is on surface, we are happy as well if (averageOnSurface) { - const auto sIntersection = targetSurface.intersect( - state.geoContext, stepper.position(state.stepping), - state.options.direction * stepper.direction(state.stepping), true, - averageOnSurfaceTolerance); - - if (sIntersection.intersection.status == - Intersection3D::Status::onSurface) { + const auto sIntersection = + targetSurface + .intersect( + state.geoContext, stepper.position(state.stepping), + state.options.direction * stepper.direction(state.stepping), + true, averageOnSurfaceTolerance) + .closest(); + + if (sIntersection.status() == Intersection3D::Status::onSurface) { ACTS_VERBOSE("Reached target in average mode"); navigator.currentSurface(state.navigation, &targetSurface); navigator.targetReached(state.navigation, true); diff --git a/Core/include/Acts/Propagator/Navigator.hpp b/Core/include/Acts/Propagator/Navigator.hpp index 6fb7807a618..6cf206d6dd0 100644 --- a/Core/include/Acts/Propagator/Navigator.hpp +++ b/Core/include/Acts/Propagator/Navigator.hpp @@ -274,6 +274,10 @@ class Navigator { bool targetReached(const State& state) const { return state.targetReached; } + bool endOfWorldReached(State& state) const { + return state.currentVolume == nullptr; + } + bool navigationBreak(const State& state) const { return state.navigationBreak; } @@ -297,7 +301,7 @@ class Navigator { /// @brief Initialize call - start of propagation /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -521,7 +525,7 @@ class Navigator { state.navigation.lastHierarchySurfaceReached = false; // Update volume information // get the attached volume information - auto boundary = state.navigation.navBoundary().object; + auto boundary = state.navigation.navBoundary().object(); state.navigation.currentVolume = boundary->attachedVolume( state.geoContext, stepper.position(state.stepping), stepper.direction(state.stepping), state.options.direction); @@ -571,9 +575,9 @@ class Navigator { /// If there are surfaces to be handled, check if the current /// state is on the surface /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation - /// @tparam navigation_surfaces_t Type of the propagagor + /// @tparam navigation_surfaces_t Type of the propagator /// /// @param [in,out] state is the propagation state object /// @param [in] stepper Stepper in use @@ -591,7 +595,7 @@ class Navigator { return false; } // Take the current surface - auto surface = navSurfaces.at(navIndex).representation; + auto surface = navSurfaces.at(navIndex).representation(); // Check if we are at a surface // If we are on the surface pointed at by the index, we can make // it the current one to pass it to the other actors @@ -618,7 +622,7 @@ class Navigator { /// then return with updated step size /// - if an intersect is not valid, switch to next /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -659,7 +663,7 @@ class Navigator { << "No surfaces present, target at layer first."); return false; } - auto layerID = state.navigation.navSurface().object->geometryId().layer(); + auto layerID = state.navigation.navSurface().object()->geometryId().layer(); std::pair externalSurfaceRange = state.navigation.externalSurfaces.equal_range(layerID); @@ -673,7 +677,7 @@ class Navigator { << " out of " << state.navigation.navSurfaces.size() << " surfaces remain to try."); // Take the surface - auto surface = state.navigation.navSurface().object; + auto surface = state.navigation.navSurface().object(); // Screen output which surface you are on ACTS_VERBOSE(volInfo(state) << "Next surface candidate will be " << surface->geometryId()); @@ -735,7 +739,7 @@ class Navigator { /// If we unpack a surface, the step size is set to the path length /// to the first surface, as determined by straight line intersect. /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -806,9 +810,10 @@ class Navigator { // did we find any surfaces? // Check: are we on the first surface? + // TODO magic number `1_um` if ((state.navigation.currentSurface == nullptr && state.navigation.navSurfaces.empty()) || - protoNavSurfaces.front().intersection.pathLength > 1_um) { + protoNavSurfaces.front().pathLength() > 1_um) { // we are not, go on // state.navigation.navSurfaces = std::move(protoNavSurfaces); state.navigation.navSurfaces.clear(); @@ -821,7 +826,8 @@ class Navigator { state.navigation.navLayerIndex = state.navigation.navLayers.size(); // The stepper updates the step size ( single / multi component) stepper.updateStepSize(state.stepping, - state.navigation.navSurface(), true); + state.navigation.navSurface(), + state.options.direction, true); ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to " << stepper.outputStepSize(state.stepping)); @@ -839,7 +845,7 @@ class Navigator { while (state.navigation.navLayerIndex != state.navigation.navLayers.size()) { // The layer surface - auto layerSurface = state.navigation.navLayer().representation; + auto layerSurface = state.navigation.navLayer().representation(); // We are on the layer if (state.navigation.currentSurface == layerSurface) { ACTS_VERBOSE(volInfo(state) << "We are on a layer, resolve Surfaces."); @@ -906,7 +912,7 @@ class Navigator { /// line intersect is found, the boundary surface is skipped. /// If we are out of boundary surfaces, the navigation is terminated. /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -968,7 +974,7 @@ class Navigator { os << state.navigation.navBoundaries.size(); os << " boundary candidates found at path(s): "; for (auto& bc : state.navigation.navBoundaries) { - os << bc.intersection.pathLength << " "; + os << bc.pathLength() << " "; } logger().log(Logging::VERBOSE, os.str()); } @@ -977,7 +983,7 @@ class Navigator { if (not state.navigation.navBoundaries.empty()) { // Set to the first and return to the stepper stepper.updateStepSize(state.stepping, state.navigation.navBoundary(), - true); + state.options.direction, true); ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to " << stepper.outputStepSize(state.stepping)); return true; @@ -994,7 +1000,7 @@ class Navigator { while (state.navigation.navBoundaryIndex != state.navigation.navBoundaries.size()) { // That is the current boundary surface - auto boundarySurface = state.navigation.navBoundary().representation; + auto boundarySurface = state.navigation.navBoundary().representation(); // Step towards the boundary surfrace auto boundaryStatus = stepper.updateSurfaceStatus( state.stepping, *boundarySurface, state.options.direction, true, @@ -1040,7 +1046,7 @@ class Navigator { /// - attempted volume switch /// Target finding by association will not be done again /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -1077,18 +1083,21 @@ class Navigator { } // Slow navigation initialization for target: // target volume and layer search through global search - auto targetIntersection = state.navigation.targetSurface->intersect( - state.geoContext, stepper.position(state.stepping), - state.options.direction * stepper.direction(state.stepping), false, - state.options.targetTolerance); + auto targetIntersection = + state.navigation.targetSurface + ->intersect( + state.geoContext, stepper.position(state.stepping), + state.options.direction * stepper.direction(state.stepping), + false, state.options.targetTolerance) + .closest(); if (targetIntersection) { ACTS_VERBOSE(volInfo(state) << "Target estimate position (" - << targetIntersection.intersection.position.x() << ", " - << targetIntersection.intersection.position.y() << ", " - << targetIntersection.intersection.position.z() << ")"); + << targetIntersection.position().x() << ", " + << targetIntersection.position().y() << ", " + << targetIntersection.position().z() << ")"); /// get the target volume from the intersection - auto tPosition = targetIntersection.intersection.position; + auto tPosition = targetIntersection.position(); state.navigation.targetVolume = m_cfg.trackingGeometry->lowestTrackingVolume(state.geoContext, tPosition); @@ -1108,7 +1117,7 @@ class Navigator { /// @brief Resolve the surfaces of this layer /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -1121,8 +1130,8 @@ class Navigator { const Layer* cLayer = nullptr) const { // get the layer and layer surface auto layerSurface = cLayer ? state.navigation.startSurface - : state.navigation.navLayer().representation; - auto navLayer = cLayer ? cLayer : state.navigation.navLayer().object; + : state.navigation.navLayer().representation(); + auto navLayer = cLayer ? cLayer : state.navigation.navLayer().object(); // are we on the start layer bool onStart = (navLayer == state.navigation.startLayer); auto startSurface = onStart ? state.navigation.startSurface : layerSurface; @@ -1165,7 +1174,7 @@ class Navigator { os << state.navigation.navSurfaces.size(); os << " surface candidates found at path(s): "; for (auto& sfc : state.navigation.navSurfaces) { - os << sfc.intersection.pathLength << " "; + os << sfc.pathLength() << " "; } logger().log(Logging::VERBOSE, os.str()); } @@ -1174,7 +1183,7 @@ class Navigator { state.navigation.navSurfaceIndex = 0; // The stepper updates the step size ( single / multi component) stepper.updateStepSize(state.stepping, state.navigation.navSurface(), - true); + state.options.direction, true); ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to " << stepper.outputStepSize(state.stepping)); return true; @@ -1191,7 +1200,7 @@ class Navigator { /// This initializes the layer candidates when starting /// or when entering a new volume /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -1236,7 +1245,7 @@ class Navigator { os << state.navigation.navLayers.size(); os << " layer candidates found at path(s): "; for (auto& lc : state.navigation.navLayers) { - os << lc.intersection.pathLength << " "; + os << lc.pathLength() << " "; } logger().log(Logging::VERBOSE, os.str()); } @@ -1244,11 +1253,11 @@ class Navigator { state.navigation.navLayerIndex = 0; // Setting the step size towards first if (state.navigation.startLayer && - state.navigation.navLayer().object != state.navigation.startLayer) { + state.navigation.navLayer().object() != state.navigation.startLayer) { ACTS_VERBOSE(volInfo(state) << "Target at layer."); // The stepper updates the step size ( single / multi component) stepper.updateStepSize(state.stepping, state.navigation.navLayer(), - true); + state.options.direction, true); ACTS_VERBOSE(volInfo(state) << "Navigation stepSize updated to " << stepper.outputStepSize(state.stepping)); return true; @@ -1270,7 +1279,7 @@ class Navigator { /// This checks if a navigation break had been triggered or navigator /// is misconfigured /// - /// @tparam propagator_state_t The state type of the propagagor + /// @tparam propagator_state_t The state type of the propagator /// @tparam stepper_t The type of stepper used for the propagation /// /// @param [in,out] state is the propagation state object @@ -1283,7 +1292,7 @@ class Navigator { if (!m_cfg.trackingGeometry) { return true; } - // turn the navigator into void when you are intructed to do nothing + // turn the navigator into void when you are instructed to do nothing if (!m_cfg.resolveSensitive && !m_cfg.resolveMaterial && !m_cfg.resolvePassive) { return true; diff --git a/Core/include/Acts/Propagator/Propagator.hpp b/Core/include/Acts/Propagator/Propagator.hpp index 147f04892c0..ddfd01c4022 100644 --- a/Core/include/Acts/Propagator/Propagator.hpp +++ b/Core/include/Acts/Propagator/Propagator.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2016-2019 CERN for the benefit of the Acts project +// Copyright (C) 2016-2023 CERN for the benefit of the Acts project // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -16,12 +16,14 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/TrackParametersConcept.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Propagator/AbortList.hpp" #include "Acts/Propagator/ActionList.hpp" #include "Acts/Propagator/StandardAborters.hpp" #include "Acts/Propagator/StepperConcept.hpp" +#include "Acts/Propagator/detail/ParameterTraits.hpp" #include "Acts/Propagator/detail/VoidPropagatorComponents.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" @@ -59,14 +61,6 @@ struct PropagatorPlainOptions { /// Propagation direction Direction direction = Direction::Forward; - /// The |pdg| code for (eventual) material integration - - /// pion default - PdgParticle absPdgCode = PdgParticle::ePionPlus; - - /// The mass of the particle for (eventual) material integration - - /// pion default - double mass = 139.57018 * UnitConstants::MeV; - /// Maximum number of steps for one propagate call unsigned int maxSteps = 1000; @@ -132,8 +126,6 @@ struct PropagatorOptions : public PropagatorPlainOptions { geoContext, magFieldContext); // Copy the options over eoptions.direction = direction; - eoptions.absPdgCode = absPdgCode; - eoptions.mass = mass; eoptions.maxSteps = maxSteps; eoptions.maxRungeKuttaStepTrials = maxRungeKuttaStepTrials; eoptions.maxStepSize = maxStepSize; @@ -158,8 +150,6 @@ struct PropagatorOptions : public PropagatorPlainOptions { void setPlainOptions(const PropagatorPlainOptions& pOptions) { // Copy the options over direction = pOptions.direction; - absPdgCode = pOptions.absPdgCode; - mass = pOptions.mass; maxSteps = pOptions.maxSteps; maxRungeKuttaStepTrials = pOptions.maxRungeKuttaStepTrials; maxStepSize = pOptions.maxStepSize; @@ -211,10 +201,26 @@ struct PropagatorOptions : public PropagatorPlainOptions { /// template class Propagator final { + /// Re-define bound track parameters dependent on the stepper + using StepperBoundTrackParameters = + detail::stepper_bound_parameters_type_t; + static_assert( + Concepts::BoundTrackParametersConcept, + "Stepper bound track parameters do not fulfill bound " + "parameters concept."); + + /// Re-define curvilinear track parameters dependent on the stepper + using StepperCurvilinearTrackParameters = + detail::stepper_curvilinear_parameters_type_t; + static_assert( + Concepts::BoundTrackParametersConcept, + "Stepper bound track parameters do not fulfill bound " + "parameters concept."); + using Jacobian = BoundMatrix; - using BoundState = std::tuple; + using BoundState = std::tuple; using CurvilinearState = - std::tuple; + std::tuple; static_assert(StepperStateConcept, "Stepper does not fulfill stepper concept."); @@ -353,6 +359,7 @@ class Propagator final { /// /// @param [in] start initial track parameters to propagate /// @param [in] options Propagation options, type Options<,> + /// @param [in] makeCurvilinear Produce curvilinear parameters at the end of the propagation /// /// @return Propagation result containing the propagation status, final /// track parameters, and output of actions (if they produce any) @@ -360,10 +367,10 @@ class Propagator final { template Result< - action_list_t_result_t> - propagate(const parameters_t& start, - const propagator_options_t& options) const; + propagate(const parameters_t& start, const propagator_options_t& options, + bool makeCurvilinear = true) const; /// @brief Propagate track parameters /// @@ -378,6 +385,7 @@ class Propagator final { /// /// @param [in] start initial track parameters to propagate /// @param [in] options Propagation options, type Options<,> + /// @param [in] makeCurvilinear Produce curvilinear parameters at the end of the propagation /// @param [in] inputResult an existing result object to start from /// /// @return Propagation result containing the propagation status, final @@ -386,11 +394,12 @@ class Propagator final { template Result< - action_list_t_result_t> propagate( const parameters_t& start, const propagator_options_t& options, - action_list_t_result_t&& inputResult) const; @@ -415,8 +424,9 @@ class Propagator final { template - Result> + Result< + action_list_t_result_t> propagate(const parameters_t& start, const Surface& target, const propagator_options_t& options) const; @@ -442,12 +452,13 @@ class Propagator final { template - Result> + Result< + action_list_t_result_t> propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options, - action_list_t_result_t inputResult) const; diff --git a/Core/include/Acts/Propagator/Propagator.ipp b/Core/include/Acts/Propagator/Propagator.ipp index 718d03dcc96..79d21ac680b 100644 --- a/Core/include/Acts/Propagator/Propagator.ipp +++ b/Core/include/Acts/Propagator/Propagator.ipp @@ -90,13 +90,14 @@ auto Acts::Propagator::propagate_impl(propagator_state_t& state, template template -auto Acts::Propagator::propagate( - const parameters_t& start, const propagator_options_t& options) const +auto Acts::Propagator::propagate(const parameters_t& start, + const propagator_options_t& options, + bool makeCurvilinear) const -> Result> { // Type of track parameters produced by the propagation - using ReturnParameterType = CurvilinearTrackParameters; + using ReturnParameterType = StepperCurvilinearTrackParameters; static_assert(std::is_copy_constructible::value, "return track parameter type must be copy-constructible"); @@ -107,7 +108,7 @@ auto Acts::Propagator::propagate( typename propagator_options_t::action_list_type>; return propagate( - start, options, ResultType{}); + start, options, makeCurvilinear, ResultType{}); } template @@ -115,11 +116,12 @@ template auto Acts::Propagator::propagate( const parameters_t& start, const propagator_options_t& options, - action_list_t_result_t&& inputResult) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); @@ -127,7 +129,7 @@ auto Acts::Propagator::propagate( using ResultType = std::decay_t; // Type of track parameters produced by the propagation - using ReturnParameterType = CurvilinearTrackParameters; + using ReturnParameterType = StepperCurvilinearTrackParameters; static_assert(std::is_copy_constructible::value, "return track parameter type must be copy-constructible"); @@ -158,17 +160,20 @@ auto Acts::Propagator::propagate( // Apply the loop protection - it resets the internal path limit detail::setupLoopProtection( state, m_stepper, state.options.abortList.template get(), - logger()); + false, logger()); // Perform the actual propagation & check its outcome auto result = propagate_impl(state, inputResult); if (result.ok()) { - /// Convert into return type and fill the result object - auto curvState = m_stepper.curvilinearState(state.stepping); - // Fill the end parameters - inputResult.endParameters = std::get(curvState); - // Only fill the transport jacobian when covariance transport was done - if (state.stepping.covTransport) { - inputResult.transportJacobian = std::get(curvState); + if (makeCurvilinear) { + /// Convert into return type and fill the result object + auto curvState = m_stepper.curvilinearState(state.stepping); + // Fill the end parameters + inputResult.endParameters = + std::get(curvState); + // Only fill the transport jacobian when covariance transport was done + if (state.stepping.covTransport) { + inputResult.transportJacobian = std::get(curvState); + } } return Result::success(std::forward(inputResult)); } else { @@ -183,13 +188,13 @@ auto Acts::Propagator::propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); // Type of track parameters produced at the end of the propagation - using return_parameter_type = BoundTrackParameters; + using return_parameter_type = StepperBoundTrackParameters; // Type of the full propagation result, including output from actions using ResultType = @@ -206,11 +211,11 @@ template ::propagate( const parameters_t& start, const Surface& target, const propagator_options_t& options, - action_list_t_result_t inputResult) const -> Result> { static_assert(Concepts::BoundTrackParametersConcept, "Parameters do not fulfill bound parameters concept."); @@ -244,7 +249,7 @@ auto Acts::Propagator::propagate( // Apply the loop protection, it resets the internal path limit detail::setupLoopProtection( state, m_stepper, state.options.abortList.template get(), - logger()); + false, logger()); // Perform the actual propagation auto result = propagate_impl(state, inputResult); @@ -259,7 +264,7 @@ auto Acts::Propagator::propagate( const auto& bs = *bsRes; // Fill the end parameters - inputResult.endParameters = std::get(bs); + inputResult.endParameters = std::get(bs); // Only fill the transport jacobian when covariance transport was done if (state.stepping.covTransport) { inputResult.transportJacobian = std::get(bs); diff --git a/Core/include/Acts/Propagator/RiddersPropagator.ipp b/Core/include/Acts/Propagator/RiddersPropagator.ipp index 4f7f5496488..314307dcca3 100644 --- a/Core/include/Acts/Propagator/RiddersPropagator.ipp +++ b/Core/include/Acts/Propagator/RiddersPropagator.ipp @@ -6,6 +6,8 @@ // 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/TrackParametrization.hpp" + template template auto Acts::RiddersPropagator::propagate( @@ -41,9 +43,8 @@ auto Acts::RiddersPropagator::propagate( // replace the covariance of the nominal result w/ the ridders covariance nominalResult.endParameters = CurvilinearTrackParameters( nominalFinalParameters.fourPosition(options.geoContext), - nominalFinalParameters.direction(), - nominalFinalParameters.absoluteMomentum(), - nominalFinalParameters.charge(), std::move(cov)); + nominalFinalParameters.direction(), nominalFinalParameters.qOverP(), + std::move(cov), nominalFinalParameters.particleHypothesis()); } return ThisResult::success(std::move(nominalResult)); @@ -84,8 +85,8 @@ auto Acts::RiddersPropagator::propagate( // replace the covariance of the nominal result w/ the ridders covariance nominalResult.endParameters = BoundTrackParameters( nominalFinalParameters.referenceSurface().getSharedPtr(), - nominalFinalParameters.parameters(), nominalFinalParameters.charge(), - std::move(cov)); + nominalFinalParameters.parameters(), std::move(cov), + nominalFinalParameters.particleHypothesis()); } return ThisResult::success(std::move(nominalResult)); @@ -193,7 +194,7 @@ Acts::RiddersPropagator::wiggleParameter( // Propagate with updated start parameters BoundTrackParameters tp(start.referenceSurface().getSharedPtr(), values, - start.covariance()); + start.covariance(), start.particleHypothesis()); const auto& r = m_propagator.propagate(tp, target, options).value(); // Collect the slope derivatives.push_back((r.endParameters->parameters() - nominal) / h); diff --git a/Core/include/Acts/Propagator/StandardAborters.hpp b/Core/include/Acts/Propagator/StandardAborters.hpp index a28571fee7b..8ea571187bc 100644 --- a/Core/include/Acts/Propagator/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/StandardAborters.hpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Direction.hpp" +#include "Acts/Definitions/Units.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -17,6 +18,7 @@ #include "Acts/Utilities/Logger.hpp" #include +#include #include #include @@ -66,28 +68,30 @@ struct PathLimitReached { double distance = std::abs(internalLimit) - std::abs(state.stepping.pathAccumulated); double tolerance = state.options.targetTolerance; - stepper.setStepSize(state.stepping, distance, ConstrainedStep::aborter, - false); bool limitReached = (std::abs(distance) < std::abs(tolerance)); if (limitReached) { ACTS_VERBOSE("Target: x | " << "Path limit reached at distance " << distance); // reaching the target means navigation break navigator.targetReached(state.navigation, true); - } else { - ACTS_VERBOSE("Target: 0 | " - << "Target stepSize (path limit) updated to " - << stepper.outputStepSize(state.stepping)); + return true; } - // path limit check - return limitReached; + stepper.setStepSize(state.stepping, distance, ConstrainedStep::aborter, + false); + ACTS_VERBOSE("Target: 0 | " + << "Target stepSize (path limit) updated to " + << stepper.outputStepSize(state.stepping)); + return false; } }; /// This is the condition that the Surface has been reached /// it then triggers an propagation abort of the propagation struct SurfaceReached { + std::optional overstepLimit; + SurfaceReached() = default; + SurfaceReached(double oLimit) : overstepLimit(oLimit) {} /// boolean operator for abort condition without using the result /// @@ -126,6 +130,7 @@ struct SurfaceReached { if (navigator.targetReached(state.navigation)) { return true; } + // Check if the cache filled the currentSurface - or if we are on the // surface if ((navigator.currentSurface(state.navigation) && @@ -136,23 +141,25 @@ struct SurfaceReached { navigator.targetReached(state.navigation, true); return true; } + + // TODO the following code is mostly duplicated in updateSingleSurfaceStatus + // Calculate the distance to the surface const double tolerance = state.options.targetTolerance; + const auto sIntersection = targetSurface.intersect( state.geoContext, stepper.position(state.stepping), state.options.direction * stepper.direction(state.stepping), true, tolerance); - - // The target is reached - bool targetReached = (sIntersection.intersection.status == - Intersection3D::Status::onSurface); - double distance = sIntersection.intersection.pathLength; + const auto closest = sIntersection.closest(); // Return true if you fall below tolerance - if (targetReached) { + if (closest.status() == Intersection3D::Status::onSurface) { + const double distance = closest.pathLength(); ACTS_VERBOSE("Target: x | " << "Target surface reached at distance (tolerance) " << distance << " (" << tolerance << ")"); + // assigning the currentSurface navigator.currentSurface(state.navigation, &targetSurface); ACTS_VERBOSE("Target: x | " @@ -161,23 +168,32 @@ struct SurfaceReached { // reaching the target calls a navigation break navigator.targetReached(state.navigation, true); - } else { - // Target is not reached, update the step size - const double overstepLimit = stepper.overstepLimit(state.stepping); - // Check the alternative solution - if (distance < overstepLimit and sIntersection.alternative) { - // Update the distance to the alternative solution - distance = sIntersection.alternative.pathLength; - } - stepper.setStepSize(state.stepping, distance, ConstrainedStep::aborter, - false); - ACTS_VERBOSE("Target: 0 | " - << "Target stepSize (surface) updated to " - << stepper.outputStepSize(state.stepping)); + return true; + } + + const double pLimit = + state.stepping.stepSize.value(ConstrainedStep::aborter); + // not using the stepper overstep limit here because it does not always work + // for perigee surfaces + const double oLimit = + overstepLimit.value_or(stepper.overstepLimit(state.stepping)); + + for (const auto& intersection : sIntersection.split()) { + if (intersection && + detail::checkIntersection(intersection.intersection(), pLimit, oLimit, + tolerance, logger)) { + stepper.setStepSize(state.stepping, intersection.pathLength(), + ConstrainedStep::aborter, false); + break; + } } - // path limit check - return targetReached; + + ACTS_VERBOSE("Target: 0 | " + << "Target stepSize (surface) updated to " + << stepper.outputStepSize(state.stepping)); + + return false; } }; @@ -198,11 +214,9 @@ struct EndOfWorldReached { bool operator()(propagator_state_t& state, const stepper_t& /*stepper*/, const navigator_t& navigator, const Logger& /*logger*/) const { - if (navigator.currentVolume(state.navigation) != nullptr) { - return false; - } - navigator.targetReached(state.navigation, true); - return true; + bool endOfWorld = navigator.endOfWorldReached(state.navigation); + navigator.targetReached(state.navigation, endOfWorld); + return endOfWorld; } }; diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp index a6d4d8befaf..0e54510db4f 100644 --- a/Core/include/Acts/Propagator/StraightLineStepper.hpp +++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp @@ -36,8 +36,6 @@ #include namespace Acts { -template -class GenericBoundTrackParameters; /// @brief straight line stepper based on Surface intersection /// @@ -60,8 +58,6 @@ class StraightLineStepper { /// Constructor from the initial bound track parameters /// - /// @tparam charge_t Type of the bound parameter charge - /// /// @param [in] gctx is the context object for the geometry /// @param [in] mctx is the context object for the magnetic field /// @param [in] par The track parameters at start @@ -69,13 +65,12 @@ class StraightLineStepper { /// @param [in] stolerance is the stepping tolerance /// /// @note the covariance matrix is copied when needed - template explicit State(const GeometryContext& gctx, const MagneticFieldContext& mctx, - const GenericBoundTrackParameters& par, + const BoundTrackParameters& par, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) - : absCharge(std::abs(par.charge())), + : particleHypothesis(par.particleHypothesis()), stepSize(ssize), tolerance(stolerance), geoContext(gctx) { @@ -109,8 +104,8 @@ class StraightLineStepper { /// Internal free vector parameters FreeVector pars = FreeVector::Zero(); - /// The absolute charge as the free vector can be 1/p or q/p - double absCharge = UnitConstants::e; + /// Particle hypothesis + ParticleHypothesis particleHypothesis = ParticleHypothesis::pion(); /// Boolean to indicate if you need covariance transport bool covTransport = false; @@ -138,10 +133,9 @@ class StraightLineStepper { StraightLineStepper() = default; - template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const GenericBoundTrackParameters& par, + const BoundTrackParameters& par, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { return State{gctx, mctx, par, ssize, stolerance}; @@ -194,8 +188,7 @@ class StraightLineStepper { /// /// @param state [in] The stepping state (thread-local cache) double absoluteMomentum(const State& state) const { - auto q = charge(state); - return std::abs((q == 0 ? 1 : q) / qOverP(state)); + return particleHypothesis(state).extractMomentum(qOverP(state)); } /// Momentum accessor @@ -209,7 +202,14 @@ class StraightLineStepper { /// /// @param state [in] The stepping state (thread-local cache) double charge(const State& state) const { - return std::copysign(state.absCharge, qOverP(state)); + return particleHypothesis(state).extractCharge(qOverP(state)); + } + + /// Particle hypothesis + /// + /// @param state [in] The stepping state (thread-local cache) + const ParticleHypothesis& particleHypothesis(const State& state) const { + return state.particleHypothesis; } /// Time access @@ -222,7 +222,7 @@ class StraightLineStepper { /// @param state The stepping state (thread-local cache) double overstepLimit(const State& state) const { (void)state; - return s_onSurfaceTolerance; + return -m_overstepLimit; } /// Update surface status @@ -257,7 +257,7 @@ class StraightLineStepper { /// @param release [in] boolean to trigger step size release template void updateStepSize(State& state, const object_intersection_t& oIntersection, - bool release = true) const { + Direction /*direction*/, bool release = true) const { detail::updateSingleStepSize(state, oIntersection, release); } @@ -390,9 +390,10 @@ class StraightLineStepper { const navigator_t& /*navigator*/) const { // use the adjusted step size const auto h = state.stepping.stepSize.value() * state.options.direction; - const double p = absoluteMomentum(state.stepping); + const auto m = state.stepping.particleHypothesis.mass(); + const auto p = absoluteMomentum(state.stepping); // time propagates along distance as 1/b = sqrt(1 + m²/p²) - const auto dtds = std::hypot(1., state.options.mass / p); + const auto dtds = std::hypot(1., m / p); // Update the track parameters according to the equations of motion Vector3 dir = direction(state.stepping); state.stepping.pars.template segment<3>(eFreePos0) += h * dir; @@ -404,8 +405,7 @@ class StraightLineStepper { D.block<3, 3>(0, 4) = ActsSquareMatrix<3>::Identity() * h; // Extend the calculation by the time propagation // Evaluate dt/dlambda - D(3, 7) = h * state.options.mass * state.options.mass * - state.stepping.pars[eFreeQOverP] / dtds; + D(3, 7) = h * m * m * state.stepping.pars[eFreeQOverP] / dtds; // Set the derivative factor the time state.stepping.derivative(3) = dtds; // Update jacobian and derivative @@ -418,6 +418,9 @@ class StraightLineStepper { // return h return h; } + + private: + double m_overstepLimit = s_onSurfaceTolerance; }; } // namespace Acts diff --git a/Core/include/Acts/Propagator/detail/CovarianceEngine.hpp b/Core/include/Acts/Propagator/detail/CovarianceEngine.hpp index a2df348afb1..e5b61043844 100644 --- a/Core/include/Acts/Propagator/detail/CovarianceEngine.hpp +++ b/Core/include/Acts/Propagator/detail/CovarianceEngine.hpp @@ -47,6 +47,7 @@ namespace detail { /// @param [in, out] jacToGlobal Projection jacobian of the last bound /// parametrisation to free parameters /// @param [in, out] parameters Free, nominal parametrisation +/// @param [in] particleHypothesis Particle hypothesis /// @param [in] covTransport Decision whether the covariance transport should be /// performed /// @param [in] accumulatedPath Propagated distance @@ -61,8 +62,8 @@ Result> boundState( const GeometryContext& geoContext, BoundSquareMatrix& covarianceMatrix, BoundMatrix& jacobian, FreeMatrix& transportJacobian, FreeVector& derivatives, BoundToFreeMatrix& jacToGlobal, - FreeVector& parameters, bool covTransport, double accumulatedPath, - const Surface& surface, + FreeVector& parameters, const ParticleHypothesis& particleHypothesis, + bool covTransport, double accumulatedPath, const Surface& surface, const FreeToBoundCorrection& freeToBoundCorrection = FreeToBoundCorrection(false)); @@ -78,6 +79,7 @@ Result> boundState( /// @param [in, out] jacToGlobal Projection jacobian of the last bound /// parametrisation to free parameters /// @param [in] parameters Free, nominal parametrisation +/// @param [in] particleHypothesis Particle hypothesis /// @param [in] covTransport Decision whether the covariance transport should be /// performed /// @param [in] accumulatedPath Propagated distance @@ -90,7 +92,8 @@ std::tuple curvilinearState( BoundSquareMatrix& covarianceMatrix, BoundMatrix& jacobian, FreeMatrix& transportJacobian, FreeVector& derivatives, BoundToFreeMatrix& jacToGlobal, const FreeVector& parameters, - bool covTransport, double accumulatedPath); + const ParticleHypothesis& particleHypothesis, bool covTransport, + double accumulatedPath); /// @brief Method for on-demand covariance transport of a bound/curvilinear to /// another bound representation. diff --git a/Core/include/Acts/Propagator/detail/GenericDefaultExtension.hpp b/Core/include/Acts/Propagator/detail/GenericDefaultExtension.hpp index dcebeb3d1a8..c4511a3aa23 100644 --- a/Core/include/Acts/Propagator/detail/GenericDefaultExtension.hpp +++ b/Core/include/Acts/Propagator/detail/GenericDefaultExtension.hpp @@ -132,15 +132,18 @@ struct GenericDefaultExtension { typename navigator_t> void propagateTime(propagator_state_t& state, const stepper_t& stepper, const navigator_t& /*navigator*/, const double h) const { + // using because of autodiff + using std::hypot; + /// This evaluation is based on dt/ds = 1/v = 1/(beta * c) with the velocity /// v, the speed of light c and beta = v/c. This can be re-written as dt/ds /// = sqrt(m^2/p^2 + c^{-2}) with the mass m and the momentum p. - using std::hypot; - auto derivative = - hypot(1, state.options.mass / stepper.absoluteMomentum(state.stepping)); - state.stepping.pars[eFreeTime] += h * derivative; + auto m = stepper.particleHypothesis(state.stepping).mass(); + auto p = stepper.absoluteMomentum(state.stepping); + auto dtds = hypot(1, m / p); + state.stepping.pars[eFreeTime] += h * dtds; if (state.stepping.covTransport) { - state.stepping.derivative(3) = derivative; + state.stepping.derivative(3) = dtds; } } @@ -180,9 +183,15 @@ struct GenericDefaultExtension { /// constant offset does not exist for rectangular matrix dGdu' (due to the /// missing Lambda part) and only exists for dFdu' in dlambda/dlambda. + // using because of autodiff + using std::hypot; + + auto m = state.stepping.particleHypothesis.mass(); auto& sd = state.stepping.stepData; auto dir = stepper.direction(state.stepping); auto qop = stepper.qOverP(state.stepping); + auto p = stepper.absoluteMomentum(state.stepping); + auto dtds = hypot(1, m / p); D = FreeMatrix::Identity(); @@ -241,10 +250,7 @@ struct GenericDefaultExtension { dGdL = h / 6. * (dk1dL + 2. * (dk2dL + dk3dL) + dk4dL); - D(3, 7) = h * state.options.mass * state.options.mass * - stepper.qOverP(state.stepping) / - std::hypot(1., state.options.mass / - stepper.absoluteMomentum(state.stepping)); + D(3, 7) = h * m * m * qop / dtds; return true; } }; diff --git a/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp b/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp index 503d457b247..f731c76457a 100644 --- a/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp +++ b/Core/include/Acts/Propagator/detail/GenericDenseEnvironmentExtension.hpp @@ -11,6 +11,7 @@ // Workaround for building on clang+libstdc++ #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp" +#include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Material/Interactions.hpp" @@ -77,8 +78,12 @@ struct GenericDenseEnvironmentExtension { typename navigator_t> int bid(const propagator_state_t& state, const stepper_t& stepper, const navigator_t& navigator) const { + const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); + float absQ = particleHypothesis.absoluteCharge(); + float mass = particleHypothesis.mass(); + // Check for valid particle properties - if (stepper.charge(state.stepping) == 0. || state.options.mass == 0. || + if (absQ == 0. || mass == 0. || stepper.absoluteMomentum(state.stepping) < state.options.momentumCutOff) { return 0; @@ -116,28 +121,33 @@ struct GenericDenseEnvironmentExtension { const navigator_t& navigator, ThisVector3& knew, const Vector3& bField, std::array& kQoP, const int i = 0, const double h = 0., const ThisVector3& kprev = ThisVector3::Zero()) { - auto q = stepper.charge(state.stepping); + // using because of autodiff + using std::hypot; + + double q = stepper.charge(state.stepping); + const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); + float mass = particleHypothesis.mass(); // i = 0 is used for setup and evaluation of k if (i == 0) { // Set up container for energy loss auto volumeMaterial = navigator.currentVolumeMaterial(state.navigation); ThisVector3 position = stepper.position(state.stepping); - material = (volumeMaterial->material(position.template cast())); + material = volumeMaterial->material(position.template cast()); initialMomentum = stepper.absoluteMomentum(state.stepping); currentMomentum = initialMomentum; - qop[0] = q / initialMomentum; - initializeEnergyLoss(state); + qop[0] = stepper.qOverP(state.stepping); + initializeEnergyLoss(state, stepper); // Evaluate k knew = qop[0] * stepper.direction(state.stepping).cross(bField); // Evaluate k for the time propagation Lambdappi[0] = -qop[0] * qop[0] * qop[0] * g * energy[0] / (q * q); - //~ tKi[0] = std::hypot(1, state.options.mass / initialMomentum); - tKi[0] = hypot(1, state.options.mass * qop[0]); + //~ tKi[0] = std::hypot(1, mass / initialMomentum); + tKi[0] = hypot(1, mass * qop[0]); kQoP[0] = Lambdappi[0]; } else { // Update parameters and check for momentum condition - updateEnergyLoss(state.options.mass, h, state, stepper, i); + updateEnergyLoss(mass, h, state, stepper, i); if (currentMomentum < state.options.momentumCutOff) { return false; } @@ -147,7 +157,7 @@ struct GenericDenseEnvironmentExtension { // Evaluate k_i for the time propagation auto qopNew = qop[0] + h * Lambdappi[i - 1]; Lambdappi[i] = -qopNew * qopNew * qopNew * g * energy[i] / (q * q); - tKi[i] = hypot(1, state.options.mass * qopNew); + tKi[i] = hypot(1, mass * qopNew); kQoP[i] = Lambdappi[i]; } return true; @@ -171,6 +181,12 @@ struct GenericDenseEnvironmentExtension { typename navigator_t> bool finalize(propagator_state_t& state, const stepper_t& stepper, const navigator_t& /*navigator*/, const double h) const { + // using because of autodiff + using std::hypot; + + const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); + float mass = particleHypothesis.mass(); + // Evaluate the new momentum auto newMomentum = stepper.absoluteMomentum(state.stepping) + @@ -182,14 +198,14 @@ struct GenericDenseEnvironmentExtension { } // Add derivative dlambda/ds = Lambda'' - state.stepping.derivative(7) = -hypot(state.options.mass, newMomentum) * g / + state.stepping.derivative(7) = -hypot(mass, newMomentum) * g / (newMomentum * newMomentum * newMomentum); // Update momentum state.stepping.pars[eFreeQOverP] = stepper.charge(state.stepping) / newMomentum; // Add derivative dt/ds = 1/(beta * c) = sqrt(m^2 * p^{-2} + c^{-2}) - state.stepping.derivative(3) = hypot(1, state.options.mass / newMomentum); + state.stepping.derivative(3) = hypot(1, mass / newMomentum); // Update time state.stepping.pars[eFreeTime] += (h / 6.) * (tKi[0] + 2. * (tKi[1] + tKi[2]) + tKi[3]); @@ -261,8 +277,13 @@ struct GenericDenseEnvironmentExtension { /// constant offset does not exist for rectangular matrix dFdu' (due to the /// missing Lambda part) and only exists for dGdu' in dlambda/dlambda. + // using because of autodiff + using std::hypot; + auto& sd = state.stepping.stepData; auto dir = stepper.direction(state.stepping); + const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); + float mass = particleHypothesis.mass(); D = FreeMatrix::Identity(); const double half_h = h * 0.5; @@ -337,42 +358,32 @@ struct GenericDenseEnvironmentExtension { // The following comment lines refer to the application of the time being // treated as a position. Since t and qop are treated independently for now, // this just serves as entry point for building their relation - //~ double dtpp1dl = -state.options.mass * state.options.mass * qop[0] * - //~ qop[0] * - //~ (3. * g + qop[0] * dgdqop(energy[0], state.options.mass, - //~ state.options.absPdgCode, - //~ state.options.meanEnergyLoss)); - - double dtp1dl = qop[0] * state.options.mass * state.options.mass / - std::hypot(1, qop[0] * state.options.mass); + //~ double dtpp1dl = -mass * mass * qop[0] * qop[0] * + //~ (3. * g + qop[0] * dgdqop(energy[0], .mass, + //~ absPdg, meanEnergyLoss)); + + double dtp1dl = qop[0] * mass * mass / hypot(1, qop[0] * mass); double qopNew = qop[0] + half_h * Lambdappi[0]; - //~ double dtpp2dl = -state.options.mass * state.options.mass * qopNew * + //~ double dtpp2dl = -mass * mass * qopNew * //~ qopNew * //~ (3. * g * (1. + half_h * jdL[0]) + - //~ qopNew * dgdqop(energy[1], state.options.mass, - //~ state.options.absPdgCode, - //~ state.options.meanEnergyLoss)); + //~ qopNew * dgdqop(energy[1], mass, absPdgCode, meanEnergyLoss)); - double dtp2dl = qopNew * state.options.mass * state.options.mass / - std::hypot(1, qopNew * state.options.mass); + double dtp2dl = qopNew * mass * mass / std::hypot(1, qopNew * mass); qopNew = qop[0] + half_h * Lambdappi[1]; - //~ double dtpp3dl = -state.options.mass * state.options.mass * qopNew * + //~ double dtpp3dl = -mass * mass * qopNew * //~ qopNew * //~ (3. * g * (1. + half_h * jdL[1]) + - //~ qopNew * dgdqop(energy[2], state.options.mass, - //~ state.options.absPdgCode, - //~ state.options.meanEnergyLoss)); + //~ qopNew * dgdqop(energy[2], mass, absPdg, meanEnergyLoss)); - double dtp3dl = qopNew * state.options.mass * state.options.mass / - std::hypot(1, qopNew * state.options.mass); + double dtp3dl = qopNew * mass * mass / hypot(1, qopNew * mass); qopNew = qop[0] + half_h * Lambdappi[2]; - double dtp4dl = qopNew * state.options.mass * state.options.mass / - std::hypot(1, qopNew * state.options.mass); + double dtp4dl = qopNew * mass * mass / hypot(1, qopNew * mass); - //~ D(3, 7) = h * state.options.mass * state.options.mass * qop[0] / - //~ std::hypot(1., state.options.mass * qop[0]) + //~ D(3, 7) = h * mass * mass * qop[0] / + //~ hypot(1., mass * qop[0]) //~ + h * h / 6. * (dtpp1dl + dtpp2dl + dtpp3dl); D(3, 7) = (h / 6.) * (dtp1dl + 2. * (dtp2dl + dtp3dl) + dtp4dl); @@ -383,23 +394,32 @@ struct GenericDenseEnvironmentExtension { /// loss of a particle in material /// /// @tparam propagator_state_t Type of the state of the propagator + /// @tparam stepper_t Type of the stepper + /// /// @param [in] state Deliverer of configurations - template - void initializeEnergyLoss(const propagator_state_t& state) { - energy[0] = hypot(initialMomentum, state.options.mass); + template + void initializeEnergyLoss(const propagator_state_t& state, + const stepper_t& stepper) { + // using because of autodiff + using std::hypot; + + const auto& particleHypothesis = stepper.particleHypothesis(state.stepping); + float mass = particleHypothesis.mass(); + PdgParticle absPdg = particleHypothesis.absolutePdg(); + float absQ = particleHypothesis.absoluteCharge(); + + energy[0] = hypot(initialMomentum, mass); // use unit length as thickness to compute the energy loss per unit length Acts::MaterialSlab slab(material, 1); // Use the same energy loss throughout the step. if (state.options.meanEnergyLoss) { - g = -computeEnergyLossMean( - slab, state.options.absPdgCode, state.options.mass, - static_cast(qop[0]), state.stepping.absCharge); + g = -computeEnergyLossMean(slab, absPdg, mass, static_cast(qop[0]), + absQ); } else { // TODO using the unit path length is not quite right since the most // probably energy loss is not independent from the path length. - g = -computeEnergyLossMode( - slab, state.options.absPdgCode, state.options.mass, - static_cast(qop[0]), state.stepping.absCharge); + g = -computeEnergyLossMode(slab, absPdg, mass, static_cast(qop[0]), + absQ); } // Change of the momentum per path length // dPds = dPdE * dEds @@ -410,13 +430,11 @@ struct GenericDenseEnvironmentExtension { if (state.options.includeGgradient) { if (state.options.meanEnergyLoss) { dgdqopValue = deriveEnergyLossMeanQOverP( - slab, state.options.absPdgCode, state.options.mass, - static_cast(qop[0]), state.stepping.absCharge); + slab, absPdg, mass, static_cast(qop[0]), absQ); } else { // TODO path length dependence; see above dgdqopValue = deriveEnergyLossModeQOverP( - slab, state.options.absPdgCode, state.options.mass, - static_cast(qop[0]), state.stepping.absCharge); + slab, absPdg, mass, static_cast(qop[0]), absQ); } } // Calculate term for later error propagation @@ -440,6 +458,9 @@ struct GenericDenseEnvironmentExtension { void updateEnergyLoss(const double mass, const double h, const propagator_state_t& state, const stepper_t& stepper, const int i) { + // using because of autodiff + using std::hypot; + // Update parameters related to a changed momentum currentMomentum = initialMomentum + h * dPds[i - 1]; energy[i] = hypot(currentMomentum, mass); diff --git a/Core/include/Acts/Propagator/detail/LoopProtection.hpp b/Core/include/Acts/Propagator/detail/LoopProtection.hpp index ddff0556eb8..c8170ce0976 100644 --- a/Core/include/Acts/Propagator/detail/LoopProtection.hpp +++ b/Core/include/Acts/Propagator/detail/LoopProtection.hpp @@ -18,11 +18,17 @@ namespace detail { template void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, - path_aborter_t& pathAborter, const Logger& logger) { + path_aborter_t& pathAborter, bool releaseLimit, + const Logger& logger) { if (!state.options.loopProtection) { return; } + if (releaseLimit) { + pathAborter.internalLimit = + state.options.direction * std::numeric_limits::max(); + } + // Get the field at the start position auto fieldRes = stepper.getField(state.stepping, stepper.position(state.stepping)); @@ -32,7 +38,7 @@ void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, ACTS_WARNING("Field lookup was unsuccessful, this is very likely an error"); return; } - Vector3 field = *fieldRes; + const Vector3 field = *fieldRes; const double B = field.norm(); if (B == 0) { return; @@ -43,13 +49,18 @@ void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, // Calculate the full helix path const double helixPath = state.options.direction * 2 * M_PI * p / B; // And set it as the loop limit if it overwrites the internal limit - double loopLimit = state.options.loopFraction * helixPath; - double pathLimit = pathAborter.internalLimit; - if (std::abs(loopLimit) < std::abs(pathLimit)) { + const double loopLimit = state.options.loopFraction * helixPath; + const double previousLimit = pathAborter.internalLimit; + if (std::abs(loopLimit) < std::abs(previousLimit)) { pathAborter.internalLimit = loopLimit; ACTS_VERBOSE("Path aborter limit set to " - << loopLimit << " (full helix = " << helixPath << ")"); + << loopLimit << " (full helix = " << helixPath + << ", previous limit = " << previousLimit << ")"); + } else { + ACTS_VERBOSE("Path aborter limit not updated to " + << loopLimit << " (full helix = " << helixPath + << ", previous limit = " << previousLimit << ")"); } } diff --git a/Core/include/Acts/Propagator/detail/ParameterTraits.hpp b/Core/include/Acts/Propagator/detail/ParameterTraits.hpp new file mode 100644 index 00000000000..33cfaa681f0 --- /dev/null +++ b/Core/include/Acts/Propagator/detail/ParameterTraits.hpp @@ -0,0 +1,42 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include +#include + +namespace Acts::detail { + +template +struct stepper_bound_parameters { + using result_type = decltype(std::declval().boundState( + std::declval(), std::declval(), + std::declval(), std::declval())); + using type = + std::decay_t(*std::declval()))>; +}; + +template +using stepper_bound_parameters_type_t = + typename stepper_bound_parameters::type; + +template +struct stepper_curvilinear_parameters { + using result_type = decltype(std::declval().curvilinearState( + std::declval(), std::declval())); + using type = std::decay_t(std::declval()))>; +}; + +template +using stepper_curvilinear_parameters_type_t = + typename stepper_curvilinear_parameters::type; +} // namespace Acts::detail diff --git a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp index 84ea3ac9d0f..048b4d606c4 100644 --- a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp @@ -82,10 +82,10 @@ struct PointwiseMaterialInteraction { time(stepper.time(state.stepping)), dir(stepper.direction(state.stepping)), qOverP(stepper.qOverP(state.stepping)), - absQ(state.stepping.absCharge), + absQ(stepper.particleHypothesis(state.stepping).absoluteCharge()), momentum(stepper.absoluteMomentum(state.stepping)), - mass(state.options.mass), - absPdg(state.options.absPdgCode), + mass(stepper.particleHypothesis(state.stepping).mass()), + absPdg(stepper.particleHypothesis(state.stepping).absolutePdg()), performCovarianceTransport(state.stepping.covTransport), navDir(state.options.direction) {} diff --git a/Core/include/Acts/Propagator/detail/SteppingHelper.hpp b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp index 865aac8b11c..8ffe3ec05eb 100644 --- a/Core/include/Acts/Propagator/detail/SteppingHelper.hpp +++ b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp @@ -43,32 +43,27 @@ Acts::Intersection3D::Status updateSingleSurfaceStatus( navDir * stepper.direction(state), bcheck, surfaceTolerance); // The intersection is on surface already - if (sIntersection.intersection.status == Intersection3D::Status::onSurface) { + if (sIntersection.closest().status() == Intersection3D::Status::onSurface) { // Release navigation step size state.stepSize.release(ConstrainedStep::actor); ACTS_VERBOSE("Intersection: state is ON SURFACE"); return Intersection3D::Status::onSurface; - } else if (sIntersection.intersection or sIntersection.alternative) { - // Path and overstep limit checking - double pLimit = state.stepSize.value(ConstrainedStep::aborter); - double oLimit = stepper.overstepLimit(state); + } - // If either of the two intersections are viable return reachable - if (detail::checkIntersection(sIntersection.intersection, pLimit, oLimit, - surfaceTolerance, logger)) { - ACTS_VERBOSE("Surface is reachable"); - stepper.setStepSize(state, sIntersection.intersection.pathLength); - return Intersection3D::Status::reachable; - } + // Path and overstep limit checking + const double pLimit = state.stepSize.value(ConstrainedStep::aborter); + const double oLimit = stepper.overstepLimit(state); - if (sIntersection.alternative and - detail::checkIntersection(sIntersection.alternative, pLimit, oLimit, + for (const auto& intersection : sIntersection.split()) { + if (intersection && + detail::checkIntersection(intersection.intersection(), pLimit, oLimit, surfaceTolerance, logger)) { ACTS_VERBOSE("Surface is reachable"); - stepper.setStepSize(state, sIntersection.alternative.pathLength); + stepper.setStepSize(state, intersection.pathLength()); return Intersection3D::Status::reachable; } } + ACTS_VERBOSE("Surface is NOT reachable"); return Intersection3D::Status::unreachable; } @@ -85,7 +80,7 @@ template void updateSingleStepSize(typename stepper_t::State& state, const object_intersection_t& oIntersection, bool release = true) { - double stepSize = oIntersection.intersection.pathLength; + double stepSize = oIntersection.pathLength(); state.stepSize.update(stepSize, ConstrainedStep::actor, release); } diff --git a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp index ef67180b300..55c757ac090 100644 --- a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp +++ b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp @@ -13,6 +13,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" +#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" #include @@ -21,19 +22,21 @@ namespace Acts { class Surface; -class Layer; -class TrackingVolume; namespace detail { -/// @brief the step information for +/// @brief The step information for recording +/// +/// The surface object could be a temporarily created object +/// and as the Step vector is collected and written out at a +/// later stage, the surface is referenced counted here. struct Step { ConstrainedStep stepSize; Direction navDir; Vector3 position = Vector3(0., 0., 0.); Vector3 momentum = Vector3(0., 0., 0.); std::shared_ptr surface = nullptr; - const TrackingVolume* volume = nullptr; + GeometryIdentifier geoID = 0; }; /// @brief a step length logger for debugging the stepping @@ -65,23 +68,25 @@ struct SteppingLogger { void operator()(propagator_state_t& state, const stepper_t& stepper, const navigator_t& navigator, result_type& result, const Logger& /*logger*/) const { - // don't log if you have reached the target + // Don't log if you have reached the target or are sterile if (sterile or navigator.targetReached(state.navigation)) { return; } - // record the propagation state + // Record the propagation state Step step; step.stepSize = state.stepping.stepSize; step.navDir = state.options.direction; step.position = stepper.position(state.stepping); step.momentum = stepper.momentum(state.stepping); + // Record the information about the surface if (navigator.currentSurface(state.navigation) != nullptr) { - // hang on to surface step.surface = navigator.currentSurface(state.navigation)->getSharedPtr(); + step.geoID = step.surface->geometryId(); + } else if (navigator.currentVolume(state.navigation) != nullptr) { + // If there's no surface but a volume, this sets the geoID + step.geoID = navigator.currentVolume(state.navigation)->geometryId(); } - - step.volume = navigator.currentVolume(state.navigation); result.steps.push_back(std::move(step)); } }; diff --git a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp index a5badc7a6d3..3ab90d4a8d3 100644 --- a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp @@ -12,6 +12,7 @@ #include "Acts/Definitions/PdgParticle.hpp" #include "Acts/Geometry/TrackingVolume.hpp" #include "Acts/Material/ISurfaceMaterial.hpp" +#include "Acts/Material/MaterialInteraction.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -20,8 +21,8 @@ namespace detail { /// @brief Struct to handle volume material interaction struct VolumeMaterialInteraction { - /// Data from the propagation state - const TrackingVolume* volume = nullptr; + /// The material interaction volume + InteractionVolume volume{}; /// The particle current position const Vector3 pos = Vector3::Zero(); /// The particle current time @@ -65,10 +66,10 @@ struct VolumeMaterialInteraction { time(stepper.time(state.stepping)), dir(stepper.direction(state.stepping)), qOverP(stepper.qOverP(state.stepping)), - absQ(state.stepping.absCharge), + absQ(stepper.particleHypothesis(state.stepping).absoluteCharge()), momentum(stepper.absoluteMomentum(state.stepping)), - mass(state.options.mass), - absPdg(state.options.absPdgCode), + mass(stepper.particleHypothesis(state.stepping).mass()), + absPdg(stepper.particleHypothesis(state.stepping).absolutePdg()), performCovarianceTransport(state.stepping.covTransport), navDir(state.options.direction) {} diff --git a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp index 291b42f63c6..be50a0546ed 100644 --- a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp +++ b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp @@ -34,7 +34,7 @@ struct TripletCandidate { /// @param q Whether the candidate is high or low quality TripletCandidate(external_space_point_t& b, external_space_point_t& m, external_space_point_t& t, float w, float z, bool q) - : bottom(&b), middle(&m), top(&t), weight(w), zOrigin(z), isQuality(q){}; + : bottom(&b), middle(&m), top(&t), weight(w), zOrigin(z), isQuality(q) {} /// @brief Copy operations TripletCandidate(const TripletCandidate&) = default; @@ -121,7 +121,7 @@ class CandidatesForMiddleSp { /// @param isQuality Whether the triplet candidate is high or low quality /// @returns whether the triplet candidate has been added or not to the collection bool push(std::vector& indices, std::size_t& n, - const std::size_t& n_max, external_space_point_t& SpB, + const std::size_t n_max, external_space_point_t& SpB, external_space_point_t& SpM, external_space_point_t& SpT, float weight, float zOrigin, bool isQuality); @@ -130,7 +130,7 @@ class CandidatesForMiddleSp { /// @param n Index of the requested element /// @param max_size Number of elements currently stored in the collection /// @returns Whether the element exists - bool exists(const std::size_t& n, const std::size_t& max_size) const; + bool exists(const std::size_t n, const std::size_t max_size) const; /// @brief Pop an element from a collection. The removal of the element from the collection /// does not imply its destruction. In fact, the number of stored elements is @@ -171,7 +171,7 @@ class CandidatesForMiddleSp { /// @param n_max The maximum number of elements that can be stored in the collection /// @param element The element that must be added to the collection void addToCollection(std::vector& indices, std::size_t& n, - const std::size_t& n_max, value_type&& element); + const std::size_t n_max, value_type&& element); private: // sizes @@ -189,7 +189,7 @@ class CandidatesForMiddleSp { // The following vectors store indexes to elements in the storage // They are sorted as a min heap tree, in which - // Each node is lower then its children + // Each node is lower than its children // Thus, it is guaranteed that the lower elements is at the front // Sorting criteria is the seed quality // diff --git a/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp b/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp index 4f513e5cff7..7660391b9ab 100644 --- a/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp +++ b/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp @@ -43,7 +43,7 @@ inline void CandidatesForMiddleSp::pop( template inline bool CandidatesForMiddleSp::exists( - const std::size_t& n, const std::size_t& max_size) const { + const std::size_t n, const std::size_t max_size) const { // If the element exists, its index is lower than the current number // of stored elements return n < max_size; @@ -84,7 +84,7 @@ bool CandidatesForMiddleSp::push( template bool CandidatesForMiddleSp::push( - std::vector& indices, std::size_t& n, const std::size_t& n_max, + std::vector& indices, std::size_t& n, const std::size_t n_max, external_space_point_t& SpB, external_space_point_t& SpM, external_space_point_t& SpT, float weight, float zOrigin, bool isQuality) { // If we do not want to store candidates, returns @@ -115,7 +115,7 @@ bool CandidatesForMiddleSp::push( template void CandidatesForMiddleSp::addToCollection( - std::vector& indices, std::size_t& n, const std::size_t& n_max, + std::vector& indices, std::size_t& n, const std::size_t n_max, value_type&& element) { // adds elements to the end of the collection if (indices.size() == n_max) { @@ -143,7 +143,7 @@ void CandidatesForMiddleSp::bubbledw( // This is done by comparing its weight with the weights of its two // children. Few things can happen: // - there are no children - // - the current weight is lower then the weight of the children + // - the current weight is lower than the weight of the children // - at least one of the children has a lower weight // In the first two cases we stop, since we are already in the correct // position diff --git a/Core/include/Acts/Seeding/GNN_DataStorage.hpp b/Core/include/Acts/Seeding/GNN_DataStorage.hpp new file mode 100644 index 00000000000..7a6b1c3b0e8 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_DataStorage.hpp @@ -0,0 +1,316 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Seeding/GNN_Geometry.hpp" + +#include +#include +#include + +namespace Acts { + +constexpr size_t MAX_SEG_PER_NODE = 1000; // was 30 +constexpr size_t N_SEG_CONNS = 6; // was 6 + +// new sp struct +template +struct FTF_SP { + const space_point_t *SP; // want inside to have pointer + int FTF_ID; + int combined_ID; + FTF_SP(const space_point_t *sp, int id, int combined_id) + : SP(sp), FTF_ID(id), combined_ID{combined_id} { + if (SP->sourceLinks().size() == 1) { // pixels have 1 SL + m_isPixel = true; + } else { + m_isPixel = false; + } + m_phi = std::atan(SP->x() / SP->y()); + }; + bool isPixel() const { return m_isPixel; } + bool isSCT() const { return !m_isPixel; } + float phi() const { return m_phi; } + bool m_isPixel; + float m_phi; +}; + +template +class TrigFTF_GNN_Node { + public: + struct CompareByPhi { + bool operator()(const TrigFTF_GNN_Node *n1, + const TrigFTF_GNN_Node *n2) { + return (n1->m_sp_FTF.phi() < n2->m_sp_FTF.phi()); + } + }; + + TrigFTF_GNN_Node(const FTF_SP &FTF_sp, float minT = -100.0, + float maxT = 100.0) + : m_sp_FTF(FTF_sp), m_minCutOnTau(minT), m_maxCutOnTau(maxT) {} + + inline void addIn(int i) { + if (m_in.size() < MAX_SEG_PER_NODE) { + m_in.push_back(i); + } + } + + inline void addOut(int i) { + if (m_out.size() < MAX_SEG_PER_NODE) { + m_out.push_back(i); + } + } + + inline bool isConnector() const { + if (m_in.empty() || m_out.empty()) { + return false; + } + return true; + } + + inline bool isFull() const { + if (m_in.size() == MAX_SEG_PER_NODE && m_out.size() == MAX_SEG_PER_NODE) { + return true; + } else { + return false; + } + } + + const FTF_SP &m_sp_FTF; + + std::vector m_in; // indices of the edges in the edge storage + std::vector m_out; + float m_minCutOnTau, m_maxCutOnTau; +}; + +template +class TrigFTF_GNN_EtaBin { + public: + TrigFTF_GNN_EtaBin() { m_vn.clear(); } + + ~TrigFTF_GNN_EtaBin() { + for (typename std::vector *>::iterator it = + m_vn.begin(); + it != m_vn.end(); ++it) { + delete (*it); + } + } + + void sortByPhi() { + std::sort(m_vn.begin(), m_vn.end(), + typename Acts::TrigFTF_GNN_Node::CompareByPhi()); + } + + bool empty() const { return m_vn.empty(); } + + void generatePhiIndexing(float dphi) { + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + // float phi = pN->m_sp.phi(); + // float phi = (std::atan(pN->m_sp.x() / pN->m_sp.y())); + float phi = pN->m_sp_FTF.phi(); + if (phi <= M_PI - dphi) { + continue; + } + + m_vPhiNodes.push_back( + std::pair(phi - 2 * M_PI, nIdx)); + } + + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + float phi = pN->m_sp_FTF.phi(); + m_vPhiNodes.push_back(std::pair(phi, nIdx)); + } + + for (unsigned int nIdx = 0; nIdx < m_vn.size(); nIdx++) { + TrigFTF_GNN_Node *pN = m_vn.at(nIdx); + float phi = pN->m_sp_FTF.phi(); + if (phi >= -M_PI + dphi) { + break; + } + m_vPhiNodes.push_back( + std::pair(phi + 2 * M_PI, nIdx)); + } + } + + std::vector *> m_vn; + // TODO change to + // std::vector>> m_vn; + std::vector> m_vPhiNodes; +}; + +template +class TrigFTF_GNN_DataStorage { + public: + TrigFTF_GNN_DataStorage(const TrigFTF_GNN_Geometry &g) + : m_geo(g) { + m_etaBins.reserve(g.num_bins()); + for (int k = 0; k < g.num_bins(); k++) { + m_etaBins.emplace_back(TrigFTF_GNN_EtaBin()); + } + } + + int addSpacePoint(const FTF_SP &sp, bool useClusterWidth) { + const TrigFTF_GNN_Layer *pL = + m_geo.getTrigFTF_GNN_LayerByKey(sp.combined_ID); + + if (pL == nullptr) { + return -1; + } + + int binIndex = pL->getEtaBin(sp.SP->z(), sp.SP->r()); + + if (binIndex == -1) { + return -2; + } + + bool isBarrel = (pL->m_layer.m_type == 0); + + if (isBarrel) { + float min_tau = -100.0; + float max_tau = 100.0; + // can't do this bit yet as dont have cluster width + if (useClusterWidth) { + // const Trk::SpacePoint* osp = sp.offlineSpacePoint(); + // const InDet::PixelCluster* pCL = dynamic_cast(osp->clusterList().first); + // float cluster_width = pCL->width().widthPhiRZ().y(); + float cluster_width = 1; // temporary while cluster width not available + min_tau = 6.7 * (cluster_width - 0.2); + max_tau = + 1.6 + 0.15 / (cluster_width + 0.2) + 6.1 * (cluster_width - 0.2); + } + + m_etaBins.at(binIndex).m_vn.push_back(new TrigFTF_GNN_Node( + sp, min_tau, max_tau)); // adding ftf member to nodes + } else { + if (useClusterWidth) { + // const Trk::SpacePoint* osp = sp.offlineSpacePoint(); + // const InDet::PixelCluster* pCL = dynamic_cast(osp->clusterList().first); + // float cluster_width = pCL->width().widthPhiRZ().y(); + float cluster_width = 1; // temporary while cluster width not available + if (cluster_width > 0.2) { + return -3; + } + } + m_etaBins.at(binIndex).m_vn.push_back( + new TrigFTF_GNN_Node(sp)); + } + + return 0; + } + + // for safety to prevent passing as copy + TrigFTF_GNN_DataStorage(const TrigFTF_GNN_DataStorage &) = delete; + TrigFTF_GNN_DataStorage &operator=(const TrigFTF_GNN_DataStorage &) = delete; + + unsigned int numberOfNodes() const { + unsigned int n = 0; + + for (auto &b : m_etaBins) { + n += b.m_vn.size(); + } + return n; + } + + void getConnectingNodes( + std::vector *> &vn) { + vn.clear(); + vn.reserve(numberOfNodes()); + for (const auto &b : m_etaBins) { + for (typename std::vector< + TrigFTF_GNN_Node *>::const_iterator nIt = + b.m_vn.begin(); + nIt != b.m_vn.end(); ++nIt) { + if ((*nIt)->m_in.empty()) { + continue; + } + if ((*nIt)->m_out.empty()) { + continue; + } + vn.push_back(*nIt); + } + } + } + + void sortByPhi() { + for (auto &b : m_etaBins) { + b.sortByPhi(); + } + } + + void generatePhiIndexing(float dphi) { + for (auto &b : m_etaBins) { + b.generatePhiIndexing(dphi); + } + } + + const TrigFTF_GNN_EtaBin &getEtaBin(int idx) const { + if (idx >= static_cast(m_etaBins.size())) { + idx = idx - 1; + } + return m_etaBins.at(idx); + } + + protected: + const TrigFTF_GNN_Geometry &m_geo; + + std::vector> m_etaBins; +}; + +template +class TrigFTF_GNN_Edge { + public: + struct CompareLevel { + public: + bool operator()(const TrigFTF_GNN_Edge *pS1, const TrigFTF_GNN_Edge *pS2) { + return pS1->m_level > pS2->m_level; + } + }; + + TrigFTF_GNN_Edge(TrigFTF_GNN_Node *n1, + TrigFTF_GNN_Node *n2, float p1, float p2, + float p3, float p4) + : m_n1(n1), m_n2(n2), m_level(1), m_next(1) { + m_p[0] = p1; + m_p[1] = p2; + m_p[2] = p3; + m_p[3] = p4; + } + + TrigFTF_GNN_Edge() : m_n1(nullptr), m_n2(nullptr), m_level(-1), m_next(-1) {} + + // TrigFTF_GNN_Edge(const TrigFTF_GNN_Edge &e) + // : m_n1(e.m_n1), m_n2(e.m_n2) {} + + // inline void initialize(TrigFTF_GNN_Node *n1, + // TrigFTF_GNN_Node *n2) { + // m_n1 = n1; + // m_n2 = n2; + // m_level = 1; + // m_next = 1; + // m_nNei = 0; + // } + + TrigFTF_GNN_Node *m_n1{nullptr}; + TrigFTF_GNN_Node *m_n2{nullptr}; + + signed char m_level{}, m_next{}; + + unsigned char m_nNei{0}; + float m_p[4]{}; + + unsigned int m_vNei[N_SEG_CONNS]{}; // global indices of the connected edges +}; + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/GNN_Geometry.hpp b/Core/include/Acts/Seeding/GNN_Geometry.hpp new file mode 100644 index 00000000000..ed4909caa87 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_Geometry.hpp @@ -0,0 +1,386 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/TrackFinding/FasTrackConnector.hpp" + +#include +#include +#include +#include +#include + +namespace Acts { +class TrigInDetSiLayer { + public: + int m_subdet; // combined ID + int m_type; // 0: barrel, +/-n : endcap + float m_refCoord; + float m_minBound, m_maxBound; + + TrigInDetSiLayer(int subdet, short int type, float center, float min, + float max) + : m_subdet(subdet), + m_type(type), + m_refCoord(center), + m_minBound(min), + m_maxBound(max) {} +}; + +template +class TrigFTF_GNN_Layer { + public: + TrigFTF_GNN_Layer(const TrigInDetSiLayer &ls, float ew, int bin0) + : m_layer(ls), m_etaBinWidth(ew) { + if (m_layer.m_type == 0) { // barrel + m_r1 = m_layer.m_refCoord; + m_r2 = m_layer.m_refCoord; + m_z1 = m_layer.m_minBound; + m_z2 = m_layer.m_maxBound; + } else { // endcap + m_r1 = m_layer.m_minBound; + m_r2 = m_layer.m_maxBound; + m_z1 = m_layer.m_refCoord; + m_z2 = m_layer.m_refCoord; + } + + float t1 = m_z1 / m_r1; + float eta1 = -std::log(sqrt(1 + t1 * t1) - t1); + + float t2 = m_z2 / m_r2; + float eta2 = -std::log(sqrt(1 + t2 * t2) - t2); + + m_minEta = eta1; + m_maxEta = eta2; + + if (m_maxEta < m_minEta) { + m_minEta = eta2; + m_maxEta = eta1; + } + + m_maxEta += 1e-6; // increasing them slightly to avoid range_check + // exceptions + m_minEta -= 1e-6; + + float deltaEta = m_maxEta - m_minEta; + + int binCounter = bin0; + + if (deltaEta < m_etaBinWidth) { + m_nBins = 1; + m_bins.push_back(binCounter++); + m_etaBin = deltaEta; + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } else { // endcap + m_minRadius.push_back(m_layer.m_minBound - 2.0); + m_maxRadius.push_back(m_layer.m_maxBound + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } + } else { + float nB = static_cast(deltaEta / m_etaBinWidth); + m_nBins = nB; + if (deltaEta - m_etaBinWidth * nB > 0.5 * m_etaBinWidth) { + m_nBins++; + } + m_etaBin = deltaEta / m_nBins; + + if (m_nBins == 1) { + m_bins.push_back(binCounter++); + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } else { // endcap + m_minRadius.push_back(m_layer.m_minBound - 2.0); + m_maxRadius.push_back(m_layer.m_maxBound + 2.0); + m_minBinCoord.push_back(m_layer.m_minBound); + m_maxBinCoord.push_back(m_layer.m_maxBound); + } + } else { + float eta = m_minEta + 0.5 * m_etaBin; + + for (int i = 1; i <= m_nBins; i++) { + m_bins.push_back(binCounter++); + + float e1 = eta - 0.5 * m_etaBin; + float e2 = eta + 0.5 * m_etaBin; + + if (m_layer.m_type == 0) { // barrel + m_minRadius.push_back(m_layer.m_refCoord - 2.0); + m_maxRadius.push_back(m_layer.m_refCoord + 2.0); + float z1 = m_layer.m_refCoord * std::sinh(e1); + m_minBinCoord.push_back(z1); + float z2 = m_layer.m_refCoord * std::sinh(e2); + m_maxBinCoord.push_back(z2); + } else { // endcap + float r = m_layer.m_refCoord / std::sinh(e1); + m_minBinCoord.push_back(r); + m_minRadius.push_back(r - 2.0); + r = m_layer.m_refCoord / std::sinh(e2); + m_maxBinCoord.push_back(r); + m_maxRadius.push_back(r + 2.0); + } + + eta += m_etaBin; + } + } + } + } + + int getEtaBin(float zh, float rh) const { + if (m_bins.size() == 1) { + return m_bins.at(0); + } + float t1 = zh / rh; + float eta = -std::log(std::sqrt(1 + t1 * t1) - t1); + + int idx = static_cast((eta - m_minEta) / m_etaBin); + + if (idx < 0) { + idx = 0; + } + if (idx >= static_cast(m_bins.size())) { + idx = static_cast(m_bins.size()) - 1; + } + return m_bins.at(idx); // index in the global storage + } + + float getMinBinRadius(int idx) const { + if (idx >= static_cast(m_minRadius.size())) { + idx = idx - 1; + } + if (idx < 0) { + idx = 0; + } + return m_minRadius.at(idx); + } + + float getMaxBinRadius(int idx) const { + if (idx >= static_cast(m_maxRadius.size())) { + idx = idx - 1; + } + if (idx < 0) { + idx = 0; + } + return m_maxRadius.at(idx); + } + + int num_bins() const { return m_bins.size(); } + + bool verifyBin(const TrigFTF_GNN_Layer *pL, int b1, int b2, + float min_z0, float max_z0) const { + float z1min = m_minBinCoord.at(b1); + float z1max = m_maxBinCoord.at(b1); + float r1 = m_layer.m_refCoord; + + if (m_layer.m_type == 0 && pL->m_layer.m_type == 0) { // barrel -> barrel + + const float tol = 5.0; + + float min_b2 = pL->m_minBinCoord.at(b2); + float max_b2 = pL->m_maxBinCoord.at(b2); + + float r2 = pL->m_layer.m_refCoord; + + float A = r2 / (r2 - r1); + float B = r1 / (r2 - r1); + + float z0_min = z1min * A - max_b2 * B; + float z0_max = z1max * A - min_b2 * B; + + if (z0_max < min_z0 - tol || z0_min > max_z0 + tol) { + return false; + } + return true; + } + + if (m_layer.m_type == 0 && pL->m_layer.m_type != 0) { // barrel -> endcap + + const float tol = 10.0; + + float z2 = pL->m_layer.m_refCoord; + float r2max = pL->m_maxBinCoord.at(b2); + float r2min = pL->m_minBinCoord.at(b2); + + if (r2max <= r1) { + return false; + } + if (r2min <= r1) { + r2min = r1 + 1e-3; + } + + float z0_max = 0.0; + float z0_min = 0.0; + + if (z2 > 0) { + z0_max = (z1max * r2max - z2 * r1) / (r2max - r1); + z0_min = (z1min * r2min - z2 * r1) / (r2min - r1); + } else { + z0_max = (z1max * r2min - z2 * r1) / (r2min - r1); + z0_min = (z1min * r2max - z2 * r1) / (r2max - r1); + } + + if (z0_max < min_z0 - tol || z0_min > max_z0 + tol) { + return false; + } + return true; + } + + return true; + } + + const TrigInDetSiLayer &m_layer; + std::vector m_bins; // eta-bin indices + std::vector m_minRadius; + std::vector m_maxRadius; + std::vector m_minBinCoord; + std::vector m_maxBinCoord; + + float m_minEta{}, m_maxEta{}; + + protected: + float m_etaBinWidth{}, m_phiBinWidth{}; + + float m_r1{}, m_z1{}, m_r2{}, m_z2{}; + float m_nBins{}; + float m_etaBin{}; +}; + +template +class TrigFTF_GNN_Geometry { + public: + TrigFTF_GNN_Geometry(const std::vector &layers, + std::unique_ptr &conn) + + : m_fastrack(std::move(conn)) { + const float min_z0 = -168.0; + const float max_z0 = 168.0; + + m_etaBinWidth = m_fastrack->m_etaBin; + for (const auto &layer : layers) { + const TrigFTF_GNN_Layer *pL = + addNewLayer(layer, m_nEtaBins); + m_nEtaBins += pL->num_bins(); + } + + // calculating bin tables in the connector... + + for (std::map>::const_iterator it = + m_fastrack->m_connMap.begin(); + it != m_fastrack->m_connMap.end(); ++it) { + const std::vector &vConn = (*it).second; + + for (std::vector::const_iterator cIt = + vConn.begin(); + cIt != vConn.end(); ++cIt) { + unsigned int src = (*cIt)->m_src; // n2 : the new connectors + unsigned int dst = (*cIt)->m_dst; // n1 + + const TrigFTF_GNN_Layer *pL1 = + getTrigFTF_GNN_LayerByKey(dst); + const TrigFTF_GNN_Layer *pL2 = + getTrigFTF_GNN_LayerByKey(src); + + if (pL1 == nullptr) { + std::cout << " skipping invalid dst layer " << dst << std::endl; + continue; + } + if (pL2 == nullptr) { + std::cout << " skipping invalid src layer " << src << std::endl; + continue; + } + int nSrcBins = pL2->m_bins.size(); + int nDstBins = pL1->m_bins.size(); + + (*cIt)->m_binTable.resize(nSrcBins * nDstBins, 0); + + for (int b1 = 0; b1 < nDstBins; b1++) { // loop over bins in Layer 1 + for (int b2 = 0; b2 < nSrcBins; b2++) { // loop over bins in Layer 2 + if (!pL1->verifyBin(pL2, b1, b2, min_z0, max_z0)) { + continue; + } + int address = b1 + b2 * nDstBins; + (*cIt)->m_binTable.at(address) = 1; + } + } + } + } + } + + TrigFTF_GNN_Geometry() = default; + + // for safety to prevent passing as copy + TrigFTF_GNN_Geometry(const TrigFTF_GNN_Geometry &) = delete; + TrigFTF_GNN_Geometry &operator=(const TrigFTF_GNN_Geometry &) = delete; + + ~TrigFTF_GNN_Geometry() { + for (typename std::vector *>::iterator it = + m_layArray.begin(); + it != m_layArray.end(); ++it) { + delete (*it); + } + + m_layMap.clear(); + m_layArray.clear(); + } + + const TrigFTF_GNN_Layer *getTrigFTF_GNN_LayerByKey( + unsigned int key) const { + typename std::map *>::const_iterator it = + m_layMap.find(key); + if (it == m_layMap.end()) { + return nullptr; + } + + return (*it).second; + } + + const TrigFTF_GNN_Layer *getTrigFTF_GNN_LayerByIndex( + int idx) const { + return m_layArray.at(idx); + } + + int num_bins() const { return m_nEtaBins; } + + Acts::FasTrackConnector *fastrack() const { return m_fastrack.get(); } + + protected: + const TrigFTF_GNN_Layer *addNewLayer(const TrigInDetSiLayer &l, + int bin0) { + unsigned int layerKey = l.m_subdet; // this should be combined ID + float ew = m_etaBinWidth; + + TrigFTF_GNN_Layer *pHL = + new TrigFTF_GNN_Layer(l, ew, bin0); + + m_layMap.insert(std::pair *>( + layerKey, pHL)); + m_layArray.push_back(pHL); + return pHL; + } + + float m_etaBinWidth{}; + + std::map *> m_layMap; + std::vector *> m_layArray; + + int m_nEtaBins{0}; + + std::unique_ptr m_fastrack; +}; + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp b/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp new file mode 100644 index 00000000000..5c388c83504 --- /dev/null +++ b/Core/include/Acts/Seeding/GNN_TrackingFilter.hpp @@ -0,0 +1,386 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Seeding/GNN_DataStorage.hpp" //includes geo which has trigindetsilayer, may move this to trigbase + +#include +#include +#include +#include +#include +#include + +template +struct TrigFTF_GNN_EdgeState { + public: + struct Compare { + bool operator()(const struct TrigFTF_GNN_EdgeState* s1, + const struct TrigFTF_GNN_EdgeState* s2) { + return s1->m_J > s2->m_J; + } + }; + + TrigFTF_GNN_EdgeState() = default; + + TrigFTF_GNN_EdgeState(bool f) : m_initialized(f) {} + + void initialize(Acts::TrigFTF_GNN_Edge* pS) { + m_initialized = true; + + m_J = 0.0; + m_vs.clear(); + + // n2->n1 + + float dx = pS->m_n1->m_sp_FTF.SP->x() - pS->m_n2->m_sp_FTF.SP->x(); + float dy = pS->m_n1->m_sp_FTF.SP->y() - pS->m_n2->m_sp_FTF.SP->y(); + float L = std::sqrt(dx * dx + dy * dy); + + m_s = dy / L; + m_c = dx / L; + + // transform for extrapolation and update + // x' = x*m_c + y*m_s + // y' = -x*m_s + y*m_c + + m_refY = pS->m_n2->m_sp_FTF.SP->r(); + m_refX = + pS->m_n2->m_sp_FTF.SP->x() * m_c + pS->m_n2->m_sp_FTF.SP->y() * m_s; + + // X-state: y, dy/dx, d2y/dx2 + + m_X[0] = + -pS->m_n2->m_sp_FTF.SP->x() * m_s + pS->m_n2->m_sp_FTF.SP->y() * m_c; + m_X[1] = 0.0; + m_X[2] = 0.0; + + // Y-state: z, dz/dr + + m_Y[0] = pS->m_n2->m_sp_FTF.SP->z(); + m_Y[1] = (pS->m_n1->m_sp_FTF.SP->z() - pS->m_n2->m_sp_FTF.SP->z()) / + (pS->m_n1->m_sp_FTF.SP->r() - pS->m_n2->m_sp_FTF.SP->r()); + + memset(&m_Cx[0][0], 0, sizeof(m_Cx)); + memset(&m_Cy[0][0], 0, sizeof(m_Cy)); + + m_Cx[0][0] = 0.25; + m_Cx[1][1] = 0.001; + m_Cx[2][2] = 0.001; + + m_Cy[0][0] = 1.5; + m_Cy[1][1] = 0.001; + } + + void clone(const struct TrigFTF_GNN_EdgeState& st) { + memcpy(&m_X[0], &st.m_X[0], sizeof(m_X)); + memcpy(&m_Y[0], &st.m_Y[0], sizeof(m_Y)); + memcpy(&m_Cx[0][0], &st.m_Cx[0][0], sizeof(m_Cx)); + memcpy(&m_Cy[0][0], &st.m_Cy[0][0], sizeof(m_Cy)); + m_refX = st.m_refX; + m_refY = st.m_refY; + m_c = st.m_c; + m_s = st.m_s; + m_J = st.m_J; + m_vs.clear(); + m_vs.reserve(st.m_vs.size()); + std::copy(st.m_vs.begin(), st.m_vs.end(), std::back_inserter(m_vs)); + + m_initialized = true; + } + + float m_J{}; + + std::vector*> m_vs; + + float m_X[3]{}, m_Y[2]{}, m_Cx[3][3]{}, m_Cy[2][2]{}; + float m_refX{}, m_refY{}, m_c{}, m_s{}; + + bool m_initialized{false}; +}; + +#define MAX_EDGE_STATE 2500 + +template +class TrigFTF_GNN_TrackingFilter { + public: + TrigFTF_GNN_TrackingFilter( + const std::vector& g, + std::vector>& sb) + : m_geo(g), m_segStore(sb) {} + + void followTrack(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& output) { + if (pS->m_level == -1) { + return; // already collected + } + m_globalStateCounter = 0; + + // create track state + + TrigFTF_GNN_EdgeState* pInitState = + &m_stateStore[m_globalStateCounter++]; + + pInitState->initialize(pS); + + m_stateVec.clear(); + + // recursive branching and propagation + + propagate(pS, *pInitState); + + if (m_stateVec.empty()) { + return; + } + std::sort(m_stateVec.begin(), m_stateVec.end(), + typename TrigFTF_GNN_EdgeState::Compare()); + + TrigFTF_GNN_EdgeState* best = (*m_stateVec.begin()); + + output.clone(*best); + + m_globalStateCounter = 0; + } + + protected: + void propagate(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& ts) { + if (m_globalStateCounter >= MAX_EDGE_STATE) { + return; + } + TrigFTF_GNN_EdgeState* p_new_ts = + &m_stateStore[m_globalStateCounter++]; + + TrigFTF_GNN_EdgeState& new_ts = *p_new_ts; + new_ts.clone(ts); + + new_ts.m_vs.push_back(pS); + + bool accepted = update(pS, new_ts); // update using n1 of the segment + + if (!accepted) { + return; // stop further propagation + } + int level = pS->m_level; + + std::list*> lCont; + + for (int nIdx = 0; nIdx < pS->m_nNei; + nIdx++) { // loop over the neighbours of this segment + unsigned int nextSegmentIdx = pS->m_vNei[nIdx]; + + Acts::TrigFTF_GNN_Edge* pN = + &(m_segStore.at(nextSegmentIdx)); + + if (pN->m_level == -1) { + continue; // already collected + } + if (pN->m_level == level - 1) { + lCont.push_back(pN); + } + } + if (lCont.empty()) { // the end of chain + + // store in the vector + if (m_globalStateCounter < MAX_EDGE_STATE) { + if (m_stateVec.empty()) { // add the first segment state + TrigFTF_GNN_EdgeState* p = + &m_stateStore[m_globalStateCounter++]; + p->clone(new_ts); + m_stateVec.push_back(p); + } else { // compare with the best and add + float best_so_far = (*m_stateVec.begin())->m_J; + if (new_ts.m_J > best_so_far) { + TrigFTF_GNN_EdgeState* p = + &m_stateStore[m_globalStateCounter++]; + p->clone(new_ts); + m_stateVec.push_back(p); + } + } + } + } else { // branching + int nBranches = 0; + for (typename std::list< + Acts::TrigFTF_GNN_Edge*>::iterator sIt = + lCont.begin(); + sIt != lCont.end(); ++sIt, nBranches++) { + propagate((*sIt), new_ts); // recursive call + } + } + } + + bool update(Acts::TrigFTF_GNN_Edge* pS, + TrigFTF_GNN_EdgeState& ts) { + const float sigma_t = 0.0003; + const float sigma_w = 0.00009; + + const float sigmaMS = 0.016; + + const float sigma_x = 0.25; // was 0.22 + const float sigma_y = 2.5; // was 1.7 + + const float weight_x = 0.5; + const float weight_y = 0.5; + + const float maxDChi2_x = 60.0; // 35.0; + const float maxDChi2_y = 60.0; // 31.0; + + const float add_hit = 14.0; + + if (ts.m_Cx[2][2] < 0.0 || ts.m_Cx[1][1] < 0.0 || ts.m_Cx[0][0] < 0.0) { + std::cout << "Negative cov_x" << std::endl; + } + + if (ts.m_Cy[1][1] < 0.0 || ts.m_Cy[0][0] < 0.0) { + std::cout << "Negative cov_y" << std::endl; + } + + // add ms. + + ts.m_Cx[2][2] += sigma_w * sigma_w; + ts.m_Cx[1][1] += sigma_t * sigma_t; + + int type1 = getLayerType(pS->m_n1->m_sp_FTF.combined_ID); + + float t2 = type1 == 0 ? 1.0 + ts.m_Y[1] * ts.m_Y[1] + : 1.0 + 1.0 / (ts.m_Y[1] * ts.m_Y[1]); + float s1 = sigmaMS * t2; + float s2 = s1 * s1; + + s2 *= std::sqrt(t2); + + ts.m_Cy[1][1] += s2; + + // extrapolation + + float X[3], Y[2]; + float Cx[3][3], Cy[2][2]; + + float refX{}, refY{}, mx{}, my{}; + + float x{}, y{}, z{}, r{}; + + x = pS->m_n1->m_sp_FTF.SP->x(); + y = pS->m_n1->m_sp_FTF.SP->y(); + z = pS->m_n1->m_sp_FTF.SP->z(); + r = pS->m_n1->m_sp_FTF.SP->r(); + + refX = x * ts.m_c + y * ts.m_s; + mx = -x * ts.m_s + y * ts.m_c; // measured X[0] + refY = r; + my = z; // measured Y[0] + + float A = refX - ts.m_refX; + float B = 0.5 * A * A; + float dr = refY - ts.m_refY; + + X[0] = ts.m_X[0] + ts.m_X[1] * A + ts.m_X[2] * B; + X[1] = ts.m_X[1] + ts.m_X[2] * A; + X[2] = ts.m_X[2]; + + Cx[0][0] = ts.m_Cx[0][0] + 2 * ts.m_Cx[0][1] * A + 2 * ts.m_Cx[0][2] * B + + A * A * ts.m_Cx[1][1] + 2 * A * B * ts.m_Cx[1][2] + + B * B * ts.m_Cx[2][2]; + Cx[0][1] = Cx[1][0] = ts.m_Cx[0][1] + ts.m_Cx[1][1] * A + + ts.m_Cx[1][2] * B + ts.m_Cx[0][2] * A + + A * A * ts.m_Cx[1][2] + A * B * ts.m_Cx[2][2]; + Cx[0][2] = Cx[2][0] = ts.m_Cx[0][2] + ts.m_Cx[1][2] * A + ts.m_Cx[2][2] * B; + + Cx[1][1] = ts.m_Cx[1][1] + 2 * A * ts.m_Cx[1][2] + A * A * ts.m_Cx[2][2]; + Cx[1][2] = Cx[2][1] = ts.m_Cx[1][2] + ts.m_Cx[2][2] * A; + + Cx[2][2] = ts.m_Cx[2][2]; + + Y[0] = ts.m_Y[0] + ts.m_Y[1] * dr; + Y[1] = ts.m_Y[1]; + + Cy[0][0] = ts.m_Cy[0][0] + 2 * ts.m_Cy[0][1] * dr + dr * dr * ts.m_Cy[1][1]; + Cy[0][1] = Cy[1][0] = ts.m_Cy[0][1] + dr * ts.m_Cy[1][1]; + Cy[1][1] = ts.m_Cy[1][1]; + + // chi2 test + + float resid_x = mx - X[0]; + float resid_y = my - Y[0]; + + float CHx[3] = {Cx[0][0], Cx[0][1], Cx[0][2]}; + float CHy[2] = {Cy[0][0], Cy[0][1]}; + + float sigma_rz = 0.0; + + int type = getLayerType(pS->m_n1->m_sp_FTF.combined_ID); + + if (type == 0) { // barrel TO-DO: split into barrel Pixel and barrel SCT + sigma_rz = sigma_y * sigma_y; + } else { + sigma_rz = sigma_y * ts.m_Y[1]; + sigma_rz = sigma_rz * sigma_rz; + } + + float Dx = 1.0 / (Cx[0][0] + sigma_x * sigma_x); + + float Dy = 1.0 / (Cy[0][0] + sigma_rz); + + float dchi2_x = resid_x * resid_x * Dx; + float dchi2_y = resid_y * resid_y * Dy; + + if (dchi2_x > maxDChi2_x || dchi2_y > maxDChi2_y) { + return false; + } + + ts.m_J += add_hit - dchi2_x * weight_x - dchi2_y * weight_y; + + // state update + + float Kx[3] = {Dx * Cx[0][0], Dx * Cx[0][1], Dx * Cx[0][2]}; + float Ky[2] = {Dy * Cy[0][0], Dy * Cy[0][1]}; + + for (int i = 0; i < 3; i++) { + ts.m_X[i] = X[i] + Kx[i] * resid_x; + } + for (int i = 0; i < 2; i++) { + ts.m_Y[i] = Y[i] + Ky[i] * resid_y; + } + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + ts.m_Cx[i][j] = Cx[i][j] - Kx[i] * CHx[j]; + } + } + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + ts.m_Cy[i][j] = Cy[i][j] - Ky[i] * CHy[j]; + } + } + ts.m_refX = refX; + ts.m_refY = refY; + return true; + } + + int getLayerType(int l) { + auto iterator = find_if(m_geo.begin(), m_geo.end(), [l](auto n) { + return n.m_subdet == l; + }); // iterator to vector member with this id + int index = std::distance(m_geo.begin(), iterator); + + return m_geo.at(index).m_type; + } + + const std::vector& m_geo; + + std::vector>& m_segStore; + + std::vector*> m_stateVec; + + TrigFTF_GNN_EdgeState m_stateStore[MAX_EDGE_STATE]; + + int m_globalStateCounter{0}; +}; diff --git a/Core/include/Acts/Seeding/InternalSpacePoint.hpp b/Core/include/Acts/Seeding/InternalSpacePoint.hpp index 7e82318ae86..15c25dc0cf7 100644 --- a/Core/include/Acts/Seeding/InternalSpacePoint.hpp +++ b/Core/include/Acts/Seeding/InternalSpacePoint.hpp @@ -36,13 +36,13 @@ class InternalSpacePoint { const InternalSpacePoint&) = delete; std::size_t index() const { return m_index; } - const float& x() const { return m_x; } - const float& y() const { return m_y; } - const float& z() const { return m_z; } - const float& radius() const { return m_r; } - float phi() const { return atan2f(m_y, m_x); } - const float& varianceR() const { return m_varianceR; } - const float& varianceZ() const { return m_varianceZ; } + float x() const { return m_x; } + float y() const { return m_y; } + float z() const { return m_z; } + float radius() const { return m_r; } + float phi() const { return m_phi; } + float varianceR() const { return m_varianceR; } + float varianceZ() const { return m_varianceZ; } const SpacePoint& sp() const { return m_sp; } protected: @@ -51,6 +51,7 @@ class InternalSpacePoint { float m_y; // y-coordinate in beam system coordinates float m_z; // z-coordinate in beam system coordinetes float m_r; // radius in beam system coordinates + float m_phi; // float m_varianceR; // float m_varianceZ; // std::reference_wrapper m_sp; // external space point @@ -69,6 +70,7 @@ inline InternalSpacePoint::InternalSpacePoint( m_y(globalPos.y() - offsetXY.y()), m_z(globalPos.z()), m_r(std::hypot(m_x, m_y)), + m_phi(std::atan2(m_y, m_x)), m_varianceR(variance.x()), m_varianceZ(variance.y()), m_sp(sp) {} diff --git a/Core/include/Acts/Seeding/Neighbour.hpp b/Core/include/Acts/Seeding/Neighbour.hpp index 8dc6b0bfe5f..0146c98086b 100644 --- a/Core/include/Acts/Seeding/Neighbour.hpp +++ b/Core/include/Acts/Seeding/Neighbour.hpp @@ -84,7 +84,7 @@ Neighbour::Neighbour( /// radius is > lower bound. We use a binary search in this case else { itr = std::lower_bound(collection.begin(), collection.end(), lowerBound, - [](const auto& sp, const float& target) -> bool { + [](const auto& sp, const float target) -> bool { return sp->radius() < target; }); } diff --git a/Core/include/Acts/Seeding/SeedFilter.hpp b/Core/include/Acts/Seeding/SeedFilter.hpp index e92d717f292..af6e6ff4dfb 100644 --- a/Core/include/Acts/Seeding/SeedFilter.hpp +++ b/Core/include/Acts/Seeding/SeedFilter.hpp @@ -83,7 +83,7 @@ class SeedFilter { Acts::SpacePointData& spacePointData, CandidatesForMiddleSp>& candidates_collector, - std::size_t& numQualitySeeds, + const std::size_t numQualitySeeds, std::back_insert_iterator>> outIt) const; @@ -98,7 +98,7 @@ class SeedFilter { std::vector>::value_type>& candidates, - std::size_t& numQualitySeeds, + const std::size_t numQualitySeeds, std::back_insert_iterator>> outIt) const; diff --git a/Core/include/Acts/Seeding/SeedFilter.ipp b/Core/include/Acts/Seeding/SeedFilter.ipp index 66fea06a2e6..34d1356d40d 100644 --- a/Core/include/Acts/Seeding/SeedFilter.ipp +++ b/Core/include/Acts/Seeding/SeedFilter.ipp @@ -68,7 +68,7 @@ void SeedFilter::filterSeeds_2SpFixed( if (topSpVec.size() > 2) { // sort indexes based on comparing values in invHelixDiameterVec std::sort(topSPIndexVec.begin(), topSPIndexVec.end(), - [&invHelixDiameterVec](const size_t& i1, const size_t& i2) { + [&invHelixDiameterVec](const size_t i1, const size_t i2) { return invHelixDiameterVec[i1] < invHelixDiameterVec[i2]; }); } @@ -125,7 +125,7 @@ void SeedFilter::filterSeeds_2SpFixed( continue; } bool newCompSeed = true; - for (const float& previousDiameter : compatibleSeedR) { + for (const float previousDiameter : compatibleSeedR) { // original ATLAS code uses higher min distance for 2nd found compatible // seed (20mm instead of 5mm) // add new compatible seed only if distance larger than rmin to all @@ -196,9 +196,9 @@ void SeedFilter::filterSeeds_2SpFixed( // if we have not yet reached our max number of quality seeds we add the // new seed to outCont - // Internally, "push" will also checks the max number of quality seeds + // Internally, "push" will also check the max number of quality seeds // for a middle sp. - // If this is reached, we remove the seed with lowest weight. + // If this is reached, we remove the seed with the lowest weight. candidates_collector.push(bottomSP, middleSP, *topSpVec[topSPIndex], weight, zOrigin, true); if (seedFilterState.numQualitySeeds < m_cfg.maxQualitySeedsPerSpMConf) { @@ -248,7 +248,7 @@ void SeedFilter::filterSeeds_1SpFixed( Acts::SpacePointData& spacePointData, CandidatesForMiddleSp>& candidates_collector, - std::size_t& numQualitySeeds, + const std::size_t numQualitySeeds, std::back_insert_iterator>> outIt) const { // retrieve all candidates @@ -265,7 +265,7 @@ void SeedFilter::filterSeeds_1SpFixed( std::vector>::value_type>& candidates, - std::size_t& numQualitySeeds, + const std::size_t numQualitySeeds, std::back_insert_iterator>> outIt) const { if (m_experimentCuts != nullptr) { diff --git a/Core/include/Acts/Seeding/SeedFinder.hpp b/Core/include/Acts/Seeding/SeedFinder.hpp index aa661434d3d..46eab674778 100644 --- a/Core/include/Acts/Seeding/SeedFinder.hpp +++ b/Core/include/Acts/Seeding/SeedFinder.hpp @@ -156,8 +156,8 @@ class SeedFinder { otherSPsNeighbours, const InternalSpacePoint& mediumSP, std::vector& linCircleVec, out_range_t& outVec, - const float& deltaRMinSP, const float& deltaRMaxSP, const float& uIP, - const float& uIP2, const float& cosPhiM, const float& sinPhiM) const; + const float deltaRMinSP, const float deltaRMaxSP, const float uIP, + const float uIP2, const float cosPhiM, const float sinPhiM) const; /// Iterates over the seed candidates tests the compatibility between three /// SPs and calls for the seed confirmation diff --git a/Core/include/Acts/Seeding/SeedFinder.ipp b/Core/include/Acts/Seeding/SeedFinder.ipp index f628255b35e..b2c6980955d 100644 --- a/Core/include/Acts/Seeding/SeedFinder.ipp +++ b/Core/include/Acts/Seeding/SeedFinder.ipp @@ -203,8 +203,8 @@ SeedFinder::getCompatibleDoublets( otherSPsNeighbours, const InternalSpacePoint& mediumSP, std::vector& linCircleVec, out_range_t& outVec, - const float& deltaRMinSP, const float& deltaRMaxSP, const float& uIP, - const float& uIP2, const float& cosPhiM, const float& sinPhiM) const { + const float deltaRMinSP, const float deltaRMaxSP, const float uIP, + const float uIP2, const float cosPhiM, const float sinPhiM) const { float impactMax = m_config.impactMax; if constexpr (candidateType == Acts::SpacePointCandidateType::eBottom) { impactMax = -impactMax; @@ -222,12 +222,12 @@ SeedFinder::getCompatibleDoublets( linCircleVec.reserve(nsp); outVec.reserve(nsp); - const float& rM = mediumSP.radius(); - const float& xM = mediumSP.x(); - const float& yM = mediumSP.y(); - const float& zM = mediumSP.z(); - const float& varianceRM = mediumSP.varianceR(); - const float& varianceZM = mediumSP.varianceZ(); + const float rM = mediumSP.radius(); + const float xM = mediumSP.x(); + const float yM = mediumSP.y(); + const float zM = mediumSP.z(); + const float varianceRM = mediumSP.varianceR(); + const float varianceZM = mediumSP.varianceZ(); float vIPAbs = 0; if (m_config.interactionPointCut) { @@ -464,13 +464,13 @@ inline void SeedFinder::filterCandidates( if constexpr (detailedMeasurement == Acts::DetectorMeasurementInfo::eDefault) { std::sort(sorted_bottoms.begin(), sorted_bottoms.end(), - [&state](const std::size_t& a, const std::size_t& b) -> bool { + [&state](const std::size_t a, const std::size_t b) -> bool { return state.linCircleBottom[a].cotTheta < state.linCircleBottom[b].cotTheta; }); std::sort(sorted_tops.begin(), sorted_tops.end(), - [&state](const std::size_t& a, const std::size_t& b) -> bool { + [&state](const std::size_t a, const std::size_t b) -> bool { return state.linCircleTop[a].cotTheta < state.linCircleTop[b].cotTheta; }); @@ -486,7 +486,7 @@ inline void SeedFinder::filterCandidates( // clear previous results and then loop on bottoms and tops state.candidates_collector.clear(); - for (const std::size_t& b : sorted_bottoms) { + for (const std::size_t b : sorted_bottoms) { // break if we reached the last top SP if (t0 == numTopSP) { break; @@ -543,7 +543,7 @@ inline void SeedFinder::filterCandidates( } for (size_t index_t = t0; index_t < numTopSP; index_t++) { - const std::size_t& t = sorted_tops[index_t]; + const std::size_t t = sorted_tops[index_t]; auto lt = state.linCircleTop[t]; @@ -723,7 +723,7 @@ inline void SeedFinder::filterCandidates( // sqrt(S2)/B = 2 * helixradius // calculated radius must not be smaller than minimum radius - if (S2 < B2 * options.minHelixDiameter2) { + if (S2 < B2 * options.minHelixDiameter2 * m_config.helixCut) { continue; } @@ -776,7 +776,7 @@ inline void SeedFinder::filterCandidates( } state.topSpVec.push_back(state.compatTopSP[t]); - // inverse diameter is signed depending if the curvature is + // inverse diameter is signed depending on if the curvature is // positive/negative in phi state.curvatures.push_back(B / std::sqrt(S2)); state.impactParameters.push_back(Im); diff --git a/Core/include/Acts/Seeding/SeedFinderConfig.hpp b/Core/include/Acts/Seeding/SeedFinderConfig.hpp index 7c5a4bd58a1..30e18247398 100644 --- a/Core/include/Acts/Seeding/SeedFinderConfig.hpp +++ b/Core/include/Acts/Seeding/SeedFinderConfig.hpp @@ -29,8 +29,8 @@ struct SeedFinderConfig { // lower cutoff for seeds float minPt = 400. * Acts::UnitConstants::MeV; // cot of maximum theta angle - // equivalent to 2.7 eta (pseudorapidity) - float cotThetaMax = 7.40627; + // equivalent to 3 eta (pseudorapidity) + float cotThetaMax = 10.01788; // minimum distance in r between two measurements within one seed float deltaRMin = 5 * Acts::UnitConstants::mm; // maximum distance in r between two measurements within one seed @@ -101,6 +101,10 @@ struct SeedFinderConfig { // xyz float toleranceParam = 1.1 * Acts::UnitConstants::mm; + // Parameter which can loosen the tolerance of the track seed to form to a + // helix, useful for (e.g.) misaligned seeding + float helixCut = 1.; + // Geometry Settings // Detector ROI // limiting location of collision region in z diff --git a/Core/include/Acts/Seeding/SeedFinderFTF.hpp b/Core/include/Acts/Seeding/SeedFinderFTF.hpp new file mode 100644 index 00000000000..b03d7306082 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTF.hpp @@ -0,0 +1,103 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style + +#include "Acts/EventData/SpacePointData.hpp" +#include "Acts/Seeding/InternalSeed.hpp" +#include "Acts/Seeding/InternalSpacePoint.hpp" +#include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" +#include "Acts/TrackFinding/RoiDescriptor.hpp" +#include "Acts/Utilities/KDTree.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Acts { + +template +struct GNN_TrigTracklet { + public: + GNN_TrigTracklet(std::vector *> &vSP, + std::vector> &tbuf) + : m_track(vSP), m_seeds(tbuf) {} + + std::vector *> m_track; + std::vector> m_seeds; +}; + +template +class SeedFinderFTF { + public: + static constexpr std::size_t NDims = 3; + + using seed_t = Seed; + // using internal_sp_t = InternalSpacePoint; + // using tree_t = KDTree; + + // constructors + SeedFinderFTF(const SeedFinderFTFConfig &config, + const TrigFTF_GNN_Geometry &GNNgeo); + + ~SeedFinderFTF(); //!!! is it dangerous not to use default? got def in ipp + SeedFinderFTF() = default; + SeedFinderFTF(const SeedFinderFTF &) = delete; + SeedFinderFTF &operator=( + const SeedFinderFTF &) = delete; + + void loadSpacePoints( + const std::vector> &FTF_SP_vect); + + void createSeeds( + const Acts::RoiDescriptor &roi, + const Acts::TrigFTF_GNN_Geometry &gnngeo); + + // create seeeds function + template + void createSeeds_old(const Acts::SeedFinderOptions &options, + const input_container_t &spacePoints, + output_container_t &out_cont, + callable_t &&extract_coordinates) const; + + template + std::vector createSeeds_old(const Acts::SeedFinderOptions &options, + const input_container_t &spacePoints, + callable_t &&extract_coordinates) const; + + private: + enum Dim { DimPhi = 0, DimR = 1, DimZ = 2 }; + + // config object + SeedFinderFTFConfig m_config; + + void runGNN_TrackFinder( + std::vector> &vTracks, + const Acts::RoiDescriptor &roi, + const Acts::TrigFTF_GNN_Geometry &gnngeo); + + // needs to be member of class so can accessed by all member functions + TrigFTF_GNN_DataStorage *m_storage; + + // for create seeds: + std::vector> m_triplets; +}; + +} // namespace Acts + +#include "Acts/Seeding/SeedFinderFTF.ipp" diff --git a/Core/include/Acts/Seeding/SeedFinderFTF.ipp b/Core/include/Acts/Seeding/SeedFinderFTF.ipp new file mode 100644 index 00000000000..38696c7ade1 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTF.ipp @@ -0,0 +1,732 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// SeedFinderFTF.ipp +// TODO: update to C++17 style + +#include "Acts/Definitions/Algebra.hpp" //for M_PI +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Seeding/SeedFilter.hpp" +#include "Acts/Seeding/SeedFinder.hpp" +#include "Acts/Seeding/SeedFinderFTFConfig.hpp" +#include "Acts/Seeding/SeedFinderUtils.hpp" +#include "Acts/Utilities/BinningType.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +// core so in ACTS namespace + +namespace Acts { + +template +SeedFinderFTF::SeedFinderFTF( + const SeedFinderFTFConfig& config, + const TrigFTF_GNN_Geometry& GNNgeo) + : m_config(config) { + m_storage = new TrigFTF_GNN_DataStorage(GNNgeo); +} + +template +SeedFinderFTF::~SeedFinderFTF() { + delete m_storage; + + m_storage = nullptr; +} + +// define loadspace points function +template +void SeedFinderFTF::loadSpacePoints( + const std::vector>& FTF_SP_vect) { + for (const auto& FTF_sp : FTF_SP_vect) { + bool is_Pixel = FTF_sp.isPixel(); + if (!is_Pixel) { + continue; + } + m_storage->addSpacePoint(FTF_sp, (m_config.m_useClusterWidth > 0)); + } + + m_config.m_nMaxPhiSlice = 1; + m_config.m_phiSliceWidth = 2 * M_PI / m_config.m_nMaxPhiSlice; + + m_storage->sortByPhi(); + + m_storage->generatePhiIndexing(1.5 * m_config.m_phiSliceWidth); +} + +template +void SeedFinderFTF::runGNN_TrackFinder( + std::vector>& vTracks, + const Acts::RoiDescriptor& roi, + const Acts::TrigFTF_GNN_Geometry& gnngeo) { + // long term move these to ftf finder config, then m_config. to access them + const int MaxEdges = 2000000; + + const float cut_dphi_max = 0.012; + const float cut_dcurv_max = 0.001; + const float cut_tau_ratio_max = 0.007; + const float min_z0 = -2800; // roiDescriptor->zedMinus(); //blank for now, + // get from config eventually + const float max_z0 = 2800; // roiDescriptor->zedPlus(); + + const float maxOuterRadius = 550.0; + const float cut_zMinU = + min_z0 + + maxOuterRadius * roi.dzdrMinus(); // dzdr can only find =0 in athena + const float cut_zMaxU = max_z0 + maxOuterRadius * roi.dzdrPlus(); + + float m_minR_squ = 1; // set earlier + float m_maxCurv = 1; + + const float maxKappa_high_eta = 0.8 / m_minR_squ; + const float maxKappa_low_eta = 0.6 / m_minR_squ; + + // 1. loop over stages + + int currentStage = 0; + + const Acts::FasTrackConnector& fastrack = *(gnngeo.fastrack()); + + std::vector> edgeStorage; + + edgeStorage.reserve(MaxEdges); + + int nEdges = 0; + + for (std::map>::const_iterator + it = fastrack.m_layerGroups.begin(); + it != fastrack.m_layerGroups.end(); ++it, currentStage++) { + // loop over L1 layers for the current stage + + for (const auto& layerGroup : (*it).second) { + unsigned int dst = layerGroup.m_dst; // n1 : inner nodes + + const TrigFTF_GNN_Layer* pL1 = + gnngeo.getTrigFTF_GNN_LayerByKey(dst); + + if (pL1 == nullptr) { + continue; + } + + for (const auto& conn : + layerGroup.m_sources) { // loop over L2(L1) for the current stage + + unsigned int src = conn->m_src; // n2 : the new connectors + + const TrigFTF_GNN_Layer* pL2 = + gnngeo.getTrigFTF_GNN_LayerByKey(src); + + if (pL2 == nullptr) { + continue; + } + int nDstBins = pL1->m_bins.size(); + int nSrcBins = pL2->m_bins.size(); + + for (int b1 = 0; b1 < nDstBins; b1++) { // loop over bins in Layer 1 + + const TrigFTF_GNN_EtaBin& B1 = + m_storage->getEtaBin(pL1->m_bins.at(b1)); + + if (B1.empty()) { + continue; + } + + float rb1 = pL1->getMinBinRadius(b1); + + // 3. loops over source eta-bins + + for (int b2 = 0; b2 < nSrcBins; b2++) { // loop over bins in Layer 2 + + if (m_config.m_useEtaBinning && (nSrcBins + nDstBins > 2)) { + if (conn->m_binTable[b1 + b2 * nDstBins] != 1) { + continue; // using precomputed LUT + } + } + + const TrigFTF_GNN_EtaBin& B2 = + m_storage->getEtaBin(pL2->m_bins.at(b2)); + + if (B2.empty()) { + continue; + } + + float rb2 = pL2->getMaxBinRadius(b2); + + // calculated delta Phi for rb1 ---> rb2 extrapolation + + float deltaPhi = + 0.5f * + m_config + .m_phiSliceWidth; // the default sliding window along phi + + if (m_config.m_useEtaBinning) { + deltaPhi = 0.001f + m_maxCurv * std::fabs(rb2 - rb1); + } + + unsigned int first_it = 0; + for (typename std::vector< + TrigFTF_GNN_Node*>::const_iterator + n1It = B1.m_vn.begin(); + n1It != B1.m_vn.end(); ++n1It) { // loop over nodes in Layer 1 + + TrigFTF_GNN_Node* n1 = (*n1It); + + if (n1->m_in.size() >= MAX_SEG_PER_NODE) { + continue; + } + + float r1 = n1->m_sp_FTF.SP->r(); + float x1 = n1->m_sp_FTF.SP->x(); + float y1 = n1->m_sp_FTF.SP->y(); + float z1 = n1->m_sp_FTF.SP->z(); + float phi1 = std::atan(x1 / y1); + + float minPhi = phi1 - deltaPhi; + float maxPhi = phi1 + deltaPhi; + + for (unsigned int n2PhiIdx = first_it; + n2PhiIdx < B2.m_vPhiNodes.size(); + n2PhiIdx++) { // sliding window over nodes in Layer 2 + + float phi2 = B2.m_vPhiNodes.at(n2PhiIdx).first; + + if (phi2 < minPhi) { + first_it = n2PhiIdx; + continue; + } + if (phi2 > maxPhi) { + break; + } + + TrigFTF_GNN_Node* n2 = + B2.m_vn.at(B2.m_vPhiNodes.at(n2PhiIdx).second); + + if (n2->m_out.size() >= MAX_SEG_PER_NODE) { + continue; + } + if (n2->isFull()) { + continue; + } + + float r2 = n2->m_sp_FTF.SP->r(); + + float dr = r2 - r1; + + if (dr < m_config.m_minDeltaRadius) { + continue; + } + + float z2 = n2->m_sp_FTF.SP->z(); + + float dz = z2 - z1; + float tau = dz / dr; + float ftau = std::fabs(tau); + if (ftau > 36.0) { + continue; + } + + if (ftau < n1->m_minCutOnTau) { + continue; + } + if (ftau < n2->m_minCutOnTau) { + continue; + } + if (ftau > n1->m_maxCutOnTau) { + continue; + } + if (ftau > n2->m_maxCutOnTau) { + continue; + } + + if (m_config.m_doubletFilterRZ) { + float z0 = z1 - r1 * tau; + + if (z0 < min_z0 || z0 > max_z0) { + continue; + } + + float zouter = z0 + maxOuterRadius * tau; + + if (zouter < cut_zMinU || zouter > cut_zMaxU) { + continue; + } + } + + float dx = n2->m_sp_FTF.SP->x() - x1; + float dy = n2->m_sp_FTF.SP->y() - y1; + + float L2 = 1 / (dx * dx + dy * dy); + + float D = + (n2->m_sp_FTF.SP->y() * x1 - y1 * n2->m_sp_FTF.SP->x()) / + (r1 * r2); + + float kappa = D * D * L2; + + if (ftau < 4.0) { // eta = 2.1 + if (kappa > maxKappa_low_eta) { + continue; + } + + } else { + if (kappa > maxKappa_high_eta) { + continue; + } + } + + // match edge candidate against edges incoming to n2 + + float exp_eta = std::sqrt(1 + tau * tau) - tau; + + bool isGood = + n2->m_in.size() <= + 2; // we must have enough incoming edges to decide + + if (!isGood) { + float uat_1 = 1.0f / exp_eta; + + for (const auto& n2_in_idx : n2->m_in) { + float tau2 = edgeStorage.at(n2_in_idx).m_p[0]; + float tau_ratio = tau2 * uat_1 - 1.0f; + + if (std::fabs(tau_ratio) > cut_tau_ratio_max) { // bad + // match + continue; + } + isGood = true; // good match found + break; + } + } + if (!isGood) { + continue; // no moatch found, skip creating [n1 <- n2] edge + } + + float curv = D * std::sqrt(L2); // signed curvature + float dPhi2 = std::asin(curv * r2); + float dPhi1 = std::asin(curv * r1); + + if (nEdges < MaxEdges) { + edgeStorage.emplace_back(n1, n2, exp_eta, curv, phi1 + dPhi1, + phi2 + dPhi2); + + n1->addIn(nEdges); + n2->addOut(nEdges); + + nEdges++; + } + } // loop over n2 (outer) nodes + } // loop over n1 (inner) nodes + } // loop over source eta bins + } // loop over dst eta bins + } // loop over L2(L1) layers + } // loop over dst layers + } // loop over the stages of doublet making + + std::vector*> vNodes; + + m_storage->getConnectingNodes(vNodes); + + if (vNodes.empty()) { + return; + } + + int nNodes = vNodes.size(); + + for (int nodeIdx = 0; nodeIdx < nNodes; nodeIdx++) { + const TrigFTF_GNN_Node* pN = vNodes.at(nodeIdx); + + std::vector> in_sort, out_sort; + in_sort.resize(pN->m_in.size()); + out_sort.resize(pN->m_out.size()); + + for (int inIdx = 0; inIdx < static_cast(pN->m_in.size()); inIdx++) { + int inEdgeIdx = pN->m_in.at(inIdx); + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(inEdgeIdx)); + in_sort[inIdx].second = inEdgeIdx; + in_sort[inIdx].first = pS->m_p[0]; + } + for (int outIdx = 0; outIdx < static_cast(pN->m_out.size()); + outIdx++) { + int outEdgeIdx = pN->m_out.at(outIdx); + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(outEdgeIdx)); + out_sort[outIdx].second = outEdgeIdx; + out_sort[outIdx].first = pS->m_p[0]; + } + + std::sort(in_sort.begin(), in_sort.end()); + std::sort(out_sort.begin(), out_sort.end()); + + unsigned int last_out = 0; + + for (unsigned int in_idx = 0; in_idx < in_sort.size(); + in_idx++) { // loop over incoming edges + + int inEdgeIdx = in_sort[in_idx].second; + + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(inEdgeIdx)); + + pS->m_nNei = 0; + float tau1 = pS->m_p[0]; + float uat_1 = 1.0f / tau1; + float curv1 = pS->m_p[1]; + float Phi1 = pS->m_p[2]; + + for (unsigned int out_idx = last_out; out_idx < out_sort.size(); + out_idx++) { + int outEdgeIdx = out_sort[out_idx].second; + + Acts::TrigFTF_GNN_Edge* pNS = + &(edgeStorage.at(outEdgeIdx)); + + float tau2 = pNS->m_p[0]; + float tau_ratio = tau2 * uat_1 - 1.0f; + + if (tau_ratio < -cut_tau_ratio_max) { + last_out = out_idx; + continue; + } + if (tau_ratio > cut_tau_ratio_max) { + break; + } + + float dPhi = pNS->m_p[3] - Phi1; + + if (dPhi < -M_PI) { + dPhi += 2 * M_PI; + } else if (dPhi > M_PI) { + dPhi -= 2 * M_PI; + } + + if (dPhi < -cut_dphi_max || dPhi > cut_dphi_max) { + continue; + } + + float curv2 = pNS->m_p[1]; + float dcurv = curv2 - curv1; + + if (dcurv < -cut_dcurv_max || dcurv > cut_dcurv_max) { + continue; + } + + pS->m_vNei[pS->m_nNei++] = outEdgeIdx; + if (pS->m_nNei >= N_SEG_CONNS) { + break; + } + } + } + } + + const int maxIter = 15; + + int maxLevel = 0; + + int iter = 0; + + std::vector*> v_old; + + for (int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) { + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(edgeIndex)); + if (pS->m_nNei == 0) { + continue; + } + v_old.push_back(pS); // TO-DO: increment level for segments as they already + // have at least one neighbour + } + + for (; iter < maxIter; iter++) { + // generate proposals + std::vector*> v_new; + v_new.clear(); + + for (auto pS : v_old) { + int next_level = pS->m_level; + + for (int nIdx = 0; nIdx < pS->m_nNei; nIdx++) { + unsigned int nextEdgeIdx = pS->m_vNei[nIdx]; + + Acts::TrigFTF_GNN_Edge* pN = + &(edgeStorage.at(nextEdgeIdx)); + + if (pS->m_level == pN->m_level) { + next_level = pS->m_level + 1; + v_new.push_back(pS); + break; + } + } + + pS->m_next = next_level; // proposal + } + // update + + int nChanges = 0; + + for (auto pS : v_new) { + if (pS->m_next != pS->m_level) { + nChanges++; + pS->m_level = pS->m_next; + if (maxLevel < pS->m_level) { + maxLevel = pS->m_level; + } + } + } + + if (nChanges == 0) { + break; + } + + v_old = std::move(v_new); + v_new.clear(); + } + + int minLevel = 3; // a triplet + 2 confirmation + + std::vector*> vSeeds; + + vSeeds.reserve(MaxEdges / 2); + + for (int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) { + Acts::TrigFTF_GNN_Edge* pS = + &(edgeStorage.at(edgeIndex)); + + if (pS->m_level < minLevel) { + continue; + } + + vSeeds.push_back(pS); + } + + m_triplets.clear(); + + std::sort( + vSeeds.begin(), vSeeds.end(), + typename Acts::TrigFTF_GNN_Edge::CompareLevel()); + + if (vSeeds.empty()) { + return; + } + + // backtracking + + TrigFTF_GNN_TrackingFilter tFilter( + m_config.m_layerGeometry, edgeStorage); + + for (auto pS : vSeeds) { + if (pS->m_level == -1) { + continue; + } + + TrigFTF_GNN_EdgeState rs(false); + + tFilter.followTrack(pS, rs); + + if (!rs.m_initialized) { + continue; + } + + if (static_cast(rs.m_vs.size()) < minLevel) { + continue; + } + + std::vector*> vSP; + + for (typename std::vector*>:: + reverse_iterator sIt = rs.m_vs.rbegin(); + sIt != rs.m_vs.rend(); ++sIt) { + (*sIt)->m_level = -1; // mark as collected + + if (sIt == rs.m_vs.rbegin()) { + vSP.push_back(&(*sIt)->m_n1->m_sp_FTF); + } + vSP.push_back(&(*sIt)->m_n2->m_sp_FTF); + } + + if (vSP.size() < 3) { + continue; + } + + // making triplets + + unsigned int nTriplets = 0; + + std::vector> output; + + for (unsigned int idx_m = 1; idx_m < vSP.size() - 1; idx_m++) { + const FTF_SP& spM = *vSP.at(idx_m); + const double pS_r = spM.SP->r(); + const double pS_x = spM.SP->x(); + const double pS_y = spM.SP->y(); + const double cosA = pS_x / pS_r; + const double sinA = pS_y / pS_r; + + for (unsigned int idx_o = idx_m + 1; idx_o < vSP.size(); idx_o++) { + const FTF_SP& spO = *vSP.at(idx_o); + + double dx = spO.SP->x() - pS_x; + double dy = spO.SP->y() - pS_y; + double R2inv = 1.0 / (dx * dx + dy * dy); + double xn = dx * cosA + dy * sinA; + double yn = -dx * sinA + dy * cosA; + + const double uo = xn * R2inv; + const double vo = yn * R2inv; + + for (unsigned int idx_i = 0; idx_i < idx_m; idx_i++) { + const FTF_SP& spI = *vSP.at(idx_i); + + dx = spI.SP->x() - pS_x; + dy = spI.SP->y() - pS_y; + R2inv = 1.0 / (dx * dx + dy * dy); + + xn = dx * cosA + dy * sinA; + yn = -dx * sinA + dy * cosA; + + const double ui = xn * R2inv; + const double vi = yn * R2inv; + + // 1. pT estimate + + const double du = uo - ui; + if (du == 0.0) { + continue; + } + const double A = (vo - vi) / du; + const double B = vi - A * ui; + const double R_squ = (1 + A * A) / (B * B); + + if (R_squ < m_minR_squ) { + continue; + } + + // 2. d0 cut + + const double fabs_d0 = std::abs(pS_r * (B * pS_r - A)); + + if (fabs_d0 > m_config.m_tripletD0Max) { + continue; + } + + // 3. phi0 cut + + // if (!roi.isFullscan()) { + // const double uc = 2 * B * pS_r - A; + // // const double phi0 = std::atan2(sinA - uc * cosA, cosA + uc * + // sinA); + // // if ( !RoiUtil::containsPhi( *roiDescriptor, phi0 ) ) + // { + // // continue; + // // } + // } + + // 4. add new triplet + + const double Q = fabs_d0 * fabs_d0; + + output.emplace_back(spI, spM, spO, Q); + + nTriplets++; + + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + if (nTriplets >= m_config.m_maxTripletBufferLength) { + break; + } + } + + if (output.empty()) { + continue; + } + + vTracks.emplace_back(vSP, output); + } +} + +template +void SeedFinderFTF::createSeeds( + const Acts::RoiDescriptor& roi, + const Acts::TrigFTF_GNN_Geometry& gnngeo) { + std::vector> + vTracks; // make empty vector + + vTracks.reserve(5000); + + runGNN_TrackFinder(vTracks, roi, gnngeo); // returns filled vector + + if (vTracks.empty()) { + return; + } + + m_triplets.clear(); // member of class , saying not declared, maybe public? + + for (auto& track : vTracks) { + for (auto& seed : track.m_seeds) { // access mmeber of GNN_TrigTracklet + + float newQ = seed.Q(); // function of TrigInDetTriplet + if (m_config.m_LRTmode) { + // In LRT mode penalize pixels in Triplets + if (seed.s1().isPixel()) { + newQ += 1000; // functions of TrigSiSpacePointBase + } + if (seed.s2().isPixel()) { + newQ += 1000; + } + if (seed.s3().isPixel()) { + newQ += 1000; + } + } else { + // In normal (non LRT) mode penalise SSS by 1000, PSS (if enabled) and + // PPS by 10000 + if (seed.s3().isSCT()) { + newQ += seed.s1().isSCT() ? 1000.0 : 10000.0; + } + } + seed.Q(newQ); + m_triplets.emplace_back(seed); + } + } + vTracks.clear(); +} + +// // still to be developed +template +template +void SeedFinderFTF::createSeeds_old( + const Acts::SeedFinderOptions& /*options*/, + const input_container_t& /*spacePoints*/, output_container_t& /*out_cont*/, + callable_t&& /*extract_coordinates*/) const {} + +template +template +std::vector> +SeedFinderFTF::createSeeds_old( + const Acts::SeedFinderOptions& options, + const input_container_t& spacePoints, + callable_t&& extract_coordinates) const { + std::vector r; + createSeeds_old(options, spacePoints, r, + std::forward(extract_coordinates)); + return r; +} + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp b/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp new file mode 100644 index 00000000000..320d8136548 --- /dev/null +++ b/Core/include/Acts/Seeding/SeedFinderFTFConfig.hpp @@ -0,0 +1,90 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Seeding/SeedConfirmationRangeConfig.hpp" +#include "Acts/Seeding/TrigBase.hpp" //definition of Trigsispacepoint base and trigtriplets + +#include + +// core algorithm so in acts namespace +namespace Acts { + +template +class SeedFilter; + +template +struct SeedFinderFTFConfig { + // // how many sigmas of scattering angle should be considered? + float sigmaScattering = 5; + + // Seed cut + float minPt = 400. * Acts::UnitConstants::MeV; + + ///////////some declared not filled in by reco: ////// + std::shared_ptr> seedFilter; + + // //detector ROI + // // derived values, set on SeedFinder construction + float highland = 0; + float maxScatteringAngle2 = 0; + // bool isInInternalUnits = false; + /// for load space points + unsigned int maxSeedsPerSpM = 5; + + float m_phiSliceWidth{}; + float m_nMaxPhiSlice{}; + bool m_useClusterWidth = false; + std::string fastrack_input_file; + std::vector m_layerGeometry; + + // for run function + // m_settings: + bool m_LRTmode = true; // eventually want to set from full chain + bool m_useEtaBinning = true; + bool m_doubletFilterRZ = true; + float m_minDeltaRadius = 5.0; // eventually set in config or to equivalent + // acts 2.0 but increasing to test loops + // float m_maxDeltaRadius = 270.0 ; + float m_tripletD0Max = 4.0; // m_settings + unsigned int m_maxTripletBufferLength = 3; + + // ROI: + bool containsPhi() { + return false; + // need to implement this function + } + + //// + // 2 member functions + SeedFinderFTFConfig calculateDerivedQuantities() const { + // thorw statement if the isInternalUnits member is false, ie if dont call + // this function + SeedFinderFTFConfig config = *this; + // use a formula to calculate scattering + + return config; + } + + SeedFinderFTFConfig toInternalUnits() const { + // throw statement if the isInternalUnits member is false, ie if dont call + // this function + SeedFinderFTFConfig config = *this; + // divides inputs by 1mm, all ones input + // changes member inInInternalUnits to true + return config; + } + +}; // end of config struct + +} // namespace Acts diff --git a/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp b/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp index 601ed92c177..6ee33cdf45d 100644 --- a/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp +++ b/Core/include/Acts/Seeding/SeedFinderOrthogonal.ipp @@ -249,6 +249,7 @@ void SeedFinderOrthogonal::filterCandidates( &candidates_collector, Acts::SpacePointData &spacePointData) const { float rM = middle.radius(); + float zM = middle.z(); float varianceRM = middle.varianceR(); float varianceZM = middle.varianceZ(); @@ -256,8 +257,8 @@ void SeedFinderOrthogonal::filterCandidates( if (m_config.seedConfirmation == true) { // check if middle SP is in the central or forward region SeedConfirmationRangeConfig seedConfRange = - (middle.z() > m_config.centralSeedConfirmationRange.zMaxSeedConf || - middle.z() < m_config.centralSeedConfirmationRange.zMinSeedConf) + (zM > m_config.centralSeedConfirmationRange.zMaxSeedConf || + zM < m_config.centralSeedConfirmationRange.zMinSeedConf) ? m_config.forwardSeedConfirmationRange : m_config.centralSeedConfirmationRange; // set the minimum number of top SP depending on whether the middle SP is @@ -300,33 +301,22 @@ void SeedFinderOrthogonal::filterCandidates( std::sort( sorted_bottoms.begin(), sorted_bottoms.end(), - [&linCircleBottom](const std::size_t &a, const std::size_t &b) -> bool { + [&linCircleBottom](const std::size_t a, const std::size_t b) -> bool { return linCircleBottom[a].cotTheta < linCircleBottom[b].cotTheta; }); - std::sort( - sorted_tops.begin(), sorted_tops.end(), - [&linCircleTop](const std::size_t &a, const std::size_t &b) -> bool { - return linCircleTop[a].cotTheta < linCircleTop[b].cotTheta; - }); + std::sort(sorted_tops.begin(), sorted_tops.end(), + [&linCircleTop](const std::size_t a, const std::size_t b) -> bool { + return linCircleTop[a].cotTheta < linCircleTop[b].cotTheta; + }); - std::vector tanLM; std::vector tanMT; - - tanLM.reserve(bottom.size()); tanMT.reserve(top.size()); - size_t numBotSP = bottom.size(); size_t numTopSP = top.size(); - - for (size_t b = 0; b < numBotSP; b++) { - tanLM.push_back(std::atan2(middle.radius() - bottom[b]->radius(), - middle.z() - bottom[b]->z())); - } - for (size_t t = 0; t < numTopSP; t++) { - tanMT.push_back(std::atan2(top[t]->radius() - middle.radius(), - top[t]->z() - middle.z())); + tanMT.push_back( + std::atan2(top[t]->radius() - middle.radius(), top[t]->z() - zM)); } size_t t0 = 0; @@ -338,14 +328,9 @@ void SeedFinderOrthogonal::filterCandidates( } auto lb = linCircleBottom[b]; - float cotThetaB = lb.cotTheta; - float Vb = lb.V; - float Ub = lb.U; - float ErB = lb.Er; - float iDeltaRB = lb.iDeltaR; // 1+(cot^2(theta)) = 1/sin^2(theta) - float iSinTheta2 = (1. + cotThetaB * cotThetaB); + float iSinTheta2 = (1. + lb.cotTheta * lb.cotTheta); float sigmaSquaredPtDependent = iSinTheta2 * options.sigmapT2perRadius; // calculate max scattering for min momentum at the seed's theta angle // scaling scatteringAngle^2 by sin^2(theta) to convert pT^2 to p^2 @@ -375,22 +360,23 @@ void SeedFinderOrthogonal::filterCandidates( curvatures.clear(); impactParameters.clear(); + float tanLM = std::atan2(rM - bottom[b]->radius(), zM - bottom[b]->z()); + for (size_t index_t = t0; index_t < numTopSP; index_t++) { const std::size_t t = sorted_tops[index_t]; auto lt = linCircleTop[t]; - float cotThetaT = lt.cotTheta; - if (std::abs(tanLM[b] - tanMT[t]) > 0.005) { + if (std::abs(tanLM - tanMT[t]) > 0.005) { continue; } // add errors of spB-spM and spM-spT pairs and add the correlation term // for errors on spM - float error2 = lt.Er + ErB + - 2 * (cotThetaB * lt.cotTheta * varianceRM + varianceZM) * - iDeltaRB * lt.iDeltaR; + float error2 = lt.Er + lb.Er + + 2 * (lb.cotTheta * lt.cotTheta * varianceRM + varianceZM) * + lb.iDeltaR * lt.iDeltaR; - float deltaCotTheta = cotThetaB - lt.cotTheta; + float deltaCotTheta = lb.cotTheta - lt.cotTheta; float deltaCotTheta2 = deltaCotTheta * deltaCotTheta; // Apply a cut on the compatibility between the r-z slope of the two @@ -407,20 +393,20 @@ void SeedFinderOrthogonal::filterCandidates( // skip top SPs based on cotTheta sorting when producing triplets // break if cotTheta from bottom SP < cotTheta from top SP because // the SP are sorted by cotTheta - if (cotThetaB - cotThetaT < 0) { + if (deltaCotTheta < 0) { break; } t0 = index_t + 1; continue; } - float dU = lt.U - Ub; + float dU = lt.U - lb.U; // A and B are evaluated as a function of the circumference parameters // x_0 and y_0 - float A = (lt.V - Vb) / dU; + float A = (lt.V - lb.V) / dU; float S2 = 1. + A * A; - float B = Vb - A * Ub; + float B = lb.V - A * lb.U; float B2 = B * B; // sqrt(S2)/B = 2 * helixradius // calculated radius must not be smaller than minimum radius @@ -429,10 +415,9 @@ void SeedFinderOrthogonal::filterCandidates( } // 1/helixradius: (B/sqrt(S2))*2 (we leave everything squared) - float iHelixDiameter2 = B2 / S2; // convert p(T) to p scaling by sin^2(theta) AND scale by 1/sin^4(theta) // from rad to deltaCotTheta - float p2scatterSigma = iHelixDiameter2 * sigmaSquaredPtDependent; + float p2scatterSigma = B2 / S2 * sigmaSquaredPtDependent; if (!std::isinf(m_config.maxPtScattering)) { // if pT > maxPtScattering, calculate allowed scattering angle using // maxPtScattering instead of pt. @@ -446,7 +431,7 @@ void SeedFinderOrthogonal::filterCandidates( } // if deltaTheta larger than allowed scattering for calculated pT, skip if (deltaCotTheta2 > (error2 + p2scatterSigma)) { - if (cotThetaB - cotThetaT < 0) { + if (deltaCotTheta < 0) { break; } t0 = index_t; @@ -460,7 +445,7 @@ void SeedFinderOrthogonal::filterCandidates( if (Im <= m_config.impactMax) { top_valid.push_back(top[t]); - // inverse diameter is signed depending if the curvature is + // inverse diameter is signed depending on if the curvature is // positive/negative in phi curvatures.push_back(B / std::sqrt(S2)); impactParameters.push_back(Im); @@ -472,7 +457,7 @@ void SeedFinderOrthogonal::filterCandidates( continue; } - seedFilterState.zOrigin = middle.z() - rM * lb.cotTheta; + seedFilterState.zOrigin = zM - rM * lb.cotTheta; m_config.seedFilter->filterSeeds_2SpFixed( spacePointData, *bottom[b], middle, top_valid, curvatures, @@ -790,7 +775,7 @@ void SeedFinderOrthogonal::createSeeds( middle.z() > m_config.zOutermostLayers.second) { continue; } - float spPhi = std::atan2(middle.y(), middle.x()); + float spPhi = middle.phi(); if (spPhi > m_config.phiMax or spPhi < m_config.phiMin) { continue; } diff --git a/Core/include/Acts/Seeding/SeedFinderUtils.ipp b/Core/include/Acts/Seeding/SeedFinderUtils.ipp index 46e2fd55c9a..87df978eccf 100644 --- a/Core/include/Acts/Seeding/SeedFinderUtils.ipp +++ b/Core/include/Acts/Seeding/SeedFinderUtils.ipp @@ -92,7 +92,7 @@ inline void transformCoordinates(Acts::SpacePointData& spacePointData, callable_t&& extractFunction) { auto [xM, yM, zM, rM, varianceRM, varianceZM] = extractFunction(spM); - // resize + operator[] is faster then reserve and push_back + // resize + operator[] is faster than reserve and push_back linCircleVec.resize(vec.size()); float cosPhiM = xM / rM; @@ -169,12 +169,12 @@ inline bool xyzCoordinateCheck( const Acts::Vector3& stripCenterDistance = spacePointData.getStripCenterDistance(index); - const double& xTopStripVector = topStripVector[0]; - const double& yTopStripVector = topStripVector[1]; - const double& zTopStripVector = topStripVector[2]; - const double& xBottomStripVector = bottomStripVector[0]; - const double& yBottomStripVector = bottomStripVector[1]; - const double& zBottomStripVector = bottomStripVector[2]; + const double xTopStripVector = topStripVector[0]; + const double yTopStripVector = topStripVector[1]; + const double zTopStripVector = topStripVector[2]; + const double xBottomStripVector = bottomStripVector[0]; + const double yBottomStripVector = bottomStripVector[1]; + const double zBottomStripVector = bottomStripVector[2]; // cross product between top strip vector and spacepointPosition double d1[3] = {yTopStripVector * spacepointPosition[2] - diff --git a/Core/include/Acts/Seeding/TrigBase.hpp b/Core/include/Acts/Seeding/TrigBase.hpp new file mode 100644 index 00000000000..4737bf819fa --- /dev/null +++ b/Core/include/Acts/Seeding/TrigBase.hpp @@ -0,0 +1,45 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include "Acts/Seeding/GNN_TrackingFilter.hpp" + +#include + +#define MAX_SILICON_LAYER_NUM 19 +#define OffsetEndcapPixels 7 +#define OffsetBarrelSCT 3 +#define OffsetEndcapSCT 10 + +template +class TrigInDetTriplet { + public: + TrigInDetTriplet() = delete; // to prevent creation w/o initialization + + TrigInDetTriplet(Acts::FTF_SP s1, + Acts::FTF_SP s2, + Acts::FTF_SP s3, float Q) + : m_s1(std::move(s1)), m_s2(std::move(s2)), m_s3(std::move(s3)), m_Q(Q) {} + + TrigInDetTriplet(TrigInDetTriplet* t) + : m_s1(t->m_s1), m_s2(t->m_s2), m_s3(t->m_s3), m_Q(t->m_Q) {} + + const Acts::FTF_SP& s1() const { return m_s1; } + const Acts::FTF_SP& s2() const { return m_s2; } + const Acts::FTF_SP& s3() const { return m_s3; } + float Q() const { return m_Q; } + void Q(double newQ) { m_Q = newQ; } + + protected: + Acts::FTF_SP m_s1; + Acts::FTF_SP m_s2; + Acts::FTF_SP m_s3; + float m_Q; // Quality +}; diff --git a/Core/include/Acts/Surfaces/AnnulusBounds.hpp b/Core/include/Acts/Surfaces/AnnulusBounds.hpp index 9d10ca16c83..d71f6937242 100644 --- a/Core/include/Acts/Surfaces/AnnulusBounds.hpp +++ b/Core/include/Acts/Surfaces/AnnulusBounds.hpp @@ -133,7 +133,7 @@ class AnnulusBounds : public DiscBounds { std::vector corners() const; /// This method returns the xy coordinates of the four corners of the - /// bounds in module coorindates (in x/y) + /// bounds in module coordinates (in x/y) /// Starting from the upper right (max R, pos locX) and proceeding clock-wise /// i.e. (max R; pos locX), (min R; pos locX), (min R; neg loc X), (max R: neg /// locX) @@ -194,7 +194,7 @@ class AnnulusBounds : public DiscBounds { virtual bool inside(const Vector2& lposition, double tolR, double tolPhi) const final; - /// Transform the strip cartesien + /// Transform the strip cartesian /// into the module polar system /// /// @param vStripXY the position in the cartesian strip system diff --git a/Core/include/Acts/Surfaces/ConeSurface.hpp b/Core/include/Acts/Surfaces/ConeSurface.hpp index cf659eda6be..debe2a47934 100644 --- a/Core/include/Acts/Surfaces/ConeSurface.hpp +++ b/Core/include/Acts/Surfaces/ConeSurface.hpp @@ -181,8 +181,8 @@ class ConeSurface : public Surface { /// /// If possible returns both solutions for the cylinder /// - /// @return SurfaceIntersection object (contains intersection & surface) - SurfaceIntersection intersect( + /// @return @c SurfaceMultiIntersection object (contains intersection & surface) + SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, double tolerance = s_onSurfaceTolerance) const final; diff --git a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp index 885a07d4066..fabe59e7a65 100644 --- a/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp +++ b/Core/include/Acts/Surfaces/ConvexPolygonBounds.hpp @@ -142,6 +142,8 @@ constexpr int PolygonDynamic = -1; template <> class ConvexPolygonBounds : public ConvexPolygonBoundsBase { public: + constexpr static int eSize = -1; + /// Default constructor, deleted ConvexPolygonBounds() = delete; diff --git a/Core/include/Acts/Surfaces/CylinderSurface.hpp b/Core/include/Acts/Surfaces/CylinderSurface.hpp index df4bac3b7c2..847a925aca2 100644 --- a/Core/include/Acts/Surfaces/CylinderSurface.hpp +++ b/Core/include/Acts/Surfaces/CylinderSurface.hpp @@ -46,13 +46,6 @@ class CylinderSurface : public Surface { #endif protected: - /// Constructor from DetectorElementBase: Element proxy - /// - /// @param cbounds are the provided cylinder bounds (shared) - /// @param detelement is the linked detector element to this surface - CylinderSurface(std::shared_ptr cbounds, - const DetectorElementBase& detelement); - /// Constructor from Transform3 and CylinderBounds /// /// @param transform The transform to position the surface @@ -72,7 +65,14 @@ class CylinderSurface : public Surface { /// @param cbounds is a shared pointer to a cylindeer bounds object, /// it must exist (assert test) CylinderSurface(const Transform3& transform, - const std::shared_ptr& cbounds); + std::shared_ptr cbounds); + + /// Constructor from DetectorElementBase: Element proxy + /// + /// @param cbounds are the provided cylinder bounds (shared) + /// @param detelement is the linked detector element to this surface + CylinderSurface(std::shared_ptr cbounds, + const DetectorElementBase& detelement); /// Copy constructor /// @@ -192,7 +192,7 @@ class CylinderSurface : public Surface { /// If possible returns both solutions for the cylinder /// /// @return SurfaceIntersection object (contains intersection & surface) - SurfaceIntersection intersect( + SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, ActsScalar tolerance = s_onSurfaceTolerance) const final; diff --git a/Core/include/Acts/Surfaces/DiamondBounds.hpp b/Core/include/Acts/Surfaces/DiamondBounds.hpp index 61d11b5e0ac..be293711193 100644 --- a/Core/include/Acts/Surfaces/DiamondBounds.hpp +++ b/Core/include/Acts/Surfaces/DiamondBounds.hpp @@ -137,4 +137,4 @@ inline void DiamondBounds::checkConsistency() noexcept(false) { } } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/DiscBounds.hpp b/Core/include/Acts/Surfaces/DiscBounds.hpp index 2810bf541e4..e37ee4d19d4 100644 --- a/Core/include/Acts/Surfaces/DiscBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscBounds.hpp @@ -49,4 +49,4 @@ class DiscBounds : public SurfaceBounds { virtual bool insideRadialBounds(double R, double tolerance = 0.) const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/DiscSurface.hpp b/Core/include/Acts/Surfaces/DiscSurface.hpp index 59a4ccf799e..df4e58f6d34 100644 --- a/Core/include/Acts/Surfaces/DiscSurface.hpp +++ b/Core/include/Acts/Surfaces/DiscSurface.hpp @@ -91,7 +91,7 @@ class DiscSurface : public Surface { /// /// @param dbounds The disc bounds describing the surface coverage /// @param detelement The detector element represented by this surface - DiscSurface(const std::shared_ptr& dbounds, + DiscSurface(std::shared_ptr dbounds, const DetectorElementBase& detelement); /// Copy Constructor @@ -271,8 +271,8 @@ class DiscSurface : public Surface { /// - either in the plane /// - perpendicular to the normal of the plane /// - /// @return The SurfaceIntersection object - SurfaceIntersection intersect( + /// @return The @c SurfaceMultiIntersection object + SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, ActsScalar tolerance = s_onSurfaceTolerance) const final; diff --git a/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp b/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp index d9e210786c7..626a32d233f 100644 --- a/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscTrapezoidBounds.hpp @@ -227,4 +227,4 @@ inline void DiscTrapezoidBounds::checkConsistency() noexcept(false) { } } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/EllipseBounds.hpp b/Core/include/Acts/Surfaces/EllipseBounds.hpp index 4cc42989b6a..7f918cc93c2 100644 --- a/Core/include/Acts/Surfaces/EllipseBounds.hpp +++ b/Core/include/Acts/Surfaces/EllipseBounds.hpp @@ -141,4 +141,4 @@ inline void EllipseBounds::checkConsistency() noexcept(false) { } } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/LineBounds.hpp b/Core/include/Acts/Surfaces/LineBounds.hpp index a65277a22fb..fe94e830955 100644 --- a/Core/include/Acts/Surfaces/LineBounds.hpp +++ b/Core/include/Acts/Surfaces/LineBounds.hpp @@ -96,4 +96,4 @@ inline void LineBounds::checkConsistency() noexcept(false) { } } -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/LineSurface.hpp b/Core/include/Acts/Surfaces/LineSurface.hpp index 89b25aae5ad..f6d83e89540 100644 --- a/Core/include/Acts/Surfaces/LineSurface.hpp +++ b/Core/include/Acts/Surfaces/LineSurface.hpp @@ -250,7 +250,7 @@ class LineSurface : public Surface { /// @param bcheck The boundary check directive for the estimate /// @param tolerance the tolerance used for the intersection /// @return is the intersection object - SurfaceIntersection intersect( + SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, ActsScalar tolerance = s_onSurfaceTolerance) const final; diff --git a/Core/include/Acts/Surfaces/PlanarBounds.hpp b/Core/include/Acts/Surfaces/PlanarBounds.hpp index 0b16251b5fe..22762fc3e1e 100644 --- a/Core/include/Acts/Surfaces/PlanarBounds.hpp +++ b/Core/include/Acts/Surfaces/PlanarBounds.hpp @@ -41,4 +41,4 @@ class PlanarBounds : public SurfaceBounds { virtual const RectangleBounds& boundingBox() const = 0; }; -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Surfaces/PlaneSurface.hpp b/Core/include/Acts/Surfaces/PlaneSurface.hpp index a0c444127b6..a7200bc82f3 100644 --- a/Core/include/Acts/Surfaces/PlaneSurface.hpp +++ b/Core/include/Acts/Surfaces/PlaneSurface.hpp @@ -67,9 +67,9 @@ class PlaneSurface : public Surface { /// Constructor from DetectorElementBase : Element proxy /// - /// @param pbounds are the provided planar bounds (shared) + /// @param pbounds are the provided planar bounds /// @param detelement is the linked detector element to this surface - PlaneSurface(const std::shared_ptr& pbounds, + PlaneSurface(std::shared_ptr pbounds, const DetectorElementBase& detelement); /// Constructor for Planes with (optional) shared bounds object @@ -186,8 +186,8 @@ class PlaneSurface : public Surface { /// - either in the plane /// - perpendicular to the normal of the plane /// - /// @return the SurfaceIntersection object - SurfaceIntersection intersect( + /// @return the @c SurfaceMultiIntersection object + SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, ActsScalar tolerance = s_onSurfaceTolerance) const final; diff --git a/Core/include/Acts/Surfaces/Surface.hpp b/Core/include/Acts/Surfaces/Surface.hpp index c5615cda832..84336f92f0f 100644 --- a/Core/include/Acts/Surfaces/Surface.hpp +++ b/Core/include/Acts/Surfaces/Surface.hpp @@ -46,6 +46,8 @@ class Surface; /// Typedef of the surface intersection using SurfaceIntersection = ObjectIntersection; +/// Typedef of the surface multi-intersection +using SurfaceMultiIntersection = ObjectMultiIntersection; /// @class Surface /// @@ -407,8 +409,8 @@ class Surface : public virtual GeometryObject, /// @param bcheck the Boundary Check /// @param tolerance the tolerance used for the intersection /// - /// @return SurfaceIntersection object (contains intersection & surface) - virtual SurfaceIntersection intersect( + /// @return @c SurfaceMultiIntersection object (contains intersection & surface) + virtual SurfaceMultiIntersection intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck = false, ActsScalar tolerance = s_onSurfaceTolerance) const = 0; diff --git a/Core/include/Acts/Surfaces/SurfaceError.hpp b/Core/include/Acts/Surfaces/SurfaceError.hpp index f98e3cd495c..9530cb31822 100644 --- a/Core/include/Acts/Surfaces/SurfaceError.hpp +++ b/Core/include/Acts/Surfaces/SurfaceError.hpp @@ -26,4 +26,4 @@ namespace std { // register with STL template <> struct is_error_code_enum : std::true_type {}; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp b/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp index eb8034b90a4..32c6a8bf75d 100644 --- a/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp +++ b/Core/include/Acts/Surfaces/detail/PlanarHelper.hpp @@ -43,7 +43,7 @@ inline Intersection3D intersect(const Transform3& transform, // Return the intersection return Intersection3D{(position + path * direction), path, status}; } - return Intersection3D(); + return Intersection3D::invalid(); } } // namespace PlanarHelper diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 3c3cdc1ea2c..16e89fe91ce 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -33,19 +33,30 @@ #include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp" #include "Acts/TrackFinding/SourceLinkAccessorConcept.hpp" #include "Acts/TrackFitting/KalmanFitter.hpp" -#include "Acts/TrackFitting/detail/VoidKalmanComponents.hpp" +#include "Acts/TrackFitting/detail/VoidFitterComponents.hpp" #include "Acts/Utilities/CalibrationContext.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" #include "Acts/Utilities/Zip.hpp" #include +#include #include #include #include namespace Acts { +enum class CombinatorialKalmanFilterTargetSurfaceStrategy { + /// Use the first trackstate to reach target surface + first, + /// Use the last trackstate to reach target surface + last, + /// Use the first or last trackstate to reach target surface depending on the + /// distance + firstOrLast, +}; + /// Track quality summary for one trajectory. /// /// This could be used to decide if a track is to be recorded when the @@ -94,9 +105,9 @@ struct CombinatorialKalmanFilterExtensions { /// Default constructor which connects the default void components CombinatorialKalmanFilterExtensions() { - calibrator.template connect<&voidKalmanCalibrator>(); - updater.template connect<&voidKalmanUpdater>(); - smoother.template connect<&voidKalmanSmoother>(); + calibrator.template connect<&detail::voidFitterCalibrator>(); + updater.template connect<&detail::voidFitterUpdater>(); + smoother.template connect<&detail::voidFitterSmoother>(); branchStopper.connect(); measurementSelector.template connect(); } @@ -199,6 +210,11 @@ struct CombinatorialKalmanFilterOptions { /// The smoothing target surface const Surface* smoothingTargetSurface = nullptr; + /// Strategy to propagate to reference surface + CombinatorialKalmanFilterTargetSurfaceStrategy + smoothingTargetSurfaceStrategy = + CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -211,51 +227,52 @@ struct CombinatorialKalmanFilterOptions { template struct CombinatorialKalmanFilterResult { - // Fitted states that the actor has handled. + /// Fitted states that the actor has handled. traj_t* fittedStates{nullptr}; - // These is used internally to store candidate trackstates + /// This is used internally to store candidate trackstates std::shared_ptr stateBuffer; std::vector trackStateCandidates; - // This is the indices of the 'tip' of the tracks stored in multitrajectory. - // This correspond to the last measurement state in the multitrajectory. + /// This is the indices of the 'tip' of the tracks stored in multitrajectory. + /// This corresponds to the last measurement state in the multitrajectory. std::vector lastMeasurementIndices; - // This is the indices of the 'tip' of the tracks stored in multitrajectory. - // This correspond to the last state in the multitrajectory. + /// This is the indices of the 'tip' of the tracks stored in multitrajectory. + /// This corresponds to the last state in the multitrajectory. std::vector lastTrackIndices; - // The Parameters at the provided surface for separate tracks + /// The Parameters at the provided surface for separate tracks std::unordered_map fittedParameters; - // The indices of the 'tip' of the unfinished tracks + /// The indices of the 'tip' of the unfinished tracks std::vector> activeTips; - // The indices of track states and corresponding source links on different - // surfaces + /// The indices of track states and corresponding source links on different + /// surfaces std::unordered_map> sourcelinkTips; - // Indicator if filtering has been done + /// Indicator if filtering has been done bool filtered = false; - // Indicator if smoothing has been done. + /// Indicator if smoothing has been done. bool smoothed = false; - // The index for the current smoothing track + /// The index for the current smoothing track MultiTrajectoryTraits::IndexType iSmoothed = 0; - // Indicator if track finding has been done + /// Indicator if track finding has been done bool finished = false; - Result result{Result::success()}; + /// Last encountered error + Result lastError{Result::success()}; - // TODO place into options and make them accessible? - AbortList abortList; + /// Path limit aborter + PathLimitReached pathLimitReached; }; /// Combinatorial Kalman filter to find tracks. @@ -298,7 +315,7 @@ class CombinatorialKalmanFilter { private: using KalmanNavigator = typename propagator_t::Navigator; - /// The propgator for the transport and material update + /// The propagator for the transport and material update propagator_t m_propagator; std::unique_ptr m_logger; @@ -332,6 +349,11 @@ class CombinatorialKalmanFilter { /// The smoothing target surface const Surface* smoothingTargetSurface = nullptr; + /// Strategy to propagate to reference surface + CombinatorialKalmanFilterTargetSurfaceStrategy + smoothingTargetSurfaceStrategy = + CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -346,19 +368,19 @@ class CombinatorialKalmanFilter { /// @brief CombinatorialKalmanFilter actor operation /// - /// @tparam propagator_state_t Type of the Propagagor state + /// @tparam propagator_state_t Type of the Propagator state /// @tparam stepper_t Type of the stepper /// /// @param state is the mutable propagator state object /// @param stepper is the stepper in use /// @param navigator is the navigator in use /// @param result is the mutable result state object - /// @param logger the logger object to be used + /// @param _logger the logger object associated to the @c Propagator. CKF uses its own logger instance. template void operator()(propagator_state_t& state, const stepper_t& stepper, const navigator_t& navigator, result_type& result, - const Logger& logger) const { + const Logger& _logger) const { assert(result.fittedStates && "No MultiTrajectory set"); if (result.finished) { @@ -396,7 +418,7 @@ class CombinatorialKalmanFilter { auto res = filter(surface, state, stepper, navigator, result); if (!res.ok()) { ACTS_ERROR("Error in filter: " << res.error()); - result.result = res.error(); + result.lastError = res.error(); } } @@ -411,7 +433,7 @@ class CombinatorialKalmanFilter { // The last active tip const auto& lastActiveTip = result.activeTips.back().first; // Get the index of previous state - const auto& iprevious = + auto iprevious = result.fittedStates->getTrackState(lastActiveTip).previous(); // Find the track states which have the same previous state and remove // them from active tips @@ -464,7 +486,8 @@ class CombinatorialKalmanFilter { } } - if (result.abortList(state, stepper, navigator, result, logger())) { + if (endOfWorldReached(state, stepper, navigator, logger()) || + result.pathLimitReached(state, stepper, navigator, logger())) { navigator.targetReached(state.navigation, false); if (result.activeTips.empty()) { // we are already done @@ -509,9 +532,19 @@ class CombinatorialKalmanFilter { auto res = finalize(state, stepper, navigator, result); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); - result.result = res.error(); + result.lastError = res.error(); } result.smoothed = true; + + // TODO another ugly control flow hack + navigator.preStep(state, stepper); + } + + if (result.smoothed) { + // Update state and stepper with material effects + materialInteractor(navigator.currentSurface(state.navigation), + state, stepper, navigator, + MaterialUpdateStage::FullUpdate); } // -> then progress to target/reference surface and built the final @@ -519,7 +552,9 @@ class CombinatorialKalmanFilter { if (result.smoothed and (smoothingTargetSurface == nullptr or targetReached(state, stepper, navigator, - *smoothingTargetSurface, logger()))) { + *smoothingTargetSurface, logger()) or + result.pathLimitReached(state, stepper, navigator, + logger()))) { ACTS_VERBOSE( "Completing the track with last measurement index = " << result.lastMeasurementIndices.at(result.iSmoothed)); @@ -530,15 +565,14 @@ class CombinatorialKalmanFilter { stepper.boundState(state.stepping, *smoothingTargetSurface); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); - result.result = res.error(); - return; + result.lastError = res.error(); + } else { + auto fittedState = *res; + // Assign the fitted parameters + result.fittedParameters.emplace( + result.lastMeasurementIndices.at(result.iSmoothed), + std::get(fittedState)); } - - auto fittedState = *res; - // Assign the fitted parameters - result.fittedParameters.emplace( - result.lastMeasurementIndices.at(result.iSmoothed), - std::get(fittedState)); } // If there are more trajectories to handle: @@ -554,7 +588,7 @@ class CombinatorialKalmanFilter { // TODO this is kinda silly but I dont see a better solution // with the current CKF control flow - operator()(state, stepper, navigator, result, logger); + operator()(state, stepper, navigator, result, _logger); } else { ACTS_VERBOSE("Finish Kalman filtering and smoothing"); // Remember that track finding is done @@ -566,9 +600,9 @@ class CombinatorialKalmanFilter { } // if filtering is done } - /// @brief Kalman actor operation : reset propagation + /// @brief Kalman actor operation: reset propagation /// - /// @tparam propagator_state_t Type of Propagagor state + /// @tparam propagator_state_t Type of Propagator state /// @tparam stepper_t Type of the stepper /// @tparam navigator_t Type of the navigator /// @@ -583,7 +617,7 @@ class CombinatorialKalmanFilter { auto currentState = result.fittedStates->getTrackState(result.activeTips.back().first); - // Update the stepping state + // Reset the stepping state stepper.resetState(state.stepping, currentState.filtered(), currentState.filteredCovariance(), currentState.referenceSurface(), @@ -602,17 +636,16 @@ class CombinatorialKalmanFilter { materialInteractor(navigator.currentSurface(state.navigation), state, stepper, navigator, MaterialUpdateStage::FullUpdate); - detail::setupLoopProtection( - state, stepper, result.abortList.template get(), - logger()); + detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, + logger()); } - /// @brief CombinatorialKalmanFilter actor operation : + /// @brief CombinatorialKalmanFilter actor operation: /// - filtering for all measurement(s) on surface /// - store selected track states in multiTrajectory /// - update propagator state to the (last) selected track state /// - /// @tparam propagator_state_t Type of the Propagagor state + /// @tparam propagator_state_t Type of the Propagator state /// @tparam stepper_t Type of the stepper /// @tparam navigator_t Type of the navigator /// @@ -786,11 +819,10 @@ class CombinatorialKalmanFilter { nBranchesOnSurface = 0; } } - if (surface->surfaceMaterial() != nullptr) { - // Update state and stepper with material effects - materialInteractor(surface, state, stepper, navigator, - MaterialUpdateStage::FullUpdate); - } + + // Update state and stepper with material effects + materialInteractor(surface, state, stepper, navigator, + MaterialUpdateStage::FullUpdate); } else { // Neither measurement nor material on surface, this branch is still // valid. Count the branch on current surface @@ -1007,7 +1039,7 @@ class CombinatorialKalmanFilter { return Result::success(); } - /// @brief CombinatorialKalmanFilter actor operation : add hole or material track state + /// @brief CombinatorialKalmanFilter actor operation: add a hole or material track state /// /// @param stateMask The bitmask that instructs which components to allocate /// @param boundState The bound state on current surface @@ -1064,9 +1096,9 @@ class CombinatorialKalmanFilter { return currentTip; } - /// @brief CombinatorialKalmanFilter actor operation : material interaction + /// @brief CombinatorialKalmanFilter actor operation: material interaction /// - /// @tparam propagator_state_t is the type of Propagagor state + /// @tparam propagator_state_t is the type of Propagator state /// @tparam stepper_t Type of the stepper /// @tparam navigator_t Type of the navigator /// @@ -1074,7 +1106,7 @@ class CombinatorialKalmanFilter { /// @param state The mutable propagator state object /// @param stepper The stepper in use /// @param navigator The navigator in use - /// @param updateStage The materal update stage + /// @param updateStage The material update stage /// template @@ -1082,10 +1114,14 @@ class CombinatorialKalmanFilter { const stepper_t& stepper, const navigator_t& navigator, const MaterialUpdateStage& updateStage) const { + if (surface == nullptr) { + return; + } + // Indicator if having material bool hasMaterial = false; - if (surface and surface->surfaceMaterial()) { + if (surface->surfaceMaterial() != nullptr) { // Prepare relevant input particle properties detail::PointwiseMaterialInteraction interaction(surface, state, stepper); @@ -1103,7 +1139,7 @@ class CombinatorialKalmanFilter { << surface->geometryId() << " at update stage: " << updateStage << " are :"); ACTS_VERBOSE("eLoss = " - << interaction.Eloss << ", " + << interaction.Eloss * interaction.navDir << ", " << "variancePhi = " << interaction.variancePhi << ", " << "varianceTheta = " << interaction.varianceTheta << ", " @@ -1122,9 +1158,9 @@ class CombinatorialKalmanFilter { } } - /// @brief Kalman actor operation : finalize + /// @brief Kalman actor operation: finalize /// - /// @tparam propagator_state_t is the type of Propagagor state + /// @tparam propagator_state_t is the type of Propagator state /// @tparam stepper_t Type of the stepper /// @tparam navigator_t Type of the navigator /// @@ -1149,8 +1185,11 @@ class CombinatorialKalmanFilter { result.fittedStates->applyBackwards(lastMeasurementIndex, [&](auto st) { bool isMeasurement = st.typeFlags().test(TrackStateFlag::MeasurementFlag); - bool isMaterial = st.typeFlags().test(TrackStateFlag::MaterialFlag); - if (isMeasurement || isMaterial) { + bool isOutlier = st.typeFlags().test(TrackStateFlag::OutlierFlag); + // We are excluding non measurement states and outlier here. Those can + // decrease resolution because only the smoothing corrected the very + // first prediction as filtering is not possible. + if (isMeasurement && !isOutlier) { firstStateIndex = st.index(); } nStates++; @@ -1164,6 +1203,7 @@ class CombinatorialKalmanFilter { // Screen output for debugging ACTS_VERBOSE("Apply smoothing on " << nStates << " filtered track states."); + // Smooth the track states auto smoothRes = m_extensions.smoother(state.geoContext, *result.fittedStates, @@ -1178,8 +1218,7 @@ class CombinatorialKalmanFilter { return Result::success(); } - // Obtain the smoothed parameters at first/last measurement state. - // The first state can also be a material state + // Obtain the smoothed parameters at first/last measurement state auto firstCreatedState = result.fittedStates->getTrackState(firstStateIndex); auto lastCreatedMeasurement = @@ -1187,13 +1226,16 @@ class CombinatorialKalmanFilter { // Lambda to get the intersection of the free params on the target surface auto target = [&](const FreeVector& freeVector) -> SurfaceIntersection { - return smoothingTargetSurface->intersect( - state.geoContext, freeVector.segment<3>(eFreePos0), - state.options.direction * freeVector.segment<3>(eFreeDir0), true, - state.options.targetTolerance); + return smoothingTargetSurface + ->intersect( + state.geoContext, freeVector.segment<3>(eFreePos0), + state.options.direction * freeVector.segment<3>(eFreeDir0), + true, state.options.targetTolerance) + .closest(); }; - // The smoothed free params at the first/last measurement state + // The smoothed free params at the first/last measurement state. + // (the first state can also be a material state) auto firstParams = MultiTrajectoryHelpers::freeSmoothed( state.options.geoContext, firstCreatedState); auto lastParams = MultiTrajectoryHelpers::freeSmoothed( @@ -1204,38 +1246,43 @@ class CombinatorialKalmanFilter { const auto lastIntersection = target(lastParams); // Update the stepping parameters - in order to progress to destination. - // At the same time, reverse navigation direction for further - // stepping if necessary. + // At the same time, reverse navigation direction for further stepping if + // necessary. // @note The stepping parameters is updated to the smoothed parameters at // either the first measurement state or the last measurement state. It // assumes the target surface is not within the first and the last // smoothed measurement state. Also, whether the intersection is on // surface is not checked here. + bool useFirstTrackState = true; + switch (smoothingTargetSurfaceStrategy) { + case CombinatorialKalmanFilterTargetSurfaceStrategy::first: + useFirstTrackState = true; + break; + case CombinatorialKalmanFilterTargetSurfaceStrategy::last: + useFirstTrackState = false; + break; + case CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast: + useFirstTrackState = std::abs(firstIntersection.pathLength()) <= + std::abs(lastIntersection.pathLength()); + break; + default: + ACTS_ERROR("Unknown target surface strategy"); + return KalmanFitterError::SmoothFailed; + } bool reverseDirection = false; - bool closerToFirstCreatedState = - (std::abs(firstIntersection.intersection.pathLength) <= - std::abs(lastIntersection.intersection.pathLength)); - if (closerToFirstCreatedState) { - stepper.update(state.stepping, firstParams, - firstCreatedState.smoothed(), - firstCreatedState.smoothedCovariance(), - firstCreatedState.referenceSurface()); - reverseDirection = (firstIntersection.intersection.pathLength < 0); + if (useFirstTrackState) { + stepper.resetState(state.stepping, firstCreatedState.smoothed(), + firstCreatedState.smoothedCovariance(), + firstCreatedState.referenceSurface(), + state.options.maxStepSize); + reverseDirection = firstIntersection.pathLength() < 0; } else { - stepper.update(state.stepping, lastParams, - lastCreatedMeasurement.smoothed(), - lastCreatedMeasurement.smoothedCovariance(), - lastCreatedMeasurement.referenceSurface()); - reverseDirection = (lastIntersection.intersection.pathLength < 0); + stepper.resetState(state.stepping, lastCreatedMeasurement.smoothed(), + lastCreatedMeasurement.smoothedCovariance(), + lastCreatedMeasurement.referenceSurface(), + state.options.maxStepSize); + reverseDirection = lastIntersection.pathLength() < 0; } - const auto& surface = closerToFirstCreatedState - ? firstCreatedState.referenceSurface() - : lastCreatedMeasurement.referenceSurface(); - ACTS_VERBOSE( - "Smoothing successful, updating stepping state to smoothed " - "parameters at surface " - << surface.geometryId() << ". Prepared to reach the target surface."); - // Reverse the navigation direction if necessary if (reverseDirection) { ACTS_VERBOSE( @@ -1243,19 +1290,25 @@ class CombinatorialKalmanFilter { "target surface"); state.options.direction = state.options.direction.invert(); } - // Reinitialize the stepping jacobian - state.stepping.jacobian = BoundMatrix::Identity(); - state.stepping.jacTransport = FreeMatrix::Identity(); - state.stepping.derivative = FreeVector::Zero(); - // Reset the step size - state.stepping.stepSize = ConstrainedStep(state.options.maxStepSize); - // Set accumulatd path to zero before targeting surface - state.stepping.pathAccumulated = 0.; + const auto& surface = useFirstTrackState + ? firstCreatedState.referenceSurface() + : lastCreatedMeasurement.referenceSurface(); + + ACTS_VERBOSE( + "Smoothing successful, updating stepping state to smoothed " + "parameters at surface " + << surface.geometryId() << ". Prepared to reach the target surface."); // Reset the navigation state to enable propagation towards the target // surface - navigator.targetReached(state.navigation, false); - navigator.currentSurface(state.navigation, nullptr); + // Set targetSurface to nullptr as it is handled manually in the actor + navigator.resetState( + state.navigation, state.geoContext, stepper.position(state.stepping), + state.options.direction * stepper.direction(state.stepping), &surface, + nullptr); + + detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, + logger()); return Result::success(); } @@ -1266,7 +1319,10 @@ class CombinatorialKalmanFilter { source_link_accessor_t m_sourcelinkAccessor; /// The Surface being targeted - SurfaceReached targetReached; + SurfaceReached targetReached{std::numeric_limits::lowest()}; + + /// End of world aborter + EndOfWorldReached endOfWorldReached; /// Actor logger instance const Logger* actorLogger{nullptr}; @@ -1289,7 +1345,7 @@ class CombinatorialKalmanFilter { bool operator()(propagator_state_t& /*state*/, const stepper_t& /*stepper*/, const navigator_t& /*navigator*/, const result_t& result, const Logger& /*logger*/) const { - if (!result.result.ok() or result.finished) { + if (result.finished) { return true; } return false; @@ -1353,6 +1409,8 @@ class CombinatorialKalmanFilter { propOptions.actionList.template get(); combKalmanActor.filterTargetSurface = tfOptions.filterTargetSurface; combKalmanActor.smoothingTargetSurface = tfOptions.smoothingTargetSurface; + combKalmanActor.smoothingTargetSurfaceStrategy = + tfOptions.smoothingTargetSurfaceStrategy; combKalmanActor.multipleScattering = tfOptions.multipleScattering; combKalmanActor.energyLoss = tfOptions.energyLoss; combKalmanActor.smoothing = tfOptions.smoothing; @@ -1380,10 +1438,10 @@ class CombinatorialKalmanFilter { r.stateBuffer->clear(); auto result = m_propagator.template propagate( - initialParameters, propOptions, std::move(inputResult)); + initialParameters, propOptions, false, std::move(inputResult)); if (!result.ok()) { - ACTS_ERROR("Propapation failed: " << result.error() << " " + ACTS_ERROR("Propagation failed: " << result.error() << " " << result.error().message() << " with the initial parameters: \n" << initialParameters.parameters()); @@ -1403,27 +1461,31 @@ class CombinatorialKalmanFilter { // This is regarded as a failure. // @TODO: Implement distinguishment between the above two cases if // necessary - if (combKalmanResult.result.ok() and not combKalmanResult.finished) { - combKalmanResult.result = Result( + if (combKalmanResult.lastError.ok() and not combKalmanResult.finished) { + combKalmanResult.lastError = Result( CombinatorialKalmanFilterError::PropagationReachesMaxSteps); } - if (!combKalmanResult.result.ok()) { + if (!combKalmanResult.lastError.ok()) { ACTS_ERROR("CombinatorialKalmanFilter failed: " - << combKalmanResult.result.error() << " " - << combKalmanResult.result.error().message() + << combKalmanResult.lastError.error() << " " + << combKalmanResult.lastError.error().message() << " with the initial parameters: \n" << initialParameters.parameters()); - return combKalmanResult.result.error(); } std::vector tracks; for (auto tip : combKalmanResult.lastMeasurementIndices) { + auto it = combKalmanResult.fittedParameters.find(tip); + if (it == combKalmanResult.fittedParameters.end()) { + continue; + } + auto track = trackContainer.getTrack(trackContainer.addTrack()); track.tipIndex() = tip; - const BoundTrackParameters& parameters = - combKalmanResult.fittedParameters.find(tip)->second; + + const BoundTrackParameters& parameters = it->second; track.parameters() = parameters.parameters(); track.covariance() = *parameters.covariance(); track.setReferenceSurface(parameters.referenceSurface().getSharedPtr()); diff --git a/Core/include/Acts/TrackFinding/FasTrackConnector.hpp b/Core/include/Acts/TrackFinding/FasTrackConnector.hpp new file mode 100644 index 00000000000..658dbae9271 --- /dev/null +++ b/Core/include/Acts/TrackFinding/FasTrackConnector.hpp @@ -0,0 +1,53 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +// Consider to moving to detail subdirectory +#include +#include +#include +#include + +namespace Acts { + +struct FasTrackConnection { + public: + FasTrackConnection(unsigned int s, unsigned int d); + + unsigned int m_src, m_dst; + std::vector m_binTable; +}; + +class FasTrackConnector { + public: + struct LayerGroup { + LayerGroup(unsigned int l1Key, + const std::vector &v) + : m_dst(l1Key), m_sources(v) {} + + unsigned int m_dst; // the target layer of the group + std::vector + m_sources; // the source layers of the group + }; + + FasTrackConnector(std::ifstream &inFile); + + ~FasTrackConnector(); + + float m_etaBin{}; + + std::map> m_layerGroups; + std::map> m_connMap; + // TODO: change to std::map > + // m_connMap; or std::map> > m_connMap; +}; + +} // namespace Acts diff --git a/Core/include/Acts/TrackFinding/MeasurementSelector.hpp b/Core/include/Acts/TrackFinding/MeasurementSelector.hpp index 457cdf31fd5..4205a2c0982 100644 --- a/Core/include/Acts/TrackFinding/MeasurementSelector.hpp +++ b/Core/include/Acts/TrackFinding/MeasurementSelector.hpp @@ -19,6 +19,7 @@ #include "Acts/Utilities/Result.hpp" #include "Acts/Utilities/TypeTraits.hpp" +#include #include #include #include @@ -106,60 +107,82 @@ class MeasurementSelector { return CombinatorialKalmanFilterError::MeasurementSelectionFailed; } + assert(!cuts->chi2CutOff.empty()); + const auto& chi2CutOff = cuts->chi2CutOff; + auto maxChi2Cut = *std::max_element(chi2CutOff.begin(), chi2CutOff.end()); double minChi2 = std::numeric_limits::max(); size_t minIndex = 0; - size_t index = 0; - // Loop over all measurements to select the compatible measurements - for (auto& trackState : candidates) { - // Take the parameter covariance - // const auto predicted = tackState.predicted(); - // const auto predictedCovariance = trackState.predictedCovariance(); - - double chi2 = calculateChi2( - // This abuses an incorrectly sized vector / matrix to access the - // data pointer! This works (don't use the matrix as is!), but be - // careful! - trackState - .template calibrated() - .data(), - trackState - .template calibratedCovariance< - MultiTrajectoryTraits::MeasurementSizeMax>() - .data(), - trackState.predicted(), trackState.predictedCovariance(), - trackState.projector(), trackState.calibratedSize()); - - trackState.chi2() = chi2; - - // Search for the measurement with the min chi2 - if (chi2 < minChi2) { - minChi2 = chi2; - minIndex = index; + auto trackStateIterEnd = candidates.end(); + { + auto trackStateIter = candidates.begin(); + // Loop over all measurements to select the compatible measurements + // Sort track states which do not satisfy the chi2 cut to the end. + // When done trackStateIterEnd will point to the first element that + // does not satisfy the chi2 cut. + assert(trackStateIter != trackStateIterEnd); + for (;;) { + double chi2 = calculateChi2( + // This abuses an incorrectly sized vector / matrix to access the + // data pointer! This works (don't use the matrix as is!), but be + // careful! + trackStateIter + ->template calibrated< + MultiTrajectoryTraits::MeasurementSizeMax>() + .data(), + trackStateIter + ->template calibratedCovariance< + MultiTrajectoryTraits::MeasurementSizeMax>() + .data(), + trackStateIter->predicted(), trackStateIter->predictedCovariance(), + trackStateIter->projector(), trackStateIter->calibratedSize()); + + trackStateIter->chi2() = chi2; + + // only consider track states which pass the chi2 cut + if (chi2 >= maxChi2Cut || + chi2 >= VariableCut(*trackStateIter, cuts, chi2CutOff, + logger)) { + --trackStateIterEnd; + // still check whether the element has the smallest chi2 in case an + // outlier is returned. + if (chi2 < minChi2) { + minChi2 = chi2; + // the current element will be swapped with the last unchecked + // element if they are different + minIndex = std::distance(candidates.begin(), trackStateIterEnd); + } + + if (trackStateIter == trackStateIterEnd) { + break; + } else { + // swap rejected element with last element in list + std::swap(*trackStateIter, *trackStateIterEnd); + } + } else { + // Search for the measurement with the min chi2 + // @if there is a track state which passes the cut-off there is + // no need to remember the track state with the smallest chi2. + ++trackStateIter; + if (trackStateIter == trackStateIterEnd) { + break; + } + } } - - index++; } - const auto& chi2CutOff = cuts->chi2CutOff; - - { - // If there is no selected measurement, return the measurement with the - // best chi2 and tag it as an outlier + // If there are no measurements below the chi2 cut off, return the + // measurement with the best chi2 and tag it as an outlier + if (candidates.begin() == trackStateIterEnd) { const auto bestIt = std::next(candidates.begin(), minIndex); - const auto chi2 = bestIt->chi2(); - const auto chi2Cut = - VariableCut(*bestIt, cuts, chi2CutOff, logger); - ACTS_VERBOSE("Chi2: " << chi2 << ", max: " << chi2Cut); - if (chi2 >= chi2Cut) { - ACTS_VERBOSE( - "No measurement candidate. Return an outlier measurement."); - isOutlier = true; - // return single item range, no sorting necessary - return Result::success(std::pair{bestIt, std::next(bestIt, 1)}); - } + ACTS_VERBOSE( + "No measurement candidate. Return an outlier measurement chi2=" + << bestIt->chi2()); + isOutlier = true; + // return single item range, no sorting necessary + return Result::success(std::pair{bestIt, std::next(bestIt, 1)}); } - std::sort(candidates.begin(), candidates.end(), + std::sort(candidates.begin(), trackStateIterEnd, [](const auto& tsa, const auto& tsb) { return tsa.chi2() < tsb.chi2(); }); @@ -168,30 +191,18 @@ class MeasurementSelector { const auto numMeasurementsCut = VariableCut( *candidates.begin(), cuts, cuts->numMeasurementsCutOff, logger); - auto endIterator = candidates.begin(); - auto maxIterator = candidates.end(); - if (candidates.size() > numMeasurementsCut && numMeasurementsCut > 0) { - maxIterator = std::next(candidates.begin(), numMeasurementsCut); + if (static_cast(std::distance( + candidates.begin(), trackStateIterEnd)) > numMeasurementsCut && + numMeasurementsCut > 0) { + trackStateIterEnd = std::next(candidates.begin(), numMeasurementsCut); } - ++endIterator; // best measurement already confirmed good - for (; endIterator != maxIterator; ++endIterator) { - const auto chi2 = endIterator->chi2(); - const auto chi2Cut = - VariableCut(*endIterator, cuts, chi2CutOff, logger); - ACTS_VERBOSE("Chi2: " << chi2 << ", max: " << chi2Cut); - if (chi2 >= chi2Cut) { - break; // endIterator now points at the first track state with chi2 - // larger than our cutoff => defines the end of our returned - // range - } - } ACTS_VERBOSE("Number of selected measurements: " - << std::distance(candidates.begin(), endIterator) + << std::distance(candidates.begin(), trackStateIterEnd) << ", max: " << numMeasurementsCut); isOutlier = false; - return std::pair{candidates.begin(), endIterator}; + return std::pair{candidates.begin(), trackStateIterEnd}; } private: diff --git a/Core/include/Acts/TrackFinding/RoiDescriptor.hpp b/Core/include/Acts/TrackFinding/RoiDescriptor.hpp new file mode 100644 index 00000000000..33f8c5d2ac5 --- /dev/null +++ b/Core/include/Acts/TrackFinding/RoiDescriptor.hpp @@ -0,0 +1,212 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2021 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +// TODO: update to C++17 style +#include +#include +#include +#include +#include + +#include + +namespace Acts { + +class RoiDescriptor { + public: + // iterator + using roi_iterator = std::vector::const_iterator; + /// convenient + static constexpr bool FULLSCAN = true; + static constexpr bool ROI = false; + + /** + * @brief constructor + * @param eta eta of RoI + * @param etaMinus eta at rear of RoI + * @param etaPlus eta at front of RoI + * @param phi phi of RoI + * @param phiMinus minimum phi of RoI + * @param phiPlus maximum phi of RoI + * @param zed zed of RoI + * @param zedMinus zed at rear of RoI + * @param zedPlus zed at front of RoI + */ + RoiDescriptor(double eta, double etaMinus, double etaPlus, double phi, + double phiMinus, double phiPlus, double zed = 0, + double zedMinus = -s_zedWidthDefault, + double zedPlus = s_zedWidthDefault); + // zedminus - s_zedWidthDefault = 225 //from ROIDescriptor + + /* + * need an explicit class copy constructor + */ + RoiDescriptor(const RoiDescriptor& roi); + RoiDescriptor& operator=(const RoiDescriptor& r); + + // Destructor + ~RoiDescriptor(); + + // Methods to retrieve data members + + double phi() const { return m_phi; } + double eta() const { return m_eta; } + double zed() const { return m_zed; } + + /// these quantities probably don't need to be used any more + /// - they are implemented here only because we had them in + /// the original legacy interface + + double zedPlus() const { + return m_zedPlus; + } //!< z at the most forward end of the RoI + double zedMinus() const { + return m_zedMinus; + } //!< z at the most backward end of the RoI + + double etaPlus() const { return m_etaPlus; } //!< gets eta at zedPlus + double etaMinus() const { return m_etaMinus; } //!< gets eta at zMinus + + double phiPlus() const { return m_phiPlus; } //!< gets phiPlus + double phiMinus() const { return m_phiMinus; } //!< gets phiMinus + + /// versioning + int version() const { return m_version; } + void version(int v) { m_version = v; } + + /// output + // virtual operator std::string() const ; + + /// is this a full scan RoI? + bool isFullscan() const { return m_fullscan; } + + /// SuperRoI compatibility methods + + /// am I a SuperRoi? + bool composite() const { return m_composite; } + void setComposite(bool b = true) { m_composite = b; } + + /// always manage constituents ??? + bool manageConstituents() const { return m_manageConstituents; } + void manageConstituents(bool b) { m_manageConstituents = b; } + + /// number of constituents + unsigned size() const { return m_roiDescriptors.size(); } + + /// find an RoiDescriptor constituent + const RoiDescriptor* at(int i) const { return m_roiDescriptors.at(i); } + + /// clear the vector + void clear() { m_roiDescriptors.clear(); } // setComposite(false); } + + /// reserve elements in vector + void reserve(size_t s) { m_roiDescriptors.reserve(s); } + + /// add a RoiDescriptor + void push_back(const RoiDescriptor* roi) { + m_roiDescriptors.push_back(roi); + setComposite(true); + } + + /// iterators + roi_iterator begin() const { return m_roiDescriptors.begin(); } + roi_iterator end() const { return m_roiDescriptors.end(); } + + /// return the gradients + double dzdrMinus() const { + return m_dzdrMinus; + } //!< dz/dr at the rear of the RoI + double dzdrPlus() const { + return m_dzdrPlus; + } //!< dz/dr at the front of the RoI + + double drdzMinus() const { + return m_drdzMinus; + } //!< dr/dz at the rear of the RoI + double drdzPlus() const { + return m_drdzPlus; + } //!< dr/dz at the front of the RoI + + /// methods to calculate z position at the RoI boundary + /// at a given radius + double zedMin(double r) const; + double zedMax(double r) const; + + double zedOuterPlus() const { + return m_zedOuterPlus; + } //!< z at the most forward end of the RoI + double zedOuterMinus() const { + return m_zedOuterMinus; + } //!< z at the most backward end of the RoI + + double rhoMin(double z) const; + double rhoMax(double z) const; + + static double zedWidthDefault() { return s_zedWidthDefault; } + + /// set default z-width (but only before any RoiDescriptor has been created) + static void zedWidthDefault(double d); + + // fromn trig + unsigned int roiId() const { return m_roiId; } + unsigned int l1Id() const { return m_l1Id; } + unsigned int roiWord() const { return m_roiWord; } + + private: + /// default parameters - there may be better ways, but this will do + static std::atomic s_zedWidthDefault; + /// to ensure default width is only set once at job startup + static std::atomic s_firstInstanceCreated; + + float m_phi{}; //!< phi of RoI center + float m_eta{}; //!< eta of RoI center + float m_zed{}; //!< zed of RoI center + + float m_phiMinus{}; //!< most negative RoI in azimuthal + float m_phiPlus{}; //!< most positive RoI in azimuthal + float m_etaMinus{}; //!< eta of RoI at zedMinus + float m_etaPlus{}; //!< eta of RoI at zedPlus + float m_zedMinus{}; //!< z position at most negative position along the + //!< beamline + float + m_zedPlus{}; //!< z position at most positive position along the beamline + + float m_dzdrMinus{}; //!< dz/dr at the rear of the RoI + float m_dzdrPlus{}; //!< dz/dr at the front of the RoI + + float m_drdzMinus{}; //!< dr/dz at the rear of the RoI + float m_drdzPlus{}; //!< dr/dz at the front of the RoI + + float + m_zedOuterMinus{}; //!< z at rear of RoI at the outer radius ( = 1100 mm) + float + m_zedOuterPlus{}; //!< z at front of RoI at the outer radius ( = 1100 mm) + + bool m_fullscan{}; //!< flag this as a full detector RoI + bool m_composite{}; //!< flag this as a composite RoI + bool m_manageConstituents{}; //!< flag to determine whether constituents + //!< should be managed + + int m_version{}; //!< transient version identifier + + std::vector m_roiDescriptors; //!< roi constituents + + // from trig + unsigned int m_l1Id{0}; //!< lvl1 event number + unsigned int m_roiId{0}; //!< RoI number + unsigned int m_roiWord{0}; //!< lvl1 RoI word from which this RoI was + //!< initially constructed + + // std::string str( const RoiDescriptor& d ); // cutSets = {}; + std::vector cutSets = {}; /// Eta bin edges for varying cuts by eta std::vector absEtaEdges = {}; @@ -114,43 +114,44 @@ class TrackSelector { /// Construct an empty (accepts everything) configuration. /// Results in a single cut set and one abs eta bin from 0 to infinity. - Config() : cutSets{{}}, absEtaEdges{{0, inf}} {}; + EtaBinnedConfig() : cutSets{{}}, absEtaEdges{{0, inf}} {}; /// Constructor to create a config object that is not upper-bounded. /// This is useful to use the "fluent" API to populate the configuration. /// @param etaMin Minimum eta bin edge - Config(double etaMin) : cutSets{}, absEtaEdges{etaMin} {} + EtaBinnedConfig(double etaMin) : cutSets{}, absEtaEdges{etaMin} {} /// Constructor from a vector of eta bin edges. This automatically /// initializes all the cuts to be the same for all eta and be essentially /// no-op. /// @param absEtaEdgesIn is the vector of eta bin edges - Config(std::vector absEtaEdgesIn) + EtaBinnedConfig(std::vector absEtaEdgesIn) : absEtaEdges{std::move(absEtaEdgesIn)} { cutSets.resize(absEtaEdges.size() - 1); } /// Auto-converting constructor from a single cut configuration. /// Results in a single absolute eta bin from 0 to infinity. - Config(CutConfig cutSet) : cutSets{cutSet}, absEtaEdges{{0, inf}} {} + EtaBinnedConfig(Config cutSet) : cutSets{cutSet}, absEtaEdges{{0, inf}} {} /// Add a new eta bin with the given upper bound. /// @param etaMax Upper bound of the new eta bin /// @param callback Callback to configure the cuts for this eta bin /// @return Reference to this object - Config& addCuts(double etaMax, - const std::function& callback = {}); + EtaBinnedConfig& addCuts(double etaMax, + const std::function& callback = {}); /// Add a new eta bin with an upper bound of +infinity. /// @param callback Callback to configure the cuts for this eta bin /// @return Reference to this object - Config& addCuts(const std::function& callback = {}); + EtaBinnedConfig& addCuts(const std::function& callback = {}); /// Print this configuration to an output stream /// @param os Output stream /// @param cfg Configuration to print /// @return Reference to the output stream - friend std::ostream& operator<<(std::ostream& os, const Config& cfg); + friend std::ostream& operator<<(std::ostream& os, + const EtaBinnedConfig& cfg); /// Get the index of the eta bin for a given eta /// @param eta Eta value @@ -160,13 +161,17 @@ class TrackSelector { /// Get the cuts for a given eta /// @param eta Eta value /// @return Cuts for the given eta - const CutConfig& getCuts(double eta) const; + const Config& getCuts(double eta) const; }; - /// Constructor from a config object + /// Constructor from a single cut config object /// @param config is the configuration object TrackSelector(const Config& config); + /// Constructor from a multi-eta + /// @param config is the configuration object + TrackSelector(const EtaBinnedConfig& config); + /// Select tracks from an input container and copy them into an output /// container /// @tparam input_tracks_t is the type of the input track container @@ -186,42 +191,44 @@ class TrackSelector { /// Get readonly access to the config parameters /// @return the config object - const Config& config() const { return m_cfg; } + const EtaBinnedConfig& config() const { return m_cfg; } private: - Config m_cfg; + EtaBinnedConfig m_cfg; + bool m_isUnbinned; + bool m_noEtaCuts; }; -inline TrackSelector::CutConfig& TrackSelector::CutConfig::loc0(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::loc0(double min, + double max) { loc0Min = min; loc0Max = max; return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::loc1(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::loc1(double min, + double max) { loc1Min = min; loc1Max = max; return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::time(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::time(double min, + double max) { timeMin = min; timeMax = max; return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::phi(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::phi(double min, + double max) { phiMin = min; phiMax = max; return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::eta(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::eta(double min, + double max) { if (absEtaMin != 0.0 || absEtaMax != inf) { throw std::invalid_argument( "Cannot set both eta and absEta cuts in the same cut set"); @@ -231,8 +238,8 @@ inline TrackSelector::CutConfig& TrackSelector::CutConfig::eta(double min, return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::absEta(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::absEta(double min, + double max) { if (etaMin != -inf || etaMax != inf) { throw std::invalid_argument( "Cannot set both eta and absEta cuts in the same cut set"); @@ -242,15 +249,15 @@ inline TrackSelector::CutConfig& TrackSelector::CutConfig::absEta(double min, return *this; } -inline TrackSelector::CutConfig& TrackSelector::CutConfig::pt(double min, - double max) { +inline TrackSelector::Config& TrackSelector::Config::pt(double min, + double max) { ptMin = min; ptMax = max; return *this; } inline std::ostream& operator<<(std::ostream& os, - const TrackSelector::CutConfig& cuts) { + const TrackSelector::Config& cuts) { auto print = [&](const char* name, const auto& min, const auto& max) { os << " - " << min << " <= " << name << " < " << max << "\n"; }; @@ -267,8 +274,8 @@ inline std::ostream& operator<<(std::ostream& os, return os; } -inline TrackSelector::Config& TrackSelector::Config::addCuts( - double etaMax, const std::function& callback) { +inline TrackSelector::EtaBinnedConfig& TrackSelector::EtaBinnedConfig::addCuts( + double etaMax, const std::function& callback) { if (etaMax <= absEtaEdges.back()) { throw std::invalid_argument{ "Abs Eta bin edges must be in increasing order"}; @@ -286,12 +293,12 @@ inline TrackSelector::Config& TrackSelector::Config::addCuts( return *this; } -inline TrackSelector::Config& TrackSelector::Config::addCuts( - const std::function& callback) { +inline TrackSelector::EtaBinnedConfig& TrackSelector::EtaBinnedConfig::addCuts( + const std::function& callback) { return addCuts(inf, callback); } -inline std::size_t TrackSelector::Config::binIndex(double eta) const { +inline std::size_t TrackSelector::EtaBinnedConfig::binIndex(double eta) const { if (std::abs(eta) >= absEtaEdges.back()) { throw std::invalid_argument{"Eta is outside the abs eta bin edges"}; } @@ -302,14 +309,14 @@ inline std::size_t TrackSelector::Config::binIndex(double eta) const { return index; } -inline const TrackSelector::CutConfig& TrackSelector::Config::getCuts( +inline const TrackSelector::Config& TrackSelector::EtaBinnedConfig::getCuts( double eta) const { return nEtaBins() == 1 ? cutSets[0] : cutSets[binIndex(eta)]; } inline std::ostream& operator<<(std::ostream& os, - const TrackSelector::Config& cfg) { - os << "TrackSelector::Config:\n"; + const TrackSelector::EtaBinnedConfig& cfg) { + os << "TrackSelector::EtaBinnedConfig:\n"; for (std::size_t i = 1; i < cfg.absEtaEdges.size(); i++) { os << cfg.absEtaEdges[i - 1] << " <= eta < " << cfg.absEtaEdges[i] << "\n"; @@ -340,19 +347,36 @@ bool TrackSelector::isValidTrack(const track_proxy_t& track) const { }; const auto theta = track.theta(); - const auto eta = -std::log(std::tan(theta / 2)); - const auto absEta = std::abs(eta); - if (absEta < m_cfg.absEtaEdges.front() || - absEta >= m_cfg.absEtaEdges.back()) { - return false; + constexpr double kUnset = -std::numeric_limits::infinity(); + + double _eta = kUnset; + double _absEta = kUnset; + + auto absEta = [&]() { + if (_absEta == kUnset) { + _eta = -std::log(std::tan(theta / 2)); + _absEta = std::abs(_eta); + } + return _absEta; + }; + + const Config* cutsPtr{nullptr}; + if (!m_isUnbinned) { + if (absEta() < m_cfg.absEtaEdges.front() || + _absEta >= m_cfg.absEtaEdges.back()) { + return false; + } + cutsPtr = &m_cfg.getCuts(_eta); + } else { + cutsPtr = &m_cfg.cutSets.front(); } - const CutConfig& cuts = m_cfg.getCuts(eta); + const Config& cuts = *cutsPtr; return within(track.transverseMomentum(), cuts.ptMin, cuts.ptMax) and - within(std::abs(eta), cuts.absEtaMin, cuts.absEtaMax) and - within(eta, cuts.etaMin, cuts.etaMax) and + (m_noEtaCuts || (within(absEta(), cuts.absEtaMin, cuts.absEtaMax) and + within(_eta, cuts.etaMin, cuts.etaMax))) and within(track.phi(), cuts.phiMin, cuts.phiMax) and within(track.loc0(), cuts.loc0Min, cuts.loc0Max) and within(track.loc1(), cuts.loc1Min, cuts.loc1Max) and @@ -360,18 +384,21 @@ bool TrackSelector::isValidTrack(const track_proxy_t& track) const { checkMin(track.nMeasurements(), cuts.minMeasurements); } -inline TrackSelector::TrackSelector(const TrackSelector::Config& config) +inline TrackSelector::TrackSelector( + const TrackSelector::EtaBinnedConfig& config) : m_cfg(config) { if (m_cfg.cutSets.size() != m_cfg.nEtaBins()) { throw std::invalid_argument{ "TrackSelector cut / eta bin configuration is inconsistent"}; } + m_isUnbinned = false; if (m_cfg.nEtaBins() == 1) { static const std::vector infVec = {0, inf}; bool limitEta = m_cfg.absEtaEdges != infVec; + m_isUnbinned = !limitEta; // single bin, no eta edges given - const CutConfig& cuts = m_cfg.cutSets[0]; + const Config& cuts = m_cfg.cutSets[0]; if (limitEta && (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 || cuts.absEtaMax != inf)) { @@ -379,6 +406,17 @@ inline TrackSelector::TrackSelector(const TrackSelector::Config& config) "Explicit eta cuts are only valid for single eta bin"}; } } + + m_noEtaCuts = m_isUnbinned; + for (const auto& cuts : m_cfg.cutSets) { + if (cuts.etaMin != -inf || cuts.etaMax != inf || cuts.absEtaMin != 0.0 || + cuts.absEtaMax != inf) { + m_noEtaCuts = false; + } + } } +inline TrackSelector::TrackSelector(const Config& config) + : TrackSelector{EtaBinnedConfig{config}} {} + } // namespace Acts diff --git a/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp b/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp index a6b773f0ccd..5985f37a6a3 100644 --- a/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp +++ b/Core/include/Acts/TrackFinding/detail/AmbiguityTrackClustering.hpp @@ -18,7 +18,7 @@ namespace detail { /// Clusterise tracks based on shared hits /// -/// @param trackMap : Multimap storing pair of track ID and vector of measurement ID. The keys are the number of measurement and are just there to focilitate the ordering. +/// @param trackMap : Multimap storing pair of track ID and vector of measurement ID. The keys are the number of measurement and are just there to facilitate the ordering. /// @return an unordered map representing the clusters, the keys the ID of the primary track of each cluster and the store a vector of track IDs. std::unordered_map> clusterDuplicateTracks( const std::multimap>>& trackMap); diff --git a/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp b/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp index 5138917b271..66579bb9905 100644 --- a/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp +++ b/Core/include/Acts/TrackFitting/BetheHeitlerApprox.hpp @@ -63,8 +63,6 @@ inline auto inverseTransformComponent(double transformed_weight, } // namespace detail -namespace Experimental { - /// This class approximates the Bethe-Heitler with only one component. This is /// mainly inside @ref AtlasBetheHeitlerApprox, but can also be used as the /// only component approximation (then probably for debugging) @@ -248,8 +246,8 @@ class AtlasBetheHeitlerApprox { } if (PolyDegree != degree) { - throw std::invalid_argument("Wrong wrong polynom order in '" + - filepath + "'"); + throw std::invalid_argument("Wrong polynom order in '" + filepath + + "'"); } Data data; @@ -283,5 +281,4 @@ class AtlasBetheHeitlerApprox { /// the GSF without the need to load files AtlasBetheHeitlerApprox<6, 5> makeDefaultBetheHeitlerApprox(); -} // namespace Experimental } // namespace Acts diff --git a/Core/include/Acts/TrackFitting/GainMatrixSmoother.hpp b/Core/include/Acts/TrackFitting/GainMatrixSmoother.hpp index 37af8839860..3119150641a 100644 --- a/Core/include/Acts/TrackFitting/GainMatrixSmoother.hpp +++ b/Core/include/Acts/TrackFitting/GainMatrixSmoother.hpp @@ -101,15 +101,15 @@ class GainMatrixSmoother { // covariances. assert(ts.hasFiltered()); assert(ts.hasPredicted()); - assert(ts.hasJacobian()); // previous trackstate should have smoothed and predicted assert(prev_ts.hasSmoothed()); assert(prev_ts.hasPredicted()); + assert(prev_ts.hasJacobian()); ACTS_VERBOSE("Calculate smoothing matrix:"); ACTS_VERBOSE("Filtered covariance:\n" << ts.filteredCovariance()); - ACTS_VERBOSE("Jacobian:\n" << ts.jacobian()); + ACTS_VERBOSE("Jacobian:\n" << prev_ts.jacobian()); if (auto res = calculate(&ts, &prev_ts, filtered, filteredCovariance, smoothed, predicted, predictedCovariance, diff --git a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp index 8e905a32167..66c20af191a 100644 --- a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp +++ b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp @@ -28,23 +28,14 @@ namespace detail { /// to inspect its charge representation if not TODO this probably gives an ugly /// error message if detectCharge does not compile template -struct IsMultiComponentBoundParameters : public std::false_type { - template