diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index ca2c7d79d1c..288bb20187b 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -257,6 +257,10 @@ jobs: - name: Python level tests shell: bash + env: + PYTEST_MD_REPORT: true + PYTEST_MD_REPORT_VERBOSE: 0 + PYTEST_MD_REPORT_OUTPUT: pytest.md run: > /usr/local/bin/geant4-config --install-datasets && source /usr/local/bin/thisroot.sh @@ -266,7 +270,9 @@ jobs: && export PYTHONPATH=/usr/local/python:$PYTHONPATH && export LD_LIBRARY_PATH=$PWD/build/thirdparty/OpenDataDetector/factory:$LD_LIBRARY_PATH && pip3 install -r Examples/Python/tests/requirements.txt + && pip3 install pytest-md-report && pytest -rFsv -k "not exatrkx" -v + && cat ${PYTEST_MD_REPORT_OUTPUT} >> $GITHUB_STEP_SUMMARY linux_physmon: runs-on: ubuntu-latest @@ -303,7 +309,7 @@ jobs: run: > echo "::group::Dependencies" && git config --global safe.directory "$GITHUB_WORKSPACE" - && pip3 install histcmp==0.6.2 spyral-cli==1.1.0 matplotlib + && pip3 install histcmp==0.6.3 spyral-cli==1.1.1 matplotlib && pip3 install -r Examples/Scripts/requirements.txt && /usr/local/bin/geant4-config --install-datasets && source /usr/local/bin/thisroot.sh @@ -314,6 +320,7 @@ jobs: && echo "::endgroup::" && export PYTHONPATH="${PYTHONPATH}":"${GITHUB_WORKSPACE}/Examples/Scripts/Python" && CI/physmon/phys_perf_mon.sh all physmon + && cat physmon/summary.md >> $GITHUB_STEP_SUMMARY - uses: actions/upload-artifact@v3 if: always() @@ -401,7 +408,7 @@ jobs: -DACTS_BUILD_ODD=ON -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON -DACTS_BUILD_EXAMPLES_EDM4HEP=ON - -DACTS_FORCE_ASSERTIONS=ON + -DACTS_FORCE_ASSERTIONS=OFF -DACTS_BUILD_ANALYSIS_APPS=ON -DACTS_BUILD_PLUGIN_ACTSVG=ON diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 1e815677268..82272c617e5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -97,6 +97,16 @@ jobs: - name: Check run: > CI/check_spelling + missing_includes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install clang + run: > + sudo apt-get install -y clang libeigen3-dev libboost-dev + - name: Check + run: > + CI/missing_include_check.sh fpe_masks: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/pr_commands.yml b/.github/workflows/pr_commands.yml new file mode 100644 index 00000000000..8a5bfa08611 --- /dev/null +++ b/.github/workflows/pr_commands.yml @@ -0,0 +1,26 @@ +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/report.yml b/.github/workflows/report.yml index 8ad7f1a893a..374ab1c8921 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -91,9 +91,14 @@ jobs: - name: Render comment if: steps.get-pr-number.outputs.result != 'false' run: | - pip install Jinja2 ls -al $GITHUB_WORKSPACE/physmon - CI/physmon/generate_comment.py $GITHUB_WORKSPACE/physmon comment.md + echo "# 📊: Physics performance monitoring for ${PR_SHA}" >> comment.md + echo "[Full contents](${ARTIFACT_URL})" >> comment.md + if [ -f "$GITHUB_WORKSPACE/physmon/summary.md" ]; then + cat $GITHUB_WORKSPACE/physmon/summary.md >> comment.md + else + echo "🟥 summary not found!" >> comment.md + fi cat comment.md - name: Find Comment diff --git a/.github/workflows/trigger_athena.yml b/.github/workflows/trigger_athena.yml index d66775227de..7e01a3dd9f7 100644 --- a/.github/workflows/trigger_athena.yml +++ b/.github/workflows/trigger_athena.yml @@ -12,4 +12,5 @@ jobs: curl -X POST --fail -F token=${{ secrets.GITLAB_ATHENA_BUILD_TRIGGER_TOKEN}} -F ref=main + --form variables[SOURCE_SHA]="${{ github.sha }}" https://gitlab.cern.ch/api/v4/projects/153873/trigger/pipeline diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ab7767e3605..94ee96f8d24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,10 +37,8 @@ clang_tidy: -DACTS_BUILD_EVERYTHING=on -DACTS_RUN_CLANG_TIDY=on - - mkdir clang-tidy - # Main clang-tidy run during cmake compilation - - CI/clang_tidy/run_clang_tidy.sh build | tee clang-tidy/clang-tidy.log + - CI/clang_tidy/run_clang_tidy.sh clang-tidy build # Install dependencies for processing scripts - pip install -r CI/clang_tidy/requirements.txt @@ -74,7 +72,6 @@ build_exatrkx: - build/ exclude: - build/**/*.o - - build/bin/ActsUnitTest* - build/bin/ActsIntegrationTest* script: @@ -96,13 +93,24 @@ build_exatrkx: -DCMAKE_CUDA_ARCHITECTURES="75;86" -DACTS_BUILD_PLUGIN_EXATRKX=ON -DACTS_BUILD_EXAMPLES_EXATRKX=ON + -DACTS_BUILD_UNITTESTS=ON -DACTS_EXATRKX_ENABLE_TORCH=ON -DACTS_EXATRKX_ENABLE_ONNX=ON -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON -DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON - cmake --build build -- -j3 -test_exatrkx: +test_exatrkx_unittests: + stage: test + needs: + - build_exatrkx + image: ghcr.io/acts-project/ubuntu2004_exatrkx:v41 + tags: + - docker-gpu-nvidia + script: + - ctest --test-dir build -R ExaTrkX + +test_exatrkx_python: stage: test needs: - build_exatrkx @@ -110,7 +118,7 @@ test_exatrkx: tags: - docker-gpu-nvidia script: - - apt-get update -y + - apt-get update -y || true # TODO revert - apt-get install -y python3 libxxhash0 - source /usr/local/bin/thisroot.sh - source build/python/setup.sh diff --git a/.policy.yml b/.policy.yml index 96c8f8b0274..ccadc6dccf0 100644 --- a/.policy.yml +++ b/.policy.yml @@ -12,7 +12,7 @@ approval_rules: options: allow_author: false # just for completeness allow_contributor: true # Update button 'contributions' should be ignored - invalidate_on_push: false + invalidate_on_push: true ignore_update_merges: true if: targets_branch: diff --git a/CI/clang_tidy/run_clang_tidy.sh b/CI/clang_tidy/run_clang_tidy.sh index b30d712ebf5..ecc3054d6c8 100755 --- a/CI/clang_tidy/run_clang_tidy.sh +++ b/CI/clang_tidy/run_clang_tidy.sh @@ -1,8 +1,27 @@ #!/bin/bash + +set -e + +output_dir=$1 +shift build_dir=$1 +shift -export NINJA_STATUS="[ninja][%f/%t] " +mkdir -p $output_dir +output_dir=$(realpath $output_dir) pushd $build_dir -ninja | grep -v '\[ninja\]' +NINJA_STATUS="[ninja] [%f/%t] " ninja $@ | tee $output_dir/ninja.log popd + +# grep fails if it does not find anything +set +e +rm $output_dir/clang-tidy.log +cat $output_dir/ninja.log | grep -v '\[ninja\]' > $output_dir/clang-tidy.log +set -e + +if [ ! -f $output_dir/clang-tidy.log ]; then + exit 1 +fi + +exit 0 diff --git a/CI/commands/pr_commands.py b/CI/commands/pr_commands.py new file mode 100755 index 00000000000..eaee61cbe2f --- /dev/null +++ b/CI/commands/pr_commands.py @@ -0,0 +1,196 @@ +#!/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 new file mode 100644 index 00000000000..9ed565536ec --- /dev/null +++ b/CI/commands/requirements.in @@ -0,0 +1,3 @@ +typer +aiohttp +gidgethub diff --git a/CI/commands/requirements.txt b/CI/commands/requirements.txt new file mode 100644 index 00000000000..3a1add6221f --- /dev/null +++ b/CI/commands/requirements.txt @@ -0,0 +1,46 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile CI/commands/requirements.in +# +aiohttp==3.8.5 + # via -r CI/commands/requirements.in +aiosignal==1.3.1 + # via aiohttp +async-timeout==4.0.3 + # via aiohttp +attrs==23.1.0 + # via aiohttp +cffi==1.15.1 + # via cryptography +charset-normalizer==3.2.0 + # via aiohttp +click==8.1.7 + # via typer +cryptography==41.0.3 + # via pyjwt +frozenlist==1.4.0 + # via + # aiohttp + # aiosignal +gidgethub==5.3.0 + # via -r CI/commands/requirements.in +idna==3.4 + # 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.7.1 + # via typer +uritemplate==4.1.1 + # via gidgethub +yarl==1.9.2 + # via aiohttp diff --git a/CI/missing_include_check.sh b/CI/missing_include_check.sh new file mode 100755 index 00000000000..970c4a396c4 --- /dev/null +++ b/CI/missing_include_check.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +RET=0 +ERRORS=0 + +FILES=$(find Core/include/ -name "*.hpp" | grep -v "/detail/") +N_FILES=$(echo "$FILES" | wc -l) +echo "Check $N_FILES files" + +ITER=0 + +for file in $(find Core/include/ -name "*.hpp" | grep -v "/detail/"); do + ITER=$((ITER+1)) + echo "$(date +%H:%M:%S) $((100*ITER/N_FILES))% check $file" + out=$(printf "#include <${file:13}>\nint main() { return 0; }" | clang++ -std=c++17 -O0 -c -I "Core/include" -I "/usr/include/eigen3" -x c++ - 2>&1) + if [[ "$?" -ne "0" ]]; then + echo "------------------------------------" + echo "$out" + echo "------------------------------------" + RET=1 + ERRORS=$((ERRORS+1)) + fi +done + +echo "Total errors: $ERRORS" +exit $RET diff --git a/CI/physmon/comment_template.md b/CI/physmon/comment_template.md deleted file mode 100644 index 6717aa480ad..00000000000 --- a/CI/physmon/comment_template.md +++ /dev/null @@ -1,167 +0,0 @@ -## :bar_chart: Physics performance monitoring for {{ commit }} -{% if has_errors %} -> :red_square: **ERROR** The result has missing elements! -> This is likely a physmon job failure -{% endif %} - -[Summary]({{ url }}/summary.html) -[Full report]({{ url }}/) -Seeding: {{ make_url("seeded", "seeding_seeded.html") }}, {{ make_url("truth estimated", "seeding_truth_estimated.html") }}, {{ make_url("orthogonal", "seeding_orthogonal.html") }} -CKF: {{ make_url("seeded", "ckf_seeded.html") }}, {{ make_url("truth smeared", "ckf_truth_smeared.html") }}, {{ make_url("truth estimated", "ckf_truth_estimated.html") }}, {{ make_url("orthogonal", "ckf_orthogonal.html") }} -IVF: {{ make_url("seeded", "ivf_seeded.html") }}, {{ make_url("truth smeared", "ivf_truth_smeared.html") }}, {{ make_url("truth estimated", "ivf_truth_estimated.html") }}, {{ make_url("orthogonal", "ivf_orthogonal.html") }} -AMVF: {{ make_url("seeded", "amvf_seeded.html") }}, {{ make_url("truth smeared", "amvf_truth_smeared.html") }}, {{ make_url("truth estimated", "amvf_truth_estimated.html") }}, {{ make_url("orthogonal", "amvf_orthogonal.html") }} -Ambiguity resolution: {{ make_url("seeded", "ambi_seeded.html") }}, {{ make_url("orthogonal", "ambi_orthogonal.html") }} -{{ make_url("Truth tracking", "truth_tracking.html") }} -{{ make_url("Truth tracking (GSF)", "gsf.html")}} - -### Vertexing {{ "" if all_exist( - "vertexing_mu_scan.pdf", - "ivf_seeded_plots", - "ivf_truth_smeared_plots", - "ivf_truth_estimated_plots", - "ivf_orthogonal_plots", - "amvf_seeded_plots", - "amvf_truth_smeared_plots", - "amvf_truth_estimated_plots", - "amvf_orthogonal_plots", -) else ":x: "}} - -{% call detail_block("Vertexing vs. mu", "vertexing_mu_scan.pdf") %} -{{ make_image("vertexing_mu_scan.pdf", 350) }} -{% endcall %} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("IVF "+mode, "ivf_"+mode+"_plots") %} - -{% for url in [ - "covXX.pdf", - "covYY.pdf", - "covZZ.pdf", - "resX.pdf", - "resY.pdf", - "resZ.pdf", - "recoOverTrue.pdf", -] -%} -{{- make_image("ivf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("AMVF "+mode, "amvf_"+mode+"_plots") %} - -{% for url in [ - "covXX.pdf", - "covYY.pdf", - "covZZ.pdf", - "resX.pdf", - "resY.pdf", - "resZ.pdf", - "recoOverTrue.pdf", -] -%} -{{- make_image("amvf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### Seeding {{ "" if all_exist( - "seeding_seeded_plots", - "seeding_truth_estimated_plots", - "seeding_orthogonal_plots", -) else ":x: "}} - -{% for mode in ["seeded", "truth_estimated", "orthogonal"] %} - -{% call detail_block("Seeding "+mode, "seeding_"+mode+"_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nDuplicated_vs_eta.pdf", - "nDuplicated_vs_pT.pdf", -] -%} -{{- make_image("seeding_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### CKF {{ "" if all_exist( - "ckf_seeded_plots", - "ckf_truth_smeared_plots", - "ckf_truth_estimated_plots", - "ckf_orthogonal_plots", -) else ":x: "}} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("CKF "+mode, "ckf_"+mode+"_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", -] -%} -{{- make_image("ckf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### Ambiguity resolution {{ "" if exists("ambi_seeded_plots") else ":x: "}} - -{% call detail_block("seeded", "ambi_seeded_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", -] -%} -{{- make_image("ambi_seeded_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -### Truth tracking (Kalman Filter) {{ "" if exists("truth_tracking_plots") else ":x: "}} - -{% call detail_block("Truth tracking", "truth_tracking_plots") %} - -{% for url in [ - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", - "pull_d0.pdf", - "pull_z0.pdf", - "pull_theta.pdf", - "pull_phi.pdf", - "pull_qop.pdf", - "pull_t.pdf", -] -%} -{{- make_image("truth_tracking_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -### Truth tracking (GSF) {{ "" if exists("truth_tracking_plots") else ":x: "}} - -{% call detail_block("Truth tracking", "truth_tracking_plots") %} - -{% for url in [ - "pull_d0.pdf", - "res_d0.pdf", - "pull_qop.pdf", - "res_qop.pdf", -] -%} -{{- make_image("gsf_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} diff --git a/CI/physmon/generate_comment.py b/CI/physmon/generate_comment.py deleted file mode 100755 index 00854a3107c..00000000000 --- a/CI/physmon/generate_comment.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 - - -### CHANGE BELOW WITH CAUTION ### - -import sys -import os -from pathlib import Path, PurePath - -import jinja2 - -template_source = (Path(__file__).parent / "comment_template.md").read_text() - -macro_source = """ -{% macro detail_block(title, check) %} -{% if exists(check) %} -
- {{ title }} - {{ caller() }} -
-{% else %} -:x: {{ title }} -{% endif %} -{% endmacro %} -""" - -template_source = macro_source + template_source - -template = jinja2.Template(template_source) - -_, artifact_dir, outfile = sys.argv -artifact_dir = Path(artifact_dir) -outfile = Path(outfile) - -artifact_url = os.environ["ARTIFACT_URL"] -pr_sha = os.environ["PR_SHA"] - -print("artifact_url:", artifact_url) -print("pr_sha:", pr_sha) - -has_errors = False - - -def exists(arg): - file = artifact_dir / arg - result = file.exists() - print("Check exists:", file, "=>", result) - global has_errors - if not result: - has_errors = True - return result - - -def all_exist(*args): - result = all(exists(a) for a in args) - global has_errors - if not result: - has_errors = True - return result - - -def make_url(title, target): - if exists(target): - url = artifact_url + "/" + target - return f"[{title}]({url})" - else: - global has_errors - has_errors = True - return f":x: {title}" - - -def make_image(target, width): - file = target - if "?" in target: - file = target[: target.index("?")] - if exists(file): - url = artifact_url + "/" + file - return f'' - else: - global has_errors - has_errors = True - return f":framed_picture: {target} :x:" - - -kwargs = dict( - url=artifact_url, - commit=pr_sha, - exists=exists, - all_exist=all_exist, - make_url=make_url, - make_image=make_image, - has_errors=has_errors, -) - -# render once to fill `has_errors` -template.render(**kwargs) - -kwargs["has_errors"] = has_errors - -outfile.write_text(template.render(**kwargs)) diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index 961bf8461f5..8b4e023be7c 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -18,52 +18,96 @@ refcommit=$(cat $refdir/commit) commit=$(git rev-parse --short HEAD) SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -SPYRAL_BIN="spyral" -SPYRAL="${SPYRAL_BIN} run -i 0.1 --summary" +# File to accumulate the histcmp results +histcmp_results=$outdir/histcmp_results.csv +echo -n "" > $histcmp_results -mkdir ${outdir}/memory +SPYRAL_BIN=${SPYRAL_BIN:=spyral} +SPYRAL="${SPYRAL_BIN} run -i 0.05 --summary" + +mkdir -p ${outdir}/memory + +set +e +ec=0 source $SCRIPT_DIR/setup.sh + +function run_physmon_gen() { + title=$1 + slug=$2 + + script=CI/physmon/workflows/physmon_${slug}.py + + $SPYRAL -l "$title" -o "$outdir/memory/mem_${slug}.csv" -- ${script} $outdir 2>&1 > $outdir/run_${slug}.log + + this_ec=$? + ec=$(($ec | $this_ec)) + + if [ $this_ec -ne 0 ]; then + echo "::error::🟥 Dataset generation failed: ${script} -> ec=$this_ec" + else + echo "::notice::✅ Dataset generation succeeded: ${script}" + fi + + $SPYRAL_BIN plot $outdir/memory/mem_${slug}.csv --output $outdir/memory +} + echo "::group::Generate validation dataset" if [[ "$mode" == "all" || "$mode" == "kalman" ]]; then - $SPYRAL -l "Truth Tracking KF" -o "$outdir/memory/mem_truth_tracking_kalman.csv" -- CI/physmon/workflows/physmon_truth_tracking_kalman.py $outdir 2>&1 > $outdir/run_truth_tracking_kalman.log + run_physmon_gen "Truth Tracking KF" "truth_tracking_kalman" fi if [[ "$mode" == "all" || "$mode" == "gsf" ]]; then - $SPYRAL -l "Truth Tracking GSF" -o "$outdir/memory/mem_truth_tracking_gsf.csv" -- CI/physmon/workflows/physmon_truth_tracking_gsf.py $outdir 2>&1 > $outdir/run_truth_tracking_gsf.log + run_physmon_gen "Truth Tracking GSF" "truth_tracking_gsf" fi if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then - $SPYRAL -l "CKF Tracking" -o "$outdir/memory/mem_ckf_tracking.csv" -- CI/physmon/workflows/physmon_ckf_tracking.py $outdir 2>&1 > $outdir/run_ckf_tracking.log + run_physmon_gen "CKF Tracking" "ckf_tracking" + run_physmon_gen "Track finding ttbar" "track_finding_ttbar" + fi if [[ "$mode" == "all" || "$mode" == "vertexing" ]]; then - $SPYRAL -l "Vertexing" -o "$outdir/memory/mem_vertexing.csv" -- CI/physmon/workflows/physmon_vertexing.py $outdir 2>&1 > $outdir/run_vertexing.log + run_physmon_gen "Vertexing" "vertexing" fi if [[ "$mode" == "all" || "$mode" == "simulation" ]]; then - $SPYRAL -l "Simulation" -o "$outdir/memory/mem_simulation.csv" -- CI/physmon/workflows/physmon_simulation.py $outdir 2>&1 > $outdir/run_simulation.log + run_physmon_gen "Simulation" "simulation" fi echo "::endgroup::" -$SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_kalman.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_gsf.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_ckf_tracking.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_vertexing.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_simulation.csv --output $outdir/memory - -set +e - -ec=0 -function run() { +function run_histcmp() { a=$1 b=$2 + title=$3 + slug=$4 + shift 4 echo "::group::Comparing $a vs. $b" - histcmp \ + if [ ! -f "$a" ]; then + echo "::error::histcmp failed: File $a does not exist" + ec=1 + fi + + if [ ! -f "$b" ]; then + echo "::error::histcmp failed: File $b does not exist" + ec=1 + fi + + histcmp $a $b \ --label-reference=reference \ --label-monitored=monitored \ + --title="$title" \ + -o $outdir/${slug}.html \ + -p $outdir/${slug}_plots \ "$@" - ec=$(($ec | $?)) + this_ec=$? + ec=$(($ec | $this_ec)) + + if [ $this_ec -ne 0 ]; then + echo "::error::histcmp failed (${slug}): ec=$this_ec" + fi + + echo "\"${title}\",${slug},${this_ec}" >> $histcmp_results echo "::endgroup::" } @@ -79,22 +123,20 @@ function full_chain() { echo $config if [ $suffix != truth_smeared ]; then - run \ - $outdir/performance_seeding_${suffix}.root \ - $refdir/performance_seeding_${suffix}.root \ - --title "Seeding ${suffix}" \ - -c $config \ - -o $outdir/seeding_${suffix}.html \ - -p $outdir/seeding_${suffix}_plots + run_histcmp \ + $outdir/performance_seeding_${suffix}.root \ + $refdir/performance_seeding_${suffix}.root \ + "Seeding ${suffix}" \ + seeding_${suffix} \ + -c $config fi - run \ + run_histcmp \ $outdir/performance_ckf_${suffix}.root \ $refdir/performance_ckf_${suffix}.root \ - --title "CKF ${suffix}" \ - -c $config \ - -o $outdir/ckf_${suffix}.html \ - -p $outdir/ckf_${suffix}_plots + "CKF ${suffix}" \ + ckf_${suffix} \ + -c $config Examples/Scripts/generic_plotter.py \ $outdir/performance_ivf_${suffix}.root \ @@ -107,12 +149,11 @@ function full_chain() { # remove ntuple file because it's large rm $outdir/performance_ivf_${suffix}.root - run \ + run_histcmp \ $outdir/performance_ivf_${suffix}_hist.root \ $refdir/performance_ivf_${suffix}_hist.root \ - --title "IVF ${suffix}" \ - -o $outdir/ivf_${suffix}.html \ - -p $outdir/ivf_${suffix}_plots + "IVF ${suffix}" \ + ivf_${suffix} Examples/Scripts/generic_plotter.py \ $outdir/performance_amvf_${suffix}.root \ @@ -125,12 +166,30 @@ function full_chain() { # remove ntuple file because it's large rm $outdir/performance_amvf_${suffix}.root - run \ + run_histcmp \ $outdir/performance_amvf_${suffix}_hist.root \ $refdir/performance_amvf_${suffix}_hist.root \ - --title "AMVF ${suffix}" \ - -o $outdir/amvf_${suffix}.html \ - -p $outdir/amvf_${suffix}_plots + "AMVF ${suffix}" \ + amvf_${suffix} + + if [ $suffix == seeded ]; then + Examples/Scripts/generic_plotter.py \ + $outdir/performance_amvf_gridseeder_${suffix}.root \ + vertexing \ + $outdir/performance_amvf_gridseeder_${suffix}_hist.root \ + --silent \ + --config CI/physmon/vertexing_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/performance_amvf_gridseeder_${suffix}.root + + run_histcmp \ + $outdir/performance_amvf_gridseeder_${suffix}_hist.root \ + $refdir/performance_amvf_gridseeder_${suffix}_hist.root \ + "AMVF (+grid seeder) ${suffix}" \ + amvf_gridseeder_${suffix} + fi Examples/Scripts/generic_plotter.py \ $outdir/tracksummary_ckf_${suffix}.root \ @@ -143,12 +202,12 @@ function full_chain() { # remove ntuple file because it's large rm $outdir/tracksummary_ckf_${suffix}.root - run \ + run_histcmp \ $outdir/tracksummary_ckf_${suffix}_hist.root \ $refdir/tracksummary_ckf_${suffix}_hist.root \ - --title "Track Summary CKF ${suffix}" \ - -o $outdir/tracksummary_ckf_${suffix}.html \ - -p $outdir/tracksummary_ckf_${suffix}_plots + "Track Summary CKF ${suffix}" \ + tracksummary_ckf_${suffix} + } function simulation() { @@ -167,12 +226,11 @@ function simulation() { # remove ntuple file because it's large rm $outdir/particles_initial_${suffix}.root - run \ + run_histcmp \ $outdir/particles_initial_${suffix}_hist.root \ $refdir/particles_initial_${suffix}_hist.root \ - --title "Particles inital ${suffix}" \ - -o $outdir/particles_initial_${suffix}.html \ - -p $outdir/particles_initial_${suffix}_plots + "Particles inital ${suffix}" \ + particles_initial_${suffix} Examples/Scripts/generic_plotter.py \ $outdir/particles_final_${suffix}.root \ @@ -185,12 +243,11 @@ function simulation() { # remove ntuple file because it's large rm $outdir/particles_final_${suffix}.root - run \ + run_histcmp \ $outdir/particles_final_${suffix}_hist.root \ $refdir/particles_final_${suffix}_hist.root \ - --title "Particles final ${suffix}" \ - -o $outdir/particles_final_${suffix}.html \ - -p $outdir/particles_final_${suffix}_plots + "Particles final ${suffix}" \ + particles_final_${suffix} } if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then @@ -199,39 +256,89 @@ if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then full_chain seeded full_chain orthogonal - run \ + run_histcmp \ $outdir/performance_ambi_seeded.root \ $refdir/performance_ambi_seeded.root \ - --title "Ambisolver seeded" \ - -o $outdir/ambi_seeded.html \ - -p $outdir/ambi_seeded_plots + "Ambisolver seeded" \ + ambi_seeded - run \ + run_histcmp \ $outdir/performance_ambi_orthogonal.root \ $refdir/performance_ambi_orthogonal.root \ - --title "Ambisolver orthogonal" \ - -o $outdir/ambi_orthogonal.html \ - -p $outdir/ambi_orthogonal_plots + "Ambisolver orthogonal" \ + ambi_orthogonal + + run_histcmp \ + $outdir/performance_seeding_ttbar.root \ + $refdir/performance_seeding_ttbar.root \ + "Seeding ttbar" \ + seeding_ttbar \ + -c $config + + run_histcmp \ + $outdir/performance_ckf_ttbar.root \ + $refdir/performance_ckf_ttbar.root \ + "CKF ttbar" \ + ckf_ttbar \ + -c $config + + run_histcmp \ + $outdir/performance_ambi_ttbar.root \ + $refdir/performance_ambi_ttbar.root \ + "Ambisolver " \ + ambi_ttbar + + Examples/Scripts/generic_plotter.py \ + $outdir/performance_amvf_ttbar.root \ + vertexing \ + $outdir/performance_amvf_ttbar_hist.root \ + --silent \ + --config CI/physmon/vertexing_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/performance_amvf_ttbar.root + + run_histcmp \ + $outdir/performance_amvf_ttbar_hist.root \ + $refdir/performance_amvf_ttbar_hist.root \ + "AMVF ttbar" \ + amvf_ttbar + + Examples/Scripts/generic_plotter.py \ + $outdir/performance_amvf_gridseeder_ttbar.root \ + vertexing \ + $outdir/performance_amvf_gridseeder_ttbar_hist.root \ + --silent \ + --config CI/physmon/vertexing_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/performance_amvf_gridseeder_ttbar.root + + run_histcmp \ + $outdir/performance_amvf_gridseeder_ttbar_hist.root \ + $refdir/performance_amvf_gridseeder_ttbar_hist.root \ + "AMVF (+grid seeder) ttbar" \ + amvf_gridseeder_ttbar fi if [[ "$mode" == "all" || "$mode" == "gsf" ]]; then - run \ + run_histcmp \ $outdir/performance_gsf.root \ $refdir/performance_gsf.root \ - --title "Truth tracking (GSF)" \ - -c CI/physmon/gsf.yml \ - -o $outdir/gsf.html \ - -p $outdir/gsf_plots + "Truth tracking (GSF)" \ + gsf \ + -c CI/physmon/gsf.yml fi if [[ "$mode" == "all" || "$mode" == "kalman" ]]; then - run \ + run_histcmp \ $outdir/performance_truth_tracking.root \ $refdir/performance_truth_tracking.root \ - --title "Truth tracking" \ - -c CI/physmon/truth_tracking.yml \ - -o $outdir/truth_tracking.html \ - -p $outdir/truth_tracking_plots + "Truth tracking" \ + truth_tracking \ + -c CI/physmon/truth_tracking.yml fi if [[ "$mode" == "all" || "$mode" == "vertexing" ]]; then @@ -247,7 +354,9 @@ if [[ "$mode" == "all" || "$mode" == "simulation" ]]; then simulation geant4 fi -CI/physmon/summary.py $outdir/*.html $outdir/summary.html +CI/physmon/summary.py $histcmp_results \ + --md $outdir/summary.md \ + --html $outdir/summary.html ec=$(($ec | $?)) exit $ec diff --git a/CI/physmon/reference/performance_ambi_orthogonal.root b/CI/physmon/reference/performance_ambi_orthogonal.root index f74ed6eb2d1..d7eb451a72d 100644 Binary files a/CI/physmon/reference/performance_ambi_orthogonal.root and b/CI/physmon/reference/performance_ambi_orthogonal.root differ diff --git a/CI/physmon/reference/performance_ambi_seeded.root b/CI/physmon/reference/performance_ambi_seeded.root index 297ef62e32a..22205d19e77 100644 Binary files a/CI/physmon/reference/performance_ambi_seeded.root and b/CI/physmon/reference/performance_ambi_seeded.root differ diff --git a/CI/physmon/reference/performance_ambi_ttbar.root b/CI/physmon/reference/performance_ambi_ttbar.root new file mode 100644 index 00000000000..f6881fb8505 Binary files /dev/null and b/CI/physmon/reference/performance_ambi_ttbar.root differ diff --git a/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root b/CI/physmon/reference/performance_amvf_gridseeder_seeded_hist.root new file mode 100644 index 00000000000..030e33c82b2 Binary files /dev/null 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 new file mode 100644 index 00000000000..3e4aac418f6 Binary files /dev/null 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 17b0760346f..eec6b742536 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 7a0f77a886d..32b996c9501 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 6000e7addd0..3d141f77833 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 08bb9dc987b..acf32965203 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 new file mode 100644 index 00000000000..3bdd6a02aa2 Binary files /dev/null and b/CI/physmon/reference/performance_amvf_ttbar_hist.root differ diff --git a/CI/physmon/reference/performance_ckf_orthogonal.root b/CI/physmon/reference/performance_ckf_orthogonal.root index ba87fdea1ad..360a2180982 100644 Binary files a/CI/physmon/reference/performance_ckf_orthogonal.root and b/CI/physmon/reference/performance_ckf_orthogonal.root differ diff --git a/CI/physmon/reference/performance_ckf_seeded.root b/CI/physmon/reference/performance_ckf_seeded.root index 39fe59502e0..f2f570a2893 100644 Binary files a/CI/physmon/reference/performance_ckf_seeded.root and b/CI/physmon/reference/performance_ckf_seeded.root differ diff --git a/CI/physmon/reference/performance_ckf_truth_estimated.root b/CI/physmon/reference/performance_ckf_truth_estimated.root index d5305829f96..2deb9405130 100644 Binary files a/CI/physmon/reference/performance_ckf_truth_estimated.root and b/CI/physmon/reference/performance_ckf_truth_estimated.root differ diff --git a/CI/physmon/reference/performance_ckf_truth_smeared.root b/CI/physmon/reference/performance_ckf_truth_smeared.root index 4fdde3d41b2..ad81f99f99a 100644 Binary files a/CI/physmon/reference/performance_ckf_truth_smeared.root and b/CI/physmon/reference/performance_ckf_truth_smeared.root differ diff --git a/CI/physmon/reference/performance_ckf_ttbar.root b/CI/physmon/reference/performance_ckf_ttbar.root new file mode 100644 index 00000000000..edc0ae36e8f Binary files /dev/null and b/CI/physmon/reference/performance_ckf_ttbar.root differ diff --git a/CI/physmon/reference/performance_ivf_orthogonal_hist.root b/CI/physmon/reference/performance_ivf_orthogonal_hist.root index 323c0b3f6b7..bab8c251daf 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 99726632fd4..cce4117ab05 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 03f7ef5f048..6783cccc531 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 442e142586d..f0b20b4287d 100644 Binary files a/CI/physmon/reference/performance_ivf_truth_smeared_hist.root and b/CI/physmon/reference/performance_ivf_truth_smeared_hist.root differ diff --git a/CI/physmon/reference/performance_seeding_ttbar.root b/CI/physmon/reference/performance_seeding_ttbar.root new file mode 100644 index 00000000000..7ef18d19e55 Binary files /dev/null and b/CI/physmon/reference/performance_seeding_ttbar.root differ diff --git a/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root b/CI/physmon/reference/tracksummary_ckf_orthogonal_hist.root index ddbd94df769..5074a1b56cc 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 b9f7642ca0d..71cbf7f38c3 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 7666b45b9e0..855efbfc3e6 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 821cff96826..a52cc0dc0d7 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/summary.py b/CI/physmon/summary.py index 9525a3ed44c..ae20ddfce4f 100755 --- a/CI/physmon/summary.py +++ b/CI/physmon/summary.py @@ -5,38 +5,38 @@ import re import functools import os +import csv + +HERALD_URL = "https://herald.dokku.paulgessinger.com/view/{repo}/runs/{run_id}/artifacts/{artifact_name}/{path}" +IS_CI = "GITHUB_ACTIONS" in os.environ parser = argparse.ArgumentParser() -parser.add_argument("html", nargs="+") -parser.add_argument("output") +parser.add_argument("results") +parser.add_argument("--html") +parser.add_argument("--md") args = parser.parse_args() re_title = re.compile(r'

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

physmon summary

- - -""" - ) +if args.md: + with open(args.md, mode="w", encoding="utf-8") as f: + f.write("# physmon summary\n") + for s in summary: + if IS_CI: + url = HERALD_URL.format( + repo=os.environ["GITHUB_REPOSITORY"], + run_id=os.environ["GITHUB_RUN_ID"], + artifact_name="physmon", + path=s["path"], + ) + else: + url = s["path"] + f.write(f" - {'✅' if s['total'] else '🔴'} [{s['title']}]({url})\n") diff --git a/CI/physmon/vertexing_config.yml b/CI/physmon/vertexing_config.yml index 0bcda614920..d870b2b7903 100644 --- a/CI/physmon/vertexing_config.yml +++ b/CI/physmon/vertexing_config.yml @@ -4,21 +4,31 @@ histograms: min: -0.1 max: 0.1 + "trk_res.*": + nbins: 100 + min: -0.1 + max: 0.1 + "pull.*": nbins: 50 min: -6 max: 6 + + "trk_pull.*": + nbins: 50 + min: -10 + max: 10 + "cov.*": nbins: 100 min: -0.0005 max: 0.0005 - # fixed mu=50 "^n.*$": - nbins: 50 + nbins: 250 min: 0 - max: 51 + max: 251 "truthX|truthY|recoX|recoY": nbins: 100 @@ -35,6 +45,26 @@ histograms: min: -2000 max: 2000 + "trk_truthPhi|trk_recoPhi|trk_recoPhiFitted": + nbins: 100 + min: -3.2 + max: 3.2 + + "trk_truthTheta|trk_recoTheta|trk_recoThetaFitted": + nbins: 100 + min: -0.1 + max: 3.1 + + "trk_truthQOverP|trk_recoQOverP|trk_recoQOverPFitted": + nbins: 100 + min: -1 + max: 1 + + "trk_momOverlap|trk_momOverlapFitted": + nbins: 100 + min: 0.99 + max: 1 + extra_histograms: - expression: df["nRecoVtx"] / df["nTrueVtx"] name: "recoOverTrue" diff --git a/CI/physmon/workflows/physmon_ckf_tracking.py b/CI/physmon/workflows/physmon_ckf_tracking.py index 50cfb3f0573..9b81f311dce 100755 --- a/CI/physmon/workflows/physmon_ckf_tracking.py +++ b/CI/physmon/workflows/physmon_ckf_tracking.py @@ -75,6 +75,7 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): s, setup.trackingGeometry, setup.field, + enableInteractions=True, rnd=rnd, ) @@ -141,6 +142,7 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): addVertexFitting( s, setup.field, + seeder=acts.VertexSeedFinder.GaussianSeeder, associatedParticles=None if label in ["seeded", "orthogonal"] else "particles_input", @@ -153,6 +155,7 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): addVertexFitting( s, setup.field, + seeder=acts.VertexSeedFinder.GaussianSeeder, associatedParticles=None if label in ["seeded", "orthogonal"] else "particles_input", @@ -162,6 +165,20 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): outputDirRoot=tp / "amvf", ) + # Use the adaptive grid vertex seeder in combination with the AMVF + # To avoid having too many physmon cases, we only do this for the label "seeded" + if label == "seeded": + addVertexFitting( + s, + setup.field, + seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, + associatedParticles=None, + outputProtoVertices="amvf_gridseeder_protovertices", + outputVertices="amvf_gridseeder_fittedVertices", + vertexFinder=VertexFinder.AMVF, + outputDirRoot=tp / "amvf_gridseeder", + ) + s.run() del s @@ -171,17 +188,28 @@ def run_ckf_tracking(truthSmearedSeeded, truthEstimatedSeeded, label): tp / f"performance_{vertexing}.root", ) - for stem in [ - "performance_ckf", - "tracksummary_ckf", - "performance_ivf", - "performance_amvf", - ] + ( - ["performance_seeding", "performance_ambi"] - if label in ["seeded", "orthogonal"] - else ["performance_seeding"] - if label == "truth_estimated" - else [] + if label == "seeded": + vertexing = "amvf_gridseeder" + shutil.move( + tp / f"{vertexing}/performance_vertexing.root", + tp / f"performance_{vertexing}.root", + ) + + for stem in ( + [ + "performance_ckf", + "tracksummary_ckf", + "performance_ivf", + "performance_amvf", + ] + + (["performance_amvf_gridseeder"] if label == "seeded" else []) + + ( + ["performance_seeding", "performance_ambi"] + if label in ["seeded", "orthogonal"] + else ["performance_seeding"] + if label == "truth_estimated" + else [] + ) ): perf_file = tp / f"{stem}.root" assert perf_file.exists(), "Performance file not found" diff --git a/CI/physmon/workflows/physmon_track_finding_ttbar.py b/CI/physmon/workflows/physmon_track_finding_ttbar.py new file mode 100755 index 00000000000..938446f46f3 --- /dev/null +++ b/CI/physmon/workflows/physmon_track_finding_ttbar.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +import tempfile +from pathlib import Path +import shutil + +import acts +from acts.examples.simulation import ( + addPythia8, + addFatras, + addDigitization, +) + +from acts.examples.reconstruction import ( + addSeeding, + TruthSeedRanges, + ParticleSmearingSigmas, + SeedFinderConfigArg, + SeedFinderOptionsArg, + SeedingAlgorithm, + TruthEstimatedSeedingAlgorithmConfigArg, + addCKFTracks, + addAmbiguityResolution, + AmbiguityResolutionConfig, + addVertexFitting, + VertexFinder, + TrackSelectorConfig, +) + +from physmon_common import makeSetup + +u = acts.UnitConstants + +setup = makeSetup() + + +with tempfile.TemporaryDirectory() as temp: + fpeMasks = acts.examples.Sequencer.FpeMask.fromFile( + Path(__file__).parent.parent / "fpe_masks.yml" + ) + [ + acts.examples.Sequencer.FpeMask( + "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp:172", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp:172", + acts.FpeType.FLTOVF, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp:371", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Core/src/Utilities/AnnealingUtility.cpp:38", + acts.FpeType.FLTUND, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp:110", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Fatras/include/ActsFatras/Kernel/Simulation.hpp:98", + acts.FpeType.FLTOVF, + 1, + ), + ] + s = acts.examples.Sequencer( + events=3, + numThreads=-1, + logLevel=acts.logging.INFO, + fpeMasks=fpeMasks, + ) + + tp = Path(temp) + + for d in setup.decorators: + s.addContextDecorator(d) + + rnd = acts.examples.RandomNumbers(seed=42) + + addPythia8( + s, + hardProcess=["Top:qqbar2ttbar=on"], + npileup=200, + vtxGen=acts.examples.GaussianVertexGenerator( + mean=acts.Vector4(0, 0, 0, 0), + stddev=acts.Vector4(0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns), + ), + rnd=rnd, + ) + + addFatras( + s, + setup.trackingGeometry, + setup.field, + rnd=rnd, + ) + + addDigitization( + s, + setup.trackingGeometry, + setup.field, + digiConfigFile=setup.digiConfig, + rnd=rnd, + ) + + addSeeding( + s, + setup.trackingGeometry, + setup.field, + TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), + ParticleSmearingSigmas(pRel=0.01), # only used by SeedingAlgorithm.TruthSmeared + SeedFinderConfigArg( + r=(None, 200 * u.mm), # rMin=default, 33mm + deltaR=(1 * u.mm, 60 * u.mm), + collisionRegion=(-250 * u.mm, 250 * u.mm), + z=(-2000 * u.mm, 2000 * u.mm), + maxSeedsPerSpM=1, + sigmaScattering=5, + radLengthPerSeed=0.1, + minPt=500 * u.MeV, + impactMax=3 * u.mm, + ), + SeedFinderOptionsArg(bFieldInZ=1.99724 * u.T, beamPos=(0.0, 0.0)), + TruthEstimatedSeedingAlgorithmConfigArg(deltaR=(10.0 * u.mm, None)), + seedingAlgorithm=SeedingAlgorithm.Default, + geoSelectionConfigFile=setup.geoSel, + rnd=rnd, # only used by SeedingAlgorithm.TruthSmeared + outputDirRoot=tp, + ) + + addCKFTracks( + s, + setup.trackingGeometry, + setup.field, + TrackSelectorConfig( + pt=(500 * u.MeV, None), + loc0=(-4.0 * u.mm, 4.0 * u.mm), + nMeasurementsMin=6, + ), + outputDirRoot=tp, + ) + + addAmbiguityResolution( + s, + AmbiguityResolutionConfig(maximumSharedHits=3), + outputDirRoot=tp, + ) + + addVertexFitting( + s, + setup.field, + seeder=acts.VertexSeedFinder.GaussianSeeder, + associatedParticles=None, + outputProtoVertices="amvf_protovertices", + outputVertices="amvf_fittedVertices", + vertexFinder=VertexFinder.AMVF, + outputDirRoot=tp / "amvf", + ) + + addVertexFitting( + s, + setup.field, + seeder=acts.VertexSeedFinder.AdaptiveGridSeeder, + associatedParticles=None, + outputProtoVertices="amvf_gridseeder_protovertices", + outputVertices="amvf_gridseeder_fittedVertices", + vertexFinder=VertexFinder.AMVF, + outputDirRoot=tp / "amvf_gridseeder", + ) + + s.run() + del s + + for vertexing in ["amvf", "amvf_gridseeder"]: + shutil.move( + tp / f"{vertexing}/performance_vertexing.root", + tp / f"performance_{vertexing}.root", + ) + + for stem in [ + "performance_ckf", + "tracksummary_ckf", + "performance_amvf", + "performance_amvf_gridseeder", + ] + (["performance_seeding", "performance_ambi"]): + perf_file = tp / f"{stem}.root" + assert perf_file.exists(), "Performance file not found" + shutil.copy(perf_file, setup.outdir / f"{stem}_ttbar.root") diff --git a/CI/physmon/workflows/physmon_vertexing.py b/CI/physmon/workflows/physmon_vertexing.py index c0a7805e0cb..41098229cd2 100755 --- a/CI/physmon/workflows/physmon_vertexing.py +++ b/CI/physmon/workflows/physmon_vertexing.py @@ -73,6 +73,7 @@ def run_vertexing(fitter, mu, events): s, setup.trackingGeometry, setup.field, + enableInteractions=True, rnd=rnd, ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bb3cf37ccc..1735449311e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ project(Acts VERSION ${_acts_version} LANGUAGES CXX) option(ACTS_BUILD_EVERYTHING "Build with most options enabled (except HepMC3 and documentation)" OFF) # core related options set(ACTS_PARAMETER_DEFINITIONS_HEADER "" CACHE FILEPATH "Use a different (track) parameter definitions header") +set(ACTS_SOURCELINK_SBO_SIZE "" CACHE STRING "Customize the SBO size used by SourceLink") option(ACTS_FORCE_ASSERTIONS "Force assertions regardless of build type" OFF) # external library options option(ACTS_USE_SYSTEM_LIBS "Use system libraries by default" OFF) @@ -181,7 +182,7 @@ endif() # minimal dependency versions. they are defined here in a single place so # they can be easily upgraded, although they might not be used if the # dependency is included via `add_subdirectory(...)`. -set(_acts_actsvg_version 0.4.33) +set(_acts_actsvg_version 0.4.35) set(_acts_autodiff_version 0.6) set(_acts_boost_version 1.71.0) set(_acts_dd4hep_version 1.21) @@ -292,6 +293,8 @@ if(ACTS_BUILD_PLUGIN_CUDA) enable_cuda() endif() if(ACTS_BUILD_PLUGIN_DD4HEP) + # Explicitly find python so we can more easily override the version + find_package(Python 3.8 REQUIRED COMPONENTS Interpreter) find_package(DD4hep ${_acts_dd4hep_version} REQUIRED CONFIG COMPONENTS DDCore DDDetectors) endif() if(ACTS_BUILD_PLUGIN_JSON) diff --git a/Core/ActsVersion.hpp.in b/Core/ActsVersion.hpp.in index 29b24edd7e5..4dc1ba7acb7 100644 --- a/Core/ActsVersion.hpp.in +++ b/Core/ActsVersion.hpp.in @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2016-2020 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 @@ -13,6 +13,8 @@ // will cause a recompile every time a new Acts version is // used. +#include + namespace Acts { // clang-format does not like the CMake @...@ replacement variables @@ -26,4 +28,28 @@ constexpr unsigned int Version = constexpr const char* const CommitHash = "@_acts_commit_hash@"; constexpr const char* const CommitHashShort = "@_acts_commit_hash_short@"; +struct VersionInfo { + unsigned int versionMajor; + unsigned int versionMinor; + unsigned int versionPatch; + const char* const commitHash; + + VersionInfo() = delete; + + static VersionInfo fromHeader() { + return VersionInfo(VersionMajor, VersionMinor, VersionPatch, CommitHash); + } + + static VersionInfo fromLibrary(); + + bool operator==(const VersionInfo& other) const; + bool operator!=(const VersionInfo& other) const { return !(*this == other); } + + friend std::ostream& operator<<(std::ostream& os, const VersionInfo& vi); + + private: + VersionInfo(unsigned int majorIn, unsigned int minorIn, unsigned int patchIn, + const char* const commitHashIn); +}; + } // namespace Acts diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 3513a2a8993..3cff9ffbf7c 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -6,6 +6,11 @@ configure_file( add_library( ActsCore SHARED "") +target_sources( + ActsCore + PRIVATE + src/ActsVersion.cpp) + target_compile_features( ActsCore PUBLIC ${ACTS_CXX_STANDARD_FEATURE}) @@ -32,6 +37,12 @@ if(ACTS_PARAMETER_DEFINITIONS_HEADER) PUBLIC -DACTS_PARAMETER_DEFINITIONS_HEADER="${ACTS_PARAMETER_DEFINITIONS_HEADER}") endif() +if(ACTS_SOURCELINK_SBO_SIZE) + target_compile_definitions( + ActsCore + PUBLIC -DACTS_SOURCELINK_SBO_SIZE=${ACTS_SOURCELINK_SBO_SIZE}) +endif() + if(ACTS_CUSTOM_SCALARTYPE) target_compile_definitions( ActsCore diff --git a/Core/include/Acts/Definitions/Algebra.hpp b/Core/include/Acts/Definitions/Algebra.hpp index 1a637ad6fe5..ac34a91b11c 100644 --- a/Core/include/Acts/Definitions/Algebra.hpp +++ b/Core/include/Acts/Definitions/Algebra.hpp @@ -57,7 +57,7 @@ template using ActsMatrix = Eigen::Matrix; template -using ActsSymMatrix = Eigen::Matrix; +using ActsSquareMatrix = Eigen::Matrix; using ActsDynamicVector = Eigen::Matrix; @@ -79,10 +79,10 @@ using Vector2 = ActsVector<2>; using Vector3 = ActsVector<3>; using Vector4 = ActsVector<4>; -// symmetric matrices e.g. for coordinate covariance matrices -using SymMatrix2 = ActsSymMatrix<2>; -using SymMatrix3 = ActsSymMatrix<3>; -using SymMatrix4 = ActsSymMatrix<4>; +// square matrices e.g. for coordinate covariance matrices +using SquareMatrix2 = ActsSquareMatrix<2>; +using SquareMatrix3 = ActsSquareMatrix<3>; +using SquareMatrix4 = ActsSquareMatrix<4>; // pure translation transformations using Translation2 = Eigen::Translation; diff --git a/Core/include/Acts/Definitions/Direction.hpp b/Core/include/Acts/Definitions/Direction.hpp index 864fe028916..537e5c246c1 100644 --- a/Core/include/Acts/Definitions/Direction.hpp +++ b/Core/include/Acts/Definitions/Direction.hpp @@ -84,8 +84,6 @@ class Direction final { /// Reverse the direction /// - /// @param dir is the direction at input - /// /// @return an opposite direction inline constexpr Direction invert() const { return (m_value == Value::Positive) ? Value::Negative : Value::Positive; diff --git a/Core/include/Acts/Definitions/TrackParametrization.hpp b/Core/include/Acts/Definitions/TrackParametrization.hpp index 56c341fa11b..18a2fa75490 100644 --- a/Core/include/Acts/Definitions/TrackParametrization.hpp +++ b/Core/include/Acts/Definitions/TrackParametrization.hpp @@ -116,14 +116,14 @@ static_assert(eFreeDir2 == eFreeDir0 + 2u, "Direction must be continuous"); // Shorthand vector/matrix types related to bound track parameters. using BoundVector = ActsVector; using BoundMatrix = ActsMatrix; -using BoundSymMatrix = ActsSymMatrix; +using BoundSquareMatrix = ActsSquareMatrix; // Mapping from bound track parameters. using BoundToFreeMatrix = ActsMatrix; // Shorthand vector/matrix types related to free track parameters. using FreeVector = ActsVector; using FreeMatrix = ActsMatrix; -using FreeSymMatrix = ActsSymMatrix; +using FreeSquareMatrix = ActsSquareMatrix; // Mapping from free track parameters. using FreeToBoundMatrix = ActsMatrix; using FreeToPathMatrix = ActsMatrix<1, eFreeSize>; diff --git a/Core/include/Acts/Definitions/Units.hpp b/Core/include/Acts/Definitions/Units.hpp index 9aecd45445c..b61515234a9 100644 --- a/Core/include/Acts/Definitions/Units.hpp +++ b/Core/include/Acts/Definitions/Units.hpp @@ -137,14 +137,14 @@ namespace Acts { namespace UnitConstants { // Length, native unit mm -constexpr double fm = 1e-12; -constexpr double pm = 1e-9; -constexpr double um = 1e-3; -constexpr double nm = 1e-6; constexpr double mm = 1.0; -constexpr double cm = 10.0; -constexpr double m = 1e3; -constexpr double km = 1e6; +constexpr double fm = 1e-12 * mm; +constexpr double pm = 1e-9 * mm; +constexpr double nm = 1e-6 * mm; +constexpr double um = 1e-3 * mm; +constexpr double cm = 1e1 * mm; +constexpr double m = 1e3 * mm; +constexpr double km = 1e6 * mm; // Shortcuts for commonly used area and volume units. This intentionally // contains not all possible combinations to avoid cluttering the namespace. // Missing area or volume units can always be defined on the fly using the @@ -158,7 +158,8 @@ constexpr double mm3 = mm * mm * mm; constexpr double cm3 = cm * cm * cm; constexpr double m3 = m * m * m; // Time, native unit mm = [speed-of-light * time] = mm/s * s -constexpr double s = 299792458000.0; +/// @note Depends on speed of light in SI units +constexpr double s = 299792458000.0; // = 299792458.0 * (m / 1.0) * 1.0; constexpr double fs = 1e-15 * s; constexpr double ps = 1e-12 * s; constexpr double ns = 1e-9 * s; @@ -169,15 +170,15 @@ constexpr double h = 3600.0 * s; // Angles, native unit radian constexpr double mrad = 1e-3; constexpr double rad = 1.0; -constexpr double degree = 0.017453292519943295; // pi / 180 +constexpr double degree = 0.017453292519943295; // = M_PI / 180.0 * rad; // Energy/mass/momentum, native unit GeV -constexpr double eV = 1e-9; -constexpr double keV = 1e-6; -constexpr double MeV = 1e-3; constexpr double GeV = 1.0; -constexpr double TeV = 1e3; +constexpr double eV = 1e-9 * GeV; +constexpr double keV = 1e-6 * GeV; +constexpr double MeV = 1e-3 * GeV; +constexpr double TeV = 1e3 * GeV; constexpr double J = 6241509074.460763 * GeV; -// atomic mass unit u +/// atomic mass unit u constexpr double u = 0.93149410242; // 1eV/c² == 1.782662e-36kg // 1GeV/c² == 1.782662e-27kg @@ -185,13 +186,14 @@ constexpr double u = 0.93149410242; // -> 1g == (1/(1e3*1.782662e-27))GeV/c² constexpr double g = 1.0 / 1.782662e-24; constexpr double kg = 1.0 / 1.782662e-27; -// Charge, native unit e (elementary charge) +/// Charge, native unit e (elementary charge) constexpr double e = 1.0; -// Magnetic field, native unit GeV/(e*mm) -constexpr double T = 0.000299792458; // equivalent to c in appropriate SI units +/// Magnetic field, native unit (eV*s)/(e*m²) +/// @note Depends on speed of light in SI units +constexpr double T = 0.000299792458; // = eV * s / (e * m2); constexpr double Gauss = 1e-4 * T; constexpr double kGauss = 1e-1 * T; -// Amount of substance, native unit mol +/// Amount of substance, native unit mol constexpr double mol = 1.0; } // namespace UnitConstants diff --git a/Core/include/Acts/Detector/Detector.hpp b/Core/include/Acts/Detector/Detector.hpp index 6f868825bbf..7c9aaf99e02 100644 --- a/Core/include/Acts/Detector/Detector.hpp +++ b/Core/include/Acts/Detector/Detector.hpp @@ -37,16 +37,16 @@ class Detector : public std::enable_shared_from_this { /// @note will throw an exception if volumes vector is empty /// @note will throw an exception if duplicate volume names exist /// @note will throw an exception if the delegate is not connected - Detector(const std::string& name, + Detector(std::string name, std::vector> rootVolumes, - DetectorVolumeUpdator&& detectorVolumeUpdator) noexcept(false); + DetectorVolumeUpdator detectorVolumeUpdator) noexcept(false); public: /// Factory for producing memory managed instances of Detector. static std::shared_ptr makeShared( - const std::string& name, + std::string name, std::vector> rootVolumes, - DetectorVolumeUpdator&& detectorVolumeUpdator); + DetectorVolumeUpdator detectorVolumeUpdator); /// Retrieve a @c std::shared_ptr for this surface (non-const version) /// @@ -119,8 +119,7 @@ class Detector : public std::enable_shared_from_this { /// Update the volume finder /// /// @param detectorVolumeUpdator the new volume finder - void updateDetectorVolumeFinder( - DetectorVolumeUpdator&& detectorVolumeUpdator); + void updateDetectorVolumeFinder(DetectorVolumeUpdator detectorVolumeUpdator); /// Const access to the volume finder const DetectorVolumeUpdator& detectorVolumeFinder() const; @@ -130,7 +129,7 @@ class Detector : public std::enable_shared_from_this { private: /// Name of the detector - std::string m_name = "Unnamed"; + std::string m_name; /// Root volumes DetectorVolume::ObjectStore> m_rootVolumes; diff --git a/Core/include/Acts/Detector/DetectorVolume.hpp b/Core/include/Acts/Detector/DetectorVolume.hpp index db49f826a5d..e10e456c491 100644 --- a/Core/include/Acts/Detector/DetectorVolume.hpp +++ b/Core/include/Acts/Detector/DetectorVolume.hpp @@ -106,12 +106,12 @@ class DetectorVolume : public std::enable_shared_from_this { /// @note throws exception if ghe portal general or navigation /// state updator delegates are not connected DetectorVolume( - const GeometryContext& gctx, const std::string& name, + const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, - DetectorVolumeUpdator&& detectorVolumeUpdator, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator) noexcept(false); + DetectorVolumeUpdator detectorVolumeUpdator, + SurfaceCandidatesUpdator surfaceCandidateUpdator) noexcept(false); /// Create a detector volume - empty/gap volume constructor /// @@ -125,28 +125,28 @@ class DetectorVolume : public std::enable_shared_from_this { /// @note throws exception if ghe portal general or navigation /// state updator delegates are not connected DetectorVolume( - const GeometryContext& gctx, const std::string& name, + const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr bounds, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator) noexcept(false); + SurfaceCandidatesUpdator surfaceCandidateUpdator) noexcept(false); /// Factory method for producing memory managed instances of DetectorVolume. /// /// @note This is called by the @class DetectorVolumeFactory static std::shared_ptr makeShared( - const GeometryContext& gctx, const std::string& name, + const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr bounds, std::vector> surfaces, std::vector> volumes, - DetectorVolumeUpdator&& detectorVolumeUpdator, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator); + DetectorVolumeUpdator detectorVolumeUpdator, + SurfaceCandidatesUpdator surfaceCandidateUpdator); /// Factory method for producing memory managed instances of DetectorVolume. /// /// @note This is called by the @class DetectorVolumeFactory static std::shared_ptr makeShared( - const GeometryContext& gctx, const std::string& name, + const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr bounds, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator); + SurfaceCandidatesUpdator surfaceCandidateUpdator); public: /// Retrieve a @c std::shared_ptr for this surface (non-const version) @@ -290,7 +290,7 @@ class DetectorVolume : public std::enable_shared_from_this { /// @param volumes the volumes the new navigation state updator points to /// void assignSurfaceCandidatesUpdator( - SurfaceCandidatesUpdator&& surfaceCandidateUpdator, + SurfaceCandidatesUpdator surfaceCandidateUpdator, const std::vector>& surfaces = {}, const std::vector>& volumes = {}); @@ -409,8 +409,8 @@ class DetectorVolumeFactory { std::shared_ptr bounds, const std::vector>& surfaces, const std::vector>& volumes, - DetectorVolumeUpdator&& detectorVolumeUpdator, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator) { + DetectorVolumeUpdator detectorVolumeUpdator, + SurfaceCandidatesUpdator surfaceCandidateUpdator) { auto dVolume = DetectorVolume::makeShared( gctx, name, transform, std::move(bounds), surfaces, volumes, std::move(detectorVolumeUpdator), std::move(surfaceCandidateUpdator)); @@ -421,12 +421,12 @@ class DetectorVolumeFactory { /// Create a detector volume - from factory static std::shared_ptr construct( const PortalGenerator& portalGenerator, const GeometryContext& gctx, - const std::string& name, const Transform3& transform, + std::string name, const Transform3& transform, std::shared_ptr bounds, - SurfaceCandidatesUpdator&& surfaceCandidateUpdator) { - auto dVolume = - DetectorVolume::makeShared(gctx, name, transform, std::move(bounds), - std::move(surfaceCandidateUpdator)); + SurfaceCandidatesUpdator surfaceCandidateUpdator) { + auto dVolume = DetectorVolume::makeShared( + gctx, std::move(name), transform, std::move(bounds), + std::move(surfaceCandidateUpdator)); dVolume->construct(gctx, portalGenerator); return dVolume; } diff --git a/Core/include/Acts/Detector/Portal.hpp b/Core/include/Acts/Detector/Portal.hpp index 30ffaa9d020..351f1014924 100644 --- a/Core/include/Acts/Detector/Portal.hpp +++ b/Core/include/Acts/Detector/Portal.hpp @@ -125,8 +125,8 @@ class Portal : public std::enable_shared_from_this { /// /// @note this overwrites the existing link void assignDetectorVolumeUpdator( - Direction dir, DetectorVolumeUpdator&& dVolumeUpdator, - const std::vector>& attachedVolumes); + Direction dir, DetectorVolumeUpdator dVolumeUpdator, + std::vector> attachedVolumes); /// Update the volume link, w/o directive, i.e. it relies that there's only /// one remaining link to be set, throws an exception if that's not the case @@ -135,10 +135,9 @@ class Portal : public std::enable_shared_from_this { /// @param attachedVolumes is the list of attached volumes for book keeping /// /// @note this overwrites the existing link - void assignDetectorVolumeUpdator( - DetectorVolumeUpdator&& dVolumeUpdator, - const std::vector>& - attachedVolumes) noexcept(false); + void assignDetectorVolumeUpdator(DetectorVolumeUpdator dVolumeUpdator, + std::vector> + attachedVolumes) noexcept(false); // Access to the portal targets: opposite/along normal vector const DetectorVolumeUpdators& detectorVolumeUpdators() const; diff --git a/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp b/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp index e640737f476..bbcdc1d5825 100644 --- a/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp +++ b/Core/include/Acts/Detector/detail/IndexedGridFiller.hpp @@ -40,66 +40,9 @@ namespace detail { /// @note for closed binning a span over half the bins flips direction /// /// @return a vector of bins to be filled -static inline std::vector binSequence( - std::array minMaxBins, std::size_t expand, - std::size_t nBins, Acts::detail::AxisBoundaryType type) { - // Return vector for iterations - std::vector rBins; - /// Helper method to fill a range - /// - /// @param lmin the minimum bin - /// @param lmax the maximum bin - auto fill_linear = [&](std::size_t lmin, std::size_t lmax) -> void { - for (std::size_t b = lmin; b <= lmax; ++b) { - rBins.push_back(b); - } - }; - std::size_t bmin = minMaxBins[0u]; - std::size_t bmax = minMaxBins[1u]; - - // Open/Bound cases - if (type != Acts::detail::AxisBoundaryType::Closed) { - rBins.reserve(bmax - bmin + 1u + 2 * expand); - // handle bmin:/max expand it down (for bound, don't fill underflow) - if (type == Acts::detail::AxisBoundaryType::Bound) { - bmin = (int(bmin) - int(expand) > 0) ? bmin - expand : 1u; - bmax = (bmax + expand <= nBins) ? bmax + expand : nBins; - } else if (type == Acts::detail::AxisBoundaryType::Open) { - bmin = (int(bmin) - int(expand) >= 0) ? bmin - expand : 0u; - bmax = (bmax + expand <= nBins + 1u) ? bmax + expand : nBins + 1u; - } - fill_linear(bmin, bmax); - } else { - // Close case - std::size_t span = bmax - bmin + 1u + 2 * expand; - // Safe with respect to the closure point, treat as bound - if (2 * span < nBins and (bmax + expand <= nBins) and - (int(bmin) - int(expand) > 0)) { - return binSequence({bmin, bmax}, expand, nBins, - Acts::detail::AxisBoundaryType::Bound); - } else if (2 * span < nBins) { - bmin = int(bmin) - int(expand) > 0 ? bmin - expand : 1u; - bmax = bmax + expand <= nBins ? bmax + expand : nBins; - fill_linear(bmin, bmax); - // deal with expansions over the phi boundary - if (bmax + expand > nBins) { - std::size_t overstep = (bmax + expand - nBins); - fill_linear(1u, overstep); - } - if (int(bmin) - int(expand) < 1) { - std::size_t understep = abs(int(bmin) - int(expand)); - fill_linear(nBins - understep, nBins); - } - std::sort(rBins.begin(), rBins.end()); - } else { - // Jump over the phi boundary - fill_linear(bmax - expand, nBins); - fill_linear(1, bmin + expand); - std::sort(rBins.begin(), rBins.end()); - } - } - return rBins; -} +std::vector binSequence(std::array minMaxBins, + std::size_t expand, std::size_t nBins, + Acts::detail::AxisBoundaryType type); /// @brief Helper method to fill local bins given a set of query points /// bin in between the extra points are filled, and a possible expansion diff --git a/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp index 7ae7efe0431..9a7a38a86b2 100644 --- a/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp +++ b/Core/include/Acts/Detector/detail/IndexedSurfacesGenerator.hpp @@ -32,7 +32,7 @@ namespace detail { /// - a chosen expansion to fill indices in neighborhood bins /// /// @tparam objects_container the objects container -template +template class indexed_updator> struct IndexedSurfacesGenerator { /// The surfaces to be indexed /// (including surfaces that are assigned to all bins) @@ -76,8 +76,10 @@ struct IndexedSurfacesGenerator { } // The indexed surfaces delegate - IndexedSurfacesImpl indexedSurfaces(std::move(grid), bvArray, - transform); + // IndexedSurfacesImpl indexedSurfaces(std::move(grid), bvArray, + // transform); + indexed_updator indexedSurfaces(std::move(grid), bvArray, + transform); // Fill the bin indices IndexedGridFiller filler{binExpansion}; @@ -88,9 +90,10 @@ struct IndexedSurfacesGenerator { AllPortalsImpl allPortals; // The chained delegate: indexed surfaces and all portals - using DelegateType = IndexedSurfacesAllPortalsImpl; + using DelegateType = + IndexedSurfacesAllPortalsImpl; auto indesSurfacesAllPortals = std::make_unique( - std::tie(indexedSurfaces, allPortals)); + std::tie(allPortals, indexedSurfaces)); // Create the delegate and connect it SurfaceCandidatesUpdator nStateUpdator; diff --git a/Core/include/Acts/Detector/detail/PortalHelper.hpp b/Core/include/Acts/Detector/detail/PortalHelper.hpp index 0c2740f19e1..76a3487190b 100644 --- a/Core/include/Acts/Detector/detail/PortalHelper.hpp +++ b/Core/include/Acts/Detector/detail/PortalHelper.hpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Detector/Portal.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/BinningType.hpp" @@ -37,6 +38,33 @@ using PortalReplacement = namespace detail { namespace PortalHelper { +/// @brief Method to attach a single detector volume to a portal +/// +/// @param portal is the portal where the detector volume is going to be attached +/// @param volume is the volume that is attached to the portal +/// @param direction is the direction to which it is attached +/// +void attachDetectorVolumeUpdator(Portal& portal, + const std::shared_ptr& volume, + const Direction& direction); + +/// @brief Create and attach the multi link updator, the portal will get +/// a volume updator attached, that points to the different sub volumes +/// depending on the global position and binning - single assignment case +/// +/// @param gctx the geometry context +/// @param portal is the portal where the detector volume is going to be attached +/// @param volumes are the volumes that are pointed to +/// @param direction is the direction to which it is attached +/// @param boundaries are the value boundaries +/// @param binning is the binning type +/// +void attachDetectorVolumesUpdator( + const GeometryContext& gctx, Portal& portal, + const std::vector>& volumes, + const Direction& direction, const std::vector& boundaries, + const BinningValue& binning); + /// @brief Create and attach the multi link updator, the portal will get /// a volume updator attached, that points to the different sub volumes /// depending on the global position and binning diff --git a/Core/include/Acts/EventData/Charge.hpp b/Core/include/Acts/EventData/Charge.hpp index e6df928ffe1..b7888cc4282 100644 --- a/Core/include/Acts/EventData/Charge.hpp +++ b/Core/include/Acts/EventData/Charge.hpp @@ -9,6 +9,8 @@ #pragma once #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/ChargeConcept.hpp" +#include "Acts/Utilities/Concepts.hpp" #include #include @@ -51,26 +53,36 @@ namespace Acts { /// Charge and momentum interpretation for neutral particles. struct Neutral { - Neutral() = default; + constexpr Neutral() = default; + + // TODO remove this method after grad refactor; currently track parameters + // depend on it /// Construct and verify the input charge magnitude (in debug builds). /// /// This constructor is only provided to allow consistent construction. - template - constexpr Neutral(T absQ) noexcept { - assert((absQ == static_cast(0)) and "Input charge must be zero"); - // suppress `unused variable` warning in non-debug builds - (void)(absQ); + constexpr Neutral(float absQ) noexcept { + assert((absQ == 0) and "Input charge must be zero"); + (void)absQ; } + constexpr float absQ() const noexcept { return 0; } + template - constexpr T extractCharge(T /* pInv */) const noexcept { - return 0; + constexpr auto extractCharge(T /*qOverP*/) const noexcept { + return 0.0f; } + template - constexpr T extractMomentum(T pInv) const noexcept { - // the abs is not strictly needed. it is added to protect against invalid, - // i.e. negative, 1/p values to ensure that the output is still correct. - return std::abs(1 / pInv); + constexpr auto extractMomentum(T qOverP) const noexcept { + assert(qOverP >= 0 && "qOverP cannot be negative"); + return 1.0f / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + assert((signedQ != 0) and "charge must be 0"); + (void)signedQ; + return 1.0f / momentum; } /// Compare for equality. @@ -82,27 +94,43 @@ struct Neutral { } }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, Neutral); + /// Charge and momentum interpretation for particles with +-e charge. struct SinglyCharged { - SinglyCharged() = default; + constexpr SinglyCharged() = default; + + // TODO remove this method after grad refactor; currently track parameters + // depend on it /// Construct and verify the input charge magnitude (in debug builds). /// /// This constructor is only provided to allow consistent construction. - template - constexpr SinglyCharged(T absQ) noexcept { - assert((absQ == static_cast(UnitConstants::e)) and - "Input charge magnitude must be e"); - // suppress `unused variable` warning in non-debug builds - (void)(absQ); + constexpr SinglyCharged(float absQ) noexcept { + assert((absQ == UnitConstants::e) and "Input charge magnitude must be e"); + (void)absQ; } + constexpr float absQ() const noexcept { return UnitConstants::e; } + template - constexpr T extractCharge(T qOverP) const noexcept { - return std::copysign(static_cast(UnitConstants::e), qOverP); + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(UnitConstants::e, qOverP); } + template - constexpr T extractMomentum(T qOverP) const noexcept { - return std::abs(static_cast(UnitConstants::e) / qOverP); + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return extractCharge(qOverP) / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + using std::abs; + assert((abs(signedQ) == UnitConstants::e) && "absolute charge must be e"); + return signedQ / momentum; } /// Compare for equality. @@ -115,6 +143,54 @@ struct SinglyCharged { } }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, SinglyCharged); + +/// Charge and momentum interpretation for arbitrarily charged but not neutral +/// particles. +class NonNeutralCharge { + public: + /// Construct with the magnitude of the input charge. + constexpr NonNeutralCharge(float absQ) noexcept : m_absQ{absQ} { + assert((0 < absQ) and "Input charge magnitude must be positive"); + } + constexpr NonNeutralCharge(SinglyCharged /*unused*/) noexcept + : m_absQ{UnitConstants::e} {} + + constexpr float absQ() const noexcept { return m_absQ; } + + template + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(m_absQ, qOverP); + } + template + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return extractCharge(qOverP) / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + // using because of autodiff + using std::abs; + assert(abs(signedQ) == m_absQ && "inconsistent charge"); + return signedQ / momentum; + } + + /// Compare for equality. + friend constexpr bool operator==(NonNeutralCharge lhs, + NonNeutralCharge rhs) noexcept { + return lhs.m_absQ == rhs.m_absQ; + } + + private: + float m_absQ{}; +}; + +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, NonNeutralCharge); + /// Charge and momentum interpretation for arbitrarily charged particles. /// /// Only a charge magnitude identical to zero is interpreted as representing a @@ -122,34 +198,48 @@ struct SinglyCharged { /// approximate comparison with an arbitrary epsilon. class AnyCharge { public: - /// Delete default constructor to ensure charge is always explicitly given. - AnyCharge() = delete; /// Construct with the magnitude of the input charge. - template - constexpr AnyCharge(T absQ) noexcept : m_magnitude(std::abs(absQ)) { + constexpr AnyCharge(float absQ) noexcept : m_absQ{absQ} { assert((0 <= absQ) and "Input charge magnitude must be zero or positive"); } + constexpr AnyCharge(SinglyCharged /*unused*/) noexcept + : m_absQ{UnitConstants::e} {} + constexpr AnyCharge(Neutral /*unused*/) noexcept {} + + constexpr float absQ() const noexcept { return m_absQ; } template - constexpr T extractCharge(T qOverP) const noexcept { - return std::copysign(static_cast(m_magnitude), qOverP); + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(m_absQ, qOverP); } template - constexpr T extractMomentum(T qOverP) const noexcept { - return (m_magnitude != 0.0f) - ? std::abs(static_cast(m_magnitude) / qOverP) - : std::abs(1 / qOverP); + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return (m_absQ != 0.0f) ? extractCharge(qOverP) / qOverP : 1.0f / qOverP; } - private: - float m_magnitude; + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + // using because of autodiff + using std::abs; + assert(abs(signedQ) == m_absQ && "inconsistent charge"); + return (m_absQ != 0.0f) ? signedQ / momentum : 1.0f / momentum; + } /// Compare for equality. friend constexpr bool operator==(AnyCharge lhs, AnyCharge rhs) noexcept { - return lhs.m_magnitude == rhs.m_magnitude; + return lhs.m_absQ == rhs.m_absQ; } + + private: + float m_absQ{}; }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, AnyCharge); + /// @} } // namespace Acts diff --git a/Core/include/Acts/EventData/ChargeConcept.hpp b/Core/include/Acts/EventData/ChargeConcept.hpp new file mode 100644 index 00000000000..9837247971b --- /dev/null +++ b/Core/include/Acts/EventData/ChargeConcept.hpp @@ -0,0 +1,44 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/Types.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/HashedString.hpp" + +#include +#include + +#if defined(ACTS_CONCEPTS_SUPPORTED) +#include + +namespace Acts { + +template +concept ChargeConcept = requires(C c, float f, double d) { + {C{f}}; + { c.absQ() } -> std::same_as; + + { c.extractCharge(f) } -> std::convertible_to; + { c.extractCharge(d) } -> std::convertible_to; + + { c.extractMomentum(f) } -> std::convertible_to; + { c.extractMomentum(d) } -> std::convertible_to; + + { c.qOverP(f, f) } -> std::same_as; + { c.qOverP(d, d) } -> std::same_as; + + { c == c } -> std::same_as; + { c != c } -> std::same_as; +}; + +} // namespace Acts + +#endif diff --git a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp similarity index 81% rename from Core/include/Acts/EventData/SingleBoundTrackParameters.hpp rename to Core/include/Acts/EventData/GenericBoundTrackParameters.hpp index da9586579ea..fa15ee61cae 100644 --- a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp @@ -33,11 +33,11 @@ namespace Acts { /// /// @note This class holds shared ownership on its reference surface. template -class SingleBoundTrackParameters { +class GenericBoundTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = BoundVector; - using CovarianceMatrix = BoundSymMatrix; + using CovarianceMatrix = BoundSquareMatrix; /// Construct from a parameters vector on the surface and particle charge. /// @@ -51,9 +51,9 @@ class SingleBoundTrackParameters { /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - SingleBoundTrackParameters(std::shared_ptr surface, - const ParametersVector& params, Scalar q, - std::optional cov = std::nullopt) + GenericBoundTrackParameters( + std::shared_ptr surface, const ParametersVector& params, + Scalar q, std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)), @@ -74,9 +74,9 @@ class SingleBoundTrackParameters { /// ambiguities, i.e. the charge type is default-constructible. template , int> = 0> - SingleBoundTrackParameters(std::shared_ptr surface, - const ParametersVector& params, - std::optional cov = std::nullopt) + GenericBoundTrackParameters( + std::shared_ptr surface, const ParametersVector& params, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)) { assert(m_surface); normalizePhiTheta(); @@ -95,7 +95,7 @@ class SingleBoundTrackParameters { /// /// @note The returned result indicates whether the free parameters could /// successfully be converted to on-surface parameters. - static Result> create( + static Result> create( std::shared_ptr surface, const GeometryContext& geoCtx, const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, std::optional cov = std::nullopt) { @@ -107,8 +107,8 @@ class SingleBoundTrackParameters { return bound.error(); } - return SingleBoundTrackParameters{std::move(surface), *bound, q, - std::move(cov)}; + return GenericBoundTrackParameters{std::move(surface), *bound, q, + std::move(cov)}; } /// Factory to construct from four-position, direction, and @@ -128,7 +128,7 @@ class SingleBoundTrackParameters { /// successfully be converted to on-surface parameters. template , int> = 0> - static Result> create( + static Result> create( std::shared_ptr surface, const GeometryContext& geoCtx, const Vector4& pos4, const Vector3& dir, Scalar qOverP, std::optional cov = std::nullopt) { @@ -138,18 +138,19 @@ class SingleBoundTrackParameters { return bound.error(); } - return SingleBoundTrackParameters{std::move(surface), *bound, - std::move(cov)}; + return GenericBoundTrackParameters{std::move(surface), *bound, + std::move(cov)}; } /// Parameters are not default constructible due to the charge type. - SingleBoundTrackParameters() = delete; - SingleBoundTrackParameters(const SingleBoundTrackParameters&) = default; - SingleBoundTrackParameters(SingleBoundTrackParameters&&) = default; - ~SingleBoundTrackParameters() = default; - SingleBoundTrackParameters& operator=(const SingleBoundTrackParameters&) = + GenericBoundTrackParameters() = delete; + GenericBoundTrackParameters(const GenericBoundTrackParameters&) = default; + GenericBoundTrackParameters(GenericBoundTrackParameters&&) = default; + ~GenericBoundTrackParameters() = default; + GenericBoundTrackParameters& operator=(const GenericBoundTrackParameters&) = + default; + GenericBoundTrackParameters& operator=(GenericBoundTrackParameters&&) = default; - SingleBoundTrackParameters& operator=(SingleBoundTrackParameters&&) = default; /// Parameters vector. ParametersVector& parameters() { return m_params; } @@ -182,7 +183,7 @@ class SingleBoundTrackParameters { Vector4 fourPosition(const GeometryContext& geoCtx) const { Vector4 pos4; pos4.segment<3>(ePos0) = - m_surface->localToGlobal(geoCtx, localPosition(), unitDirection()); + m_surface->localToGlobal(geoCtx, localPosition(), direction()); pos4[eTime] = m_params[eBoundTime]; return pos4; } @@ -196,16 +197,23 @@ class SingleBoundTrackParameters { /// select the appropriate transformation and might be a computationally /// expensive operation. Vector3 position(const GeometryContext& geoCtx) const { - return m_surface->localToGlobal(geoCtx, localPosition(), unitDirection()); + return m_surface->localToGlobal(geoCtx, localPosition(), direction()); } /// Time coordinate. Scalar time() const { return m_params[eBoundTime]; } + /// Phi direction. + Scalar phi() const { return m_params[eBoundPhi]; } + /// Theta direction. + Scalar theta() const { return m_params[eBoundTheta]; } + /// Charge over momentum. + Scalar qOverP() const { return m_params[eBoundQOverP]; } + /// Unit direction three-vector, i.e. the normalized momentum /// three-vector. - Vector3 unitDirection() const { - return makeDirectionUnitFromPhiTheta(m_params[eBoundPhi], - m_params[eBoundTheta]); + Vector3 direction() const { + return makeDirectionFromPhiTheta(m_params[eBoundPhi], + m_params[eBoundTheta]); } /// Absolute momentum. Scalar absoluteMomentum() const { @@ -216,7 +224,7 @@ class SingleBoundTrackParameters { return std::sin(m_params[eBoundTheta]) * absoluteMomentum(); } /// Momentum three-vector. - Vector3 momentum() const { return absoluteMomentum() * unitDirection(); } + Vector3 momentum() const { return absoluteMomentum() * direction(); } /// Particle electric charge. Scalar charge() const { @@ -239,7 +247,7 @@ class SingleBoundTrackParameters { private: BoundVector m_params; - std::optional m_cov; + std::optional m_cov; /// reference surface std::shared_ptr m_surface; // TODO use [[no_unique_address]] once we switch to C++20 @@ -263,20 +271,20 @@ class SingleBoundTrackParameters { /// of equality in different contexts. None of that can be handled by /// this operator. Users should think really hard if this is what they /// want and we might decided that we will remove this in the future. - friend bool operator==(const SingleBoundTrackParameters& lhs, - const SingleBoundTrackParameters& rhs) { + friend bool operator==(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return (lhs.m_params == rhs.m_params) and (lhs.m_cov == rhs.m_cov) and (lhs.m_surface == rhs.m_surface) and (lhs.m_chargeInterpreter == rhs.m_chargeInterpreter); } /// Compare two bound track parameters for bitwise in-equality. - friend bool operator!=(const SingleBoundTrackParameters& lhs, - const SingleBoundTrackParameters& rhs) { + friend bool operator!=(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return not(lhs == rhs); } /// Print information to the output stream. friend std::ostream& operator<<(std::ostream& os, - const SingleBoundTrackParameters& tp) { + const GenericBoundTrackParameters& tp) { detail::printBoundParameters( os, tp.referenceSurface(), tp.parameters(), tp.covariance().has_value() ? &tp.covariance().value() : nullptr); diff --git a/Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp similarity index 75% rename from Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp rename to Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp index 1ca67bab05c..966175ab99f 100644 --- a/Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" namespace Acts { @@ -22,16 +22,16 @@ namespace Acts { /// parameters and their corresponding covariance matrix are stored in /// curvilinear parametrization. /// -/// @see SingleBoundTrackParameters +/// @see GenericBoundTrackParameters template -class SingleCurvilinearTrackParameters - : public SingleBoundTrackParameters { - using Base = SingleBoundTrackParameters; +class GenericCurvilinearTrackParameters + : public GenericBoundTrackParameters { + using Base = GenericBoundTrackParameters; public: using Scalar = ActsScalar; using ParametersVector = BoundVector; - using CovarianceMatrix = BoundSymMatrix; + using CovarianceMatrix = BoundSquareMatrix; /// Construct from four-position, direction, absolute momentum, and charge. /// @@ -40,7 +40,7 @@ class SingleCurvilinearTrackParameters /// @param p Absolute momentum /// @param q Particle charge /// @param cov Curvilinear bound parameters covariance matrix - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, std::optional cov = std::nullopt) : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), @@ -61,7 +61,7 @@ class SingleCurvilinearTrackParameters /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, const Vector3& dir, Scalar qOverP, std::optional cov = std::nullopt) : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), @@ -77,12 +77,11 @@ class SingleCurvilinearTrackParameters /// @param p Absolute momentum /// @param q Particle charge /// @param cov Curvilinear bound parameters covariance matrix - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, Scalar phi, Scalar theta, Scalar p, Scalar q, std::optional cov = std::nullopt) : Base(Surface::makeShared( - pos4.segment<3>(ePos0), - makeDirectionUnitFromPhiTheta(phi, theta)), + pos4.segment<3>(ePos0), makeDirectionFromPhiTheta(phi, theta)), detail::transformFreeToCurvilinearParameters( pos4[eTime], phi, theta, (q != Scalar(0)) ? (q / p) : (1 / p)), q, std::move(cov)) { @@ -101,27 +100,38 @@ class SingleCurvilinearTrackParameters /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, Scalar phi, Scalar theta, Scalar qOverP, std::optional cov = std::nullopt) : Base(Surface::makeShared( - pos4.segment<3>(ePos0), - makeDirectionUnitFromPhiTheta(phi, theta)), + pos4.segment<3>(ePos0), makeDirectionFromPhiTheta(phi, theta)), detail::transformFreeToCurvilinearParameters(pos4[eTime], phi, theta, qOverP), std::move(cov)) {} /// Parameters are not default constructible due to the charge type. - SingleCurvilinearTrackParameters() = delete; - SingleCurvilinearTrackParameters(const SingleCurvilinearTrackParameters&) = + GenericCurvilinearTrackParameters() = delete; + GenericCurvilinearTrackParameters(const GenericCurvilinearTrackParameters&) = default; - SingleCurvilinearTrackParameters(SingleCurvilinearTrackParameters&&) = + GenericCurvilinearTrackParameters(GenericCurvilinearTrackParameters&&) = default; - ~SingleCurvilinearTrackParameters() = default; - SingleCurvilinearTrackParameters& operator=( - const SingleCurvilinearTrackParameters&) = default; - SingleCurvilinearTrackParameters& operator=( - SingleCurvilinearTrackParameters&&) = default; + ~GenericCurvilinearTrackParameters() = default; + GenericCurvilinearTrackParameters& operator=( + const GenericCurvilinearTrackParameters&) = default; + GenericCurvilinearTrackParameters& operator=( + GenericCurvilinearTrackParameters&&) = default; + + using GenericBoundTrackParameters::fourPosition; + using GenericBoundTrackParameters::position; + + /// Space-time position four-vector. + Vector4 fourPosition() const { + return GenericBoundTrackParameters::fourPosition({}); + } + /// Spatial position three-vector. + Vector3 position() const { + return GenericBoundTrackParameters::position({}); + } }; } // namespace Acts diff --git a/Core/include/Acts/EventData/SingleFreeTrackParameters.hpp b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp similarity index 78% rename from Core/include/Acts/EventData/SingleFreeTrackParameters.hpp rename to Core/include/Acts/EventData/GenericFreeTrackParameters.hpp index f466a07143b..738814f05b2 100644 --- a/Core/include/Acts/EventData/SingleFreeTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp @@ -9,6 +9,8 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Common.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/detail/PrintParameters.hpp" #include "Acts/Utilities/UnitVectors.hpp" @@ -26,11 +28,11 @@ namespace Acts { /// Parameters and covariance matrix are stored using the free parametrization /// defined in `enum FreeIndices`. template -class SingleFreeTrackParameters { +class GenericFreeTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = FreeVector; - using CovarianceMatrix = FreeSymMatrix; + using CovarianceMatrix = FreeSquareMatrix; /// Construct from a parameters vector and particle charge. /// @@ -43,8 +45,8 @@ class SingleFreeTrackParameters { /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - SingleFreeTrackParameters(const ParametersVector& params, Scalar q, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const ParametersVector& params, Scalar q, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_chargeInterpreter(std::abs(q)) { @@ -61,8 +63,8 @@ class SingleFreeTrackParameters { /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleFreeTrackParameters(const ParametersVector& params, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const ParametersVector& params, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)) {} /// Construct from four-position, angles, absolute momentum, and charge. @@ -73,15 +75,15 @@ class SingleFreeTrackParameters { /// @param p Absolute momentum /// @param q Particle charge /// @param cov Free parameters covariance matrix - SingleFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar p, Scalar q, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, + Scalar p, Scalar q, + std::optional cov = std::nullopt) : m_params(FreeVector::Zero()), m_cov(std::move(cov)), m_chargeInterpreter(std::abs(q)) { assert((0 <= p) and "Absolute momentum must be positive"); - auto dir = makeDirectionUnitFromPhiTheta(phi, theta); + auto dir = makeDirectionFromPhiTheta(phi, theta); m_params[eFreePos0] = pos4[ePos0]; m_params[eFreePos1] = pos4[ePos1]; m_params[eFreePos2] = pos4[ePos2]; @@ -104,11 +106,11 @@ class SingleFreeTrackParameters { /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar qOverP, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, + Scalar qOverP, + std::optional cov = std::nullopt) : m_params(FreeVector::Zero()), m_cov(std::move(cov)) { - auto dir = makeDirectionUnitFromPhiTheta(phi, theta); + auto dir = makeDirectionFromPhiTheta(phi, theta); m_params[eFreePos0] = pos4[ePos0]; m_params[eFreePos1] = pos4[ePos1]; m_params[eFreePos2] = pos4[ePos2]; @@ -120,13 +122,13 @@ class SingleFreeTrackParameters { } /// Parameters are not default constructible due to the charge type. - SingleFreeTrackParameters() = delete; - SingleFreeTrackParameters(const SingleFreeTrackParameters&) = default; - SingleFreeTrackParameters(SingleFreeTrackParameters&&) = default; - ~SingleFreeTrackParameters() = default; - SingleFreeTrackParameters& operator=(const SingleFreeTrackParameters&) = + GenericFreeTrackParameters() = delete; + GenericFreeTrackParameters(const GenericFreeTrackParameters&) = default; + GenericFreeTrackParameters(GenericFreeTrackParameters&&) = default; + ~GenericFreeTrackParameters() = default; + GenericFreeTrackParameters& operator=(const GenericFreeTrackParameters&) = default; - SingleFreeTrackParameters& operator=(SingleFreeTrackParameters&&) = default; + GenericFreeTrackParameters& operator=(GenericFreeTrackParameters&&) = default; /// Parameters vector. const ParametersVector& parameters() const { return m_params; } @@ -155,8 +157,15 @@ class SingleFreeTrackParameters { /// Time coordinate. Scalar time() const { return m_params[eFreeTime]; } + /// Phi direction. + Scalar phi() const { return phi(direction()); } + /// Theta direction. + Scalar theta() const { return theta(direction()); } + /// Charge over momentum. + Scalar qOverP() const { return m_params[eFreeQOverP]; } + /// Unit direction three-vector, i.e. the normalized momentum three-vector. - Vector3 unitDirection() const { + Vector3 direction() const { return m_params.segment<3>(eFreeDir0).normalized(); } /// Absolute momentum. @@ -177,7 +186,7 @@ class SingleFreeTrackParameters { return (transverseMagnitude / magnitude) * absoluteMomentum(); } /// Momentum three-vector. - Vector3 momentum() const { return absoluteMomentum() * unitDirection(); } + Vector3 momentum() const { return absoluteMomentum() * direction(); } /// Particle electric charge. Scalar charge() const { @@ -186,13 +195,13 @@ class SingleFreeTrackParameters { private: FreeVector m_params; - std::optional m_cov; + std::optional m_cov; // TODO use [[no_unique_address]] once we switch to C++20 charge_t m_chargeInterpreter; /// Print information to the output stream. friend std::ostream& operator<<(std::ostream& os, - const SingleFreeTrackParameters& tp) { + const GenericFreeTrackParameters& tp) { detail::printFreeParameters( os, tp.parameters(), tp.covariance().has_value() ? &tp.covariance().value() : nullptr); diff --git a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp new file mode 100644 index 00000000000..3639699cabf --- /dev/null +++ b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp @@ -0,0 +1,127 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/ParticleData.hpp" +#include "Acts/Definitions/PdgParticle.hpp" +#include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/ChargeConcept.hpp" +#include "Acts/Utilities/Concepts.hpp" + +#include + +namespace Acts { + +/// @brief Particle hypothesis used in reconstruction +/// +/// The reconstruction hypothesis consists of absolute PDG code, mass and +/// absolute charge. +template +class GenericParticleHypothesis { + public: + using ChargeType = charge_t; + + /// Creates a particle hypothesis using absolute PDG, mass and the charge + /// type. + /// + /// @param absPdg the absolute PDG + /// @param mass the particle mass + /// @param chargeType the type of charge + constexpr GenericParticleHypothesis(PdgParticle absPdg, float mass, + ChargeType chargeType) + : m_absPdg{absPdg}, m_mass{mass}, m_chargeType{std::move(chargeType)} { + assert(absPdg == makeAbsolutePdgParticle(absPdg) && + "pdg is expected to be absolute"); + } + + /// Creates a particle hypothesis using the absolute PDG. + /// The mass and charge is looked up using @ref findMass and @ref findCharge. + /// If the lookup fails an exception is thrown. + /// + /// @param absPdg the absolute PDG + GenericParticleHypothesis(PdgParticle absPdg) + : m_absPdg{absPdg}, + m_mass{findMass(absPdg).value()}, + m_chargeType{std::abs(findCharge(absPdg).value())} { + assert(absPdg == makeAbsolutePdgParticle(absPdg) && + "pdg is expected to be absolute"); + } + + /// Copy from another charge hypothesis. + /// + /// @note This enables implicit conversion. + template + constexpr GenericParticleHypothesis( + const GenericParticleHypothesis& other) + : m_absPdg{other.absolutePdg()}, + m_mass{other.mass()}, + m_chargeType{other.chargeType()} {} + + /// Get the hypothesized absolute PDG. + constexpr PdgParticle absolutePdg() const noexcept { return m_absPdg; } + + /// Get the hypothesized mass. + constexpr float mass() const noexcept { return m_mass; } + + /// Get the hypothesized absolute charge. + constexpr float absoluteCharge() const noexcept { + return m_chargeType.absQ(); + } + + /// Extracts the signed charge from the `q over p` track parameter using the + /// charge hypothesis. + /// + /// @param qOverP the `q over p` track parameter. + template + constexpr auto extractCharge(T qOverP) const noexcept { + return m_chargeType.extractCharge(qOverP); + } + + /// Extracts the signed charge from the `q over p` track parameter using the + /// charge hypothesis. + /// + /// @param qOverP the `q over p` track parameter. + template + constexpr auto extractMomentum(T qOverP) const noexcept { + return m_chargeType.extractMomentum(qOverP); + } + + /// Calculate the `q over p` track parameter with the given absolute momentum + /// and charge. + /// + /// @param momentum the absolute momentum. + /// @param signedQ the signed charge. + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + return m_chargeType.qOverP(momentum, signedQ); + } + + /// Get the hypothesized charge type. + constexpr const ChargeType& chargeType() const noexcept { + return m_chargeType; + } + + private: + PdgParticle m_absPdg; + float m_mass; + ChargeType m_chargeType; + + friend bool operator==(const GenericParticleHypothesis& lhs, + const GenericParticleHypothesis& rhs) { + return (lhs.m_absPdg == rhs.m_absPdg) && (lhs.m_mass == rhs.m_mass) && + (lhs.m_chargeType == rhs.m_chargeType); + } + friend bool operator!=(const GenericParticleHypothesis& lhs, + const GenericParticleHypothesis& rhs) { + return (lhs.m_absPdg != rhs.m_absPdg) || (lhs.m_mass != rhs.m_mass) || + (lhs.m_chargeType != rhs.m_chargeType); + } +}; + +} // namespace Acts diff --git a/Core/include/Acts/EventData/Measurement.hpp b/Core/include/Acts/EventData/Measurement.hpp index 8c13b204614..817a6938bb7 100644 --- a/Core/include/Acts/EventData/Measurement.hpp +++ b/Core/include/Acts/EventData/Measurement.hpp @@ -54,7 +54,7 @@ class Measurement { /// Vector type containing for measured parameter values. using ParametersVector = ActsVector; /// Matrix type for the measurement covariance. - using CovarianceMatrix = ActsSymMatrix; + using CovarianceMatrix = ActsSquareMatrix; /// Vector type containing all parameters in the same space. using FullParametersVector = ActsVector; using ProjectionMatrix = ActsMatrix; diff --git a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp index f1b292911c2..2182a6b2578 100644 --- a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Surfaces/Surface.hpp" #include @@ -21,7 +21,7 @@ namespace Acts { /// This class is only a light wrapper around a surface and a vector of /// parameters. Its main purpose is to provide many constructors for the /// underlying vector. Most accessors are generated from the -/// SingleBoundTrackParameters equivalent and thus may be expensive +/// GenericBoundTrackParameters equivalent and thus may be expensive /// @tparam charge_t Helper type to interpret the particle charge/momentum /// @note This class holds shared ownership on its reference surface. /// @note The accessors for parameters, covariance, position, etc. @@ -31,9 +31,9 @@ namespace Acts { /// TODO Add constructor from range and projector maybe? template class MultiComponentBoundTrackParameters { - using SingleParameters = SingleBoundTrackParameters; + using SingleParameters = GenericBoundTrackParameters; - std::vector>> + std::vector>> m_components; std::shared_ptr m_surface; @@ -73,9 +73,10 @@ class MultiComponentBoundTrackParameters { const std::vector>& cmps, ActsScalar q) : m_surface(std::move(surface)), m_chargeInterpreter(std::abs(q)) { - static_assert(std::is_same_v || - std::is_same_v, covariance_t>); - if constexpr (std::is_same_v) { + static_assert( + std::is_same_v || + std::is_same_v, covariance_t>); + if constexpr (std::is_same_v) { for (const auto& [weight, params, cov] : cmps) { m_components.push_back({weight, params, cov}); } @@ -91,9 +92,10 @@ class MultiComponentBoundTrackParameters { std::shared_ptr surface, const std::vector>& cmps) : m_surface(std::move(surface)) { - static_assert(std::is_same_v || - std::is_same_v, covariance_t>); - if constexpr (std::is_same_v) { + static_assert( + std::is_same_v || + std::is_same_v, covariance_t>); + if constexpr (std::is_same_v) { for (const auto& [weight, params, cov] : cmps) { m_components.push_back({weight, params, cov}); } @@ -117,7 +119,7 @@ class MultiComponentBoundTrackParameters { /// parameter. MultiComponentBoundTrackParameters( std::shared_ptr surface, const BoundVector& params, - ActsScalar q, std::optional cov = std::nullopt) + ActsScalar q, std::optional cov = std::nullopt) : m_surface(std::move(surface)), m_chargeInterpreter(std::abs(q)) { m_components.push_back({1., params, std::move(cov)}); } @@ -134,7 +136,7 @@ class MultiComponentBoundTrackParameters { std::enable_if_t, int> = 0> MultiComponentBoundTrackParameters( std::shared_ptr surface, const BoundVector& params, - std::optional cov = std::nullopt) + std::optional cov = std::nullopt) : m_surface(std::move(surface)) { m_components.push_back({1., params, std::move(cov)}); } @@ -157,13 +159,13 @@ class MultiComponentBoundTrackParameters { /// Reference surface onto which the parameters are bound. const Surface& referenceSurface() const { return *m_surface; } - /// Get the weight and a SingleBoundTrackParameters object for one component + /// Get the weight and a GenericBoundTrackParameters object for one component std::pair operator[](std::size_t i) const { return std::make_pair( std::get(m_components[i]), SingleParameters( m_surface, std::get(m_components[i]), - std::get>(m_components[i]))); + std::get>(m_components[i]))); } /// Parameters vector. @@ -209,8 +211,8 @@ class MultiComponentBoundTrackParameters { /// Unit direction three-vector, i.e. the normalized momentum /// three-vector. - Vector3 unitDirection() const { - return reduce([](const SingleParameters& p) { return p.unitDirection(); }) + Vector3 direction() const { + return reduce([](const SingleParameters& p) { return p.direction(); }) .normalized(); } diff --git a/Core/include/Acts/EventData/MultiTrajectoryBackendConcept.hpp b/Core/include/Acts/EventData/MultiTrajectoryBackendConcept.hpp index 2e9c4378c40..51834c37ecd 100644 --- a/Core/include/Acts/EventData/MultiTrajectoryBackendConcept.hpp +++ b/Core/include/Acts/EventData/MultiTrajectoryBackendConcept.hpp @@ -54,7 +54,7 @@ concept CommonMultiTrajectoryBackend = requires(const T& cv, HashedString key, { cv.template measurementCovariance_impl<2>(istate) - } -> std::same_as>>; + } -> std::same_as>>; { cv.has_impl(key, istate) } -> std::same_as; @@ -80,7 +80,7 @@ concept ConstMultiTrajectoryBackend = CommonMultiTrajectoryBackend && { v.template measurementCovariance_impl<2>(istate) - } -> std::same_as>>; + } -> std::same_as>>; }; template @@ -100,7 +100,7 @@ concept MutableMultiTrajectoryBackend = CommonMultiTrajectoryBackend && { v.template measurementCovariance_impl<2>(istate) - } -> std::same_as>>; + } -> std::same_as>>; { v.addTrackState_impl() } -> std::same_as; diff --git a/Core/include/Acts/EventData/NeutralTrackParameters.hpp b/Core/include/Acts/EventData/NeutralTrackParameters.hpp deleted file mode 100644 index 06f8cbc19ea..00000000000 --- a/Core/include/Acts/EventData/NeutralTrackParameters.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2016-2020 CERN for the benefit of the Acts project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" -#include "Acts/EventData/SingleFreeTrackParameters.hpp" - -namespace Acts { - -extern template class SingleBoundTrackParameters; -extern template class SingleCurvilinearTrackParameters; -extern template class SingleFreeTrackParameters; - -using NeutralBoundTrackParameters = SingleBoundTrackParameters; -using NeutralCurvilinearTrackParameters = - SingleCurvilinearTrackParameters; -using NeutralFreeTrackParameters = SingleFreeTrackParameters; - -} // namespace Acts diff --git a/Core/include/Acts/EventData/ParticleHypothesis.hpp b/Core/include/Acts/EventData/ParticleHypothesis.hpp new file mode 100644 index 00000000000..198b7d625b9 --- /dev/null +++ b/Core/include/Acts/EventData/ParticleHypothesis.hpp @@ -0,0 +1,143 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/ParticleData.hpp" +#include "Acts/Definitions/PdgParticle.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericParticleHypothesis.hpp" + +namespace Acts { + +// TODO In principle the factory methods could provide a reference to a static +// instance which would avoid copying the particle hypothesis and potentially +// save some memory. But constexpr+static seems to require C++2b extension. + +/// Specialized particle hypothesis for singly charged particles. +/// +/// @note This serves as a factory for common singly charge particles. +class SinglyChargedParticleHypothesis + : public GenericParticleHypothesis { + public: + constexpr SinglyChargedParticleHypothesis(PdgParticle absPdg, float mass) + : GenericParticleHypothesis(absPdg, mass, {}) {} + SinglyChargedParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr SinglyChargedParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static SinglyChargedParticleHypothesis muon() { + return SinglyChargedParticleHypothesis(PdgParticle::eMuon); + } + static SinglyChargedParticleHypothesis pion() { + return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); + } + static SinglyChargedParticleHypothesis electron() { + return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); + } +}; + +/// Specialized particle hypothesis for neutral particles. +/// +/// @note This serves as a factory for common neutral particles. +class NeutralParticleHypothesis : public GenericParticleHypothesis { + public: + constexpr NeutralParticleHypothesis(PdgParticle absPdg, float mass) + : GenericParticleHypothesis(absPdg, mass, {}) {} + NeutralParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr NeutralParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static NeutralParticleHypothesis photon() { + return NeutralParticleHypothesis(PdgParticle::eGamma); + } + static NeutralParticleHypothesis pion0() { + return NeutralParticleHypothesis(PdgParticle::ePionZero); + } +}; + +/// Specialized particle hypothesis for non-neutral particles. +/// +/// @note This serves as a factory for common non-neutral particles. +class NonNeutralChargedParticleHypothesis + : public GenericParticleHypothesis { + public: + constexpr NonNeutralChargedParticleHypothesis(PdgParticle absPdg, float mass, + NonNeutralCharge chargeType) + : GenericParticleHypothesis(absPdg, mass, chargeType) {} + NonNeutralChargedParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr NonNeutralChargedParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static NonNeutralChargedParticleHypothesis muon() { + return SinglyChargedParticleHypothesis::muon(); + } + static NonNeutralChargedParticleHypothesis pion() { + return SinglyChargedParticleHypothesis::pion(); + } + static NonNeutralChargedParticleHypothesis electron() { + return SinglyChargedParticleHypothesis::electron(); + } + + static NonNeutralChargedParticleHypothesis pionLike(float absQ) { + return NonNeutralChargedParticleHypothesis(pion().absolutePdg(), + pion().mass(), absQ); + } +}; + +/// Specialized particle hypothesis for any kind of charged particles. +/// +/// @note This serves as a factory for common particles with any kind of charge. +class ParticleHypothesis : public GenericParticleHypothesis { + public: + constexpr ParticleHypothesis(PdgParticle absPdg, float mass, + AnyCharge chargeType) + : GenericParticleHypothesis(absPdg, mass, chargeType) {} + ParticleHypothesis(PdgParticle absPdg) : GenericParticleHypothesis(absPdg) {} + + template + constexpr ParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static ParticleHypothesis muon() { + return SinglyChargedParticleHypothesis::muon(); + } + static ParticleHypothesis pion() { + return SinglyChargedParticleHypothesis::pion(); + } + static ParticleHypothesis electron() { + return SinglyChargedParticleHypothesis::electron(); + } + + static ParticleHypothesis photon() { + return NeutralParticleHypothesis::photon(); + } + static ParticleHypothesis pion0() { + return NeutralParticleHypothesis::pion0(); + } + + static ParticleHypothesis pionLike(float absQ) { + return ParticleHypothesis(pion().absolutePdg(), pion().mass(), absQ); + } +}; + +} // namespace Acts diff --git a/Core/include/Acts/EventData/SourceLink.hpp b/Core/include/Acts/EventData/SourceLink.hpp index 4792217ea6f..4c98c4a15a2 100644 --- a/Core/include/Acts/EventData/SourceLink.hpp +++ b/Core/include/Acts/EventData/SourceLink.hpp @@ -10,6 +10,7 @@ #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Utilities/Any.hpp" +#include "Acts/Utilities/Delegate.hpp" #include "Acts/Utilities/TypeTraits.hpp" #include @@ -17,50 +18,28 @@ #include #include -namespace Acts { +#if !defined(ACTS_SOURCELINK_SBO_SIZE) +// Do not set this in code, use CMake! +#define ACTS_SOURCELINK_SBO_SIZE 16 +#endif -namespace detail_sl { -template -using geometry_id_t = decltype(std::declval().geometryId()); -} // namespace detail_sl +namespace Acts { class SourceLink final { - using any_type = AnyBase<16>; + using any_type = AnyBase; public: - /// Getter for the geometry identifier - /// @return The GeometryIdentifier - constexpr GeometryIdentifier geometryId() const { return m_geometryId; } - SourceLink(const SourceLink& other) = default; SourceLink(SourceLink&& other) = default; SourceLink& operator=(const SourceLink& other) = default; SourceLink& operator=(SourceLink&& other) = default; - /// Constructor from source link and explicit geometry id - /// @tparam T The source link type - /// @param id The geometry identifier - /// @param upstream The upstream source link to store - template , SourceLink>>> - explicit SourceLink(GeometryIdentifier id, T&& upstream) - : m_geometryId{id}, m_upstream{std::move(upstream)} { - static_assert(!std::is_same_v, SourceLink>, - "Cannot wrap SourceLink in SourceLink"); - } - - /// Constructor from source link only, geometry identifier is determined - /// automatically + /// Constructor from concrete sourcelink /// @tparam T The source link type /// @param upstream The upstream source link to store template && !std::is_same_v, SourceLink>>> - explicit SourceLink(T&& upstream) : m_geometryId{upstream.geometryId()} { - static_assert( - std::is_same_v, GeometryIdentifier>, - "geometryId method does not return a geometry id type"); - + explicit SourceLink(T&& upstream) { static_assert(!std::is_same_v, SourceLink>, "Cannot wrap SourceLink in SourceLink"); @@ -88,7 +67,6 @@ class SourceLink final { } private: - GeometryIdentifier m_geometryId{}; any_type m_upstream{}; }; @@ -126,4 +104,7 @@ struct SourceLinkAdapterIterator { BaseIterator m_iterator; }; +/// Delegate to unpack the surface associated with a source link +using SourceLinkSurfaceAccessor = Delegate; + } // namespace Acts diff --git a/Core/include/Acts/EventData/TrackParameters.hpp b/Core/include/Acts/EventData/TrackParameters.hpp index ad9e0924281..ad8f1b90ee1 100644 --- a/Core/include/Acts/EventData/TrackParameters.hpp +++ b/Core/include/Acts/EventData/TrackParameters.hpp @@ -9,19 +9,20 @@ #pragma once #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" -#include "Acts/EventData/SingleFreeTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericFreeTrackParameters.hpp" namespace Acts { -extern template class SingleBoundTrackParameters; -extern template class SingleCurvilinearTrackParameters; -extern template class SingleFreeTrackParameters; - -using BoundTrackParameters = SingleBoundTrackParameters; +using BoundTrackParameters = GenericBoundTrackParameters; using CurvilinearTrackParameters = - SingleCurvilinearTrackParameters; -using FreeTrackParameters = SingleFreeTrackParameters; + GenericCurvilinearTrackParameters; +using FreeTrackParameters = GenericFreeTrackParameters; + +using NeutralBoundTrackParameters = GenericBoundTrackParameters; +using NeutralCurvilinearTrackParameters = + GenericCurvilinearTrackParameters; +using NeutralFreeTrackParameters = GenericFreeTrackParameters; } // namespace Acts diff --git a/Core/include/Acts/EventData/TrackParametersConcept.hpp b/Core/include/Acts/EventData/TrackParametersConcept.hpp index 7f558f1ca57..02d99ec6270 100644 --- a/Core/include/Acts/EventData/TrackParametersConcept.hpp +++ b/Core/include/Acts/EventData/TrackParametersConcept.hpp @@ -9,9 +9,11 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/TypeTraits.hpp" +#include #include namespace Acts { @@ -45,7 +47,7 @@ using ReturnTypePosition = decltype(std::declval().position()); template using ReturnTypeTime = decltype(std::declval().time()); template -using ReturnTypeUnitDirection = decltype(std::declval().unitDirection()); +using ReturnTypeDirection = decltype(std::declval().direction()); template using ReturnTypeAbsoluteMomentum = decltype(std::declval().absoluteMomentum()); @@ -69,15 +71,15 @@ struct BoundTrackParametersConceptImpl { std::is_convertible_v, BoundVector>; constexpr static bool hasMethodCovariance = std::is_convertible_v, - std::optional>; + std::optional>; constexpr static bool hasMethodFourPositionFromContext = identical_to; constexpr static bool hasMethodPositionFromContext = identical_to; constexpr static bool hasMethodTime = identical_to, ReturnTypeTime, const T>; - constexpr static bool hasMethodUnitDirection = - identical_to; + constexpr static bool hasMethodDirection = + identical_to; constexpr static bool hasMethodAbsoluteMomentum = identical_to, ReturnTypeAbsoluteMomentum, const T>; constexpr static bool hasMethodCharge = @@ -96,8 +98,7 @@ struct BoundTrackParametersConceptImpl { static_assert(hasMethodPositionFromContext, "Missing or invalid 'position' method"); static_assert(hasMethodTime, "Missing or invalid 'time' method"); - static_assert(hasMethodUnitDirection, - "Missing or invalid 'unitDirection' method"); + static_assert(hasMethodDirection, "Missing or invalid 'direction' method"); static_assert(hasMethodAbsoluteMomentum, "Missing or invalid 'absoluteMomentum' method"); static_assert(hasMethodCharge, "Missing or invalid 'charge' method"); @@ -108,7 +109,7 @@ struct BoundTrackParametersConceptImpl { require; }; @@ -126,15 +127,15 @@ struct FreeTrackParametersConceptImpl { std::is_convertible_v, FreeVector>; constexpr static bool hasMethodCovariance = std::is_convertible_v, - std::optional>; + std::optional>; constexpr static bool hasMethodFourPosition = identical_to; constexpr static bool hasMethodPosition = identical_to; constexpr static bool hasMethodTime = identical_to, ReturnTypeTime, const T>; - constexpr static bool hasMethodUnitDirection = - identical_to; + constexpr static bool hasMethodDirection = + identical_to; constexpr static bool hasMethodAbsoluteMomentum = identical_to, ReturnTypeAbsoluteMomentum, const T>; constexpr static bool hasMethodCharge = @@ -150,8 +151,7 @@ struct FreeTrackParametersConceptImpl { "Missing or invalid 'fourPosition' method"); static_assert(hasMethodPosition, "Missing or invalid 'position' method"); static_assert(hasMethodTime, "Missing or invalid 'time' method"); - static_assert(hasMethodUnitDirection, - "Missing or invalid 'unitDirection' method"); + static_assert(hasMethodDirection, "Missing or invalid 'direction' method"); static_assert(hasMethodAbsoluteMomentum, "Missing or invalid 'absoluteMomentum' method"); static_assert(hasMethodCharge, "Missing or invalid 'charge' method"); @@ -159,7 +159,7 @@ struct FreeTrackParametersConceptImpl { constexpr static bool value = require; }; diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index e1d1d2c4ae7..9ea5ae13620 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -376,14 +376,14 @@ class TrackProxy { /// Get a unit vector along the track direction at the reference surface /// @return The direction unit vector - Vector3 unitDirection() const { - return makeDirectionUnitFromPhiTheta(phi(), theta()); + Vector3 direction() const { + return makeDirectionFromPhiTheta(phi(), theta()); } /// Get the global momentum vector /// @return the global momentum vector Vector3 momentum() const { - return absoluteMomentum() * unitDirection(); + return absoluteMomentum() * direction(); } /// Get a range over the track states of this track. Return value is diff --git a/Core/include/Acts/EventData/TrackStateProxyConcept.hpp b/Core/include/Acts/EventData/TrackStateProxyConcept.hpp index fe18664b68d..bb25c8fe3c9 100644 --- a/Core/include/Acts/EventData/TrackStateProxyConcept.hpp +++ b/Core/include/Acts/EventData/TrackStateProxyConcept.hpp @@ -33,10 +33,10 @@ using ConstParameters = Eigen::Map; using ConstCovariance = Eigen::Map; using Measurement = Eigen::Map>; -using MeasurementCovariance = Eigen::Map>; +using MeasurementCovariance = Eigen::Map>; using ConstMeasurement = Eigen::Map>; -using ConstMeasurementCovariance = Eigen::Map>; +using ConstMeasurementCovariance = Eigen::Map>; using DynamicMeasurement = Eigen::Map +#include #include #include +#include +#include + namespace Acts { /// @enum TrackStateFlag diff --git a/Core/include/Acts/EventData/Utils.hpp b/Core/include/Acts/EventData/Utils.hpp index 71f4e620ce7..79a410ba53c 100644 --- a/Core/include/Acts/EventData/Utils.hpp +++ b/Core/include/Acts/EventData/Utils.hpp @@ -8,6 +8,8 @@ #pragma once +#include + namespace Acts::detail { template