diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 2a759922379..98ac1d35337 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/acts-project/ubuntu2204:v41 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache build uses: actions/cache@v3 @@ -76,7 +76,7 @@ jobs: container: ghcr.io/acts-project/ubuntu2204:v41 if: github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: pip3 install git+https://github.com/paulgessinger/cmakeperf.git@ece8fc8 - name: Configure @@ -91,7 +91,7 @@ jobs: run: cmakeperf collect build/compile_commands.json -o perf.csv - name: Results run: cmakeperf print perf.csv - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: cmakeperf path: perf.csv @@ -101,10 +101,10 @@ jobs: needs: build_performance if: github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: pip3 install git+https://github.com/paulgessinger/headwind.git@eeeaa80 - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: cmakeperf - name: Run collection diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index a959fd090e4..30616ac45b1 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -32,13 +32,13 @@ jobs: - name: Install git lfs run: apt-get update && apt-get install -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true lfs: true - name: Restore ccache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: ccache-restore with: path: ${{ github.workspace }}/ccache @@ -76,7 +76,7 @@ jobs: run: ccache -s - name: Save ccache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: always() with: path: ${{ github.workspace }}/ccache @@ -95,7 +95,7 @@ jobs: - name: Package build run: tar czf build.tar.gz -C build --exclude "*.o" --exclude "bin/ActsUnitTest*" --exclude "bin/ActsIntegrationTest*" . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: acts-linux-ubuntu path: build.tar.gz @@ -126,12 +126,12 @@ jobs: - name: Install git lfs run: apt-get update && apt-get install -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true lfs: true - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: acts-linux-ubuntu @@ -163,12 +163,12 @@ jobs: - name: Install git lfs run: apt-get update && apt-get install -y git-lfs time - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true lfs: true - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: acts-linux-ubuntu @@ -195,7 +195,7 @@ jobs: && CI/physmon/phys_perf_mon.sh all physmon && cat physmon/summary.md >> $GITHUB_STEP_SUMMARY - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: physmon @@ -209,7 +209,7 @@ jobs: - name: Install dependencies run: pip3 install spyral-cli==1.1.0 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: physmon path: physmon @@ -248,13 +248,13 @@ jobs: - name: Install git lfs run: apt-get update && apt-get install -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true lfs: true - name: Restore ccache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: ccache-restore with: path: ${{ github.workspace }}/ccache @@ -292,7 +292,7 @@ jobs: run: ccache -s - name: Save ccache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: always() with: path: ${{ github.workspace }}/ccache @@ -342,7 +342,7 @@ jobs: PRELOAD: export LD_PRELOAD=/opt/lcg/gcc/10/x86_64-centos8/lib64/libstdc++.so.6 INSTALL_DIR: ${{ github.workspace }}/install steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies # Install tbb-devel also to build the examples run: > @@ -350,7 +350,7 @@ jobs: && ln -s $(find / -type f -name 'ccache') /usr/local/bin/ccache - name: Restore ccache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: ccache-restore with: path: ${{ github.workspace }}/ccache @@ -381,7 +381,7 @@ jobs: - name: ccache stats run: ccache -s - name: Save ccache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: always() with: path: ${{ github.workspace }}/ccache @@ -435,7 +435,7 @@ jobs: env: INSTALL_DIR: ${{ github.workspace }}/install steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true lfs: true @@ -453,7 +453,7 @@ jobs: && /usr/local/acts/bin/python3.10 -m pip install pyyaml jinja2 - name: Restore ccache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: ccache-restore with: path: ${{ github.workspace }}/ccache @@ -488,7 +488,7 @@ jobs: - name: ccache stats run: ccache -s - name: Save ccache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: always() with: path: ${{ github.workspace }}/ccache @@ -499,7 +499,7 @@ jobs: run: cmake --build build --target integrationtests - name: Install run: cmake --build build --target install - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: acts-macos path: ${{ env.INSTALL_DIR }} @@ -516,42 +516,6 @@ jobs: - name: Downstream run run: ./build-downstream/bin/ShowActsVersion - cuda: - runs-on: ubuntu-latest - container: ghcr.io/acts-project/ubuntu1804_cuda:v41 - 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-cuda_${{ env.CCACHE_KEY_SUFFIX }}_${{ github.sha }} - restore-keys: | - ${{ runner.os }}-ccache-cuda_${{ env.CCACHE_KEY_SUFFIX }}_ - - - name: Configure - run: > - ccache -z && - cmake -B build -S . - -GNinja - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - -DCMAKE_CXX_COMPILER=/usr/bin/g++-8 - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_CXX_FLAGS=-Werror - -DACTS_BUILD_PLUGIN_CUDA=ON - -DACTS_BUILD_UNITTESTS=ON - - name: Build - run: cmake --build build - - name: ccache stats - run: 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 }} sycl: runs-on: ubuntu-latest @@ -560,10 +524,10 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore ccache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: ccache-restore with: path: ${{ github.workspace }}/ccache @@ -591,7 +555,7 @@ jobs: - name: ccache stats run: ccache -s - name: Save ccache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 if: always() with: path: ${{ github.workspace }}/ccache diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d863cf94965..bbf97f05ba3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -17,12 +17,12 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/acts-project/format14:v41 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check run: > git config --global safe.directory "$GITHUB_WORKSPACE" && CI/check_format . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: changed @@ -30,7 +30,7 @@ jobs: format-py: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -42,7 +42,7 @@ jobs: license: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -53,7 +53,7 @@ jobs: include_guards: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -63,14 +63,14 @@ jobs: pragma_once: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check run: > CI/check_pragma_once.sh end_of_line: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -80,7 +80,7 @@ jobs: size_t: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -90,14 +90,14 @@ jobs: boost_test_macro: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check run: > CI/check_boost_test_macro.sh smearing_config: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -107,7 +107,7 @@ jobs: cmake_options: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -117,7 +117,7 @@ jobs: spelling: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -130,7 +130,7 @@ jobs: missing_includes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install clang run: > sudo apt-get install -y clang libeigen3-dev libboost-dev @@ -140,7 +140,7 @@ jobs: fpe_masks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -153,7 +153,7 @@ jobs: unused_files: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.12' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0b89aee927c..37b447d3020 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: DOXYGEN_WARN_AS_ERROR: FAIL_ON_WARNINGS DOXYGEN_VERSION: 1.9.8 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache doxygen id: cache-doxygen @@ -54,7 +54,8 @@ jobs: -b linkcheck . _build/html/ - - uses: actions/upload-artifact@v3 + + - uses: actions/upload-artifact@v4 with: name: acts-docs path: docs/_build/html/ diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index 0ebcb14bcaa..f89b3d6c092 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -18,7 +18,7 @@ jobs: run: apt-get update && apt-get install -y git-lfs llvm-dev libclang-dev clang - name: Get IWYU source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: include-what-you-use/include-what-you-use ref: clang_14 @@ -33,7 +33,7 @@ jobs: cmake --build iwyu-build --target install - name: Get Acts source - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: acts submodules: true @@ -62,7 +62,7 @@ jobs: run: python3 iwyu-install/bin/fix_includes.py < iwyu-filtered.txt - name: Upload IWYU output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: iwyu path: | diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 528fe8c4c0c..83069bf636b 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/labeler@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr_commands.yml b/.github/workflows/pr_commands.yml deleted file mode 100644 index 8a5bfa08611..00000000000 --- a/.github/workflows/pr_commands.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: PR comment ops -on: issue_comment - -jobs: - pr_commented: - # This job only runs for pull request comments - name: PR comment - if: "${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/') }}" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install prerequisites - run: pip install -r CI/commands/requirements.txt - - name: Run command - env: - GITLAB_TRIGGER_TOKEN: ${{ secrets.GITLAB_ATHENA_BUILD_TRIGGER_TOKEN}} - GITLAB_TRIGGER_URL: https://gitlab.cern.ch/api/v4/projects/153873/trigger/pipeline - GITHUB_TOKEN: ${{ secrets.PR_COMMANDS_GH_TOKEN }} - run: | - echo "${{ github.event.comment.body }}" > body.txt - cat body.txt - CI/commands/pr_commands.py \ - --pr ${{ github.event.issue.pull_request.url }} \ - --body body.txt \ - --sender ${{ github.event.comment.user.login }} \ - --repository ${{ github.event.repository.full_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0fa7d6b3ef5..b2b4b01a3ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v4 diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml deleted file mode 100644 index 441ddd7c6dd..00000000000 --- a/.github/workflows/release_pr.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Release PR - -on: - pull_request: - -jobs: - process_release_pr: - runs-on: ubuntu-latest - if: >- - ( - (github.base_ref == 'releases' || startsWith(github.base_ref, 'release/')) - && github.event.pull_request.head.repo.full_name == github.repository - ) - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - name: Install dependencies - run: pip install -r CI/release_requirements.txt - - name: Run release script - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: CI/release.py pr-action --fail diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 7f98d9ef693..9a65a702a7e 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -10,7 +10,7 @@ jobs: post_comment: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Download artifact' uses: actions/github-script@v6 @@ -40,7 +40,7 @@ jobs: }); let fs = require('fs'); fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/physmon.zip`, Buffer.from(download.data)); - let = url = `https://herald.dokku.paulgessinger.com/view/${process.env.GITHUB_REPOSITORY}/${matchArtifact.id}`; + let = url = `https://acts-herald.app.cern.ch/view/${process.env.GITHUB_REPOSITORY}/${matchArtifact.id}`; core.exportVariable('ARTIFACT_URL', url) return true; diff --git a/CI/commands/pr_commands.py b/CI/commands/pr_commands.py deleted file mode 100755 index eaee61cbe2f..00000000000 --- a/CI/commands/pr_commands.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python3 -from dataclasses import dataclass -from typing import List, Dict, Any -from pathlib import Path -import shlex -import asyncio -import functools -import os -import click - -import typer -import gidgethub -from gidgethub.aiohttp import GitHubAPI -import aiohttp - - -def wrap_async(fn): - @functools.wraps(fn) - def wrapper(*args, **kwargs): - return asyncio.run(fn(*args, **kwargs)) - - return wrapper - - -class CommandError(Exception): - pass - - -@dataclass -class Context: - pr: Dict[str, Any] - sender: str - github_token: str - - -@click.group() -def app(): - pass - - -@app.group() -def run_experiment(): - pass - - -@run_experiment.command() -@click.option("--revert-sha", "-r", multiple=True) -@click.pass_obj -@wrap_async -async def atlas(ctx: Context, revert_sha: List[str]): - gitlab_trigger_token = os.environ["GITLAB_TRIGGER_TOKEN"] - gitlab_trigger_url = os.environ["GITLAB_TRIGGER_URL"] - async with aiohttp.ClientSession() as session: - gh = GitHubAPI(session, "acts-commands", oauth_token=ctx.github_token) - - pr = ctx.pr - - head_clone_url = pr["head"]["repo"]["clone_url"] - head_branch = pr["head"]["ref"] - head_sha = pr["head"]["sha"] - - variable_summary = f""" -| Variable | Value | -|------|------| -| `ACTS_GIT_REPO` | {head_clone_url} | -| `ACTS_REF` | `{head_branch}` | -| `SOURCE_SHA` | {head_sha} | -| `REVERT_SHAS` | {",".join(revert_sha)} | - """ - - body = f""" -@{ctx.sender} -🟡 I'm going to trigger an ATLAS experiment pipeline for you: - -{variable_summary} - """ - comment = await gh.post(pr["comments_url"], data={"body": body}) - - variables = { - "ACTS_GIT_REPO": head_clone_url, - "ACTS_REF": head_branch, - "SOURCE_SHA": head_sha, - "PR_URL": pr["url"], - "REVERT_SHAS": ",".join(revert_sha), - "REPORT_COMMENT_URL": comment["url"], - } - data = { - "token": gitlab_trigger_token, - "ref": "main", - **{f"variables[{k}]": v for k, v in variables.items()}, - } - print(gitlab_trigger_url) - print(data) - async with session.post( - url=gitlab_trigger_url, - data=data, - ) as resp: - if resp.status != 201: - body = f""" -@{ctx.sender} -🔴 I'm sorry, I couldn't run your command because of an error: -``` -{await resp.text()} -``` -{variable_summary} - """ - await gh.post(comment["url"], data={"body": body}) - - return - - data = await resp.json() - pipeline_url = data["web_url"] - - body = f""" -@{ctx.sender} -🟡 I triggered an ATLAS experiment [pipeline]({pipeline_url}) for you - -{variable_summary} - """ - await gh.post(comment["url"], data={"body": body}) - - -async def get_author_in_team(gh: GitHubAPI, author: str, allow_team: str) -> bool: - allow_org, allow_team = allow_team.split("/", 1) - - try: - membership = await gh.getitem( - f"/orgs/{allow_org}/teams/{allow_team}/memberships/{author}" - ) - return True - except gidgethub.BadRequest as e: - if e.status_code != 404: - raise e - - return False - - -async def preflight( - token: str, pr_url: str, sender: str, repository: str, allow_team: str -): - async with aiohttp.ClientSession() as session: - gh = GitHubAPI(session, "acts-commands", oauth_token=token) - - if not await get_author_in_team(gh, sender, allow_team): - raise RuntimeError(f"{sender} is not in {allow_team}") - - return await gh.getitem(pr_url) - - -async def report_error(token: str, pr: Dict[str, Any], sender: str, error: Exception): - async with aiohttp.ClientSession() as session: - gh = GitHubAPI(session, "acts-commands", oauth_token=token) - - body = f""" -@{sender} -🔴 I'm sorry, I couldn't run your command because of an error: -``` -{error} -``` -""" - await gh.post(pr["comments_url"], data={"body": body}) - - -def main( - pr: str = typer.Option(), - body: str = typer.Option(), - sender: str = typer.Option(), - repository: str = typer.Option(), - allow_team: str = typer.Option("acts-project/ci-perms", envvar="ALLOW_TEAM"), -): - if Path(body).exists(): - body = Path(body).read_text().strip() - - if len(body.split("\n")) > 1: - raise typer.BadParameter("Body must be a single line") - - if not body.startswith("/"): - raise typer.BadParameter("Body must start with a slash") - body = body[1:] - - args = shlex.split(body) - - token = os.environ["GITHUB_TOKEN"] - pr = asyncio.run(preflight(token, pr, sender, repository, allow_team)) - - try: - app( - args, - obj=Context(pr=pr, github_token=token, sender=sender), - standalone_mode=False, - ) - except (CommandError, click.exceptions.ClickException) as e: - asyncio.run(report_error(token, pr, sender, e)) - - -typer.run(main) diff --git a/CI/commands/requirements.in b/CI/commands/requirements.in deleted file mode 100644 index 9ed565536ec..00000000000 --- a/CI/commands/requirements.in +++ /dev/null @@ -1,3 +0,0 @@ -typer -aiohttp -gidgethub diff --git a/CI/commands/requirements.txt b/CI/commands/requirements.txt deleted file mode 100644 index 138e72b7585..00000000000 --- a/CI/commands/requirements.txt +++ /dev/null @@ -1,42 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile CI/commands/requirements.in -# -aiohttp==3.9.1 - # via -r CI/commands/requirements.in -aiosignal==1.3.1 - # via aiohttp -attrs==23.1.0 - # via aiohttp -cffi==1.16.0 - # via cryptography -click==8.1.7 - # via typer -cryptography==41.0.7 - # via pyjwt -frozenlist==1.4.0 - # via - # aiohttp - # aiosignal -gidgethub==5.3.0 - # via -r CI/commands/requirements.in -idna==3.6 - # via yarl -multidict==6.0.4 - # via - # aiohttp - # yarl -pycparser==2.21 - # via cffi -pyjwt[crypto]==2.8.0 - # via gidgethub -typer==0.9.0 - # via -r CI/commands/requirements.in -typing-extensions==4.8.0 - # via typer -uritemplate==4.1.1 - # via gidgethub -yarl==1.9.3 - # via aiohttp diff --git a/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root b/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root index 5123284b384..4f155712b7f 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 fc15832a748..9d60126f985 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 848d689c1b5..3920f632ab4 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 4a789e731b9..2d4c4767602 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 c3c80166596..7213c5f0c57 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 26e4868ccb7..e34429db039 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 165ba4c25af..486a8ecc008 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_ivf_orthogonal_hist.root b/CI/physmon/reference/performance_ivf_orthogonal_hist.root index 8ff2cf1f337..6aa322e1109 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 53b750615a8..f49926eb52b 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 96f5e933170..a3fdad08abe 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 04266228b60..253403afc7d 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/tracksummary_ckf_orthogonal_hist.root b/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root index f5e1bad1484..0642d53977e 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 b20c5dbc64e..c52e8b4e648 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 e69beae805c..3a8b9758304 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 6442decace7..4cd364270c8 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 index 6c9029bd6ea..2decf8ad676 100644 Binary files a/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root and b/CI/physmon/reference/tracksummary_ckf_ttbar_hist.root differ diff --git a/CI/release.py b/CI/release.py index bff1d5120a7..9fa8552876c 100755 --- a/CI/release.py +++ b/CI/release.py @@ -46,12 +46,6 @@ def get_repo(): return repo -def get_current_version(): - raw = git.describe().split("-")[0] - m = re.match(r"v(\d+\.\d+\.\d+)", raw) - return m.group(1) - - class Commit: sha: str message: str @@ -390,129 +384,5 @@ async def get_release(tag: str, repo: str, gh: GitHubAPI): return existing_release -@app.command() -@make_sync -async def pr_action( - fail: bool = False, - pr: int = None, - token: Optional[str] = typer.Option(None, envvar="GH_TOKEN"), - repo: Optional[str] = typer.Option(None, envvar="GH_REPO"), -): - print("::group::Information") - - context = os.environ.get("GITHUB_CONTEXT") - - if context is not None: - context = json.loads(context) - repo = context["repository"] - token = context["token"] - else: - if token is None or repo is None: - raise ValueError("No context, need token and repo") - if pr is None: - raise ValueError("No context, need explicit PR to run on") - - async with aiohttp.ClientSession(loop=asyncio.get_event_loop()) as session: - gh = GitHubAPI(session, __name__, oauth_token=token) - - if pr is not None: - pr = await gh.getitem(f"repos/{repo}/pulls/{pr}") - else: - pr = context["event"]["pull_request"] - - target_branch = pr["base"]["ref"] - print("Target branch:", target_branch) - sha = pr["head"]["sha"] - print("Source hash:", sha) - - merge_commit_sha = await get_merge_commit_sha( - pr["number"], - repo, - gh, - ) - print("Merge commit sha:", merge_commit_sha) - - # Get current version from target branch - current_version = await get_release_branch_version(repo, target_branch, gh) - tag_hash = await get_tag_hash(f"v{current_version}", repo, gh) - print("current_version:", current_version, "[" + tag_hash[:8] + "]") - - commits, unparsed_commits = await get_parsed_commit_range( - start=merge_commit_sha, end=tag_hash, repo=repo, gh=gh - ) - - bump = evaluate_version_bump(commits) - print("bump:", bump) - current_version_obj = Version - next_version = get_new_version(current_version, bump) - print("next version:", next_version) - next_tag = f"v{next_version}" - - print("::endgroup::") - - changes = generate_changelog(commits) - md = markdown_changelog(next_version, changes, header=False) - - body = "" - title = f"Release: {current_version} -> {next_version}" - - existing_release = await get_release(next_tag, repo, gh) - existing_tag = await get_tag(next_tag, repo, gh) - - body += f"# `v{current_version}` -> `v{next_version}`\n" - - exit_code = 0 - - if existing_release is not None or existing_tag is not None: - if current_version == next_version: - body += ( - "## :no_entry_sign: Merging this will not result in a new version (no `fix`, " - "`feat` or breaking changes). I recommend **delaying** this PR until more changes accumulate.\n" - ) - print("::warning::Merging this will not result in a new version") - - else: - exit_code = 1 - title = f":no_entry_sign: {title}" - if existing_release is not None: - body += f"## :warning: **WARNING**: A release for '{next_tag}' already exists" - body += f"[here]({existing_release['html_url']})** :warning:" - print(f"::error::A release for tag '{next_tag}' already exists") - else: - body += ( - f"## :warning: **WARNING**: A tag '{next_tag}' already exists" - ) - print(f"::error::A tag '{next_tag}' already exists") - - body += "\n" - body += ":no_entry_sign: I recommend to **NOT** merge this and double check the target branch!\n\n" - - else: - body += f"## Merging this PR will create a new release `v{next_version}`\n" - - if len(unparsed_commits) > 0: - body += "\n" * 3 - body += "## :warning: This PR contains commits which are not parseable:" - for commit in unparsed_commits: - msg, _ = commit.message.split("\n", 1) - body += f"\n - {msg} {commit.sha})" - body += "\n **Make sure these commits do not contain changes which affect the bump version!**" - - body += "\n\n" - - body += "### Changelog" - - body += md - - print("::group::PR message") - print(body) - print("::endgroup::") - - await gh.post(pr["url"], data={"body": body, "title": title}) - - if fail: - sys.exit(exit_code) - - if __name__ == "__main__": app() diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index 43cfd70832f..9a3e2f70652 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -830,6 +830,16 @@ class TrackProxy { covariance(), particleHypothesis()); } + /// Convert a track state into track parameters + /// @note The parameters are created on the fly + /// @return the track parameters + BoundTrackParameters createParametersFromState( + const ConstTrackStateProxy& trackState) const { + return BoundTrackParameters(trackState.referenceSurface().getSharedPtr(), + trackState.parameters(), + trackState.covariance(), particleHypothesis()); + } + /// Return a reference to the track container backend, mutable version. /// @note Only available if the track proxy is not read-only /// @return reference to the track container backend diff --git a/Core/include/Acts/Material/GridSurfaceMaterial.hpp b/Core/include/Acts/Material/GridSurfaceMaterial.hpp index 67002f35009..5b4065e0f85 100644 --- a/Core/include/Acts/Material/GridSurfaceMaterial.hpp +++ b/Core/include/Acts/Material/GridSurfaceMaterial.hpp @@ -32,8 +32,8 @@ struct GridMaterialAccessor { /// /// @return the material slab from the grid bin associated to the lookup point template - inline MaterialSlab& slab(grid_type& grid, - const typename grid_type::point_t& point) const { + inline const MaterialSlab& slab( + grid_type& grid, const typename grid_type::point_t& point) const { return grid.atPosition(point); } @@ -45,8 +45,9 @@ struct GridMaterialAccessor { /// @note this is not particularly fast template void scale(grid_type& grid, ActsScalar scale) { - for (auto& m : grid.values()) { - m.scaleThickness(scale); + // Loop through the grid bins, get the indices and scale the material + for (std::size_t ib = 0; ib < grid.size(); ++ib) { + grid.at(ib).scaleThickness(scale); } } }; @@ -80,6 +81,57 @@ struct IndexedMaterialAccessor { } }; +/// @brief This is an accessor for cases where the material is filled in a global +/// material vector that is accessed from the different material grids. +struct GloballyIndexedMaterialAccessor { + /// @brief The internal storage of the material + std::shared_ptr> globalMaterial = nullptr; + + /// Indicate if you have entries bins across different grids, e.g. by + /// running a compression/clustering algorithm. + /// + /// It is the responsibility of the user to set this flag correctly. + bool sharedEntries = false; + + /// @brief Direct const access to the material slap sorted in the grid + /// + /// @tparam grid_type the type of the grid, also defines the point type + /// + /// @param grid the grid holding the indices into the global material vector + /// @param point the lookup point (already casted from global, or filled from local) + /// + /// @return the material slab from the grid bin associated to the lookup point + template + inline const MaterialSlab& slab( + const grid_type& grid, const typename grid_type::point_t& point) const { + auto index = grid.atPosition(point); + return (*globalMaterial)[index]; + } + + /// @brief Scale the material (by scaling the thickness) + /// + /// @param grid the grid holding the indices into the global material vector + /// @param scale the amount of the scaling + /// + /// @note this will scale only the bins touched by this grid, however, + /// if there are shared bins, then it will throw an exception as the + /// outcome is unpredictable. + /// + template + void scale(grid_type& grid, ActsScalar scale) { + if (sharedEntries) { + throw std::invalid_argument( + "GloballyIndexedMaterialAccessor: shared entry scaling is not " + "supported."); + } + // Loop through the grid bins, get the indices and scale the material + for (std::size_t ib = 0; ib < grid.size(); ++ib) { + auto index = grid.at(ib); + (*globalMaterial)[index].scaleThickness(scale); + } + } +}; + /// @brief GridSurfaceMaterialT /// /// It extends the @c ISurfaceMaterial base class and allows to create @@ -157,6 +209,24 @@ class GridSurfaceMaterialT : public ISurfaceMaterial { return sl; } + /// @brief Accessor to the grid + const grid_type& grid() const { return m_grid; } + + /// @brief Accessor to the material accessor + const material_accessor_type& materialAccessor() const { + return m_materialAccessor; + } + + /// @brief Accessor to the bound to grid local delegate + const BoundToGridLocalDelegate& boundToGridLocal() const { + return m_boundToGridLocal; + } + + /// @brief Accessor to the global to grid local delegate + const GlobalToGridLocalDelegate& globalToGridLocal() const { + return m_globalToGridLocal; + } + private: /// @brief The grid grid_type m_grid; @@ -176,6 +246,11 @@ template using IndexedSurfaceMaterial = GridSurfaceMaterialT; +// Globally Indexed Surface material +template +using GloballyIndexedSurfaceMaterial = + GridSurfaceMaterialT; + // Grid Surface material template using GridSurfaceMaterial = GridSurfaceMaterialT; diff --git a/Core/include/Acts/Material/MaterialInteractionAssignment.hpp b/Core/include/Acts/Material/MaterialInteractionAssignment.hpp new file mode 100644 index 00000000000..062121bdc9b --- /dev/null +++ b/Core/include/Acts/Material/MaterialInteractionAssignment.hpp @@ -0,0 +1,110 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryHierarchyMap.hpp" +#include "Acts/Material/MaterialInteraction.hpp" + +#include +#include +#include +#include +#include + +namespace Acts { + +class Surface; + +namespace MaterialInteractionAssignment { + +/// The result struct of the assignment run +struct Result { + /// The assigned material interactions + std::vector assigned = {}; + /// The unassigned material interactions + std::vector unassigned = {}; +}; + +/// @brief definition of a global veto for assigning material interactions +/// +/// This can be used to restrict the assignment to a specific volume, or +/// exclude certain materials (if one wants), etc. +/// +/// @param materialInteraction is the material interaction to be checked for the veto +/// +/// It will be globally applied, i.e. every single material interaction +/// will have to go through this veto +using GlobalVeto = + std::function; + +/// @brief definition of a local veto on a material interaction +/// +/// This can take already the suggested surface assignment into account +/// return true if the assignment should be vetoed. This can be used for +/// having exclusion rules based on surface information. +/// +/// @param materialInteraction is the material interaction to be checked for the veto +/// @param suggestedAssignment is the suggested assignment: surface, position, direction +using LocalVeto = std::function& suggestedAssignment)>; + +/// @brief definition of possible re-assignments to next surface, this could e.g. +/// be used for respecting pre/post mapping directives that are not fully +/// handled by closest distance matching +/// +/// The provided parameters are the mutable material interaction, the suggested +/// assignment and the next possible assignment, due to the ordered nature of +/// the material interactions, assignment to previous is excluded +/// +/// @param materialInteraction is the material interaction to be checked for the veto +/// @param suggestedAssignment is the suggested assignment: surface, position, direction +/// @param suggestedReAssignment is the suggested assignment: surface, position, direction +/// +/// @note this changes the MaterialInteraction if the re-assignment is accepted +using ReAssignment = std::function& suggestedAssignment, + const std::tuple& suggestedReAssignment)>; + +/// @brief Options for the material interaction matcher +/// The options are used to specify the vetos for the assignment +struct Options { + /// Allow global vetos for the assignment, e.g. restricting + /// the assignment to a specific volume + std::vector globalVetos = {}; + + /// Allow specific vetoes for the assignment, e.g. only locally to + /// surface, rest to next mapper (e.g. volume mapper) + GeometryHierarchyMap localVetos = {}; + + /// Allow re-assignment of the material interaction, e.g. to respect + GeometryHierarchyMap reAssignments = {}; +}; + +/// @brief Match the material interactions to surfaces intersections while respecting +/// eventual vetos for the assignment +/// +/// @param gctx is the geometry context +/// @param materialInteractions is the vector of material interaction +/// @param intersectedSurfaces are the surfac assignment candidates +/// @param options are the options for the assignment +/// +/// @return a pair of vectors of assigned and unassigned material interactions +Result assign(const GeometryContext& gctx, + const std::vector& materialInteractions, + const std::vector>& + intersectedSurfaces, + const Options& options = Options()); + +} // namespace MaterialInteractionAssignment + +} // namespace Acts diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index b736f100a6c..2014624fc86 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2016-2021 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 @@ -49,16 +49,6 @@ 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 @@ -82,6 +72,9 @@ template struct CombinatorialKalmanFilterExtensions { using candidate_container_t = typename std::vector; + + using Calibrator = typename KalmanFitterExtensions::Calibrator; + using Updater = typename KalmanFitterExtensions::Updater; using MeasurementSelector = Delegate>( @@ -89,30 +82,21 @@ struct CombinatorialKalmanFilterExtensions { using BranchStopper = Delegate; - /// The Calibrator is a dedicated calibration algorithm that allows - /// to calibrate measurements using track information, this could be - /// e.g. sagging for wires, module deformations, etc. - typename KalmanFitterExtensions::Calibrator calibrator; + /// The Calibrator is a dedicated calibration algorithm that allows to + /// calibrate measurements using track information, this could be e.g. sagging + /// for wires, module deformations, etc. + Calibrator calibrator{ + DelegateFuncTag>{}}; /// The updater incorporates measurement information into the track parameters - typename KalmanFitterExtensions::Updater updater; - - /// The smoother back-propagates measurement information along the track - typename KalmanFitterExtensions::Smoother smoother; + Updater updater{DelegateFuncTag>{}}; /// The measurement selector is called during the filtering by the Actor. - MeasurementSelector measurementSelector; - - BranchStopper branchStopper; + MeasurementSelector measurementSelector{ + DelegateFuncTag{}}; - /// Default constructor which connects the default void components - CombinatorialKalmanFilterExtensions() { - calibrator.template connect<&detail::voidFitterCalibrator>(); - updater.template connect<&detail::voidFitterUpdater>(); - smoother.template connect<&detail::voidFitterSmoother>(); - branchStopper.template connect(); - measurementSelector.template connect(); - } + /// The branch stopper is called during the filtering by the Actor. + BranchStopper branchStopper{DelegateFuncTag{}}; private: /// Default measurement selector which will return all measurements @@ -152,8 +136,8 @@ using SourceLinkAccessorDelegate = /// Combined options for the combinatorial Kalman filter. /// -/// @tparam source_link_accessor_t Source link accessor type, should be -/// semiregular. +/// @tparam source_link_iterator_t Type of the source link iterator +/// @tparam traj_t Type of the trajectory template struct CombinatorialKalmanFilterOptions { using SourceLinkIterator = source_link_iterator_t; @@ -167,28 +151,23 @@ struct CombinatorialKalmanFilterOptions { /// @param accessor_ The source link accessor /// @param extensions_ The extension struct /// @param pOptions The plain propagator options - /// @param rSurface The reference surface for the eventual track fitting to be - /// expressed at /// @param mScattering Whether to include multiple scattering /// @param eLoss Whether to include energy loss - /// @param rSmoothing Whether to run smoothing to get fitted parameter CombinatorialKalmanFilterOptions( const GeometryContext& gctx, const MagneticFieldContext& mctx, std::reference_wrapper cctx, SourceLinkAccessor accessor_, CombinatorialKalmanFilterExtensions extensions_, - const PropagatorPlainOptions& pOptions, const Surface* rSurface = nullptr, - bool mScattering = true, bool eLoss = true, bool rSmoothing = true) + const PropagatorPlainOptions& pOptions, bool mScattering = true, + bool eLoss = true) : geoContext(gctx), magFieldContext(mctx), calibrationContext(cctx), sourcelinkAccessor(std::move(accessor_)), extensions(extensions_), propagatorPlainOptions(pOptions), - smoothingTargetSurface(rSurface), multipleScattering(mScattering), - energyLoss(eLoss), - smoothing(rSmoothing) {} + energyLoss(eLoss) {} /// Contexts are required and the options must not be default-constructible. CombinatorialKalmanFilterOptions() = delete; @@ -209,25 +188,16 @@ struct CombinatorialKalmanFilterOptions { /// The trivial propagator options PropagatorPlainOptions propagatorPlainOptions; - /// The filter target surface - const Surface* filterTargetSurface = nullptr; - - /// The smoothing target surface - const Surface* smoothingTargetSurface = nullptr; - - /// Strategy to propagate to reference surface - CombinatorialKalmanFilterTargetSurfaceStrategy - smoothingTargetSurfaceStrategy = - CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// The target surface + /// @note This is useful if the filtering should be terminated at a + /// certain surface + const Surface* targetSurface = nullptr; /// Whether to consider multiple scattering. bool multipleScattering = true; /// Whether to consider energy loss. bool energyLoss = true; - - /// Whether to run smoothing to get fitted parameter - bool smoothing = true; }; template @@ -265,12 +235,6 @@ struct CombinatorialKalmanFilterResult { /// Indicator if filtering has been done bool filtered = false; - /// Indicator if smoothing has been done. - bool smoothed = false; - - /// The index for the current smoothing track - MultiTrajectoryTraits::IndexType iSmoothed = 0; - /// Indicator if track finding has been done bool finished = false; @@ -283,26 +247,24 @@ struct CombinatorialKalmanFilterResult { /// Combinatorial Kalman filter to find tracks. /// -/// /// @tparam propagator_t Type of the propagator /// /// The CombinatorialKalmanFilter contains an Actor and a Sequencer sub-class. -/// The Sequencer has to be part of the Navigator of the Propagator -/// in order to initialize and provide the measurement surfaces. +/// The Sequencer has to be part of the Navigator of the Propagator in order to +/// initialize and provide the measurement surfaces. /// -/// The Actor is part of the Propagation call and does the Kalman update -/// and eventually the smoothing. Updater, Smoother and Calibrator are -/// given to the Actor for further use: +/// The Actor is part of the Propagation call and does the Kalman update and +/// eventually the smoothing. Updater and Calibrator are given to the Actor for +/// further use: /// - The Updater is the implemented kalman updater formalism, it /// runs via a visitor pattern through the measurements. -/// - The Smoother is called at the end of the filtering (track finding) by the -/// Actor. /// /// Measurements are not required to be ordered for the /// CombinatorialKalmanFilter, measurement ordering needs to be figured out by /// the navigation of the propagator. /// /// The void components are provided mainly for unit testing. +/// template class CombinatorialKalmanFilter { public: @@ -315,8 +277,7 @@ class CombinatorialKalmanFilter { : m_propagator(std::move(pPropagator)), m_logger(std::move(_logger)), m_actorLogger{m_logger->cloneWithSuffix("Actor")}, - m_updaterLogger{m_logger->cloneWithSuffix("Updater")}, - m_smootherLogger{m_logger->cloneWithSuffix("Smoother")} {} + m_updaterLogger{m_logger->cloneWithSuffix("Updater")} {} private: using KalmanNavigator = typename propagator_t::Navigator; @@ -327,7 +288,6 @@ class CombinatorialKalmanFilter { std::unique_ptr m_logger; std::shared_ptr m_actorLogger; std::shared_ptr m_updaterLogger; - std::shared_ptr m_smootherLogger; const Logger& logger() const { return *m_logger; } @@ -348,17 +308,8 @@ class CombinatorialKalmanFilter { /// Broadcast the result_type using result_type = CombinatorialKalmanFilterResult; - /// The filter target surface aborter - SurfaceReached filterTargetReached{std::numeric_limits::lowest()}; - - /// The smoothing target surface aborter - SurfaceReached smoothingTargetReached{ - std::numeric_limits::lowest()}; - - /// Strategy to propagate to reference surface - CombinatorialKalmanFilterTargetSurfaceStrategy - smoothingTargetSurfaceStrategy = - CombinatorialKalmanFilterTargetSurfaceStrategy::firstOrLast; + /// The target surface aborter + SurfaceReached targetReached{std::numeric_limits::lowest()}; /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -366,9 +317,6 @@ class CombinatorialKalmanFilter { /// Whether to consider energy loss. bool energyLoss = true; - /// Whether to run smoothing to get fitted parameter - bool smoothing = true; - /// Calibration context for the finding run const CalibrationContext* calibrationContext{nullptr}; @@ -381,12 +329,11 @@ class CombinatorialKalmanFilter { /// @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 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) { @@ -403,7 +350,21 @@ class CombinatorialKalmanFilter { } if (!result.filtered && - filterTargetReached(state, stepper, navigator, logger())) { + targetReached(state, stepper, navigator, logger())) { + // Bind the parameter to the target surface + auto res = stepper.boundState(state.stepping, *targetReached.surface); + if (!res.ok()) { + ACTS_ERROR("Error while acquiring bound state for target surface: " + << res.error() << " " << res.error().message()); + result.lastError = res.error(); + } else { + const auto& fittedState = *res; + // Assign the fitted parameters + result.fittedParameters.emplace( + result.lastMeasurementIndices.back(), + std::get(fittedState)); + } + navigator.navigationBreak(state.navigation, true); stepper.releaseStepSize(state.stepping, ConstrainedStep::actor); } @@ -524,108 +485,13 @@ class CombinatorialKalmanFilter { // Post-processing after filtering phase if (result.filtered) { - // Return error if filtering finds no tracks - if (result.lastTrackIndices.empty()) { - // @TODO: Tracks like this should not be in the final output! - ACTS_DEBUG("No tracks found"); - result.finished = true; - } else { - if (!smoothing) { - ACTS_VERBOSE("Finish Kalman filtering"); - // Remember that track finding is done - result.finished = true; - } else { - // Iterate over the found tracks for smoothing and getting the - // fitted parameter. This needs to be accomplished in different - // propagation steps: - // -> first run smoothing for found track indexed with iSmoothed - if (!result.smoothed) { - ACTS_VERBOSE( - "Finalize/run smoothing for track with last measurement " - "index = " - << result.lastMeasurementIndices.at(result.iSmoothed)); - // --> Search the starting state to run the smoothing - // --> Call the smoothing - // --> Set a stop condition when all track states have been - // handled - auto res = finalize(state, stepper, navigator, result); - if (!res.ok()) { - ACTS_ERROR("Error in finalize: " << 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 - // track parameters for found track indexed with iSmoothed - bool isTargetReached = - smoothingTargetReached(state, stepper, navigator, logger()); - isPathLimitReached = - result.pathLimitReached(state, stepper, navigator, logger()); - if (result.smoothed && (isTargetReached || isPathLimitReached)) { - ACTS_VERBOSE( - "Completing the track with last measurement index = " - << result.lastMeasurementIndices.at(result.iSmoothed)); - - if (smoothingTargetReached.surface != nullptr) { - // Transport & bind the parameter to the final surface - auto res = stepper.boundState(state.stepping, - *smoothingTargetReached.surface); - if (!res.ok()) { - if (isPathLimitReached) { - ACTS_ERROR("Target surface not reached due to path limit: " - << res.error() << " " << res.error().message()); - } else { - ACTS_ERROR( - "Error while acquiring bound state for target surface: " - << res.error() << " " << res.error().message()); - } - result.lastError = res.error(); - } else { - const 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: - // -> set the targetReached status to false - // -> set the smoothed status to false - // -> update the index of track to be smoothed - if (result.iSmoothed < result.lastMeasurementIndices.size() - 1) { - result.smoothed = false; - result.iSmoothed++; - // Reverse navigation direction to start targeting for the rest - // tracks - state.options.direction = state.options.direction.invert(); - - // TODO this is kinda silly but I dont see a better solution - // with the current CKF control flow - operator()(state, stepper, navigator, result, _logger); - } else { - ACTS_VERBOSE("Finish Kalman filtering and smoothing"); - // Remember that track finding is done - result.finished = true; - } - } - } // if run smoothing - } // if there are found tracks - } // if filtering is done + ACTS_VERBOSE("Finish CKF filtering"); + // Remember that track finding is done + result.finished = true; + } // if filtering is done } - /// @brief Kalman actor operation: reset propagation + /// @brief CombinatorialKalmanFilter actor operation: reset propagation /// /// @tparam propagator_state_t Type of Propagator state /// @tparam stepper_t Type of the stepper @@ -796,15 +662,7 @@ class CombinatorialKalmanFilter { tipState.nSensitiveSurfaces++; } // Add state if there is already measurement detected on this branch - // For in-sensitive surface, only add state when smoothing is - // required - bool createState = false; - if (smoothing) { - createState = (tipState.nMeasurements > 0 || isMaterial); - } else { - createState = (tipState.nMeasurements > 0 && isSensitive); - } - if (createState) { + if (tipState.nMeasurements > 0 || isMaterial) { // New state is to be added. Remove the last tip from active tips now if (!result.activeTips.empty()) { result.activeTips.erase(result.activeTips.end() - 1); @@ -876,6 +734,7 @@ class CombinatorialKalmanFilter { } /// Create and fill track states for all source links + /// /// @param gctx The current geometry context /// @param result Reference to the result struct of the actor /// @param boundState Bound state from the propagation on this surface @@ -953,6 +812,7 @@ class CombinatorialKalmanFilter { } /// Handle the list of selected track states + /// /// @param gctx The current geometry context /// @param begin The start iterator for selected track states /// @param end The end iterator for selected track states @@ -1184,159 +1044,6 @@ class CombinatorialKalmanFilter { } } - /// @brief Kalman actor operation: finalize - /// - /// @tparam propagator_state_t is the type of Propagator state - /// @tparam stepper_t Type of the stepper - /// @tparam navigator_t Type of the navigator - /// - /// @param state is the mutable propagator state object - /// @param stepper The stepper in use - /// @param navigator The navigator in use - /// @param result is the mutable result state object - template - Result finalize(propagator_state_t& state, const stepper_t& stepper, - const navigator_t& navigator, - result_type& result) const { - // The measurement tip of the track being smoothed - const auto& lastMeasurementIndex = - result.lastMeasurementIndices.at(result.iSmoothed); - - // Get the indices of the first states (can be either a measurement or - // material); - std::size_t firstStateIndex = lastMeasurementIndex; - // Count track states to be smoothed - std::size_t nStates = 0; - result.fittedStates->applyBackwards(lastMeasurementIndex, [&](auto st) { - bool isMeasurement = - st.typeFlags().test(TrackStateFlag::MeasurementFlag); - 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++; - }); - // Return error if the track has no measurement states (but this should - // not happen) - if (nStates == 0) { - ACTS_ERROR("Smoothing for a track without measurements."); - return CombinatorialKalmanFilterError::SmoothFailed; - } - // 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, - lastMeasurementIndex, *smootherLogger); - if (!smoothRes.ok()) { - ACTS_ERROR("Smoothing step failed: " << smoothRes.error()); - return smoothRes.error(); - } - - // Return in case no target surface - if (smoothingTargetReached.surface == nullptr) { - return Result::success(); - } - - // Obtain the smoothed parameters at first/last measurement state - auto firstCreatedState = - result.fittedStates->getTrackState(firstStateIndex); - auto lastCreatedMeasurement = - result.fittedStates->getTrackState(lastMeasurementIndex); - - // Lambda to get the intersection of the free params on the target surface - auto target = [&](const FreeVector& freeVector) -> SurfaceIntersection { - return smoothingTargetReached.surface - ->intersect( - state.geoContext, freeVector.segment<3>(eFreePos0), - state.options.direction * freeVector.segment<3>(eFreeDir0), - BoundaryCheck(true), state.options.surfaceTolerance) - .closest(); - }; - - // 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( - state.options.geoContext, lastCreatedMeasurement); - // Get the intersections of the smoothed free parameters with the target - // surface - const auto firstIntersection = target(firstParams); - 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. - // @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; - if (useFirstTrackState) { - stepper.resetState(state.stepping, firstCreatedState.smoothed(), - firstCreatedState.smoothedCovariance(), - firstCreatedState.referenceSurface(), - state.options.maxStepSize); - reverseDirection = firstIntersection.pathLength() < 0; - } else { - stepper.resetState(state.stepping, lastCreatedMeasurement.smoothed(), - lastCreatedMeasurement.smoothedCovariance(), - lastCreatedMeasurement.referenceSurface(), - state.options.maxStepSize); - reverseDirection = lastIntersection.pathLength() < 0; - } - // Reverse the navigation direction if necessary - if (reverseDirection) { - ACTS_VERBOSE( - "Reverse navigation direction after smoothing for reaching the " - "target surface"); - state.options.direction = state.options.direction.invert(); - } - 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 - // Set targetSurface to nullptr as it is handled manually in the actor - state.navigation = navigator.makeState(&surface, nullptr); - navigator.initialize(state, stepper); - - detail::setupLoopProtection(state, stepper, result.pathLimitReached, true, - logger()); - - return Result::success(); - } - CombinatorialKalmanFilterExtensions m_extensions; /// The source link accessor @@ -1349,8 +1056,6 @@ class CombinatorialKalmanFilter { const Logger* actorLogger{nullptr}; /// Updater logger instance const Logger* updaterLogger{nullptr}; - /// Smoother logger instance - const Logger* smootherLogger{nullptr}; const Logger& logger() const { return *actorLogger; } }; @@ -1389,7 +1094,6 @@ class CombinatorialKalmanFilter { public: /// Combinatorial Kalman Filter implementation, calls the Kalman filter - /// and smoother /// /// @tparam source_link_iterator_t Type of the source link iterator /// @tparam start_parameters_container_t Type of the initial parameters @@ -1442,17 +1146,11 @@ class CombinatorialKalmanFilter { // Catch the actor auto& combKalmanActor = propOptions.actionList.template get(); - combKalmanActor.filterTargetReached.surface = tfOptions.filterTargetSurface; - combKalmanActor.smoothingTargetReached.surface = - tfOptions.smoothingTargetSurface; - combKalmanActor.smoothingTargetSurfaceStrategy = - tfOptions.smoothingTargetSurfaceStrategy; + combKalmanActor.targetReached.surface = tfOptions.targetSurface; combKalmanActor.multipleScattering = tfOptions.multipleScattering; combKalmanActor.energyLoss = tfOptions.energyLoss; - combKalmanActor.smoothing = tfOptions.smoothing; combKalmanActor.actorLogger = m_actorLogger.get(); combKalmanActor.updaterLogger = m_updaterLogger.get(); - combKalmanActor.smootherLogger = m_smootherLogger.get(); combKalmanActor.calibrationContext = &tfOptions.calibrationContext.get(); // copy source link accessor, calibrator and measurement selector @@ -1508,18 +1206,20 @@ class CombinatorialKalmanFilter { 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 = it->second; - track.parameters() = parameters.parameters(); - track.covariance() = *parameters.covariance(); - track.setReferenceSurface(parameters.referenceSurface().getSharedPtr()); + // Set fitted track parameters if available. This will only be the case if + // a target surface is set. Without a target surface there cannot be + // fitted parameters and the user will have to extrapolate the track to a + // target surface themselves. + if (auto it = combKalmanResult.fittedParameters.find(tip); + it != combKalmanResult.fittedParameters.end()) { + const BoundTrackParameters& parameters = it->second; + track.parameters() = parameters.parameters(); + track.covariance() = *parameters.covariance(); + track.setReferenceSurface(parameters.referenceSurface().getSharedPtr()); + } calculateTrackQuantities(track); diff --git a/Core/include/Acts/Utilities/GridAccessHelpers.hpp b/Core/include/Acts/Utilities/GridAccessHelpers.hpp index 0c79ad3548b..0a34b5519be 100644 --- a/Core/include/Acts/Utilities/GridAccessHelpers.hpp +++ b/Core/include/Acts/Utilities/GridAccessHelpers.hpp @@ -222,13 +222,36 @@ class BoundCylinderToZPhi final : public IBoundToGridLocal { /// @param z the shift BoundCylinderToZPhi(ActsScalar r, ActsScalar z) : radius(r), shift(z) {} - std::array l2ZPhi(const Vector2& local) const { + std::array toGridLocal(const Vector2& local) const { return {local[1u] + shift, local[0u] / radius}; } using BoundDiscToRPhi = LocalSubspace<0u, 1u>; }; -} // namespace GridAccess +// Definition of bound (on surface) to grid local representation delegate +// 1 dimensional local grid +using BoundToGridLocal1DimDelegate = + OwningDelegate(const Vector2&), + GridAccess::IBoundToGridLocal>; + +// Definition of global to grid local representation delegate +// 1 dimensional local grid +using GlobalToGridLocal1DimDelegate = + OwningDelegate(const Vector3&), + GridAccess::IGlobalToGridLocal>; + +// Definition of bound (on surface) to grid local representation delegate +// 2 dimensional local grid +using BoundToGridLocal2DimDelegate = + OwningDelegate(const Vector2&), + GridAccess::IBoundToGridLocal>; + +// Definition of global to grid local representation delegate +// 2 dimensional local grid +using GlobalToGridLocal2DimDelegate = + OwningDelegate(const Vector3&), + GridAccess::IGlobalToGridLocal>; +} // namespace GridAccess } // namespace Acts diff --git a/Core/include/Acts/Utilities/TrackHelpers.hpp b/Core/include/Acts/Utilities/TrackHelpers.hpp new file mode 100644 index 00000000000..c7ee69ce7ea --- /dev/null +++ b/Core/include/Acts/Utilities/TrackHelpers.hpp @@ -0,0 +1,358 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/Tolerance.hpp" +#include "Acts/EventData/MultiTrajectoryHelpers.hpp" +#include "Acts/EventData/TrackStateType.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Propagator/StandardAborters.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/TrackFitting/GainMatrixSmoother.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/Result.hpp" + +#include + +namespace Acts { + +enum class TrackExtrapolationStrategy { + /// Use the first track state to reach target surface + first, + /// Use the last track state to reach target surface + last, + /// Use the first or last track state to reach target surface depending on the + /// distance + firstOrLast, +}; + +enum class TrackExtrapolationError { + CompatibleTrackStateNotFound = 1, + ReferenceSurfaceUnreachable = 2, +}; + +std::error_code make_error_code(TrackExtrapolationError e); + +template +Result findFirstMeasurementState( + const track_proxy_t &track) { + using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy; + + // TODO specialize if track is forward linked + + auto result = Result::failure( + TrackExtrapolationError::CompatibleTrackStateNotFound); + + for (const auto &trackState : track.trackStatesReversed()) { + bool isMeasurement = + trackState.typeFlags().test(TrackStateFlag::MeasurementFlag); + bool isOutlier = trackState.typeFlags().test(TrackStateFlag::OutlierFlag); + + if (isMeasurement && !isOutlier) { + result = trackState; + } + } + + return result; +} + +template +Result findLastMeasurementState( + const track_proxy_t &track) { + using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy; + + for (const auto &trackState : track.trackStatesReversed()) { + bool isMeasurement = + trackState.typeFlags().test(TrackStateFlag::MeasurementFlag); + bool isOutlier = trackState.typeFlags().test(TrackStateFlag::OutlierFlag); + + if (isMeasurement && !isOutlier) { + return trackState; + } + } + + return Result::failure( + TrackExtrapolationError::CompatibleTrackStateNotFound); +} + +/// @brief Smooth a track using the gain matrix smoother +/// +/// @tparam track_proxy_t The track proxy type +/// +/// @param geoContext The geometry context +/// @param track The track to smooth +/// @param logger The logger +/// +/// @return The result of the smoothing +template +Result smoothTrack( + const GeometryContext &geoContext, track_proxy_t &track, + const Logger &logger = *getDefaultLogger("TrackSmoother", Logging::INFO)) { + Acts::GainMatrixSmoother smoother; + + auto &trackContainer = track.container(); + auto &trackStateContainer = trackContainer.trackStateContainer(); + + auto last = findLastMeasurementState(track); + if (!last.ok()) { + ACTS_ERROR("no last track state found"); + return last.error(); + } + + auto smoothingResult = + smoother(geoContext, trackStateContainer, last->index(), logger); + + if (!smoothingResult.ok()) { + ACTS_ERROR("Smoothing track " << track.index() << " failed with error " + << smoothingResult.error()); + return smoothingResult.error(); + } + + return Result::success(); +} + +/// @brief Smooth tracks using the gain matrix smoother +/// +/// @tparam track_container_t The track container type +/// +/// @param geoContext The geometry context +/// @param trackContainer The track container +/// @param logger The logger +/// +/// @return The result of the smoothing +template +Result smoothTracks( + const GeometryContext &geoContext, const track_container_t &trackContainer, + const Logger &logger = *getDefaultLogger("TrackSmoother", Logging::INFO)) { + Result result = Result::success(); + + for (const auto &track : trackContainer) { + auto smoothingResult = smoothTrack(geoContext, track, logger); + + // Only keep the first error + if (!smoothingResult.ok() && result.ok()) { + result = smoothingResult.error(); + } + } + + return result; +} + +/// @brief Find a track state for extrapolation +/// +/// @tparam track_proxy_t The track proxy type +/// +/// @param geoContext The geometry context +/// @param track The track +/// @param referenceSurface The reference surface +/// @param strategy The extrapolation strategy +/// @param logger The logger +/// +/// @return The result of the search containing the track state +/// and the distance to the reference surface +template +Result> +findTrackStateForExtrapolation( + const GeometryContext &geoContext, track_proxy_t &track, + const Surface &referenceSurface, TrackExtrapolationStrategy strategy, + const Logger &logger = *getDefaultLogger("TrackExtrapolation", + Logging::INFO)) { + using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy; + + auto intersect = [&](const TrackStateProxy &state) -> SurfaceIntersection { + auto freeVector = MultiTrajectoryHelpers::freeSmoothed(geoContext, state); + + return referenceSurface + .intersect(geoContext, freeVector.template segment<3>(eFreePos0), + freeVector.template segment<3>(eFreeDir0), + BoundaryCheck(true), s_onSurfaceTolerance) + .closest(); + }; + + switch (strategy) { + case TrackExtrapolationStrategy::first: { + ACTS_VERBOSE("looking for first track state"); + + auto first = findFirstMeasurementState(track); + if (!first.ok()) { + ACTS_ERROR("no first track state found"); + return first.error(); + } + + SurfaceIntersection intersection = intersect(*first); + if (!intersection) { + ACTS_ERROR("no intersection found"); + return Result>::failure( + TrackExtrapolationError::ReferenceSurfaceUnreachable); + } + + ACTS_VERBOSE("found intersection at " << intersection.pathLength()); + return std::make_pair(*first, intersection.pathLength()); + } + + case TrackExtrapolationStrategy::last: { + ACTS_VERBOSE("looking for last track state"); + + auto last = findLastMeasurementState(track); + if (!last.ok()) { + ACTS_ERROR("no last track state found"); + return last.error(); + } + + SurfaceIntersection intersection = intersect(*last); + if (!intersection) { + ACTS_ERROR("no intersection found"); + return Result>::failure( + TrackExtrapolationError::ReferenceSurfaceUnreachable); + } + + ACTS_VERBOSE("found intersection at " << intersection.pathLength()); + return std::make_pair(*last, intersection.pathLength()); + } + + case TrackExtrapolationStrategy::firstOrLast: { + ACTS_VERBOSE("looking for first or last track state"); + + auto first = findFirstMeasurementState(track); + if (!first.ok()) { + ACTS_ERROR("no first track state found"); + return first.error(); + } + + auto last = findLastMeasurementState(track); + if (!last.ok()) { + ACTS_ERROR("no last track state found"); + return last.error(); + } + + SurfaceIntersection intersectionFirst = intersect(*first); + SurfaceIntersection intersectionLast = intersect(*last); + + double absDistanceFirst = std::abs(intersectionFirst.pathLength()); + double absDistanceLast = std::abs(intersectionLast.pathLength()); + + if (intersectionFirst && absDistanceFirst <= absDistanceLast) { + ACTS_VERBOSE("using first track state with intersection at " + << intersectionFirst.pathLength()); + return std::make_pair(*first, intersectionFirst.pathLength()); + } + + if (intersectionLast && absDistanceLast <= absDistanceFirst) { + ACTS_VERBOSE("using last track state with intersection at " + << intersectionLast.pathLength()); + return std::make_pair(*last, intersectionLast.pathLength()); + } + + ACTS_ERROR("no intersection found"); + return Result>::failure( + TrackExtrapolationError::ReferenceSurfaceUnreachable); + } + } + + // unreachable + return Result>::failure( + TrackExtrapolationError::CompatibleTrackStateNotFound); +} + +/// @brief Extrapolate a track to a reference surface +/// +/// @tparam track_proxy_t The track proxy type +/// @tparam propagator_t The propagator type +/// @tparam propagator_options_t The propagator options type +/// +/// @param track The track which is modified in-place +/// @param referenceSurface The reference surface +/// @param propagator The propagator +/// @param options The propagator options +/// @param strategy The extrapolation strategy +/// @param logger The logger +/// +/// @return The result of the extrapolation +template +Result extrapolateTrackToReferenceSurface( + track_proxy_t &track, const Surface &referenceSurface, + const propagator_t &propagator, propagator_options_t options, + TrackExtrapolationStrategy strategy, + const Logger &logger = *getDefaultLogger("TrackExtrapolation", + Logging::INFO)) { + auto findResult = findTrackStateForExtrapolation( + options.geoContext, track, referenceSurface, strategy, logger); + + if (!findResult.ok()) { + ACTS_ERROR("failed to find track state for extrapolation"); + return findResult.error(); + } + + auto &[trackState, distance] = *findResult; + + options.direction = Direction::fromScalarZeroAsPositive(distance); + auto propagateResult = + propagator.template propagate( + track.createParametersFromState(trackState), referenceSurface, + options); + + if (!propagateResult.ok()) { + ACTS_ERROR("failed to extrapolate track: " << propagateResult.error()); + return propagateResult.error(); + } + + track.setReferenceSurface(referenceSurface.getSharedPtr()); + track.parameters() = propagateResult->endParameters.value().parameters(); + track.covariance() = + propagateResult->endParameters.value().covariance().value(); + + return Result::success(); +} + +/// @brief Extrapolate tracks to a reference surface +/// +/// @tparam track_container_t The track container type +/// @tparam propagator_t The propagator type +/// @tparam propagator_options_t The propagator options type +/// +/// @param trackContainer The track container which is modified in-place +/// @param referenceSurface The reference surface +/// @param propagator The propagator +/// @param options The propagator options +/// @param strategy The extrapolation strategy +/// @param logger The logger +/// +/// @return The result of the extrapolation +template +Result extrapolateTracksToReferenceSurface( + const track_container_t &trackContainer, const Surface &referenceSurface, + const propagator_t &propagator, propagator_options_t options, + TrackExtrapolationStrategy strategy, + const Logger &logger = *getDefaultLogger("TrackExtrapolation", + Logging::INFO)) { + Result result = Result::success(); + + for (const auto &track : trackContainer) { + auto extrapolateResult = extrapolateTrackToReferenceSurface( + track, referenceSurface, propagator, options, strategy, logger); + + // Only keep the first error + if (!extrapolateResult.ok() && result.ok()) { + result = extrapolateResult.error(); + } + } + + return result; +} + +} // namespace Acts + +namespace std { +// register with STL +template <> +struct is_error_code_enum : std::true_type {}; +} // namespace std diff --git a/Core/include/Acts/Utilities/TransformRange.hpp b/Core/include/Acts/Utilities/TransformRange.hpp new file mode 100644 index 00000000000..d4773cc3722 --- /dev/null +++ b/Core/include/Acts/Utilities/TransformRange.hpp @@ -0,0 +1,228 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include + +namespace Acts { + +namespace detail { + +template +struct TransformRangeIterator; + +/// This type implements a transforming range over a container. +/// It functions like a view, where all element access is passed through +/// a user-defined callable, that can process values, like dereferencing them +/// or calling a specific method. +/// +/// @note The range and associated iterator maintain const-ness of the input, +/// i.e. if a const-qualified container is passed, the range and iterators +/// will not return any mutable references, even if they are mutable +/// themselves +/// @note The range and iterator assume the the callables return references +/// +/// @tparam Callable The callable to apply to each element. +/// @tparam container_t The container to wrap. +template +struct TransformRange { + private: + using internal_value_type = typename container_t::value_type; + + static_assert(std::is_reference_v()))>, + "The callable must return a reference type"); + + using raw_value_type = std::remove_reference_t()))>; + + public: + /// The underlying value type that is returned by the range. + /// If the input container has const-qualification, the range does + /// not expose mutable values + using value_type = + std::conditional_t, + std::add_const_t, raw_value_type>; + + using reference = value_type&; + using const_reference = const value_type&; + + using iterator = TransformRangeIterator< + Callable, + std::conditional_t, + typename container_t::const_iterator, + typename container_t::iterator>, + std::is_const_v>; + using const_iterator = + TransformRangeIterator; + + /// Construct a transforming range from a container. The first argument is + /// only used for type-deduction + /// @param container The container to wrap + explicit TransformRange(Callable&& /*callable*/, container_t& container) + : m_container(&container) {} + + /// Access the i-th element of the underlying container, applying the + /// callable + /// @param i The index of the element to access + /// @return Reference to the transformed i-th element + reference operator[](std::size_t i) { + return Callable::apply((*m_container)[i]); + } + + /// Access the i-th element of the underlying container, applying the + /// callable + /// @param i The index of the element to access + /// @return Const-reference to the transformed i-th element + const_reference operator[](std::size_t i) const { + return std::as_const(Callable::apply((*m_container)[i])); + } + + /// Access the i-th element of the underlying container, applying the + /// callable + /// @param i The index of the element to access + /// @return Reference to the transformed i-th element + reference at(std::size_t i) { return Callable::apply(m_container->at(i)); } + + /// Access the i-th element of the underlying container, applying the + /// callable + /// @param i The index of the element to access + /// @return Const-reference to the transformed i-th element + const_reference at(std::size_t i) const { + return std::as_const(Callable::apply(m_container->at(i))); + } + + /// Return an iterator to the beginning of the underlying container + /// @return Iterator to the beginning of the range + iterator begin() { return iterator{m_container->begin()}; } + + /// Return an iterator past the end of the underlying container + /// @return Iterator past the end of the range + iterator end() { return iterator{m_container->end()}; } + + /// Return a const-iterator to the beginning of the underlying container + /// @return Const-iterator to the beginning of the range + const_iterator begin() const { return const_iterator{m_container->begin()}; } + + /// Return a const-iterator past the end of the underlying container + /// @return Const-iterator past the end of the range + const_iterator cbegin() const { return begin(); } + + /// Return a const-iterator to the beginning of the underlying container + /// @return Const-iterator to the beginning of the range + const_iterator end() const { return const_iterator{m_container->end()}; } + + /// Return a const-iterator past the end of the underlying container + /// @return Const-iterator past the end of the range + const_iterator cend() const { return end(); } + + /// Return the size of the underlying container + /// @return The size of the underlying container + std::size_t size() const { return m_container->size(); } + + /// Check if the underlying container is empty + /// @return True if the underlying container is empty + bool empty() const { return m_container->empty(); } + + private: + container_t* m_container; +}; + +/// This type is associated with @c TransformRange and implements the iterator +/// for the range. It applies the callable to the value that is dereferences. +/// It also maintains const-ness, if instructed, by returning const references +/// only. +/// @tparam Callable The callable to apply to the value +/// @tparam iterator_t The iterator type of the underlying container +template +struct TransformRangeIterator { + private: + using internal_value_type = + typename std::iterator_traits::value_type; + + using raw_value_type = std::remove_reference_t()))>; + + public: + /// The underlying value type that is returned by the iterator. + /// If @c force_const is set to true, the iterator will only return + /// const-qualified references + using value_type = + std::conditional_t, + raw_value_type>; + + using difference_type = + typename std::iterator_traits::difference_type; + using pointer = std::remove_reference_t*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + /// Construct an iterator from an underlying iterator + explicit TransformRangeIterator(iterator_t iterator) : m_iterator(iterator) {} + + /// Return a reference to the value that is transformed by the callable + /// @return Reference to the transformed value + reference operator*() { return Callable::apply(*m_iterator); } + + /// Return a const-reference to the value that is transformed by the callable + /// @return Const-reference to the transformed value + reference operator*() const { return Callable::apply(*m_iterator); } + + /// Advance the iterator + /// @return Reference to the iterator + TransformRangeIterator& operator++() { + ++m_iterator; + return *this; + } + + /// Compare two iterators for equality + /// @param other The other iterator to compare to + bool operator==(const TransformRangeIterator& other) const { + return m_iterator == other.m_iterator; + } + + /// Compare two iterators for inequality + /// @param other The other iterator to compare to + bool operator!=(const TransformRangeIterator& other) const { + return m_iterator != other.m_iterator; + } + + private: + iterator_t m_iterator; +}; + +/// Callable that dereferences a value +struct Dereference { + template + constexpr static decltype(auto) apply(input_t&& value) { + return *value; + } +}; + +/// Callable that const-dereferences a value +struct ConstDereference { + template + constexpr static decltype(auto) apply(input_t&& value) { + return std::as_const(*value); + } +}; + +/// Callable that calls the @c get method of a value +struct DotGet { + template + constexpr static decltype(auto) apply(input_t&& value) { + return value.get(); + } +}; + +} // namespace detail +} // namespace Acts diff --git a/Core/include/Acts/Visualization/GeometryView3D.hpp b/Core/include/Acts/Visualization/GeometryView3D.hpp index 2627f16f1bf..0243e8e58ad 100644 --- a/Core/include/Acts/Visualization/GeometryView3D.hpp +++ b/Core/include/Acts/Visualization/GeometryView3D.hpp @@ -23,7 +23,6 @@ class Surface; class SurfaceArray; class TrackingVolume; struct Polyhedron; -class AbstractVolume; class IVisualization3D; namespace Experimental { @@ -78,7 +77,7 @@ struct GeometryView3D { const ViewConfig& gridConfig = s_viewGrid, const std::string& outputDir = "."); - /// Helper method to draw AbstractVolume objects + /// Helper method to draw Volume objects /// /// @param [in,out] helper The visualization helper /// @param volume The volume to be drawn @@ -124,7 +123,7 @@ struct GeometryView3D { const ViewConfig& unconnected = ViewConfig({255, 0, 0}), const ViewConfig& viewConfig = s_viewSensitive); - /// Helper method to draw AbstractVolume objects + /// Helper method to draw Layer objects /// /// @param [in,out] helper The visualization helper /// @param layer The tracking layer to be drawn @@ -140,7 +139,7 @@ struct GeometryView3D { const ViewConfig& gridConfig = s_viewGrid, const std::string& outputDir = "."); - /// Helper method to draw AbstractVolume objects + /// Helper method to draw TrackingVolume objects /// /// @param [in,out] helper The visualization helper /// @param tVolume The tracking volume to be drawn diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp index 01330ad8a0c..d2669ddc9c1 100644 --- a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -813,18 +813,22 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( // If needed, insert new cylinder if (volumes[0u]->portalPtrs().size() == 4u && volumes[1u]->portalPtrs().size() == 8u) { + const auto* cylVolBounds = + dynamic_cast(&volumes[0u]->volumeBounds()); + const auto* ccylVolBounds = dynamic_cast( + &volumes[1u]->volumeBounds()); + if (cylVolBounds == nullptr || ccylVolBounds == nullptr) { + throw std::invalid_argument( + "Wrapping the detector volume requires a cylinder and a cutout " + "cylinder volume."); + } // We need a new cylinder spanning over the entire inner tube - ActsScalar hlZ = - volumes[0u] - ->volumeBounds() - .values()[Acts::CylinderVolumeBounds::BoundValues::eHalfLengthZ]; - ActsScalar HlZ = - volumes[1u]->volumeBounds().values() - [Acts::CutoutCylinderVolumeBounds::BoundValues::eHalfLengthZ]; + ActsScalar hlZ = cylVolBounds->get( + Acts::CylinderVolumeBounds::BoundValues::eHalfLengthZ); + ActsScalar HlZ = ccylVolBounds->get( + Acts::CutoutCylinderVolumeBounds::BoundValues::eHalfLengthZ); ActsScalar innerR = - volumes[0u] - ->volumeBounds() - .values()[Acts::CylinderVolumeBounds::BoundValues::eMinR]; + cylVolBounds->get(CylinderVolumeBounds::BoundValues::eMinR); // Create the inner replacement std::vector pReplacements; pReplacements.push_back(createCylinderReplacement( @@ -1140,7 +1144,14 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( // Loop over side volume and register the z boundaries for (auto& svs : sideVolumes) { for (auto& v : svs.second) { - ActsScalar hlZ = v->volumeBounds().values()[2u]; + const auto* cylVolBounds = + dynamic_cast(&v->volumeBounds()); + if (cylVolBounds == nullptr) { + throw std::invalid_argument( + "CylindricalDetectorHelper: side volume must be a cylinder."); + } + ActsScalar hlZ = + cylVolBounds->get(CylinderVolumeBounds::BoundValues::eHalfLengthZ); zBoundaries.push_back(zBoundaries.back() + 2 * hlZ); innerVolumes.push_back(v); } diff --git a/Core/src/Material/CMakeLists.txt b/Core/src/Material/CMakeLists.txt index e7befd8c42d..22ad1642bd4 100644 --- a/Core/src/Material/CMakeLists.txt +++ b/Core/src/Material/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources( Interactions.cpp Material.cpp MaterialGridHelper.cpp + MaterialInteractionAssignment.cpp MaterialMapUtils.cpp MaterialSlab.cpp ProtoVolumeMaterial.cpp diff --git a/Core/src/Material/MaterialInteractionAssignment.cpp b/Core/src/Material/MaterialInteractionAssignment.cpp new file mode 100644 index 00000000000..5f806b64463 --- /dev/null +++ b/Core/src/Material/MaterialInteractionAssignment.cpp @@ -0,0 +1,98 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Material/MaterialInteractionAssignment.hpp" + +Acts::MaterialInteractionAssignment::Result +Acts::MaterialInteractionAssignment::assign( + const GeometryContext& gctx, + const std::vector& materialInteractions, + const std::vector>& + intersectedSurfaces, + const Options& options) { + // Assume a high assignment rate + std::vector assignedMaterialInteractions; + assignedMaterialInteractions.reserve(materialInteractions.size()); + + std::vector unassignedMaterialInteractions; + + /// Simple matching of material interactions to surfaces - no pre/post + /// matching + // ----------------------------------------------------------------------------- + // Double-Loop over the sorted material interactions + std::size_t is = 0u; + for (const auto& materialInteraction : materialInteractions) { + // First check if there is a global veto + bool veto = false; + for (const auto& gVeto : options.globalVetos) { + if (gVeto(materialInteraction)) { + unassignedMaterialInteractions.push_back(materialInteraction); + veto = true; + break; + } + } + // Now veto this assignment + if (veto) { + continue; + } + + // Walk along the sorted intersections + auto [cSurface, cPosition, cDirection] = intersectedSurfaces[is]; + ActsScalar cDistance = (cPosition - materialInteraction.position).norm(); + + // Peak forward to check if you have a closer intersection + while (is + 1u < intersectedSurfaces.size() && + ((std::get<1>(intersectedSurfaces[is + 1]) - + materialInteraction.position) + .norm() < cDistance)) { + // Recalculate the new distance + ActsScalar nDistance = (std::get<1>(intersectedSurfaces[is + 1]) - + materialInteraction.position) + .norm(); + ++is; + cDistance = nDistance; + } + + // Settled on the right intersection + auto [surface, position, direction] = intersectedSurfaces[is]; + + // Calculate the path correction + ActsScalar pathCorrection = + surface->pathCorrection(gctx, position, direction); + + // A local veta veto kicked in + GeometryIdentifier intersectionID = surface->geometryId(); + if (options.localVetos.find(intersectionID) != options.localVetos.end()) { + const auto& localVeto = *options.localVetos.find(intersectionID); + if (localVeto(materialInteraction, intersectedSurfaces[is])) { + unassignedMaterialInteractions.push_back(materialInteraction); + continue; + } + } + + // Assign the material interaction + MaterialInteraction assignedMaterialInteraction = materialInteraction; + assignedMaterialInteraction.pathCorrection = pathCorrection; + assignedMaterialInteraction.surface = surface; + assignedMaterialInteraction.position = position; + assignedMaterialInteraction.direction = direction; + assignedMaterialInteraction.intersectionID = intersectionID; + // Check for possible reassignment + if (is + 1u < intersectedSurfaces.size() && + options.reAssignments.find(intersectionID) != + options.reAssignments.end()) { + auto reAssignment = (*options.reAssignments.find(intersectionID)); + reAssignment(assignedMaterialInteraction, intersectedSurfaces[is], + intersectedSurfaces[is + 1]); + } + assignedMaterialInteractions.push_back(assignedMaterialInteraction); + } + + // return the pair of assigned and unassigned material interactions + return {assignedMaterialInteractions, unassignedMaterialInteractions}; +} diff --git a/Core/src/Utilities/CMakeLists.txt b/Core/src/Utilities/CMakeLists.txt index 63459a0cac5..53a23bfd83d 100644 --- a/Core/src/Utilities/CMakeLists.txt +++ b/Core/src/Utilities/CMakeLists.txt @@ -5,4 +5,5 @@ target_sources( BinUtility.cpp Logger.cpp SpacePointUtility.cpp + TrackHelpers.cpp ) diff --git a/Core/src/Utilities/TrackHelpers.cpp b/Core/src/Utilities/TrackHelpers.cpp new file mode 100644 index 00000000000..7d09962224b --- /dev/null +++ b/Core/src/Utilities/TrackHelpers.cpp @@ -0,0 +1,39 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Utilities/TrackHelpers.hpp" + +#include + +namespace { + +class TrackExtrapolationErrorCategory : public std::error_category { + public: + // Return a short descriptive name for the category. + const char* name() const noexcept final { return "TrackExtrapolationError"; } + + // Return what each enum means in text. + std::string message(int c) const final { + using Acts::TrackExtrapolationError; + + switch (static_cast(c)) { + case TrackExtrapolationError::CompatibleTrackStateNotFound: + return "Did not find a compatible track state"; + case TrackExtrapolationError::ReferenceSurfaceUnreachable: + return "Provided reference surface is unreachable"; + default: + return "unknown"; + } + } +}; + +} // namespace + +std::error_code Acts::make_error_code(Acts::TrackExtrapolationError e) { + return {static_cast(e), TrackExtrapolationErrorCategory()}; +} diff --git a/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MaterialMapping.hpp b/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MaterialMapping.hpp index 7161d6f154e..f1b311a8077 100644 --- a/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MaterialMapping.hpp +++ b/Examples/Algorithms/MaterialMapping/include/ActsExamples/MaterialMapping/MaterialMapping.hpp @@ -32,11 +32,6 @@ #include #include -namespace ActsExamples { -class IMaterialWriter; -struct AlgorithmContext; -} // namespace ActsExamples - namespace Acts { class TrackingGeometry; @@ -81,7 +76,7 @@ class MaterialMapping : public IAlgorithm { std::reference_wrapper magFieldContext; /// Input collection - std::string collection = "material_tracks"; + std::string inputMaterialTracks = "material_tracks"; /// The material collection to be stored std::string mappingMaterialCollection = "mapped_material_tracks"; diff --git a/Examples/Algorithms/MaterialMapping/src/MaterialMapping.cpp b/Examples/Algorithms/MaterialMapping/src/MaterialMapping.cpp index cb43f6fd553..0a6abc7d200 100644 --- a/Examples/Algorithms/MaterialMapping/src/MaterialMapping.cpp +++ b/Examples/Algorithms/MaterialMapping/src/MaterialMapping.cpp @@ -17,13 +17,10 @@ #include namespace ActsExamples { -struct AlgorithmContext; -} // namespace ActsExamples -ActsExamples::MaterialMapping::MaterialMapping( - const ActsExamples::MaterialMapping::Config& cfg, - Acts::Logging::Level level) - : ActsExamples::IAlgorithm("MaterialMapping", level), +MaterialMapping::MaterialMapping(const MaterialMapping::Config& cfg, + Acts::Logging::Level level) + : IAlgorithm("MaterialMapping", level), m_cfg(cfg), m_mappingState(cfg.geoContext, cfg.magFieldContext), m_mappingStateVol(cfg.geoContext, cfg.magFieldContext) { @@ -33,7 +30,7 @@ ActsExamples::MaterialMapping::MaterialMapping( throw std::invalid_argument("Missing tracking geometry"); } - m_inputMaterialTracks.initialize(m_cfg.collection); + m_inputMaterialTracks.initialize(m_cfg.inputMaterialTracks); m_outputMaterialTracks.initialize(m_cfg.mappingMaterialCollection); ACTS_INFO("This algorithm requires inter-event information, " @@ -51,7 +48,7 @@ ActsExamples::MaterialMapping::MaterialMapping( } } -ActsExamples::MaterialMapping::~MaterialMapping() { +MaterialMapping::~MaterialMapping() { Acts::DetectorMaterialMaps detectorMaterial; if (m_cfg.materialSurfaceMapper && m_cfg.materialVolumeMapper) { @@ -98,8 +95,7 @@ ActsExamples::MaterialMapping::~MaterialMapping() { } } -ActsExamples::ProcessCode ActsExamples::MaterialMapping::execute( - const ActsExamples::AlgorithmContext& context) const { +ProcessCode MaterialMapping::execute(const AlgorithmContext& context) const { // Take the collection from the EventStore std::unordered_map mtrackCollection = m_inputMaterialTracks(context); @@ -125,11 +121,11 @@ ActsExamples::ProcessCode ActsExamples::MaterialMapping::execute( } // Write take the collection to the EventStore m_outputMaterialTracks(context, std::move(mtrackCollection)); - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } -std::vector> -ActsExamples::MaterialMapping::scoringParameters(uint64_t surfaceID) { +std::vector> MaterialMapping::scoringParameters( + uint64_t surfaceID) { std::vector> scoringParameters; if (m_cfg.materialSurfaceMapper) { @@ -151,3 +147,5 @@ ActsExamples::MaterialMapping::scoringParameters(uint64_t surfaceID) { } return scoringParameters; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index fb1075ec716..4c6ad13c91a 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -21,6 +21,7 @@ #include "Acts/TrackFinding/TrackSelector.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" +#include "Acts/Utilities/TrackHelpers.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/Track.hpp" @@ -89,6 +90,11 @@ class TrackFindingAlgorithm final : public IAlgorithm { /// Output find trajectories collection. std::string outputTracks; + /// The tracking geometry that should be used. + std::shared_ptr trackingGeometry; + /// The magnetic field that should be used. + std::shared_ptr magneticField; + /// Type erased track finder function. std::shared_ptr findTracks; /// CKF measurement selector config @@ -100,11 +106,13 @@ class TrackFindingAlgorithm final : public IAlgorithm { Acts::TrackSelector::EtaBinnedConfig>> trackSelectorCfg = std::nullopt; - /// Run finding in two directions - bool twoWay = true; - /// Maximum number of propagation steps unsigned int maxSteps = 100000; + /// Extrapolation strategy + Acts::TrackExtrapolationStrategy extrapolationStrategy = + Acts::TrackExtrapolationStrategy::firstOrLast; + /// Run finding in two directions + bool twoWay = true; }; /// Constructor of the track finding algorithm @@ -146,6 +154,8 @@ class TrackFindingAlgorithm final : public IAlgorithm { mutable std::atomic m_nTotalSeeds{0}; mutable std::atomic m_nFailedSeeds{0}; + mutable std::atomic m_nFailedSmoothing{0}; + mutable std::atomic m_nFailedExtrapolation{0}; mutable tbb::combinable m_memoryStatistics{[]() { diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index 18ac1f72229..e72dffb59d9 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -13,9 +13,15 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/ProxyAccessor.hpp" #include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/EventData/VectorTrackContainer.hpp" +#include "Acts/Propagator/AbortList.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/MaterialInteractor.hpp" +#include "Acts/Propagator/Navigator.hpp" #include "Acts/Propagator/Propagator.hpp" +#include "Acts/Propagator/StandardAborters.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" @@ -23,6 +29,8 @@ #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/TrackFitting/KalmanFitter.hpp" #include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/TrackHelpers.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/Track.hpp" @@ -86,7 +94,6 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( PassThroughCalibrator pcalibrator; MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); Acts::GainMatrixUpdater kfUpdater; - Acts::GainMatrixSmoother kfSmoother; Acts::MeasurementSelector measSel{m_cfg.measurementSelectorCfg}; Acts::CombinatorialKalmanFilterExtensions @@ -96,9 +103,6 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( extensions.updater.connect< &Acts::GainMatrixUpdater::operator()>( &kfUpdater); - extensions.smoother.connect< - &Acts::GainMatrixSmoother::operator()>( - &kfSmoother); extensions.measurementSelector .connect<&Acts::MeasurementSelector::select>( &measSel); @@ -120,18 +124,22 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( // Set the CombinatorialKalmanFilter options ActsExamples::TrackFindingAlgorithm::TrackFinderOptions firstOptions( ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, - extensions, firstPropOptions, pSurface.get()); - firstOptions.smoothing = true; - firstOptions.smoothingTargetSurfaceStrategy = - Acts::CombinatorialKalmanFilterTargetSurfaceStrategy::first; + extensions, firstPropOptions); ActsExamples::TrackFindingAlgorithm::TrackFinderOptions secondOptions( ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, - extensions, secondPropOptions, pSurface.get()); - secondOptions.filterTargetSurface = pSurface.get(); - secondOptions.smoothing = true; - secondOptions.smoothingTargetSurfaceStrategy = - Acts::CombinatorialKalmanFilterTargetSurfaceStrategy::last; + extensions, secondPropOptions); + secondOptions.targetSurface = pSurface.get(); + + Acts::Propagator, Acts::Navigator> extrapolator( + Acts::EigenStepper<>(m_cfg.magneticField), + Acts::Navigator({m_cfg.trackingGeometry}, + logger().cloneWithSuffix("Navigator")), + logger().cloneWithSuffix("Propagator")); + + Acts::PropagatorOptions, + Acts::AbortList> + extrapolationOptions(ctx.geoContext, ctx.magFieldContext); // Perform the track finding for all initial parameters ACTS_DEBUG("Invoke track finding with " << initialParameters.size() @@ -171,6 +179,16 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( auto& firstTracksForSeed = firstResult.value(); for (auto& firstTrack : firstTracksForSeed) { + auto firstSmoothingResult = + Acts::smoothTrack(ctx.geoContext, firstTrack, logger()); + if (!firstSmoothingResult.ok()) { + m_nFailedSmoothing++; + ACTS_ERROR("Smoothing for seed " + << iseed << " and track " << firstTrack.index() + << " failed with error " << firstSmoothingResult.error()); + continue; + } + std::size_t nsecond = 0; // Set the seed number, this number decrease by 1 since the seed number @@ -223,16 +241,40 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( Acts::calculateTrackQuantities(secondTrack); + auto secondSmoothingResult = + Acts::smoothTrack(ctx.geoContext, secondTrack, logger()); + if (!secondSmoothingResult.ok()) { + m_nFailedSmoothing++; + ACTS_ERROR("Smoothing for seed " + << iseed << " and track " << secondTrack.index() + << " failed with error " + << secondSmoothingResult.error()); + continue; + } + if (!m_trackSelector.has_value() || m_trackSelector->isValidTrack(secondTrack)) { auto destProxy = tracks.getTrack(tracks.addTrack()); destProxy.copyFrom(secondTrack, true); } + ++nsecond; } } } + if (nsecond == 0) { + auto extrapolationResult = Acts::extrapolateTrackToReferenceSurface( + firstTrack, *pSurface, extrapolator, extrapolationOptions, + m_cfg.extrapolationStrategy, logger()); + if (!extrapolationResult.ok()) { + m_nFailedExtrapolation++; + ACTS_ERROR("Extrapolation for seed " + << iseed << " and track " << firstTrack.index() + << " failed with error " << extrapolationResult.error()); + continue; + } + if (!m_trackSelector.has_value() || m_trackSelector->isValidTrack(firstTrack)) { auto destProxy = tracks.getTrack(tracks.addTrack()); @@ -271,8 +313,10 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::finalize() { ACTS_INFO("TrackFindingAlgorithm statistics:"); ACTS_INFO("- total seeds: " << m_nTotalSeeds); ACTS_INFO("- failed seeds: " << m_nFailedSeeds); - ACTS_INFO("- failure ratio: " << static_cast(m_nFailedSeeds) / - m_nTotalSeeds); + ACTS_INFO("- failed smoothing: " << m_nFailedSmoothing); + ACTS_INFO("- failed extrapolation: " << m_nFailedExtrapolation); + ACTS_INFO("- failure ratio seeds: " << static_cast(m_nFailedSeeds) / + m_nTotalSeeds); auto memoryStatistics = m_memoryStatistics.combine([](const auto& a, const auto& b) { diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp index 493b5799d14..ae86f750b6c 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp @@ -94,9 +94,6 @@ ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( extensions.updater.connect< &Acts::GainMatrixUpdater::operator()>( &kfUpdater); - extensions.smoother.connect< - &Acts::GainMatrixSmoother::operator()>( - &kfSmoother); extensions.measurementSelector .connect<&Acts::MeasurementSelector::select>( &measSel); diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaNTupleReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaNTupleReader.hpp index 381deccf669..5725f840c87 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaNTupleReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootAthenaNTupleReader.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2017-2022 CERN for the benefit of the Acts project +// Copyright (C) 2022-2024 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 diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackReader.hpp index bb417d6fa3f..f755d0e8c5e 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackReader.hpp @@ -8,13 +8,13 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Material/MaterialInteraction.hpp" +#include "Acts/Propagator/MaterialInteractor.hpp" +#include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IReader.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" -#include -#include -#include #include #include @@ -28,7 +28,6 @@ class TChain; namespace ActsExamples { -struct AlgorithmContext; /// @class RootMaterialTrackReader /// @@ -39,13 +38,13 @@ class RootMaterialTrackReader : public IReader { public: /// @brief The nested configuration struct struct Config { - std::string collection = - "material-tracks"; ///< material collection to read - std::string treeName = "material-tracks"; ///< name of the output tree - std::vector fileList; ///< List of input files + /// material collection to read + std::string outputMaterialTracks = "material-tracks"; + /// name of the output tree + std::string treeName = "material-tracks"; + /// List of input files + std::vector fileList; - /// Whether the events are ordered or not - bool orderedEvents = true; // Read surface information for the root file bool readCachedSurfaceInformation = false; }; @@ -104,46 +103,62 @@ class RootMaterialTrackReader : public IReader { /// multiple entries corresponding to one event number) std::vector m_entryNumbers = {}; - float m_v_x = 0; ///< start global x - float m_v_y = 0; ///< start global y - float m_v_z = 0; ///< start global z - float m_v_px = 0; ///< start global momentum x - float m_v_py = 0; ///< start global momentum y - float m_v_pz = 0; ///< start global momentum z - float m_v_phi = 0; ///< start phi direction - float m_v_eta = 0; ///< start eta direction - float m_tX0 = 0; ///< thickness in X0/L0 - float m_tL0 = 0; ///< thickness in X0/L0 - - std::vector* m_step_x = new std::vector; ///< step x position - std::vector* m_step_y = new std::vector; ///< step y position - std::vector* m_step_z = new std::vector; ///< step z position - std::vector* m_step_dx = new std::vector; ///< step x direction - std::vector* m_step_dy = new std::vector; ///< step y direction - std::vector* m_step_dz = new std::vector; ///< step z direction - std::vector* m_step_length = new std::vector; ///< step length - std::vector* m_step_X0 = new std::vector; ///< step material x0 - std::vector* m_step_L0 = new std::vector; ///< step material l0 - std::vector* m_step_A = new std::vector; ///< step material A - std::vector* m_step_Z = new std::vector; ///< step material Z - std::vector* m_step_rho = - new std::vector; ///< step material rho - - std::vector* m_sur_id = - new std::vector; ///< ID of the surface associated with - ///< the step - std::vector* m_sur_x = - new std::vector; ///< x position of the center of the surface - ///< associated with the step - std::vector* m_sur_y = - new std::vector; ///< y position of the center of the surface - ///< associated with the step - std::vector* m_sur_z = - new std::vector; ///< z position of the center of the surface - ///< associated with the step - std::vector* m_sur_pathCorrection = - new std::vector; ///< path correction when associating - ///< material to the given surface + /// start global x + float m_v_x = 0; + /// start global y + float m_v_y = 0; + /// start global z + float m_v_z = 0; + /// start global momentum x + float m_v_px = 0; + /// start global momentum y + float m_v_py = 0; + /// start global momentum z + float m_v_pz = 0; + /// start phi direction + float m_v_phi = 0; + /// start eta direction + float m_v_eta = 0; + /// thickness in X0/L0 + float m_tX0 = 0; + /// thickness in X0/L0 + float m_tL0 = 0; + + /// step x position + std::vector* m_step_x = new std::vector; + /// step y position + std::vector* m_step_y = new std::vector; + /// step z position + std::vector* m_step_z = new std::vector; + /// step x direction + std::vector* m_step_dx = new std::vector; + /// step y direction + std::vector* m_step_dy = new std::vector; + /// step z direction + std::vector* m_step_dz = new std::vector; + /// step length + std::vector* m_step_length = new std::vector; + /// step material x0 + std::vector* m_step_X0 = new std::vector; + /// step material l0 + std::vector* m_step_L0 = new std::vector; + /// step material A + std::vector* m_step_A = new std::vector; + /// step material Z + std::vector* m_step_Z = new std::vector; + /// step material rho + std::vector* m_step_rho = new std::vector; + + /// ID of the surface associated with the step + std::vector* m_sur_id = new std::vector; + /// x position of the center of the surface associated with the step + std::vector* m_sur_x = new std::vector; + /// y position of the center of the surface associated with the step + std::vector* m_sur_y = new std::vector; + /// z position of the center of the surface associated with the step + std::vector* m_sur_z = new std::vector; + /// path correction when associating material to the given surface + std::vector* m_sur_pathCorrection = new std::vector; }; } // namespace ActsExamples diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackWriter.hpp index 9c8e9978706..dc7c5044b0d 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMaterialTrackWriter.hpp @@ -52,11 +52,14 @@ class RootMaterialTrackWriter std::unordered_map> { public: struct Config { - std::string collection = - "material-tracks"; ///< material collection to write - std::string filePath = ""; ///< path of the output file - std::string fileMode = "RECREATE"; ///< file access mode - std::string treeName = "material-tracks"; ///< name of the output tree + /// material collection to write + std::string inputMaterialTracks = "material-tracks"; + /// path of the output file + std::string filePath = ""; + /// file access mode + std::string fileMode = "RECREATE"; + /// name of the output tree + std::string treeName = "material-tracks"; /// Re-calculate total values from individual steps (for cross-checks) bool recalculateTotals = false; @@ -110,56 +113,83 @@ class RootMaterialTrackWriter /// Event identifier. uint32_t m_eventId = 0; - float m_v_x = 0; ///< start global x - float m_v_y = 0; ///< start global y - float m_v_z = 0; ///< start global z - float m_v_px = 0; ///< start global momentum x - float m_v_py = 0; ///< start global momentum y - float m_v_pz = 0; ///< start global momentum z - float m_v_phi = 0; ///< start phi direction - float m_v_eta = 0; ///< start eta direction - float m_tX0 = 0; ///< thickness in X0/L0 - float m_tL0 = 0; ///< thickness in X0/L0 - - std::vector m_step_sx; ///< step x (start) position (optional) - std::vector m_step_sy; ///< step y (start) position (optional) - std::vector m_step_sz; ///< step z (start) position (optional) - std::vector m_step_x; ///< step x position - std::vector m_step_y; ///< step y position - std::vector m_step_z; ///< step z position - std::vector m_step_ex; ///< step x (end) position (optional) - std::vector m_step_ey; ///< step y (end) position (optional) - std::vector m_step_ez; ///< step z (end) position (optional) - std::vector m_step_dx; ///< step x direction - std::vector m_step_dy; ///< step y direction - std::vector m_step_dz; ///< step z direction - std::vector m_step_length; ///< step length - std::vector m_step_X0; ///< step material x0 - std::vector m_step_L0; ///< step material l0 - std::vector m_step_A; ///< step material A - std::vector m_step_Z; ///< step material Z - std::vector m_step_rho; ///< step material rho - - std::vector - m_sur_id; ///< ID of the surface associated with the step - std::vector - m_sur_type; ///< Type of the surface associated with the step - std::vector m_sur_x; ///< x position of the center of the surface - ///< associated with the step - std::vector m_sur_y; ///< y position of the center of the surface - ///< associated with the step - std::vector m_sur_z; ///< z position of the center of the surface - ///< associated with the step - std::vector - m_sur_pathCorrection; ///< path correction when associating - ///< material to the given surface - std::vector - m_sur_range_min; ///< Min range of the surface associated with the step - std::vector - m_sur_range_max; ///< Max range of the surface associated with the step - - std::vector - m_vol_id; ///< ID of the volume associated with the step + /// start global x + float m_v_x = 0; + /// start global y + float m_v_y = 0; + /// start global z + float m_v_z = 0; + /// start global momentum x + float m_v_px = 0; + /// start global momentum y + float m_v_py = 0; + /// start global momentum z + float m_v_pz = 0; + /// start phi direction + float m_v_phi = 0; + /// start eta direction + float m_v_eta = 0; + /// thickness in X0/L0 + float m_tX0 = 0; + /// thickness in X0/L0 + float m_tL0 = 0; + + /// step x (start) position (optional) + std::vector m_step_sx; + /// step y (start) position (optional) + std::vector m_step_sy; + /// step z (start) position (optional) + std::vector m_step_sz; + /// step x position + std::vector m_step_x; + /// step y position + std::vector m_step_y; + /// step z position + std::vector m_step_z; + /// step x (end) position (optional) + std::vector m_step_ex; + /// step y (end) position (optional) + std::vector m_step_ey; + /// step z (end) position (optional) + std::vector m_step_ez; + /// step x direction + std::vector m_step_dx; + /// step y direction + std::vector m_step_dy; + /// step z direction + std::vector m_step_dz; + /// step length + std::vector m_step_length; + /// step material x0 + std::vector m_step_X0; + /// step material l0 + std::vector m_step_L0; + /// step material A + std::vector m_step_A; + /// step material Z + std::vector m_step_Z; + /// step material rho + std::vector m_step_rho; + + /// ID of the surface associated with the step + std::vector m_sur_id; + /// Type of the surface associated with the step + std::vector m_sur_type; + /// x position of the center of the surface associated with the step + std::vector m_sur_x; + /// y position of the center of the surface associated with the step + std::vector m_sur_y; + /// z position of the center of the surface associated with the step + std::vector m_sur_z; + /// path correction when associating material to the given surface + std::vector m_sur_pathCorrection; + /// Min range of the surface associated with the step + std::vector m_sur_range_min; + /// Max range of the surface associated with the step + std::vector m_sur_range_max; + + /// ID of the volume associated with the step + std::vector m_vol_id; }; } // namespace ActsExamples diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleReader.hpp index fae6750d880..064e6ad6835 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootParticleReader.hpp @@ -35,14 +35,12 @@ class RootParticleReader : public IReader { public: /// @brief The nested configuration struct struct Config { - ///< particle collection to read + /// particle collection to read std::string outputParticles = "particleCollection"; /// name of the output tree std::string treeName = "particles"; /// The name of the input file std::string filePath; - /// Whether the events are ordered or not - bool orderedEvents = true; }; /// Constructor diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp index d2b00434093..2d0bde82303 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp @@ -28,7 +28,6 @@ class TChain; namespace ActsExamples { -struct AlgorithmContext; /// @class RootParticleReader /// @@ -43,8 +42,6 @@ class RootSimHitReader : public IReader { std::string treeName = "hits"; ///< The name of the input file std::string filePath; - /// Whether the events are ordered or not - bool orderedEvents = true; }; RootSimHitReader(const RootSimHitReader &) = delete; diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackSummaryReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackSummaryReader.hpp index 8f39206c832..c4d145f20ee 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackSummaryReader.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootTrackSummaryReader.hpp @@ -29,7 +29,6 @@ class TChain; namespace ActsExamples { -struct AlgorithmContext; /// @class RootTrackSummaryReader /// @@ -47,9 +46,6 @@ class RootTrackSummaryReader : public IReader { std::string treeName = "tracksummary"; /// The name of the input file std::string filePath; - - /// Whether the events are ordered or not - bool orderedEvents = true; }; /// Constructor diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootUtility.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootUtility.hpp new file mode 100644 index 00000000000..892d11bdfaa --- /dev/null +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootUtility.hpp @@ -0,0 +1,57 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +#include + +namespace ActsExamples { +namespace RootUtility { + +/// @brief Sorts an array of elements and outputs the indices of the sorted elements. +/// +/// This function sorts an array `elements` containing `numElements` of generic +/// type `element_t`. It outputs an array `sortedIndices` of type `index_t` that +/// contains the indices of `elements` in sorted order. The sort order is +/// determined by the `sortDescending` flag; if `true`, the array is sorted in +/// descending order, otherwise in ascending order. +/// +/// This is a stable version of `TMath::Sort` that preserves the relative order +/// of equal elements. +/// +/// @tparam element_t The data type of the array elements to be sorted. +/// @tparam index_t The data type for indexing and counting elements in the arrays. +/// +/// @param numElements The number of elements in the `elements` array. +/// @param elements Pointer to the array of type `element_t` +/// @param sortedIndices Pointer to an array of `index_t` type where the sorted indices will be stored. +/// @param sortDescending Boolean flag indicating the sort order. `true` for descending, `false` for ascending. +/// +/// @note It is the caller's responsibility to ensure that the `sortedIndices` array is pre-allocated +/// with a length of at least `numElements`. Furthermore, the types of +/// `numElements` and `sortedIndices` must be consistent. +template +void stableSort(index_t numElements, const element_t* elements, + index_t* sortedIndices, Bool_t sortDescending) { + for (index_t i = 0; i < numElements; i++) { + sortedIndices[i] = i; + } + + if (sortDescending) { + std::stable_sort(sortedIndices, sortedIndices + numElements, + CompareDesc(elements)); + } else { + std::stable_sort(sortedIndices, sortedIndices + numElements, + CompareAsc(elements)); + } +} + +} // namespace RootUtility +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootAthenaNTupleReader.cpp b/Examples/Io/Root/src/RootAthenaNTupleReader.cpp index b21f74f60c6..cd1183124ac 100644 --- a/Examples/Io/Root/src/RootAthenaNTupleReader.cpp +++ b/Examples/Io/Root/src/RootAthenaNTupleReader.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2017-2022 CERN for the benefit of the Acts project +// Copyright (C) 2022-2024 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,6 +16,7 @@ #include "Acts/Vertexing/Vertex.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Io/Root/RootUtility.hpp" #include #include @@ -23,7 +24,6 @@ #include #include -#include ActsExamples::RootAthenaNTupleReader::RootAthenaNTupleReader( const ActsExamples::RootAthenaNTupleReader::Config& config, @@ -114,18 +114,6 @@ ActsExamples::RootAthenaNTupleReader::RootAthenaNTupleReader( m_events = m_inputChain->GetEntries(); ACTS_DEBUG("The full chain has " << m_events << " entries."); - - { - // The entry numbers for accessing events in increased order (there could be - // multiple entries corresponding to one event number) - std::vector m_entryNumbers; - // If the events are not in order, get the entry numbers for ordered events - m_entryNumbers.resize(m_events); - m_inputChain->Draw("EventNumber", "", "goff"); - // Sort to get the entry numbers of the ordered events - TMath::Sort(m_inputChain->GetEntries(), m_inputChain->GetV1(), - m_entryNumbers.data(), false); - } } ActsExamples::ProcessCode ActsExamples::RootAthenaNTupleReader::read( diff --git a/Examples/Io/Root/src/RootMaterialTrackReader.cpp b/Examples/Io/Root/src/RootMaterialTrackReader.cpp index 252f5e1dce5..69934cd743b 100644 --- a/Examples/Io/Root/src/RootMaterialTrackReader.cpp +++ b/Examples/Io/Root/src/RootMaterialTrackReader.cpp @@ -14,18 +14,20 @@ #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Io/Root/RootUtility.hpp" #include #include #include #include -#include #include -ActsExamples::RootMaterialTrackReader::RootMaterialTrackReader( - const Config& config, Acts::Logging::Level level) - : ActsExamples::IReader(), +namespace ActsExamples { + +RootMaterialTrackReader::RootMaterialTrackReader(const Config& config, + Acts::Logging::Level level) + : IReader(), m_logger{Acts::getDefaultLogger(name(), level)}, m_cfg(config) { if (m_cfg.fileList.empty()) { @@ -45,9 +47,7 @@ ActsExamples::RootMaterialTrackReader::RootMaterialTrackReader( // get the number of entries, which also loads the tree std::size_t nentries = m_inputChain->GetEntries(); - bool eventIdPresent = - (TTree::kMatch == m_inputChain->SetBranchAddress("event_id", &m_eventId)); - + m_inputChain->SetBranchAddress("event_id", &m_eventId); m_inputChain->SetBranchAddress("v_x", &m_v_x); m_inputChain->SetBranchAddress("v_y", &m_v_y); m_inputChain->SetBranchAddress("v_z", &m_v_z); @@ -78,10 +78,7 @@ ActsExamples::RootMaterialTrackReader::RootMaterialTrackReader( m_inputChain->SetBranchAddress("sur_pathCorrection", &m_sur_pathCorrection); } - m_events = - eventIdPresent - ? static_cast(m_inputChain->GetMaximum("event_id") + 1) - : nentries; + m_events = static_cast(m_inputChain->GetMaximum("event_id") + 1); m_batchSize = nentries / m_events; ACTS_DEBUG("The full chain has " << nentries << " entries for " << m_events @@ -90,23 +87,18 @@ ActsExamples::RootMaterialTrackReader::RootMaterialTrackReader( << " events this corresponds to a batch size of: " << m_batchSize << std::endl; - // If the events are not in order, get the entry numbers for ordered events - if (!m_cfg.orderedEvents) { - if (!eventIdPresent) { - throw std::invalid_argument{ - "'event_id' branch is missing in your tree. This is not compatible " - "with unordered events."}; - } + // Sort the entry numbers of the events + { m_entryNumbers.resize(nentries); m_inputChain->Draw("event_id", "", "goff"); - // Sort to get the entry numbers of the ordered events - TMath::Sort(m_inputChain->GetEntries(), m_inputChain->GetV1(), - m_entryNumbers.data(), false); + RootUtility::stableSort(m_inputChain->GetEntries(), m_inputChain->GetV1(), + m_entryNumbers.data(), false); } - m_outputMaterialTracks.initialize(m_cfg.collection); + + m_outputMaterialTracks.initialize(m_cfg.outputMaterialTracks); } -ActsExamples::RootMaterialTrackReader::~RootMaterialTrackReader() { +RootMaterialTrackReader::~RootMaterialTrackReader() { delete m_inputChain; delete m_step_x; @@ -129,108 +121,108 @@ ActsExamples::RootMaterialTrackReader::~RootMaterialTrackReader() { delete m_sur_pathCorrection; } -std::string ActsExamples::RootMaterialTrackReader::name() const { +std::string RootMaterialTrackReader::name() const { return "RootMaterialTrackReader"; } -std::pair -ActsExamples::RootMaterialTrackReader::availableEvents() const { +std::pair RootMaterialTrackReader::availableEvents() + const { return {0u, m_events}; } -ActsExamples::ProcessCode ActsExamples::RootMaterialTrackReader::read( - const ActsExamples::AlgorithmContext& context) { +ProcessCode RootMaterialTrackReader::read(const AlgorithmContext& context) { ACTS_DEBUG("Trying to read recorded material from tracks."); - // read in the material track - if (m_inputChain != nullptr && context.eventNumber < m_events) { - // lock the mutex - std::lock_guard lock(m_read_mutex); - // now read - - // The collection to be written - std::unordered_map - mtrackCollection; - - // Loop over the entries for this event - for (std::size_t ib = 0; ib < m_batchSize; ++ib) { - // Read the correct entry: startEntry + ib - auto entry = m_batchSize * context.eventNumber + ib; - if (!m_cfg.orderedEvents && entry < m_entryNumbers.size()) { - entry = m_entryNumbers[entry]; + + if (m_inputChain == nullptr || context.eventNumber >= m_events) { + return ProcessCode::SUCCESS; + } + + // lock the mutex + std::lock_guard lock(m_read_mutex); + // now read + + // The collection to be written + std::unordered_map mtrackCollection; + + // Loop over the entries for this event + for (std::size_t ib = 0; ib < m_batchSize; ++ib) { + // Read the correct entry: startEntry + ib + auto entry = m_batchSize * context.eventNumber + ib; + entry = m_entryNumbers.at(entry); + ACTS_VERBOSE("Reading event: " << context.eventNumber + << " with stored entry: " << entry); + m_inputChain->GetEntry(entry); + + Acts::RecordedMaterialTrack rmTrack; + // Fill the position and momentum + rmTrack.first.first = Acts::Vector3(m_v_x, m_v_y, m_v_z); + rmTrack.first.second = Acts::Vector3(m_v_px, m_v_py, m_v_pz); + + ACTS_VERBOSE("Track vertex: " << rmTrack.first.first); + ACTS_VERBOSE("Track momentum:" << rmTrack.first.second); + + // Fill the individual steps + std::size_t msteps = m_step_length->size(); + ACTS_VERBOSE("Reading " << msteps << " material steps."); + rmTrack.second.materialInteractions.reserve(msteps); + rmTrack.second.materialInX0 = 0.; + rmTrack.second.materialInL0 = 0.; + + for (std::size_t is = 0; is < msteps; ++is) { + ACTS_VERBOSE("===================="); + ACTS_VERBOSE("[" << is + 1 << "/" << msteps << "] STEP INFORMATION: "); + + double s = (*m_step_length)[is]; + if (s == 0) { + ACTS_VERBOSE("invalid step length... skipping!"); + continue; } - ACTS_VERBOSE("Reading event: " << context.eventNumber - << " with stored entry: " << entry); - m_inputChain->GetEntry(entry); - - Acts::RecordedMaterialTrack rmTrack; - // Fill the position and momentum - rmTrack.first.first = Acts::Vector3(m_v_x, m_v_y, m_v_z); - rmTrack.first.second = Acts::Vector3(m_v_px, m_v_py, m_v_pz); - - ACTS_VERBOSE("Track vertex: " << rmTrack.first.first); - ACTS_VERBOSE("Track momentum:" << rmTrack.first.second); - - // Fill the individual steps - std::size_t msteps = m_step_length->size(); - ACTS_VERBOSE("Reading " << msteps << " material steps."); - rmTrack.second.materialInteractions.reserve(msteps); - rmTrack.second.materialInX0 = 0.; - rmTrack.second.materialInL0 = 0.; - - for (std::size_t is = 0; is < msteps; ++is) { - ACTS_VERBOSE("===================="); - ACTS_VERBOSE("[" << is + 1 << "/" << msteps << "] STEP INFORMATION: "); - - double s = (*m_step_length)[is]; - if (s == 0) { - ACTS_VERBOSE("invalid step length... skipping!"); - continue; - } - - double mX0 = (*m_step_X0)[is]; - double mL0 = (*m_step_L0)[is]; - - rmTrack.second.materialInX0 += s / mX0; - rmTrack.second.materialInL0 += s / mL0; - /// Fill the position & the material - Acts::MaterialInteraction mInteraction; - mInteraction.position = - Acts::Vector3((*m_step_x)[is], (*m_step_y)[is], (*m_step_z)[is]); - ACTS_VERBOSE("POSITION : " << (*m_step_x)[is] << ", " << (*m_step_y)[is] - << ", " << (*m_step_z)[is]); - mInteraction.direction = - Acts::Vector3((*m_step_dx)[is], (*m_step_dy)[is], (*m_step_dz)[is]); - ACTS_VERBOSE("DIRECTION: " << (*m_step_dx)[is] << ", " - << (*m_step_dy)[is] << ", " - << (*m_step_dz)[is]); - mInteraction.materialSlab = Acts::MaterialSlab( - Acts::Material::fromMassDensity(mX0, mL0, (*m_step_A)[is], - (*m_step_Z)[is], (*m_step_rho)[is]), - s); - ACTS_VERBOSE("MATERIAL: " << mX0 << ", " << mL0 << ", " - << (*m_step_A)[is] << ", " << (*m_step_Z)[is] - << ", " << (*m_step_rho)[is]); - ACTS_VERBOSE("===================="); - - if (m_cfg.readCachedSurfaceInformation) { - // add the surface information to the interaction this allows the - // mapping to be speed up - mInteraction.intersectionID = - Acts::GeometryIdentifier((*m_sur_id)[is]); - mInteraction.intersection = - Acts::Vector3((*m_sur_x)[is], (*m_sur_y)[is], (*m_sur_z)[is]); - mInteraction.pathCorrection = (*m_sur_pathCorrection)[is]; - } else { - mInteraction.intersectionID = Acts::GeometryIdentifier(); - mInteraction.intersection = Acts::Vector3(0, 0, 0); - } - rmTrack.second.materialInteractions.push_back(std::move(mInteraction)); + + double mX0 = (*m_step_X0)[is]; + double mL0 = (*m_step_L0)[is]; + + rmTrack.second.materialInX0 += s / mX0; + rmTrack.second.materialInL0 += s / mL0; + /// Fill the position & the material + Acts::MaterialInteraction mInteraction; + mInteraction.position = + Acts::Vector3((*m_step_x)[is], (*m_step_y)[is], (*m_step_z)[is]); + ACTS_VERBOSE("POSITION : " << (*m_step_x)[is] << ", " << (*m_step_y)[is] + << ", " << (*m_step_z)[is]); + mInteraction.direction = + Acts::Vector3((*m_step_dx)[is], (*m_step_dy)[is], (*m_step_dz)[is]); + ACTS_VERBOSE("DIRECTION: " << (*m_step_dx)[is] << ", " << (*m_step_dy)[is] + << ", " << (*m_step_dz)[is]); + mInteraction.materialSlab = Acts::MaterialSlab( + Acts::Material::fromMassDensity(mX0, mL0, (*m_step_A)[is], + (*m_step_Z)[is], (*m_step_rho)[is]), + s); + ACTS_VERBOSE("MATERIAL: " << mX0 << ", " << mL0 << ", " << (*m_step_A)[is] + << ", " << (*m_step_Z)[is] << ", " + << (*m_step_rho)[is]); + ACTS_VERBOSE("===================="); + + if (m_cfg.readCachedSurfaceInformation) { + // add the surface information to the interaction this allows the + // mapping to be speed up + mInteraction.intersectionID = Acts::GeometryIdentifier((*m_sur_id)[is]); + mInteraction.intersection = + Acts::Vector3((*m_sur_x)[is], (*m_sur_y)[is], (*m_sur_z)[is]); + mInteraction.pathCorrection = (*m_sur_pathCorrection)[is]; + } else { + mInteraction.intersectionID = Acts::GeometryIdentifier(); + mInteraction.intersection = Acts::Vector3(0, 0, 0); } - mtrackCollection[ib] = (std::move(rmTrack)); + rmTrack.second.materialInteractions.push_back(std::move(mInteraction)); } - // Write to the collection to the EventStore - m_outputMaterialTracks(context, std::move(mtrackCollection)); + mtrackCollection[ib] = (std::move(rmTrack)); } + + // Write to the collection to the EventStore + m_outputMaterialTracks(context, std::move(mtrackCollection)); + // Return success flag - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootMaterialTrackWriter.cpp b/Examples/Io/Root/src/RootMaterialTrackWriter.cpp index 29044d78b5b..0d6ed47e7fb 100644 --- a/Examples/Io/Root/src/RootMaterialTrackWriter.cpp +++ b/Examples/Io/Root/src/RootMaterialTrackWriter.cpp @@ -35,13 +35,14 @@ using Acts::VectorHelpers::eta; using Acts::VectorHelpers::perp; using Acts::VectorHelpers::phi; -ActsExamples::RootMaterialTrackWriter::RootMaterialTrackWriter( - const ActsExamples::RootMaterialTrackWriter::Config& config, - Acts::Logging::Level level) - : WriterT(config.collection, "RootMaterialTrackWriter", level), +namespace ActsExamples { + +RootMaterialTrackWriter::RootMaterialTrackWriter( + const RootMaterialTrackWriter::Config& config, Acts::Logging::Level level) + : WriterT(config.inputMaterialTracks, "RootMaterialTrackWriter", level), m_cfg(config) { // An input collection name and tree name must be specified - if (m_cfg.collection.empty()) { + if (m_cfg.inputMaterialTracks.empty()) { throw std::invalid_argument("Missing input collection"); } else if (m_cfg.treeName.empty()) { throw std::invalid_argument("Missing tree name"); @@ -108,13 +109,13 @@ ActsExamples::RootMaterialTrackWriter::RootMaterialTrackWriter( } } -ActsExamples::RootMaterialTrackWriter::~RootMaterialTrackWriter() { +RootMaterialTrackWriter::~RootMaterialTrackWriter() { if (m_outputFile != nullptr) { m_outputFile->Close(); } } -ActsExamples::ProcessCode ActsExamples::RootMaterialTrackWriter::finalize() { +ProcessCode RootMaterialTrackWriter::finalize() { // write the tree and close the file ACTS_INFO("Writing ROOT output File : " << m_cfg.filePath); @@ -122,10 +123,10 @@ ActsExamples::ProcessCode ActsExamples::RootMaterialTrackWriter::finalize() { m_outputTree->Write(); m_outputFile->Close(); - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } -ActsExamples::ProcessCode ActsExamples::RootMaterialTrackWriter::writeT( +ProcessCode RootMaterialTrackWriter::writeT( const AlgorithmContext& ctx, const std::unordered_map& materialTracks) { @@ -366,5 +367,7 @@ ActsExamples::ProcessCode ActsExamples::RootMaterialTrackWriter::writeT( } // return success - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootParticleReader.cpp b/Examples/Io/Root/src/RootParticleReader.cpp index eb39c163ffe..34a5bd12e12 100644 --- a/Examples/Io/Root/src/RootParticleReader.cpp +++ b/Examples/Io/Root/src/RootParticleReader.cpp @@ -12,6 +12,7 @@ #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Io/Root/RootUtility.hpp" #include "ActsFatras/EventData/ProcessType.hpp" #include @@ -20,7 +21,6 @@ #include #include -#include namespace ActsExamples { @@ -73,13 +73,12 @@ RootParticleReader::RootParticleReader(const RootParticleReader::Config& config, m_events = m_inputChain->GetEntries(); ACTS_DEBUG("The full chain has " << m_events << " entries."); - // If the events are not in order, get the entry numbers for ordered events - if (!m_cfg.orderedEvents) { + // Sort the entry numbers of the events + { m_entryNumbers.resize(m_events); m_inputChain->Draw("event_id", "", "goff"); - // Sort to get the entry numbers of the ordered events - TMath::Sort(m_inputChain->GetEntries(), m_inputChain->GetV1(), - m_entryNumbers.data(), false); + RootUtility::stableSort(m_inputChain->GetEntries(), m_inputChain->GetV1(), + m_entryNumbers.data(), false); } } @@ -127,13 +126,10 @@ ProcessCode RootParticleReader::read(const AlgorithmContext& context) { SimParticleContainer particles; // Read the correct entry - auto entry = context.eventNumber; - if (!m_cfg.orderedEvents && entry < m_entryNumbers.size()) { - entry = m_entryNumbers[entry]; - } + auto entry = m_entryNumbers.at(context.eventNumber); m_inputChain->GetEntry(entry); - ACTS_INFO("Reading event: " << context.eventNumber - << " stored as entry: " << entry); + ACTS_DEBUG("Reading event: " << context.eventNumber + << " stored as entry: " << entry); unsigned int nParticles = m_particleId->size(); @@ -156,6 +152,9 @@ ProcessCode RootParticleReader::read(const AlgorithmContext& context) { particles.insert(p); } + ACTS_DEBUG("Read " << particles.size() << " particles for event " + << context.eventNumber); + // Write the collections to the EventStore m_outputParticles(context, std::move(particles)); diff --git a/Examples/Io/Root/src/RootSimHitReader.cpp b/Examples/Io/Root/src/RootSimHitReader.cpp index b115c8dc5f4..cea62954727 100644 --- a/Examples/Io/Root/src/RootSimHitReader.cpp +++ b/Examples/Io/Root/src/RootSimHitReader.cpp @@ -22,10 +22,11 @@ #include #include -ActsExamples::RootSimHitReader::RootSimHitReader( - const ActsExamples::RootSimHitReader::Config& config, - Acts::Logging::Level level) - : ActsExamples::IReader(), +namespace ActsExamples { + +RootSimHitReader::RootSimHitReader(const RootSimHitReader::Config& config, + Acts::Logging::Level level) + : IReader(), m_cfg(config), m_logger(Acts::getDefaultLogger(name(), level)) { m_inputChain = new TChain(m_cfg.treeName.c_str()); @@ -101,13 +102,11 @@ ActsExamples::RootSimHitReader::RootSimHitReader( << availableEvents().second); } -std::pair -ActsExamples::RootSimHitReader::availableEvents() const { +std::pair RootSimHitReader::availableEvents() const { return {std::get<0>(m_eventMap.front()), std::get<0>(m_eventMap.back()) + 1}; } -ActsExamples::ProcessCode ActsExamples::RootSimHitReader::read( - const ActsExamples::AlgorithmContext& context) { +ProcessCode RootSimHitReader::read(const AlgorithmContext& context) { auto it = std::find_if( m_eventMap.begin(), m_eventMap.end(), [&](const auto& a) { return std::get<0>(a) == context.eventNumber; }); @@ -125,7 +124,7 @@ ActsExamples::ProcessCode ActsExamples::RootSimHitReader::read( m_outputSimHits(context, {}); // Return success flag - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } // lock the mutex @@ -174,8 +173,13 @@ ActsExamples::ProcessCode ActsExamples::RootSimHitReader::read( hits.insert(hit); } + ACTS_DEBUG("Read " << hits.size() << " hits for event " + << context.eventNumber); + m_outputSimHits(context, std::move(hits)); // Return success flag - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootTrackSummaryReader.cpp b/Examples/Io/Root/src/RootTrackSummaryReader.cpp index af8b7dd02e2..5ce1fee3ee8 100644 --- a/Examples/Io/Root/src/RootTrackSummaryReader.cpp +++ b/Examples/Io/Root/src/RootTrackSummaryReader.cpp @@ -16,18 +16,19 @@ #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Io/Root/RootUtility.hpp" #include "ActsFatras/EventData/Particle.hpp" #include #include #include -#include -ActsExamples::RootTrackSummaryReader::RootTrackSummaryReader( - const ActsExamples::RootTrackSummaryReader::Config& config, - Acts::Logging::Level level) - : ActsExamples::IReader(), +namespace ActsExamples { + +RootTrackSummaryReader::RootTrackSummaryReader( + const RootTrackSummaryReader::Config& config, Acts::Logging::Level level) + : IReader(), m_logger{Acts::getDefaultLogger(name(), level)}, m_cfg(config) { m_inputChain = new TChain(m_cfg.treeName.c_str()); @@ -96,22 +97,21 @@ ActsExamples::RootTrackSummaryReader::RootTrackSummaryReader( m_events = m_inputChain->GetEntries(); ACTS_DEBUG("The full chain has " << m_events << " entries."); - // If the events are not in order, get the entry numbers for ordered events - if (!m_cfg.orderedEvents) { + // Sort the entry numbers of the events + { m_entryNumbers.resize(m_events); m_inputChain->Draw("event_nr", "", "goff"); - // Sort to get the entry numbers of the ordered events - TMath::Sort(m_inputChain->GetEntries(), m_inputChain->GetV1(), - m_entryNumbers.data(), false); + RootUtility::stableSort(m_inputChain->GetEntries(), m_inputChain->GetV1(), + m_entryNumbers.data(), false); } } -std::pair -ActsExamples::RootTrackSummaryReader::availableEvents() const { +std::pair RootTrackSummaryReader::availableEvents() + const { return {0u, m_events}; } -ActsExamples::RootTrackSummaryReader::~RootTrackSummaryReader() { +RootTrackSummaryReader::~RootTrackSummaryReader() { delete m_multiTrajNr; delete m_subTrajNr; delete m_nStates; @@ -155,8 +155,7 @@ ActsExamples::RootTrackSummaryReader::~RootTrackSummaryReader() { delete m_err_eT_fit; } -ActsExamples::ProcessCode ActsExamples::RootTrackSummaryReader::read( - const ActsExamples::AlgorithmContext& context) { +ProcessCode RootTrackSummaryReader::read(const AlgorithmContext& context) { ACTS_DEBUG("Trying to read recorded tracks."); // read in the fitted track parameters and particles @@ -174,10 +173,7 @@ ActsExamples::ProcessCode ActsExamples::RootTrackSummaryReader::read( SimParticleContainer truthParticleCollection; // Read the correct entry - auto entry = context.eventNumber; - if (!m_cfg.orderedEvents && entry < m_entryNumbers.size()) { - entry = m_entryNumbers[entry]; - } + auto entry = m_entryNumbers.at(context.eventNumber); m_inputChain->GetEntry(entry); ACTS_INFO("Reading event: " << context.eventNumber << " stored as entry: " << entry); @@ -229,5 +225,7 @@ ActsExamples::ProcessCode ActsExamples::RootTrackSummaryReader::read( ACTS_WARNING("Could not read in event."); } // Return success flag - return ActsExamples::ProcessCode::SUCCESS; + return ProcessCode::SUCCESS; } + +} // namespace ActsExamples diff --git a/Examples/Python/python/acts/examples/reconstruction.py b/Examples/Python/python/acts/examples/reconstruction.py index 21843a010ef..52f8947c246 100644 --- a/Examples/Python/python/acts/examples/reconstruction.py +++ b/Examples/Python/python/acts/examples/reconstruction.py @@ -1264,6 +1264,8 @@ def addCKFTracks( trackingGeometry, field, customLogLevel() ), **acts.examples.defaultKWArgs( + trackingGeometry=trackingGeometry, + magneticField=field, trackSelectorCfg=trkSelCfg, maxSteps=ckfConfig.maxSteps, twoWay=twoWay, diff --git a/Examples/Python/src/Input.cpp b/Examples/Python/src/Input.cpp index e22e63b6fad..850b989574d 100644 --- a/Examples/Python/src/Input.cpp +++ b/Examples/Python/src/Input.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2021 CERN for the benefit of the Acts project +// Copyright (C) 2021-2024 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 @@ -41,20 +41,19 @@ void addInput(Context& ctx) { // ROOT READERS ACTS_PYTHON_DECLARE_READER(ActsExamples::RootParticleReader, mex, "RootParticleReader", outputParticles, treeName, - filePath, orderedEvents); + filePath); ACTS_PYTHON_DECLARE_READER(ActsExamples::RootVertexReader, mex, "RootVertexReader", outputVertices, treeName, filePath, orderedEvents); ACTS_PYTHON_DECLARE_READER(ActsExamples::RootMaterialTrackReader, mex, - "RootMaterialTrackReader", collection, treeName, - fileList, orderedEvents, - readCachedSurfaceInformation); + "RootMaterialTrackReader", outputMaterialTracks, + treeName, fileList, readCachedSurfaceInformation); - ACTS_PYTHON_DECLARE_READER( - ActsExamples::RootTrackSummaryReader, mex, "RootTrackSummaryReader", - outputTracks, outputParticles, treeName, filePath, orderedEvents); + ACTS_PYTHON_DECLARE_READER(ActsExamples::RootTrackSummaryReader, mex, + "RootTrackSummaryReader", outputTracks, + outputParticles, treeName, filePath); // CSV READERS ACTS_PYTHON_DECLARE_READER(ActsExamples::CsvParticleReader, mex, diff --git a/Examples/Python/src/Material.cpp b/Examples/Python/src/Material.cpp index 9978fa0fdbd..cdabe0ff2c0 100644 --- a/Examples/Python/src/Material.cpp +++ b/Examples/Python/src/Material.cpp @@ -110,7 +110,7 @@ void addMaterial(Context& ctx) { const Acts::MagneticFieldContext&>()); ACTS_PYTHON_STRUCT_BEGIN(c, Alg::Config); - ACTS_PYTHON_MEMBER(collection); + ACTS_PYTHON_MEMBER(inputMaterialTracks); ACTS_PYTHON_MEMBER(mappingMaterialCollection); ACTS_PYTHON_MEMBER(materialSurfaceMapper); ACTS_PYTHON_MEMBER(materialVolumeMapper); diff --git a/Examples/Python/src/Output.cpp b/Examples/Python/src/Output.cpp index bb5ebaeb026..daa4c90cbe4 100644 --- a/Examples/Python/src/Output.cpp +++ b/Examples/Python/src/Output.cpp @@ -206,10 +206,10 @@ void addOutput(Context& ctx) { inputMeasurementParticlesMap, inputMeasurementSimHitsMap, filePath, treeName, fileMode); - ACTS_PYTHON_DECLARE_WRITER(ActsExamples::RootMaterialTrackWriter, mex, - "RootMaterialTrackWriter", collection, filePath, - fileMode, treeName, recalculateTotals, prePostStep, - storeSurface, storeVolume, collapseInteractions); + ACTS_PYTHON_DECLARE_WRITER( + ActsExamples::RootMaterialTrackWriter, mex, "RootMaterialTrackWriter", + inputMaterialTracks, filePath, fileMode, treeName, recalculateTotals, + prePostStep, storeSurface, storeVolume, collapseInteractions); { using Writer = ActsExamples::RootBFieldWriter; diff --git a/Examples/Python/src/TrackFinding.cpp b/Examples/Python/src/TrackFinding.cpp index 38791dae89b..5d0a77c42bc 100644 --- a/Examples/Python/src/TrackFinding.cpp +++ b/Examples/Python/src/TrackFinding.cpp @@ -326,11 +326,13 @@ void addTrackFinding(Context& ctx) { ACTS_PYTHON_MEMBER(inputSourceLinks); ACTS_PYTHON_MEMBER(inputInitialTrackParameters); ACTS_PYTHON_MEMBER(outputTracks); + ACTS_PYTHON_MEMBER(trackingGeometry); + ACTS_PYTHON_MEMBER(magneticField); ACTS_PYTHON_MEMBER(findTracks); ACTS_PYTHON_MEMBER(measurementSelectorCfg); ACTS_PYTHON_MEMBER(trackSelectorCfg); - ACTS_PYTHON_MEMBER(twoWay); ACTS_PYTHON_MEMBER(maxSteps); + ACTS_PYTHON_MEMBER(twoWay); ACTS_PYTHON_STRUCT_END(); } diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index d3f48b94dc3..19df6b49cba 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -44,22 +44,22 @@ test_digitization_example_input[smeared]__particles.root: 7eec62018b6944fea565da test_digitization_example_input[smeared]__measurements.root: 0c168d371d0130c68d1ee44bd77eeeb3cf702a77c2afbf12bed8354b61a29262 test_digitization_example_input[geometric]__particles.root: 7eec62018b6944fea565dad75aa41ef87d1f2737b2a814fbab189817ac8180fe test_digitization_example_input[geometric]__measurements.root: 0c6d88b4de3ee7365103b8f0d6be6b4db3d7b7f2a59d3db58a1e5f89fa8130b3 -test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: 74fec9229975a9e1b56ce880a3dded68646064c1d0354b90310ad9f085a49ed2 -test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: a242fa23ea54b652ebe248d60865f9272e0c507640cdd012a6088dd8c67d64dd +test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: aced52d6f9ae94ce0a1eb051e85818eac63e2190ac9813f5b7cfaf76ef701bc7 +test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 77369733cff95d2fc27e132ed63036f7733dcad984936617df12e83c0c1d702a test_ckf_tracks_example[generic-full_seeding]__performance_seeding_trees.root: 0e0676ffafdb27112fbda50d1cf627859fa745760f98073261dcf6db3f2f991e -test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: b0d1e19005d4f28c653073759d9fb1566ccd52f38285500d6e8e63c0770fe722 -test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 608edf2873e024a9d4295d5e7d400be024277be5b429ec41550ec82587d3f3f2 +test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: cc304bbd280d3ae25e56b30436154157d65878882d368d99d62d0b0ec2d3a096 +test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 6dd63359b91b8ddbbb41e173ac3acd64ea30750526c325c87b22113442c7af41 test_ckf_tracks_example[generic-truth_estimated]__performance_seeding.root: 1facb05c066221f6361b61f015cdf0918e94d9f3fce2269ec7b6a4dffeb2bc7e -test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 36cb2554ab9a54246672e979c9bd81bb59f1861cbb05f8169bea6adc6ece21a0 -test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 311577de204bcad4c81f30114bd7afa226e86d90a68ed4214f621dfd6d09dc51 -test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: 0ccff5edf6a96adde11b3930c32a032c8b3821e8803d012c0b4e1427aeba43ff -test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: b8dd9a4637712ab0a94ce4dbf8f2389c98b36d0a8ddb40deb88099e126a8dd34 +test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 8e4e1096c5ab7b5095be05ca12b2b89a0ce0a09911e5d8b0f21ae92e5598d74c +test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 576cbc7315e631d98b1a9c4fe8fb935dbc1539fc201a3a62363bf03f62457271 +test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: b6f5a20982d7a721b076bcf1fd44a359285932a4679600beac6fb27663d7e7d4 +test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 19df90dcc7f4597bc8d64e84f260a54d21f85af20a1d6a0e84b0eaab669aa031 test_ckf_tracks_example[odd-full_seeding]__performance_seeding_trees.root: 43c58577aafe07645e5660c4f43904efadf91d8cda45c5c04c248bbe0f59814f -test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 9857151ca2e6349fdb41303d766797a317c4c2bfad22992ea06c47dc0fc166a1 -test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 4414a14c782ae44a62afcd83aed56f68efd6b77bb3c190bd82c8664e36f589de +test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 787b46d715fb96a9d5112be1f64f063197736c3cbc2ef2c76328478f1989d3c5 +test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 8479a2bbf23311491445a55101ddc4f32a7a8a03d2a39934561a48324615ceff test_ckf_tracks_example[odd-truth_estimated]__performance_seeding.root: 1a36b7017e59f1c08602ef3c2cb0483c51df248f112e3780c66594110719c575 -test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 8aaf086f393611df4174bca6b56a08315412283a0cf999effccc01907aa2fb06 -test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: adfbf4711b1d06f716416904730f8dc4dbb77f75ef363580f1b6ab0ac78cc501 +test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: 8b8b20097b615fd2eb32036ec77a8afecba8a43a05191f011fa84b220349403f +test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: d631a6f71c8a7f0d34789fba21ca3e9896bad2578abbe7969c64ae4605494c1e test_vertex_fitting_reading[Truth-False-100]__performance_vertexing.root: 76ef6084d758dfdfc0151ddec2170e12d73394424e3dac4ffe46f0f339ec8293 test_vertex_fitting_reading[Iterative-False-100]__performance_vertexing.root: 60372210c830a04f95ceb78c6c68a9b0de217746ff59e8e73053750c837b57eb test_vertex_fitting_reading[Iterative-True-100]__performance_vertexing.root: e34f217d524a5051dbb04a811d3407df3ebe2cc4bb7f54f6bda0847dbd7b52c3 diff --git a/Examples/Python/tests/test_reader.py b/Examples/Python/tests/test_reader.py index 8cb8b3a67bd..c888453a94c 100644 --- a/Examples/Python/tests/test_reader.py +++ b/Examples/Python/tests/test_reader.py @@ -149,6 +149,7 @@ def test_root_material_track_reader(material_recording): RootMaterialTrackReader( level=acts.logging.INFO, fileList=[str(input_tracks)], + outputMaterialTracks="material-tracks", ) ) diff --git a/Examples/Scripts/Optimization/ckf.py b/Examples/Scripts/Optimization/ckf.py index 264fca80544..36ca7a634e9 100755 --- a/Examples/Scripts/Optimization/ckf.py +++ b/Examples/Scripts/Optimization/ckf.py @@ -1,16 +1,12 @@ #!/usr/bin/env python3 + from pathlib import Path -from typing import Optional, Union -from collections import namedtuple +from typing import Optional import argparse -import sys -import os - -from acts.examples import Sequencer, GenericDetector, RootParticleReader import acts - from acts import UnitConstants as u +from acts.examples import GenericDetector, RootParticleReader def getArgumentParser(): @@ -165,7 +161,6 @@ def runCKFTracks( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles="particles_input", - orderedEvents=False, ) ) diff --git a/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py b/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py index 8bf12c3359b..f30f7d355a8 100755 --- a/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py +++ b/Examples/Scripts/Python/Auto-tuning/Orion/material_mapping_optimisation.py @@ -69,7 +69,7 @@ def runMaterialMappingNoTrack( s.addReader( RootMaterialTrackReader( level=acts.logging.INFO, - collection="material-tracks", + outputMaterialTracks="material-tracks", fileList=[ os.path.join( inputDir, @@ -85,7 +85,7 @@ def runMaterialMappingNoTrack( stepper = StraightLineStepper() mmAlgCfg = MaterialMapping.Config(context.geoContext, context.magFieldContext) mmAlgCfg.trackingGeometry = trackingGeometry - mmAlgCfg.collection = "material-tracks" + mmAlgCfg.inputMaterialTracks = "material-tracks" if mapSurface: navigator = Navigator( @@ -225,7 +225,7 @@ def runMaterialMappingVariance( # Read material step information from a ROOT TTRee reader = RootMaterialTrackReader( level=acts.logging.ERROR, - collection="material-tracks", + outputMaterialTracks="material-tracks", fileList=[ os.path.join( inputPath, @@ -240,7 +240,7 @@ def runMaterialMappingVariance( stepper = StraightLineStepper() mmAlgCfg = MaterialMapping.Config(context.geoContext, context.magFieldContext) mmAlgCfg.trackingGeometry = trackingGeometryVar - mmAlgCfg.collection = "material-tracks" + mmAlgCfg.inputMaterialTracks = "material-tracks" if mapSurface: navigator = Navigator( diff --git a/Examples/Scripts/Python/ckf_tracks.py b/Examples/Scripts/Python/ckf_tracks.py index 868fd8a8404..3f2e9c5c858 100755 --- a/Examples/Scripts/Python/ckf_tracks.py +++ b/Examples/Scripts/Python/ckf_tracks.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 -from pathlib import Path -from typing import Optional, Union -from collections import namedtuple -from acts.examples import Sequencer, GenericDetector, RootParticleReader +from pathlib import Path +from typing import Optional import acts - from acts import UnitConstants as u +from acts.examples import GenericDetector, RootParticleReader def runCKFTracks( @@ -70,7 +68,6 @@ def runCKFTracks( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles="particles_input", - orderedEvents=False, ) ) diff --git a/Examples/Scripts/Python/digitization.py b/Examples/Scripts/Python/digitization.py index 16155a60e1a..a69a8f01c45 100755 --- a/Examples/Scripts/Python/digitization.py +++ b/Examples/Scripts/Python/digitization.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from pathlib import Path -from typing import Optional, Union +from typing import Optional import acts import acts.examples @@ -47,9 +47,8 @@ def runDigitization( # Read input from input collection (e.g. Pythia8 output) evGen = acts.examples.RootParticleReader( level=s.config.logLevel, - outputParticles="particles_input", filePath=str(particlesInput), - orderedEvents=False, + outputParticles="particles_input", ) s.addReader(evGen) diff --git a/Examples/Scripts/Python/material_mapping.py b/Examples/Scripts/Python/material_mapping.py index 87e7595997e..ab3b0694ea5 100755 --- a/Examples/Scripts/Python/material_mapping.py +++ b/Examples/Scripts/Python/material_mapping.py @@ -53,7 +53,7 @@ def runMaterialMapping( s.addReader( RootMaterialTrackReader( level=acts.logging.INFO, - collection="material-tracks", + outputMaterialTracks="material-tracks", fileList=[ os.path.join( inputDir, @@ -70,7 +70,7 @@ def runMaterialMapping( mmAlgCfg = MaterialMapping.Config(context.geoContext, context.magFieldContext) mmAlgCfg.trackingGeometry = trackingGeometry - mmAlgCfg.collection = "material-tracks" + mmAlgCfg.inputMaterialTracks = "material-tracks" if mapSurface: navigator = Navigator( @@ -116,7 +116,7 @@ def runMaterialMapping( s.addWriter( RootMaterialTrackWriter( level=acts.logging.INFO, - collection=mmAlgCfg.mappingMaterialCollection, + inputMaterialTracks=mmAlgCfg.mappingMaterialCollection, filePath=os.path.join( outputDir, mapName + "_tracks.root", diff --git a/Examples/Scripts/Python/material_mapping_itk.py b/Examples/Scripts/Python/material_mapping_itk.py index b9bb9023037..a0414171c9f 100644 --- a/Examples/Scripts/Python/material_mapping_itk.py +++ b/Examples/Scripts/Python/material_mapping_itk.py @@ -53,7 +53,7 @@ def runMaterialMapping( s.addReader( RootMaterialTrackReader( level=acts.logging.INFO, - collection="material-tracks", + outputMaterialTracks="material-tracks", fileList=[ os.path.join( inputDir, @@ -68,7 +68,7 @@ def runMaterialMapping( mmAlgCfg = MaterialMapping.Config(context.geoContext, context.magFieldContext) mmAlgCfg.trackingGeometry = trackingGeometry - mmAlgCfg.collection = "material-tracks" + mmAlgCfg.inputMaterialTracks = "material-tracks" if mapSurface: navigator = Navigator( @@ -102,7 +102,7 @@ def runMaterialMapping( s.addWriter( RootMaterialTrackWriter( level=acts.logging.INFO, - collection=mmAlgCfg.collection, + inputMaterialTracks=mmAlgCfg.collection, filePath=os.path.join( outputDir, mapName + "_tracks.root", diff --git a/Examples/Scripts/Python/material_recording.py b/Examples/Scripts/Python/material_recording.py index 80d713732e9..ddceefc753c 100755 --- a/Examples/Scripts/Python/material_recording.py +++ b/Examples/Scripts/Python/material_recording.py @@ -78,7 +78,7 @@ def runMaterialRecording( acts.examples.RootMaterialTrackWriter( prePostStep=True, recalculateTotals=True, - collection="material_tracks", + inputMaterialTracks="material_tracks", filePath=os.path.join(outputDir, "geant4_material_tracks.root"), level=acts.logging.INFO, ) diff --git a/Examples/Scripts/Python/material_validation.py b/Examples/Scripts/Python/material_validation.py index f8c09c887e0..3234471fdb0 100755 --- a/Examples/Scripts/Python/material_validation.py +++ b/Examples/Scripts/Python/material_validation.py @@ -49,7 +49,7 @@ def runMaterialValidation( s.addWriter( RootMaterialTrackWriter( level=acts.logging.INFO, - collection=alg.config.propagationMaterialCollection, + inputMaterialTracks=alg.config.propagationMaterialCollection, filePath=os.path.join(outputDir, (outputName + ".root")), storeSurface=True, storeVolume=True, diff --git a/Examples/Scripts/Python/material_validation_itk.py b/Examples/Scripts/Python/material_validation_itk.py index 6c606daf4a2..000a6b2a0e6 100755 --- a/Examples/Scripts/Python/material_validation_itk.py +++ b/Examples/Scripts/Python/material_validation_itk.py @@ -53,7 +53,7 @@ def runMaterialValidation( s.addWriter( RootMaterialTrackWriter( level=acts.logging.INFO, - collection=alg.config.propagationMaterialCollection, + inputMaterialTracks=alg.config.propagationMaterialCollection, filePath=os.path.join(outputDir, (outputName + ".root")), storeSurface=True, storeVolume=True, diff --git a/Examples/Scripts/Python/truth_tracking_gsf.py b/Examples/Scripts/Python/truth_tracking_gsf.py index b7474cb48d4..069f31dde5f 100755 --- a/Examples/Scripts/Python/truth_tracking_gsf.py +++ b/Examples/Scripts/Python/truth_tracking_gsf.py @@ -68,7 +68,6 @@ def runTruthTrackingGsf( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles="particles_input", - orderedEvents=False, ) ) diff --git a/Examples/Scripts/Python/truth_tracking_gx2f.py b/Examples/Scripts/Python/truth_tracking_gx2f.py index 5d7cb5c2fe5..a94107edfc8 100644 --- a/Examples/Scripts/Python/truth_tracking_gx2f.py +++ b/Examples/Scripts/Python/truth_tracking_gx2f.py @@ -59,7 +59,6 @@ def runTruthTrackingGx2f( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles="particles_input", - orderedEvents=False, ) ) diff --git a/Examples/Scripts/Python/truth_tracking_kalman.py b/Examples/Scripts/Python/truth_tracking_kalman.py index d82f4921410..bef6da9c05e 100755 --- a/Examples/Scripts/Python/truth_tracking_kalman.py +++ b/Examples/Scripts/Python/truth_tracking_kalman.py @@ -70,7 +70,6 @@ def runTruthTrackingKalman( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles="particles_input", - orderedEvents=False, ) ) diff --git a/Examples/Scripts/Python/vertex_fitting.py b/Examples/Scripts/Python/vertex_fitting.py index 12e29c0dcfc..e8797a5ed12 100755 --- a/Examples/Scripts/Python/vertex_fitting.py +++ b/Examples/Scripts/Python/vertex_fitting.py @@ -45,7 +45,6 @@ def runVertexFitting( level=acts.logging.INFO, filePath=str(inputParticlePath.resolve()), outputParticles=inputParticles, - orderedEvents=False, ) ) @@ -83,7 +82,6 @@ def runVertexFitting( outputTracks=trackParameters, outputParticles=associatedParticles, filePath=str(inputTrackSummary.resolve()), - orderedEvents=False, ) s.addReader(trackSummaryReader) diff --git a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp index 5974e32ea27..a56f8a3e98f 100644 --- a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp +++ b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp @@ -50,7 +50,31 @@ nlohmann::json toJson(const GridAccess::IGlobalToGridLocal& globalToGridLocal); /// @param jGlobalToGridLocal the json snippet /// /// @return a newly created object -std::unique_ptr globalToGridLocalFromJson( +std::unique_ptr globalToGridLocalFromJson( + const nlohmann::json& jGlobalToGridLocal); + +/// Create the delegate directly +/// +/// @param jGlobalToGridLocal the json snippet +/// +/// This is the usual workflow, as the connect method can be called on +/// the concreate type +/// +/// @note the dimension of the delegate has to be known by peeking +/// into the json object +GridAccess::GlobalToGridLocal1DimDelegate globalToGridLocal1DimDelegateFromJson( + const nlohmann::json& jGlobalToGridLocal); + +/// Create the delegate directly +/// +/// @param jGlobalToGridLocal the json snippet +/// +/// This is the usual workflow, as the connect method can be called on +/// the concreate type +/// +/// @note the dimension of the delegate has to be known by peeking +/// into the json object +GridAccess::GlobalToGridLocal2DimDelegate globalToGridLocal2DimDelegateFromJson( const nlohmann::json& jGlobalToGridLocal); /// Convert a local to local access to json @@ -68,6 +92,30 @@ nlohmann::json toJson(const GridAccess::IBoundToGridLocal& boundToGridLocal); std::unique_ptr boundToGridLocalFromJson( const nlohmann::json& jBoundToGridLocal); +/// Create the delegate directly +/// +/// @param jBoundToGridLocal the json snippe +/// +/// This is the usual workflow, as the connect method can be called on +/// the concreate type +/// +/// @note the dimension of the delegate has to be known by peeking +/// into the json object +GridAccess::BoundToGridLocal1DimDelegate boundToGridLocal1DimDelegateFromJson( + const nlohmann::json& jBoundToGridLocal); + +/// Create the delegate directly +/// +/// @param jBoundToGridLocal the json snippe +/// +/// This is the usual workflow, as the connect method can be called on +/// the concreate type +/// +/// @note the dimension of the delegate has to be known by peeking +/// into the json object +GridAccess::BoundToGridLocal2DimDelegate boundToGridLocal2DimDelegateFromJson( + const nlohmann::json& jBoundToGridLocal); + } // namespace GridAccessJsonConverter namespace GridJsonConverter { diff --git a/Plugins/Json/src/GridJsonConverter.cpp b/Plugins/Json/src/GridJsonConverter.cpp index 19ac9e85bbf..5569c932f7a 100644 --- a/Plugins/Json/src/GridJsonConverter.cpp +++ b/Plugins/Json/src/GridJsonConverter.cpp @@ -96,22 +96,95 @@ void encodeSubspaces( } template -std::unique_ptr decodeSubspace( - const nlohmann::json& jGlobalToGridLocal) { - std::unique_ptr globalToGridLocal = - nullptr; +std::unique_ptr> decodeSubspace( + const nlohmann::json& /*unused*/) { + return std::make_unique>(); +} + +template +std::unique_ptr>> +decodeTransformedSubspace(const nlohmann::json& jGlobalToGridLocal) { + Acts::Transform3 transform = Acts::Transform3JsonConverter::fromJson( + jGlobalToGridLocal.at("transform")); + Acts::GridAccess::GlobalSubspace globalSubspace; + return std::make_unique>>(std::move(globalSubspace), + transform); +} + +template +std::unique_ptr +decodeGeneralSubspace(const nlohmann::json& jGlobalToGridLocal) { if (jGlobalToGridLocal.find("transform") != jGlobalToGridLocal.end()) { - Acts::Transform3 transform = Acts::Transform3JsonConverter::fromJson( - jGlobalToGridLocal.at("transform")); - Acts::GridAccess::GlobalSubspace globalSubspace; - globalToGridLocal = std::make_unique>>(std::move(globalSubspace), - transform); - } else { - globalToGridLocal = - std::make_unique>(); + return decodeTransformedSubspace(jGlobalToGridLocal); } - return globalToGridLocal; + return decodeSubspace(jGlobalToGridLocal); +} + +template +void decorateGlobalDelegate(Delegate& delegate, + const nlohmann::json& jGlobalToGridLocal) { + // The delegate has already been connected + if (delegate.connected()) { + return; + } + + // Get the transform for json + bool hasTransform = + jGlobalToGridLocal.find("transform") != jGlobalToGridLocal.end(); + + // Get the accessors + std::vector accessors = + jGlobalToGridLocal.at("accessors").get>(); + + // One dimensional setting + if constexpr (sizeof...(Args) == 1u) { + if (std::get<0>(std::forward_as_tuple(Args...)) == accessors[0]) { + if (hasTransform) { + using TransformedSubspace = Acts::GridAccess::Affine3Transformed< + Acts::GridAccess::GlobalSubspace>; + auto globalToGridLocal = + decodeTransformedSubspace(jGlobalToGridLocal); + delegate.template connect<&TransformedSubspace::toGridLocal>( + std::move(globalToGridLocal)); + } else { + auto globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + delegate.template connect< + &Acts::GridAccess::GlobalSubspace::toGridLocal>( + std::move(globalToGridLocal)); + } + } + } + + // Two-dimensional setting + if constexpr (sizeof...(Args) == 2u) { + if (std::get<0>(std::forward_as_tuple(Args...)) == accessors[0] && + std::get<1>(std::forward_as_tuple(Args...)) == accessors[1]) { + if (hasTransform) { + using TransformedSubspace = Acts::GridAccess::Affine3Transformed< + Acts::GridAccess::GlobalSubspace>; + auto globalToGridLocal = + decodeTransformedSubspace(jGlobalToGridLocal); + delegate.template connect<&TransformedSubspace::toGridLocal>( + std::move(globalToGridLocal)); + } else { + auto globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + delegate.template connect< + &Acts::GridAccess::GlobalSubspace::toGridLocal>( + std::move(globalToGridLocal)); + } + } + } +} + +template +void decorateGlobal1DimDelegate( + Acts::GridAccess::GlobalToGridLocal1DimDelegate& delegate, + const nlohmann::json& jGlobalToGridLocal) { + (((decorateGlobalDelegate(delegate, jGlobalToGridLocal))), + ...); } } // namespace @@ -160,11 +233,11 @@ nlohmann::json Acts::GridAccessJsonConverter::toJson( return jGlobalToGridLocal; } -std::unique_ptr +std::unique_ptr Acts::GridAccessJsonConverter::globalToGridLocalFromJson( const nlohmann::json& jGlobalToGridLocal) { - std::unique_ptr globalToGridLocal = - nullptr; + std::unique_ptr + globalToGridLocal = nullptr; std::vector accessors = jGlobalToGridLocal.at("accessors").get>(); @@ -173,22 +246,22 @@ Acts::GridAccessJsonConverter::globalToGridLocalFromJson( if (accessors.size() == 1u) { switch (accessors[0]) { case binX: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; case binY: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; case binZ: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; case binR: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; case binPhi: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; case binEta: - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); break; default: // globalToGridLocal = nullptr; @@ -199,83 +272,137 @@ Acts::GridAccessJsonConverter::globalToGridLocalFromJson( // Switch and fill for 2D if (accessors.size() == 2u) { if (accessors == std::vector{binX, binY}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binY, binX}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binX, binZ}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binZ, binX}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binY, binZ}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binZ, binY}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binR, binPhi}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = + decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binPhi, binR}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = + decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binZ, binPhi}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = + decodeGeneralSubspace(jGlobalToGridLocal); } else if (accessors == std::vector{binPhi, binZ}) { - globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + globalToGridLocal = + decodeGeneralSubspace(jGlobalToGridLocal); } // else globalToGridLocal = nullptr; } return globalToGridLocal; } +Acts::GridAccess::GlobalToGridLocal1DimDelegate +Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson( + const nlohmann::json& jGlobalToGridLocal) { + // Peek into json to check the right dimension + if (jGlobalToGridLocal.at("accessors").size() != 1u) { + throw std::invalid_argument( + "GridAccessJsonConverter: json input does not describe 1D case."); + } + // Unroll the decoration + Acts::GridAccess::GlobalToGridLocal1DimDelegate delegate; + decorateGlobal1DimDelegate( + delegate, jGlobalToGridLocal); + return delegate; +} + +Acts::GridAccess::GlobalToGridLocal2DimDelegate +Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson( + const nlohmann::json& jGlobalToGridLocal) { + // Peek into json to check the right dimension + if (jGlobalToGridLocal.at("accessors").size() != 2u) { + throw std::invalid_argument( + "GridAccessJsonConverter: json input does not describe 2D case."); + } + // Unroll the decoration + Acts::GridAccess::GlobalToGridLocal2DimDelegate delegate; + // Only the matching one will be applied, matching condition is checked inside + // the call - may unroll this es well + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + decorateGlobalDelegate(delegate, jGlobalToGridLocal); + return delegate; +} + nlohmann::json Acts::GridAccessJsonConverter::toJson( const GridAccess::IBoundToGridLocal& boundToGridLocal) { - nlohmann::json jBoundtoGridLocal; + nlohmann::json jBoundToGridLocal; auto localSubSpace0 = dynamic_cast*>(&boundToGridLocal); if (localSubSpace0 != nullptr) { - jBoundtoGridLocal["type"] = "subspace"; - jBoundtoGridLocal["accessors"] = localSubSpace0->accessors; + jBoundToGridLocal["type"] = "subspace"; + jBoundToGridLocal["accessors"] = localSubSpace0->accessors; } auto localSubSpace1 = dynamic_cast*>(&boundToGridLocal); if (localSubSpace1 != nullptr) { - jBoundtoGridLocal["type"] = "subspace"; - jBoundtoGridLocal["accessors"] = localSubSpace1->accessors; + jBoundToGridLocal["type"] = "subspace"; + jBoundToGridLocal["accessors"] = localSubSpace1->accessors; } auto localSubSpace01 = dynamic_cast*>(&boundToGridLocal); if (localSubSpace01 != nullptr) { - jBoundtoGridLocal["type"] = "subspace"; - jBoundtoGridLocal["accessors"] = localSubSpace01->accessors; + jBoundToGridLocal["type"] = "subspace"; + jBoundToGridLocal["accessors"] = localSubSpace01->accessors; } auto localSubSpace10 = dynamic_cast*>(&boundToGridLocal); if (localSubSpace10 != nullptr) { - jBoundtoGridLocal["type"] = "subspace"; - jBoundtoGridLocal["accessors"] = localSubSpace10->accessors; + jBoundToGridLocal["type"] = "subspace"; + jBoundToGridLocal["accessors"] = localSubSpace10->accessors; } auto boundCylinderToZPhi = dynamic_cast(&boundToGridLocal); if (boundCylinderToZPhi != nullptr) { - jBoundtoGridLocal["type"] = "cylinder_to_zphi"; - jBoundtoGridLocal["radius"] = boundCylinderToZPhi->radius; - jBoundtoGridLocal["shift"] = boundCylinderToZPhi->shift; + jBoundToGridLocal["type"] = "cylinder_to_zphi"; + jBoundToGridLocal["radius"] = boundCylinderToZPhi->radius; + jBoundToGridLocal["shift"] = boundCylinderToZPhi->shift; } - return jBoundtoGridLocal; + return jBoundToGridLocal; } std::unique_ptr Acts::GridAccessJsonConverter::boundToGridLocalFromJson( - const nlohmann::json& jBoundtoGridLocal) { + const nlohmann::json& jBoundToGridLocal) { std::unique_ptr boundToGridLocal = nullptr; - std::string type = jBoundtoGridLocal.at("type").get(); + std::string type = jBoundToGridLocal.at("type").get(); if (type == "subspace") { std::vector accessors = - jBoundtoGridLocal.at("accessors").get>(); + jBoundToGridLocal.at("accessors").get>(); if (accessors.size() == 1 && accessors[0] == 0) { boundToGridLocal = std::make_unique>(); @@ -292,10 +419,79 @@ Acts::GridAccessJsonConverter::boundToGridLocalFromJson( std::make_unique>(); } } else if (type == "cylinder_to_zphi") { - ActsScalar radius = jBoundtoGridLocal.at("radius").get(); - ActsScalar shift = jBoundtoGridLocal.at("shift").get(); + ActsScalar radius = jBoundToGridLocal.at("radius").get(); + ActsScalar shift = jBoundToGridLocal.at("shift").get(); boundToGridLocal = std::make_unique(radius, shift); } return boundToGridLocal; } + +Acts::GridAccess::BoundToGridLocal1DimDelegate +Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson( + const nlohmann::json& jBoundToGridLocal) { + Acts::GridAccess::BoundToGridLocal1DimDelegate delegate; + + std::string type = jBoundToGridLocal.at("type").get(); + if (type == "subspace") { + std::vector accessors = + jBoundToGridLocal.at("accessors").get>(); + // Safety check + if (accessors.size() != 1u) { + throw std::invalid_argument( + "GridAccessJsonConverter: json input does not describe 1D case."); + } + // Specify the type + if (accessors[0] == 0) { + auto boundToGridLocal = + std::make_unique>(); + delegate.connect<&Acts::GridAccess::LocalSubspace<0u>::toGridLocal>( + std::move(boundToGridLocal)); + } else if (accessors[0] == 1) { + auto boundToGridLocal = + std::make_unique>(); + delegate.connect<&Acts::GridAccess::LocalSubspace<1u>::toGridLocal>( + std::move(boundToGridLocal)); + } + } + return delegate; +} + +Acts::GridAccess::BoundToGridLocal2DimDelegate +Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + const nlohmann::json& jBoundToGridLocal) { + Acts::GridAccess::BoundToGridLocal2DimDelegate delegate; + + std::string type = jBoundToGridLocal.at("type").get(); + if (type == "subspace") { + std::vector accessors = + jBoundToGridLocal.at("accessors").get>(); + + // Safety check + if (accessors.size() != 2u) { + throw std::invalid_argument( + "GridAccessJsonConverter: json input does not describe 2D case."); + } + if (accessors[0] == 0u && accessors[1] == 1u) { + auto boundToGridLocal = + std::make_unique>(); + delegate.connect<&Acts::GridAccess::LocalSubspace<0u, 1u>::toGridLocal>( + std::move(boundToGridLocal)); + } else if (accessors[0] == 1u && accessors[1] == 0u) { + auto boundToGridLocal = + std::make_unique>(); + delegate.connect<&Acts::GridAccess::LocalSubspace<1u, 0u>::toGridLocal>( + std::move(boundToGridLocal)); + } + } else if (type == "cylinder_to_zphi") { + ActsScalar radius = jBoundToGridLocal.at("radius").get(); + ActsScalar shift = jBoundToGridLocal.at("shift").get(); + auto boundToGridLocal = + std::make_unique(radius, + shift); + delegate.connect<&Acts::GridAccess::BoundCylinderToZPhi::toGridLocal>( + std::move(boundToGridLocal)); + } + + return delegate; +} diff --git a/Plugins/Json/src/MaterialJsonConverter.cpp b/Plugins/Json/src/MaterialJsonConverter.cpp index 7f32ace10c4..b951d5f9d2a 100644 --- a/Plugins/Json/src/MaterialJsonConverter.cpp +++ b/Plugins/Json/src/MaterialJsonConverter.cpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Material/BinnedSurfaceMaterial.hpp" +#include "Acts/Material/GridSurfaceMaterial.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" #include "Acts/Material/HomogeneousVolumeMaterial.hpp" #include "Acts/Material/ISurfaceMaterial.hpp" @@ -19,9 +20,13 @@ #include "Acts/Material/ProtoSurfaceMaterial.hpp" #include "Acts/Material/ProtoVolumeMaterial.hpp" #include "Acts/Plugins/Json/GeometryJsonKeys.hpp" +#include "Acts/Plugins/Json/GridJsonConverter.hpp" #include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp" #include "Acts/Utilities/BinUtility.hpp" #include "Acts/Utilities/Grid.hpp" +#include "Acts/Utilities/GridAccessHelpers.hpp" +#include "Acts/Utilities/GridAxisGenerators.hpp" +#include "Acts/Utilities/TypeList.hpp" #include #include @@ -34,6 +39,256 @@ #include #include +namespace { + +// Grid definition : eq bound +template +using GridEqBound = + Acts::Grid>; +// Grid definition : eq closed +template +using GridEqClosed = + Acts::Grid>; + +// Grid definition : eq bound eq bound +template +using GridEqBoundEqBound = + Acts::Grid, + Acts::detail::Axis>; + +// Grid definition : eq bound eq closed +template +using GridEqBoundEqClosed = + Acts::Grid, + Acts::detail::Axis>; + +// Grid definition : eq closed eq bound +template +using GridEqClosedEqBound = + Acts::Grid, + Acts::detail::Axis>; + +/// @brief Helper function to convert a grid surface material to json +/// +/// @tparam indexed_grid_materital_t +/// @param jMaterial the json object to written into +/// @param indexedMaterialCandidate the actual indexed material +template +void convertIndexedGridMaterial( + nlohmann::json& jMaterial, + const Acts::ISurfaceMaterial& indexedMaterialCandidate) { + // Check if the material is of the right type + const indexed_grid_materital_t* indexedMaterial = + dynamic_cast(&indexedMaterialCandidate); + + if (indexedMaterial != nullptr) { + // It is a grid type material + jMaterial[Acts::jsonKey().typekey] = "grid"; + nlohmann::json jMaterialAccessor; + // Assume globally indexed first + jMaterialAccessor["type"] = "globally_indexed"; + + // If we have a globally indexed map, the material data is loaded elsewhere, + // locally indexed material vectors are written though + const auto& materialAccessor = indexedMaterial->materialAccessor(); + + if constexpr (std::is_same_v) { + // It's actually locally indexed + jMaterialAccessor["type"] = "indexed"; + + nlohmann::json jMaterialData; + for (const auto& msl : materialAccessor.material) { + jMaterialData.push_back(msl); + } + jMaterialAccessor["storage_vector"] = jMaterialData; + } + // Write the index grid + jMaterialAccessor["grid"] = + Acts::GridJsonConverter::toJson(indexedMaterial->grid()); + jMaterial["accessor"] = jMaterialAccessor; + + // Global and bound -> grid local + jMaterial["global_to_grid_local"] = Acts::GridAccessJsonConverter::toJson( + *(indexedMaterial->globalToGridLocal().instance())); + + jMaterial["bound_to_grid_local"] = Acts::GridAccessJsonConverter::toJson( + *(indexedMaterial->boundToGridLocal().instance())); + } +} + +/// @brief Unrolling function for catching the right instance +/// +/// @param jMaterial is the json object to be written into +/// @param indexedMaterial is the indexed material +template +void unrollIndexedGridConversion(nlohmann::json& jMaterial, + const Acts::ISurfaceMaterial& indexedMaterial, + Acts::TypeList /*unused*/) { + (convertIndexedGridMaterial(jMaterial, indexedMaterial), ...); +} + +template +Acts::ISurfaceMaterial* indexedMaterialFromJson(nlohmann::json& jMaterial) { + // Load accessor and grid + nlohmann::json jMaterialAccessor = jMaterial["accessor"]; + + // Prepare the material and its accessor + IndexedAccessorType materialAccessor{}; + + // If it's locally indexed, we need to load the material vector + if constexpr (std::is_same_v) { + // It's actually locally indexed + for (const auto& msl : jMaterialAccessor["storage_vector"]) { + materialAccessor.material.push_back(msl); + } + } + + // Now make the grid and the axes + nlohmann::json jGrid = jMaterialAccessor["grid"]; + nlohmann::json jGridAxes = jGrid["axes"]; + + Acts::detail::AxisBoundaryType boundaryType0 = jGridAxes[0]["boundary_type"]; + + // 1-dimensional case + if (jGridAxes.size() == 1u) { + // Bound case + if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound) { + Acts::GridAxisGenerators::EqBound eqBound{jGridAxes[0]["range"], + jGridAxes[0]["bins"]}; + auto grid = + Acts::GridJsonConverter::fromJson( + jGrid, eqBound); + + auto boundToGridLocal = + Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson( + jMaterial["bound_to_grid_local"]); + + auto globalToGridLocal = + Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson( + jMaterial["global_to_grid_local"]); + + return new Acts::IndexedSurfaceMaterial( + std::move(grid), std::move(materialAccessor), + std::move(boundToGridLocal), std::move(globalToGridLocal)); + } + // Closed case + if (boundaryType0 == Acts::detail::AxisBoundaryType::Closed) { + Acts::GridAxisGenerators::EqClosed eqClosed{jGridAxes[0]["range"], + jGridAxes[0]["bins"]}; + auto grid = + Acts::GridJsonConverter::fromJson( + jGrid, eqClosed); + + auto boundToGridLocal = + Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson( + jMaterial["bound_to_grid_local"]); + + auto globalToGridLocal = + Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson( + jMaterial["global_to_grid_local"]); + + return new Acts::IndexedSurfaceMaterial( + std::move(grid), std::move(materialAccessor), + std::move(boundToGridLocal), std::move(globalToGridLocal)); + } + } + + // 2-dimensional case + if (jGridAxes.size() == 2u) { + // Second boundary type + Acts::detail::AxisBoundaryType boundaryType1 = + jGridAxes[1]["boundary_type"]; + + // Bound-bound setup + if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound && + boundaryType1 == Acts::detail::AxisBoundaryType::Bound) { + Acts::GridAxisGenerators::EqBoundEqBound eqBoundEqBound{ + jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"], + jGridAxes[1]["bins"]}; + auto grid = + Acts::GridJsonConverter::fromJson(jGrid, eqBoundEqBound); + + auto boundToGridLocal = + Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + jMaterial["bound_to_grid_local"]); + + auto globalToGridLocal = + Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson( + jMaterial["global_to_grid_local"]); + + return new Acts::IndexedSurfaceMaterial( + std::move(grid), std::move(materialAccessor), + std::move(boundToGridLocal), std::move(globalToGridLocal)); + } + + // Bound-closed setup + if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound && + boundaryType1 == Acts::detail::AxisBoundaryType::Closed) { + Acts::GridAxisGenerators::EqBoundEqClosed eqBoundEqClosed{ + jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"], + jGridAxes[1]["bins"]}; + auto grid = Acts::GridJsonConverter::fromJson( + jGrid, eqBoundEqClosed); + + auto boundToGridLocal = + Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + jMaterial["bound_to_grid_local"]); + + auto globalToGridLocal = + Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson( + jMaterial["global_to_grid_local"]); + + return new Acts::IndexedSurfaceMaterial( + std::move(grid), std::move(materialAccessor), + std::move(boundToGridLocal), std::move(globalToGridLocal)); + } + + // Closed-bound setup + if (boundaryType0 == Acts::detail::AxisBoundaryType::Closed && + boundaryType1 == Acts::detail::AxisBoundaryType::Bound) { + Acts::GridAxisGenerators::EqClosedEqBound eqClosedEqBound{ + jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"], + jGridAxes[1]["bins"]}; + auto grid = Acts::GridJsonConverter::fromJson( + jGrid, eqClosedEqBound); + + auto boundToGridLocal = + Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + jMaterial["bound_to_grid_local"]); + + auto globalToGridLocal = + Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson( + jMaterial["global_to_grid_local"]); + + return new Acts::IndexedSurfaceMaterial( + std::move(grid), std::move(materialAccessor), + std::move(boundToGridLocal), std::move(globalToGridLocal)); + } + } + + return nullptr; +} + +} // namespace + void Acts::to_json(nlohmann::json& j, const Material& t) { if (!t) { return; @@ -84,7 +339,8 @@ void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) { nlohmann::json jMaterial; // A bin utility needs to be written const Acts::BinUtility* bUtility = nullptr; - // Check if we have a proto material + + // First: Check if we have a proto material auto psMaterial = dynamic_cast(material); if (psMaterial != nullptr) { // Type is proto material @@ -109,7 +365,8 @@ void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) { j[Acts::jsonKey().materialkey] = jMaterial; return; } - // Now check if we have a homogeneous material + + // Second: check if we have a homogeneous material auto hsMaterial = dynamic_cast(material); if (hsMaterial != nullptr) { @@ -129,7 +386,8 @@ void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) { j[Acts::jsonKey().materialkey] = jMaterial; return; } - // Only option remaining: BinnedSurface material + + // Next option remaining: BinnedSurface material auto bsMaterial = dynamic_cast(material); if (bsMaterial != nullptr) { // type is binned @@ -158,6 +416,44 @@ void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) { j[Acts::jsonKey().materialkey] = jMaterial; return; } + + // Possible indexed grid types + using IndexedSurfaceGrids = Acts::TypeList< + Acts::IndexedSurfaceMaterial>, + Acts::IndexedSurfaceMaterial>, + Acts::IndexedSurfaceMaterial>, + Acts::IndexedSurfaceMaterial>, + Acts::IndexedSurfaceMaterial>>; + + unrollIndexedGridConversion(jMaterial, *material, IndexedSurfaceGrids{}); + if (!jMaterial.empty()) { + j[Acts::jsonKey().materialkey] = jMaterial; + return; + } + + // Possible: globally indexed grid types + using GloballyIndexedSurfaceGrids = Acts::TypeList< + Acts::GloballyIndexedSurfaceMaterial>, + Acts::GloballyIndexedSurfaceMaterial>, + Acts::GloballyIndexedSurfaceMaterial>, + Acts::GloballyIndexedSurfaceMaterial>, + Acts::GloballyIndexedSurfaceMaterial>>; + + unrollIndexedGridConversion(jMaterial, *material, + GloballyIndexedSurfaceGrids{}); + if (!jMaterial.empty()) { + j[Acts::jsonKey().materialkey] = jMaterial; + return; + } + + // Possible: material grid types + // using MaterialSurfaceGrids = Acts::TypeList< + // Acts::GridSurfaceMaterial>, + // Acts::GridSurfaceMaterial>, + // Acts::GridSurfaceMaterial>, + // Acts::GridSurfaceMaterial>, + // Acts::GridSurfaceMaterial>>; + // No material the json object is left empty. return; } @@ -174,6 +470,13 @@ void Acts::from_json(const nlohmann::json& j, return; } + // Grid based material maps + if (jMaterial[Acts::jsonKey().typekey] == "grid") { + material = + indexedMaterialFromJson(jMaterial); + return; + } + // The bin utility and material Acts::BinUtility bUtility; Acts::MaterialSlabMatrix mpMatrix; diff --git a/Tests/UnitTests/Core/Material/CMakeLists.txt b/Tests/UnitTests/Core/Material/CMakeLists.txt index f17dff410c4..260aa7b3349 100644 --- a/Tests/UnitTests/Core/Material/CMakeLists.txt +++ b/Tests/UnitTests/Core/Material/CMakeLists.txt @@ -10,6 +10,7 @@ add_unittest(Interactions InteractionsTests.cpp) add_unittest(InterpolatedMaterialMap InterpolatedMaterialMapTests.cpp) add_unittest(MaterialComposition MaterialCompositionTests.cpp) add_unittest(MaterialGridHelper MaterialGridHelperTests.cpp) +add_unittest(MaterialInteractionAssignment MaterialInteractionAssignmentTests.cpp) add_unittest(MaterialSlab MaterialSlabTests.cpp) add_unittest(Material MaterialTests.cpp) add_unittest(ProtoSurfaceMaterial ProtoSurfaceMaterialTests.cpp) diff --git a/Tests/UnitTests/Core/Material/GridSurfaceMaterialTests.cpp b/Tests/UnitTests/Core/Material/GridSurfaceMaterialTests.cpp index d6f9c154344..999f89ea85b 100644 --- a/Tests/UnitTests/Core/Material/GridSurfaceMaterialTests.cpp +++ b/Tests/UnitTests/Core/Material/GridSurfaceMaterialTests.cpp @@ -56,6 +56,7 @@ class LocalToZPhi final : public Acts::GridAccess::IBoundToGridLocal { BOOST_AUTO_TEST_SUITE(Material) +// This test covers some wrongly configured cases BOOST_AUTO_TEST_CASE(GridIndexedMaterial_invalid_bound2Grid_Unconnected) { std::vector material; @@ -79,6 +80,7 @@ BOOST_AUTO_TEST_CASE(GridIndexedMaterial_invalid_bound2Grid_Unconnected) { std::invalid_argument); } +// This test covers some wrongly configured cases BOOST_AUTO_TEST_CASE(GridIndexedMaterial_invalid_global2Grid_Unconnected) { std::vector material; @@ -102,6 +104,7 @@ BOOST_AUTO_TEST_CASE(GridIndexedMaterial_invalid_global2Grid_Unconnected) { std::invalid_argument); } +// This test covers the locally indexed grid material in 1D BOOST_AUTO_TEST_CASE(GridIndexedMaterial1D) { std::vector material; material.emplace_back(Acts::Material(), 0.0); // vacuum @@ -190,6 +193,7 @@ BOOST_AUTO_TEST_CASE(GridIndexedMaterial1D) { BOOST_CHECK_EQUAL(sml4.thickness(), 6.); } +// This test covers the locally indexed grid material in 2D BOOST_AUTO_TEST_CASE(GridIndexedMaterial2D) { std::vector material; material.emplace_back(Acts::Material(), 1.0); // vacuum @@ -251,4 +255,235 @@ BOOST_AUTO_TEST_CASE(GridIndexedMaterial2D) { BOOST_CHECK_EQUAL(mg3.material().X0(), 21.); } +// This test covers the globally indexed grid material with non-shared material +BOOST_AUTO_TEST_CASE(GridGloballyIndexedMaterialNonShared) { + auto material = std::make_shared>(); + + material->emplace_back(Acts::Material(), 0.0); // vacuum + material->emplace_back( + Acts::Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0), 1.0); + material->emplace_back( + Acts::Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 2.0); + material->emplace_back( + Acts::Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 3.0); + material->emplace_back( + Acts::Material::fromMolarDensity(31.0, 22.0, 23.0, 24.0, 25.0), 4.0); + + using EqBound = Acts::GridAxisGenerators::EqBound; + using EqGrid = EqBound::grid_type; + using Point = EqGrid::point_t; + + EqBound eqBound{{0., 5.}, 5}; + EqGrid eqGrid{eqBound()}; + + eqGrid.atPosition(Point{0.5}) = 1u; // material 1 + eqGrid.atPosition(Point{1.5}) = 0u; // vacuum + eqGrid.atPosition(Point{2.5}) = 2u; // material 2 + eqGrid.atPosition(Point{3.5}) = 2u; // material 2 + eqGrid.atPosition(Point{4.5}) = 3u; // material 3 + + auto localX = std::make_unique(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX; + bToX.connect<&LocalAccessX::l2X>(std::move(localX)); + + auto globalX = std::make_unique(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX; + gToX.connect<&GlobalAccessX::g2X>(std::move(globalX)); + + Acts::GloballyIndexedSurfaceMaterial ism( + std::move(eqGrid), Acts::GloballyIndexedMaterialAccessor{material, false}, + std::move(bToX), std::move(gToX)); + + // Local access test + Acts::Vector2 l0(0.5, 0.); + Acts::Vector2 l1(1.5, 0.); + Acts::Vector2 l2(2.5, 0.); + Acts::Vector2 l3(3.5, 0.); + Acts::Vector2 l4(4.5, 0.); + + const Acts::MaterialSlab& ml0 = ism.materialSlab(l0); + const Acts::MaterialSlab& ml1 = ism.materialSlab(l1); + const Acts::MaterialSlab& ml2 = ism.materialSlab(l2); + const Acts::MaterialSlab& ml3 = ism.materialSlab(l3); + const Acts::MaterialSlab& ml4 = ism.materialSlab(l4); + + BOOST_CHECK_EQUAL(ml0.material().X0(), 1.); + BOOST_CHECK(!ml1.material()); + BOOST_CHECK_EQUAL(ml2.material().X0(), 11.); + BOOST_CHECK_EQUAL(ml3.material().X0(), 11.); + BOOST_CHECK_EQUAL(ml4.material().X0(), 21.); + + EqBound eqBound1{{0., 5.}, 1}; + EqGrid eqGrid1{eqBound1()}; + + auto localX1 = std::make_unique(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX1; + bToX1.connect<&LocalAccessX::l2X>(std::move(localX1)); + + auto globalX1 = std::make_unique(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX1; + gToX1.connect<&GlobalAccessX::g2X>(std::move(globalX1)); + + eqGrid1.atPosition(Point{2.5}) = 4u; // material 4 + + Acts::GloballyIndexedSurfaceMaterial ism1( + std::move(eqGrid1), + Acts::GloballyIndexedMaterialAccessor{material, false}, std::move(bToX1), + std::move(gToX1)); + + Acts::Vector2 l0g1(2.5, 0.); + const Acts::MaterialSlab& ml0g1 = ism1.materialSlab(l0g1); + BOOST_CHECK_EQUAL(ml0g1.material().X0(), 31.); + + // Scale + ism1 *= 2.; + const Acts::MaterialSlab& sml0g1 = ism1.materialSlab(l0g1); + BOOST_CHECK_EQUAL(sml0g1.thickness(), 8.); + + // First one stays unscaled + const Acts::MaterialSlab& sml0 = ism.materialSlab(l0); + BOOST_CHECK_EQUAL(sml0.thickness(), 1.); +} + +// This test covers the globally indexed grid material with shared +BOOST_AUTO_TEST_CASE(GridGloballyIndexedMaterialShared) { + auto material = std::make_shared>(); + + material->emplace_back(Acts::Material(), 0.0); // vacuum + material->emplace_back( + Acts::Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0), 1.0); + + using EqBound = Acts::GridAxisGenerators::EqBound; + using EqGrid = EqBound::grid_type; + using Point = EqGrid::point_t; + + EqBound eqBound0{{0., 5.}, 1}; + EqGrid eqGrid0{eqBound0()}; + + eqGrid0.atPosition(Point{2.5}) = 1u; // material 1 + auto localX0 = std::make_unique(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX0; + bToX0.connect<&LocalAccessX::l2X>(std::move(localX0)); + + auto globalX0 = std::make_unique(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX0; + gToX0.connect<&GlobalAccessX::g2X>(std::move(globalX0)); + + Acts::GloballyIndexedSurfaceMaterial ism0( + std::move(eqGrid0), Acts::GloballyIndexedMaterialAccessor{material, true}, + std::move(bToX0), std::move(gToX0)); + + EqBound eqBound1{{0., 5.}, 1}; + EqGrid eqGrid1{eqBound1()}; + + eqGrid1.atPosition(Point{2.5}) = 1u; // material 1 + auto localX1 = std::make_unique(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX1; + bToX1.connect<&LocalAccessX::l2X>(std::move(localX1)); + + auto globalX1 = std::make_unique(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX1; + gToX1.connect<&GlobalAccessX::g2X>(std::move(globalX1)); + + Acts::GloballyIndexedSurfaceMaterial ism1( + std::move(eqGrid1), Acts::GloballyIndexedMaterialAccessor{material, true}, + std::move(bToX1), std::move(gToX1)); + + Acts::Vector2 l0(2.5, 0.); + + // check grid material 0 + const Acts::MaterialSlab& ml0 = ism0.materialSlab(l0); + BOOST_CHECK_EQUAL(ml0.material().X0(), 1.); + + const Acts::MaterialSlab& ml0g1 = ism1.materialSlab(l0); + BOOST_CHECK_EQUAL(ml0g1.material().X0(), 1.); + + // scaling shared material should throw a std::invalid_argument + BOOST_CHECK_THROW(ism1 *= 2., std::invalid_argument); +} + +// This test covers the grid material (non-indexed accessor) +// +// In this setup, the material is not indexed, but filled directly +// into the grid structure. +BOOST_AUTO_TEST_CASE(GridSurfaceMaterialTests) { + using EqBound = Acts::GridAxisGenerators::EqBound; + using EqGrid = EqBound::grid_type; + using Point = EqGrid::point_t; + + EqBound eqBound{{0., 5.}, 5}; + EqGrid eqGrid{eqBound()}; + + eqGrid.atPosition(Point{0.5}) = Acts::MaterialSlab(Acts::Material(), 0.0); + eqGrid.atPosition(Point{1.5}) = Acts::MaterialSlab(Acts::Material(), 1.0); + eqGrid.atPosition(Point{2.5}) = Acts::MaterialSlab(Acts::Material(), 2.0); + eqGrid.atPosition(Point{3.5}) = Acts::MaterialSlab(Acts::Material(), 3.0); + eqGrid.atPosition(Point{4.5}) = Acts::MaterialSlab(Acts::Material(), 4.0); + + auto localX = std::make_unique(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX; + bToX.connect<&LocalAccessX::l2X>(std::move(localX)); + + auto globalX = std::make_unique(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX; + gToX.connect<&GlobalAccessX::g2X>(std::move(globalX)); + + Acts::GridSurfaceMaterial gsm(std::move(eqGrid), + Acts::GridMaterialAccessor{}, + std::move(bToX), std::move(gToX)); + + // Global access test + Acts::Vector3 g0(0.5, 0., 0.); + Acts::Vector3 g1(1.5, 0., 0.); + Acts::Vector3 g2(2.5, 0., 0.); + Acts::Vector3 g3(3.5, 0., 0.); + Acts::Vector3 g4(4.5, 0., 0.); + + const Acts::MaterialSlab& mg0 = gsm.materialSlab(g0); + const Acts::MaterialSlab& mg1 = gsm.materialSlab(g1); + const Acts::MaterialSlab& mg2 = gsm.materialSlab(g2); + const Acts::MaterialSlab& mg3 = gsm.materialSlab(g3); + const Acts::MaterialSlab& mg4 = gsm.materialSlab(g4); + + BOOST_CHECK_EQUAL(mg0.thickness(), 0.); + BOOST_CHECK_EQUAL(mg1.thickness(), 1.); + BOOST_CHECK_EQUAL(mg2.thickness(), 2.); + BOOST_CHECK_EQUAL(mg3.thickness(), 3.); + BOOST_CHECK_EQUAL(mg4.thickness(), 4.); + + // Local access test + Acts::Vector2 l0(0.5, 0.); + Acts::Vector2 l1(1.5, 0.); + Acts::Vector2 l2(2.5, 0.); + Acts::Vector2 l3(3.5, 0.); + Acts::Vector2 l4(4.5, 0.); + + const Acts::MaterialSlab& ml0 = gsm.materialSlab(l0); + const Acts::MaterialSlab& ml1 = gsm.materialSlab(l1); + const Acts::MaterialSlab& ml2 = gsm.materialSlab(l2); + const Acts::MaterialSlab& ml3 = gsm.materialSlab(l3); + const Acts::MaterialSlab& ml4 = gsm.materialSlab(l4); + + BOOST_CHECK_EQUAL(ml0.thickness(), 0.); + BOOST_CHECK_EQUAL(ml1.thickness(), 1.); + BOOST_CHECK_EQUAL(ml2.thickness(), 2.); + BOOST_CHECK_EQUAL(ml3.thickness(), 3.); + BOOST_CHECK_EQUAL(ml4.thickness(), 4.); + + // Now scale it - and access again + gsm *= 2.; + + const Acts::MaterialSlab& sml0 = gsm.materialSlab(l0); + const Acts::MaterialSlab& sml1 = gsm.materialSlab(l1); + const Acts::MaterialSlab& sml2 = gsm.materialSlab(l2); + const Acts::MaterialSlab& sml3 = gsm.materialSlab(l3); + const Acts::MaterialSlab& sml4 = gsm.materialSlab(l4); + + BOOST_CHECK_EQUAL(sml0.thickness(), 0.); + BOOST_CHECK_EQUAL(sml1.thickness(), 2.); + BOOST_CHECK_EQUAL(sml2.thickness(), 4.); + BOOST_CHECK_EQUAL(sml3.thickness(), 6.); + BOOST_CHECK_EQUAL(sml4.thickness(), 8.); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Material/MaterialInteractionAssignmentTests.cpp b/Tests/UnitTests/Core/Material/MaterialInteractionAssignmentTests.cpp new file mode 100644 index 00000000000..603884bd41b --- /dev/null +++ b/Tests/UnitTests/Core/Material/MaterialInteractionAssignmentTests.cpp @@ -0,0 +1,306 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Material/Material.hpp" +#include "Acts/Material/MaterialInteraction.hpp" +#include "Acts/Material/MaterialInteractionAssignment.hpp" +#include "Acts/Material/MaterialSlab.hpp" +#include "Acts/Propagator/SurfaceCollector.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/VectorHelpers.hpp" + +#include + +namespace Acts { +namespace Test { + +auto tContext = GeometryContext(); + +BOOST_AUTO_TEST_SUITE(MaterialInteractionAssignmentSuite) + +BOOST_AUTO_TEST_CASE(AssignToClosest) { + // Create a vector of surfaces + std::vector> surfaces = { + Surface::makeShared(Transform3::Identity(), 20.0, 100.0), + Surface::makeShared(Transform3::Identity(), 30.0, 100.0), + Surface::makeShared(Transform3::Identity(), 50.0, + 100.0)}; + + for (auto [is, surface] : enumerate(surfaces)) { + surface->assignGeometryId(GeometryIdentifier().setSensitive(is + 1)); + } + + using SurfaceHit = std::tuple; + + std::vector intersectedSurfaces = { + {surfaces[0].get(), {20., 0., 0.}, {1., 0., 0.}}, + {surfaces[1].get(), {30., 0., 0.}, {1., 0., 0.}}, + {surfaces[2].get(), {50., 0., 0.}, {1., 0., 0.}}}; + + // Create a material + Material material = Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0); + + std::vector materialInteractions; + materialInteractions.reserve(50); + // Generate some material interactions + for (unsigned int i = 1; i < 50; ++i) { + MaterialInteraction materialInteraction; + materialInteraction.materialSlab = MaterialSlab(material, 0.1); + materialInteraction.position = Vector3{i * 1.0, 0, 0}; + materialInteraction.direction = Vector3{1.0, 0., 0.}; + materialInteractions.push_back(materialInteraction); + } + + MaterialInteractionAssignment::Options options; + + // Assign the material interactions to the surface hits + auto [assigned, unassigned] = MaterialInteractionAssignment::assign( + tContext, materialInteractions, intersectedSurfaces, options); + + // Check that the material interaction was assigned + BOOST_CHECK_EQUAL(assigned.size(), materialInteractions.size()); + BOOST_CHECK_EQUAL(unassigned.size(), 0u); + + // Check that it is assigned to the closest surface always + for (const auto& mi : assigned) { + ActsScalar minDistance = std::numeric_limits::max(); + const Surface* closestSurface = nullptr; + for (const auto& [surface, position, direction] : intersectedSurfaces) { + ActsScalar distance = (mi.position - position).norm(); + if (distance < minDistance) { + minDistance = distance; + closestSurface = surface; + } + } + BOOST_CHECK_EQUAL(mi.surface, closestSurface); + } +} + +BOOST_AUTO_TEST_CASE(AssignToClosest_withGlobalVeto) { + // Create a vector of surfaces + std::vector> surfaces = { + Surface::makeShared(Transform3::Identity(), 20.0, 100.0), + Surface::makeShared(Transform3::Identity(), 30.0, 100.0), + Surface::makeShared(Transform3::Identity(), 50.0, + 100.0)}; + + for (auto [is, surface] : enumerate(surfaces)) { + surface->assignGeometryId(GeometryIdentifier().setSensitive(is + 1)); + } + + using SurfaceHit = std::tuple; + + std::vector intersectedSurfaces = { + {surfaces[0].get(), {20., 0., 0.}, {1., 0., 0.}}, + {surfaces[1].get(), {30., 0., 0.}, {1., 0., 0.}}, + {surfaces[2].get(), {50., 0., 0.}, {1., 0., 0.}}}; + + // Create a material + Material material = Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0); + + std::vector materialInteractions; + materialInteractions.reserve(50); + // Generate some material interactions + for (unsigned int i = 1; i < 50; ++i) { + MaterialInteraction materialInteraction; + materialInteraction.materialSlab = MaterialSlab(material, 0.1); + materialInteraction.position = Vector3{i * 1.0, 0, 0}; + materialInteraction.direction = Vector3{1.0, 0., 0.}; + materialInteractions.push_back(materialInteraction); + } + + // Veto everything above 40 mm + struct RadialVeto { + ActsScalar rMax = 40.0; + bool operator()(const MaterialInteraction& mi) const { + return VectorHelpers::perp(mi.position) > rMax; + } + }; + MaterialInteractionAssignment::Options options; + options.globalVetos.push_back(RadialVeto{40}); + + // Assign the material interactions to the surface hits + auto [assigned, unassigned] = MaterialInteractionAssignment::assign( + tContext, materialInteractions, intersectedSurfaces, options); + + // Check that the material interaction was assigned + BOOST_CHECK_EQUAL(assigned.size(), 40u); + BOOST_CHECK_EQUAL(unassigned.size(), 9u); +} + +BOOST_AUTO_TEST_CASE(AssignToClosest_withLocalVeto) { + // Create a vector of surfaces + std::vector> surfaces = { + Surface::makeShared(Transform3::Identity(), 20.0, 100.0), + Surface::makeShared(Transform3::Identity(), 30.0, 100.0), + Surface::makeShared(Transform3::Identity(), 50.0, + 100.0)}; + + for (auto [is, surface] : enumerate(surfaces)) { + surface->assignGeometryId(GeometryIdentifier().setSensitive(is + 1)); + } + + using SurfaceHit = std::tuple; + + std::vector intersectedSurfaces = { + {surfaces[0].get(), {20., 0., 0.}, {1., 0., 0.}}, + {surfaces[1].get(), {30., 0., 0.}, {1., 0., 0.}}, + {surfaces[2].get(), {50., 0., 0.}, {1., 0., 0.}}}; + + // Create a material + Material material = Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0); + + std::vector materialInteractions; + materialInteractions.reserve(50); + // Generate some material interactions + for (unsigned int i = 1; i < 50; ++i) { + MaterialInteraction materialInteraction; + materialInteraction.materialSlab = MaterialSlab(material, 0.1); + materialInteraction.position = Vector3{i * 1.0, 0, 0}; + materialInteraction.direction = Vector3{1.0, 0., 0.}; + materialInteractions.push_back(materialInteraction); + } + + // Veto in a specific one + struct VetoThisOne { + bool operator()(const MaterialInteraction& /*unused*/, + const SurfaceHit& /*unused*/) const { + return true; + } + }; + + // We assign this to + std::vector< + std::pair> + localVetoVector = {{GeometryIdentifier().setSensitive(2), VetoThisOne{}}}; + GeometryHierarchyMap localVetos( + localVetoVector); + MaterialInteractionAssignment::Options options; + options.localVetos = localVetos; + + // Assign the material interactions to the surface hits + auto [assigned, unassigned] = MaterialInteractionAssignment::assign( + tContext, materialInteractions, intersectedSurfaces, options); + + // Check that the material interaction was assigned + BOOST_CHECK_EQUAL(assigned.size(), 34); + BOOST_CHECK_EQUAL(unassigned.size(), 15); +} + +BOOST_AUTO_TEST_CASE(AssignToClosest_withReassignment) { + // Create a vector of surfaces + std::vector> surfaces = { + Surface::makeShared(Transform3::Identity(), 20.0, 100.0), + Surface::makeShared(Transform3::Identity(), 30.0, 100.0), + Surface::makeShared(Transform3::Identity(), 50.0, + 100.0)}; + + for (auto [is, surface] : enumerate(surfaces)) { + surface->assignGeometryId(GeometryIdentifier().setSensitive(is + 1)); + } + + using SurfaceHit = std::tuple; + + std::vector intersectedSurfaces = { + {surfaces[0].get(), {20., 0., 0.}, {1., 0., 0.}}, + {surfaces[1].get(), {30., 0., 0.}, {1., 0., 0.}}, + {surfaces[2].get(), {50., 0., 0.}, {1., 0., 0.}}}; + + // Create a material + Material material = Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0); + + std::vector materialInteractions; + materialInteractions.reserve(50); + // Generate some material interactions + for (unsigned int i = 1; i < 50; ++i) { + MaterialInteraction materialInteraction; + materialInteraction.materialSlab = MaterialSlab(material, 0.1); + materialInteraction.position = Vector3{i * 1.0, 0, 0}; + materialInteraction.direction = Vector3{1.0, 0., 0.}; + materialInteractions.push_back(materialInteraction); + } + + // Veto in a specific one + struct ReAssignToNeighbor { + void operator()(MaterialInteraction& m, const SurfaceHit& /*unused*/, + const SurfaceHit& n) const { + auto [surface, position, direction] = n; + m.surface = surface; + m.position = position; + m.direction = direction; + m.intersectionID = surface->geometryId(); + return; + } + }; + + // We assign this to + std::vector> + reassignmentVector = { + {GeometryIdentifier().setSensitive(2), ReAssignToNeighbor{}}}; + GeometryHierarchyMap + reassignments(reassignmentVector); + MaterialInteractionAssignment::Options options; + options.reAssignments = reassignments; + + // Assign the material interactions to the surface hits + auto [assigned, unassigned] = MaterialInteractionAssignment::assign( + tContext, materialInteractions, intersectedSurfaces, options); + + // Check that the material interaction was assigned + BOOST_CHECK_EQUAL(assigned.size(), 49); + BOOST_CHECK_EQUAL(unassigned.size(), 0); + + // Check that the geoid with number 2 never shows up + for (const auto& mi : assigned) { + BOOST_CHECK_NE(mi.intersectionID, GeometryIdentifier().setSensitive(2)); + } +} + +BOOST_AUTO_TEST_CASE(AssignWithPathLength) { + auto surface = + Surface::makeShared(Transform3::Identity(), 20.0, 100.0); + surface->assignGeometryId(GeometryIdentifier().setSensitive(1)); + + using SurfaceHit = std::tuple; + + Vector3 position = {20., 10., 0.}; + Vector3 direction = position.normalized(); + + SurfaceHit surfaceHit = {surface.get(), position, direction}; + + Material material = Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0); + + MaterialInteraction materialInteraction; + materialInteraction.materialSlab = MaterialSlab(material, 0.1); + materialInteraction.position = position + 0.5 * direction; + materialInteraction.direction = direction; + + MaterialInteractionAssignment::Options options; + + auto [assigned, unassigned] = MaterialInteractionAssignment::assign( + tContext, {materialInteraction}, {surfaceHit}, options); + + // Check that the material interaction was assigned + BOOST_CHECK_EQUAL(assigned.size(), 1); + BOOST_CHECK_EQUAL(unassigned.size(), 0); + + // Check that the path correction is set + BOOST_CHECK_NE(assigned[0].pathCorrection, 0.); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace Test +} // namespace Acts diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index 0bb9e67e4bf..9d199c9aa90 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -201,8 +201,6 @@ struct Fixture { .template connect<&testSourceLinkCalibrator>(); extensions.updater.template connect<&KalmanUpdater::operator()>( &kfUpdater); - extensions.smoother - .template connect<&KalmanSmoother::operator()>(&kfSmoother); extensions.measurementSelector .template connect<&Acts::MeasurementSelector::select>( &measSel); @@ -308,8 +306,6 @@ BOOST_AUTO_TEST_CASE(ZeroFieldForward) { // Construct a plane surface as the target surface auto pSurface = Acts::Surface::makeShared( Acts::Vector3{-3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); - // Set the target surface - options.smoothingTargetSurface = pSurface.get(); Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; @@ -367,8 +363,6 @@ BOOST_AUTO_TEST_CASE(ZeroFieldBackward) { // Construct a plane surface as the target surface auto pSurface = Acts::Surface::makeShared( Acts::Vector3{3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); - // Set the target surface - options.smoothingTargetSurface = pSurface.get(); Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; diff --git a/Tests/UnitTests/Core/Utilities/CMakeLists.txt b/Tests/UnitTests/Core/Utilities/CMakeLists.txt index 49a67ff901f..5e607f14a25 100644 --- a/Tests/UnitTests/Core/Utilities/CMakeLists.txt +++ b/Tests/UnitTests/Core/Utilities/CMakeLists.txt @@ -39,7 +39,7 @@ add_unittest(TypeTraits TypeTraitsTest.cpp) add_unittest(UnitVectors UnitVectorsTests.cpp) add_unittest(Delegate DelegateTests.cpp) add_unittest(HashedString HashedStringTests.cpp) -if (ACTS_BUILD_CUDA_FEATURES) +if (ACTS_BUILD_CUDA_FEATURES) add_unittest(Cuda CudaTests.cu) add_unittest(CudaMostSimplified CudaMostSimplifiedTests.cu) endif() @@ -50,3 +50,4 @@ target_compile_definitions(ActsUnitTestAnyDebug PRIVATE _ACTS_ANY_ENABLE_VERBOSE add_unittest(ParticleData ParticleDataTests.cpp) add_unittest(Zip ZipTests.cpp) +add_unittest(TransformRange TransformRangeTests.cpp) diff --git a/Tests/UnitTests/Core/Utilities/GridAccessHelpersTests.cpp b/Tests/UnitTests/Core/Utilities/GridAccessHelpersTests.cpp index 550de1b6ae0..1bafdacc659 100644 --- a/Tests/UnitTests/Core/Utilities/GridAccessHelpersTests.cpp +++ b/Tests/UnitTests/Core/Utilities/GridAccessHelpersTests.cpp @@ -57,6 +57,14 @@ BOOST_AUTO_TEST_CASE(Grid1DAccess) { BOOST_CHECK_EQUAL(grid.atPosition(fgAccess), 0u); BOOST_CHECK_EQUAL(grid.atPosition(sgAccess), 3u); BOOST_CHECK_EQUAL(grid.atPosition(tgAccess), 6u); + + // Can this go into a delegate? + auto gsu = std::make_unique>(); + Acts::GridAccess::GlobalToGridLocal1DimDelegate gsuDelegate; + gsuDelegate.connect<&Acts::GridAccess::GlobalSubspace::toGridLocal>( + std::move(gsu)); + + BOOST_CHECK(gsuDelegate.connected()); } BOOST_AUTO_TEST_CASE(Grid2DAccess) { @@ -121,7 +129,7 @@ BOOST_AUTO_TEST_CASE(BoundCylinderToZPhiTests) { Acts::ActsScalar shift = 0.; Acts::GridAccess::BoundCylinderToZPhi bctzp(radius, shift); - auto zphi = bctzp.l2ZPhi(Vector2{0.25 * radius, 52.}); + auto zphi = bctzp.toGridLocal(Vector2{0.25 * radius, 52.}); CHECK_CLOSE_ABS(zphi[0], 52., 1.e-6); CHECK_CLOSE_ABS(zphi[1], 0.25, 1.e-6); diff --git a/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp b/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp new file mode 100644 index 00000000000..8b86e9af451 --- /dev/null +++ b/Tests/UnitTests/Core/Utilities/TransformRangeTests.cpp @@ -0,0 +1,230 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include + +#include "Acts/Utilities/TransformRange.hpp" + +using namespace Acts; + +BOOST_AUTO_TEST_SUITE(TransformRangeTests) + +auto checkSameAddresses = [](const auto& orig, auto& act) { + BOOST_CHECK_EQUAL(orig.size(), act.size()); + auto it = act.begin(); + for (std::size_t i = 0; i < orig.size(); ++i) { + BOOST_CHECK_EQUAL(orig.at(i).get(), &act.at(i)); + BOOST_CHECK_EQUAL(orig.at(i).get(), &act[i]); + BOOST_CHECK_EQUAL(*orig.at(i), act.at(i)); + + BOOST_CHECK_EQUAL(orig.at(i).get(), &*it); + BOOST_CHECK_EQUAL(*orig.at(i).get(), *it); + + ++it; + } + + BOOST_CHECK(it == act.end()); +}; + +BOOST_AUTO_TEST_CASE(TransformRangeDeref) { + { + std::vector> v; + v.push_back(std::make_unique(1)); + v.push_back(std::make_unique(2)); + v.push_back(std::make_unique(3)); + + { + auto r = detail::TransformRange{detail::Dereference{}, v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + r.at(2) = 4; + BOOST_CHECK_EQUAL(*v.at(2), 4); + + checkSameAddresses(v, r); + + const auto& r_cref = r; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, r_cref); + + auto cr = detail::TransformRange{detail::ConstDereference{}, v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, cr); + + const auto& cr_cref = cr; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, cr_cref); + + std::vector act; + std::copy(cr_cref.begin(), cr_cref.end(), std::back_inserter(act)); + std::vector exp = {1, 2, 4}; + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), + act.end()); + } + + std::vector raw_v; + for (auto& ptr : v) { + raw_v.push_back(ptr.get()); + } + { + auto r = detail::TransformRange{detail::Dereference{}, raw_v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, r); + + auto cr = detail::TransformRange{detail::ConstDereference{}, raw_v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, r); + } + } + + { + std::vector> v; + v.push_back(std::make_unique(1)); + v.push_back(std::make_unique(2)); + v.push_back(std::make_unique(3)); + + { + auto r = detail::TransformRange{detail::Dereference{}, v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, r); + } + + std::vector raw_v; + for (auto& ptr : v) { + raw_v.push_back(ptr.get()); + } + { + auto r = detail::TransformRange{detail::Dereference{}, raw_v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + checkSameAddresses(v, r); + } + } +} + +BOOST_AUTO_TEST_CASE(TransformRangeFromConstRef) { + std::vector> v; + v.push_back(std::make_unique(1)); + v.push_back(std::make_unique(2)); + v.push_back(std::make_unique(3)); + + const auto& cv = v; + + { + auto r_cv = detail::TransformRange{detail::Dereference{}, cv}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + checkSameAddresses(v, r_cv); + checkSameAddresses(cv, r_cv); + + std::vector act; + std::copy(r_cv.begin(), r_cv.end(), std::back_inserter(act)); + std::vector exp = {1, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), + act.end()); + } + + { + auto r_cv = detail::TransformRange{detail::ConstDereference{}, cv}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + checkSameAddresses(v, r_cv); + checkSameAddresses(cv, r_cv); + + std::vector act; + std::copy(r_cv.begin(), r_cv.end(), std::back_inserter(act)); + std::vector exp = {1, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), + act.end()); + } +} + +BOOST_AUTO_TEST_CASE(TransformRangeReferenceWrappers) { + int a = 5; + int b = 6; + int c = 7; + + std::vector> v = {a, b, c}; + BOOST_CHECK_EQUAL(&v[0].get(), &a); + + auto r = detail::TransformRange{detail::DotGet{}, v}; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + BOOST_CHECK_EQUAL(r.at(0), 5); + BOOST_CHECK_EQUAL(&r.at(0), &a); + BOOST_CHECK_EQUAL(r[0], 5); + BOOST_CHECK_EQUAL(&r[0], &a); + BOOST_CHECK_EQUAL(*r.begin(), 5); + BOOST_CHECK_EQUAL(&*r.begin(), &a); + + std::vector act; + std::copy(r.begin(), r.end(), std::back_inserter(act)); + std::vector exp = {5, 6, 7}; + BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), act.end()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/Json/CMakeLists.txt b/Tests/UnitTests/Plugins/Json/CMakeLists.txt index ec87d5de989..1f8573fdf50 100644 --- a/Tests/UnitTests/Plugins/Json/CMakeLists.txt +++ b/Tests/UnitTests/Plugins/Json/CMakeLists.txt @@ -7,6 +7,7 @@ add_unittest(DetectorVolumeFinderJsonConverter DetectorVolumeFinderJsonConverter add_unittest(ExtentJsonConverter ExtentJsonConverterTests.cpp) add_unittest(GeometryHierarchyMapJsonConverter GeometryHierarchyMapJsonConverterTests.cpp) add_unittest(GridJsonConverter GridJsonConverterTests.cpp) +add_unittest(MaterialJsonConverter MaterialJsonConverterTests.cpp) add_unittest(MaterialMapJsonConverter MaterialMapJsonConverterTests.cpp) add_unittest(PortalJsonConverter PortalJsonConverterTests.cpp) add_unittest(ProtoDetectorJsonConverter ProtoDetectorJsonConverterTests.cpp) diff --git a/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp index ffe4b8ed2e4..0cf3c030368 100644 --- a/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp @@ -274,10 +274,24 @@ void checkGlobalSubspaceTuple(const SubspactTuple& sstuple) { } // Read back in - std::vector> sspaceRead; + std::vector> + sspaceRead; for (auto& jss : jsspace) { sspaceRead.push_back( Acts::GridAccessJsonConverter::globalToGridLocalFromJson(jss)); + if (jss["accessors"].size() == 1u) { + auto delegate = + Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson( + jss); + BOOST_CHECK(delegate.connected()); + } else if (jss["accessors"].size() == 2u) { + auto delegate = + Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson( + jss); + BOOST_CHECK(delegate.connected()); + } else { + BOOST_CHECK(false); + } } // Test that none of them are empty @@ -316,7 +330,7 @@ void checkGlobalSubspaceTuple(const SubspactTuple& sstuple) { } // Read back in - std::vector> + std::vector> sspaceTransformRead; for (auto& jss : jsspaceTransform) { sspaceTransformRead.push_back( @@ -377,7 +391,7 @@ BOOST_AUTO_TEST_CASE(GlobalSubSpaceTests2D) { checkGlobalSubspaceTuple(sspace2D); } -BOOST_AUTO_TEST_CASE(LocalSubspace1D) { +BOOST_AUTO_TEST_CASE(LocalSubspaceTests) { const std::tuple, Acts::GridAccess::LocalSubspace<1u>, Acts::GridAccess::LocalSubspace<0u, 1u>, @@ -397,10 +411,24 @@ BOOST_AUTO_TEST_CASE(LocalSubspace1D) { BOOST_CHECK(!jls.empty()); } - std::vector> lspaceRead; + std::vector> + lspaceRead; for (auto& jls : jlspace) { lspaceRead.push_back( Acts::GridAccessJsonConverter::boundToGridLocalFromJson(jls)); + if (jls["accessors"].size() == 1u) { + auto delegate = + Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson( + jls); + BOOST_CHECK(delegate.connected()); + } else if (jls["accessors"].size() == 2u) { + auto delegate = + Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + jls); + BOOST_CHECK(delegate.connected()); + } else { + BOOST_CHECK(false); + } } // Test that none of them are empty @@ -439,6 +467,11 @@ BOOST_AUTO_TEST_CASE(BoundCylinderToZPhiTest) { dynamic_cast( boundCylinderToZPhiRead.get()); + auto delegate = + Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson( + jboundCylinderToZPhi); + BOOST_CHECK(delegate.connected()); + BOOST_REQUIRE(bct != nullptr); CHECK_CLOSE_ABS(bct->radius, 100., 1e-5); CHECK_CLOSE_ABS(bct->shift, 10., 1e-5); diff --git a/Tests/UnitTests/Plugins/Json/MaterialJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/MaterialJsonConverterTests.cpp new file mode 100644 index 00000000000..cbef8dafbce --- /dev/null +++ b/Tests/UnitTests/Plugins/Json/MaterialJsonConverterTests.cpp @@ -0,0 +1,153 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Material/GridSurfaceMaterial.hpp" +#include "Acts/Material/Material.hpp" +#include "Acts/Material/MaterialSlab.hpp" +#include "Acts/Plugins/Json/MaterialJsonConverter.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Utilities/GridAccessHelpers.hpp" +#include "Acts/Utilities/GridAxisGenerators.hpp" + +#include +#include +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(MaterialJsonIO) + +BOOST_AUTO_TEST_CASE(IndexedSurfaceMaterial1DTests) { + std::vector material; + material.emplace_back(Acts::Material(), 0.0); // vacuum + material.emplace_back( + Acts::Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0), 1.0); + material.emplace_back( + Acts::Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 2.0); + material.emplace_back( + Acts::Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 3.0); + + using EqBound = Acts::GridAxisGenerators::EqBound; + using EqGrid = EqBound::grid_type; + using Point = EqGrid::point_t; + + EqBound eqBound{{0., 5.}, 5}; + EqGrid eqGrid{eqBound()}; + + eqGrid.atPosition(Point{0.5}) = 1u; // material 1 + eqGrid.atPosition(Point{1.5}) = 0u; // vacuum + eqGrid.atPosition(Point{2.5}) = 2u; // material 2 + eqGrid.atPosition(Point{3.5}) = 2u; // material 2 + eqGrid.atPosition(Point{4.5}) = 3u; // material 3 + + auto localX = std::make_unique>(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToX; + bToX.connect<&Acts::GridAccess::LocalSubspace<0u>::toGridLocal>( + std::move(localX)); + + auto globalX = + std::make_unique>(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToX; + gToX.connect<&Acts::GridAccess::GlobalSubspace::toGridLocal>( + std::move(globalX)); + + Acts::IndexedSurfaceMaterial ism( + std::move(eqGrid), Acts::IndexedMaterialAccessor{std::move(material)}, + std::move(bToX), std::move(gToX)); + + nlohmann::json jMaterial = &ism; + + // Run a few tests + BOOST_REQUIRE(jMaterial.find("material") != jMaterial.end()); + + // Read it back in + const Acts::ISurfaceMaterial* ismRead = nullptr; + Acts::from_json(jMaterial, ismRead); + BOOST_REQUIRE(ismRead != nullptr); + + // Check if it's the right type + const Acts::IndexedSurfaceMaterial* ismReadTyped = + dynamic_cast*>(ismRead); + BOOST_REQUIRE(ismReadTyped != nullptr); + + auto gridRead = ismReadTyped->grid(); + BOOST_CHECK(gridRead.atPosition(Point{0.5}) == 1u); // material 1 + BOOST_CHECK(gridRead.atPosition(Point{1.5}) == 0u); // vacuum + BOOST_CHECK(gridRead.atPosition(Point{2.5}) == 2u); // material 2 + BOOST_CHECK(gridRead.atPosition(Point{3.5}) == 2u); // material 2 + BOOST_CHECK(gridRead.atPosition(Point{4.5}) == 3u); // material 3 + + // Check the accessor is there and the material is filled + auto accessorRead = ismReadTyped->materialAccessor(); + CHECK_CLOSE_ABS(accessorRead.material[0].thickness(), 0.0, 1e-5); + CHECK_CLOSE_ABS(accessorRead.material[1].thickness(), 1.0, 1e-5); + CHECK_CLOSE_ABS(accessorRead.material[2].thickness(), 2.0, 1e-5); + CHECK_CLOSE_ABS(accessorRead.material[3].thickness(), 3.0, 1e-5); +} + +BOOST_AUTO_TEST_CASE(IndexedSurfaceMaterial2DTests) { + std::vector material; + material.emplace_back(Acts::Material(), 1.0); // vacuum + material.emplace_back( + Acts::Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0), 1.0); + material.emplace_back( + Acts::Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 1.0); + material.emplace_back( + Acts::Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 1.0); + + using EqBoundEqClosed = Acts::GridAxisGenerators::EqBoundEqClosed; + using EqEqGrid = EqBoundEqClosed::grid_type; + using Point = EqEqGrid::point_t; + + EqBoundEqClosed eqeqBound{{-1., 1.}, 2, {-M_PI, M_PI}, 4}; + EqEqGrid eqeqGrid{eqeqBound()}; + + eqeqGrid.atPosition(Point{-0.5, -M_PI * 0.75}) = 1u; // material 1 + eqeqGrid.atPosition(Point{-0.5, -M_PI * 0.25}) = 1u; // material 1 + eqeqGrid.atPosition(Point{-0.5, M_PI * 0.25}) = 0u; // vacuum + eqeqGrid.atPosition(Point{-0.5, M_PI * 0.75}) = 2u; // material 2 + + eqeqGrid.atPosition(Point{0.5, -M_PI * 0.75}) = 0u; // vacuum + eqeqGrid.atPosition(Point{0.5, -M_PI * 0.25}) = 3u; // material 3 + eqeqGrid.atPosition(Point{0.5, M_PI * 0.25}) = 3u; // material 3 + eqeqGrid.atPosition(Point{0.5, M_PI * 0.75}) = 0u; // vacuum + + auto boundToGrid = + std::make_unique>(); + Acts::IndexedSurfaceMaterial::BoundToGridLocalDelegate bToZPhi; + bToZPhi.connect<&Acts::GridAccess::LocalSubspace<0u, 1u>::toGridLocal>( + std::move(boundToGrid)); + + // With z shift 10 + auto globalToGrid = std::make_unique< + const Acts::GridAccess::GlobalSubspace>(); + Acts::IndexedSurfaceMaterial::GlobalToGridLocalDelegate gToZphi; + gToZphi.connect< + &Acts::GridAccess::GlobalSubspace::toGridLocal>( + std::move(globalToGrid)); + + // Create the indexed material grid + Acts::IndexedSurfaceMaterial ism( + std::move(eqeqGrid), Acts::IndexedMaterialAccessor{std::move(material)}, + std::move(bToZPhi), std::move(gToZphi)); + + nlohmann::json jMaterial = &ism; + + // Run a few tests + BOOST_REQUIRE(jMaterial.find("material") != jMaterial.end()); + + // Read it back in + const Acts::ISurfaceMaterial* ismRead = nullptr; + Acts::from_json(jMaterial, ismRead); + BOOST_REQUIRE(ismRead != nullptr); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/docs/conf.py b/docs/conf.py index 2a50c2ab2a8..1cfc6fc39b3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -167,7 +167,7 @@ extensions += ["lazy_autodoc"] -if tags.has("white_papers"): +if on_readthedocs or tags.has("white_papers"): import white_papers white_papers.render()